fix: where HTTP node URL, JSON text, and raw text template rendering could not find the corresponding rendering variables (#745)
This commit is contained in:
@@ -23,10 +23,12 @@ import (
|
||||
|
||||
"github.com/cloudwego/eino/compose"
|
||||
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/canvas/convert"
|
||||
"github.com/coze-dev/coze-studio/backend/domain/workflow/internal/schema"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/crypto"
|
||||
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
|
||||
)
|
||||
|
||||
var extractBracesRegexp = regexp.MustCompile(`\{\{(.*?)}}`)
|
||||
@@ -43,9 +45,10 @@ func extractBracesContent(s string) []string {
|
||||
}
|
||||
|
||||
type ImplicitNodeDependency struct {
|
||||
NodeID string
|
||||
FieldPath compose.FieldPath
|
||||
TypeInfo *vo.TypeInfo
|
||||
NodeID string
|
||||
FieldPath compose.FieldPath
|
||||
TypeInfo *vo.TypeInfo
|
||||
IsIntermediateVar bool
|
||||
}
|
||||
|
||||
func extractImplicitDependency(node *vo.Node, canvas *vo.Canvas) ([]*ImplicitNodeDependency, error) {
|
||||
@@ -84,14 +87,24 @@ func extractImplicitDependency(node *vo.Node, canvas *vo.Canvas) ([]*ImplicitNod
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if node.Data.Inputs.Body.BodyType == string(BodyTypeRawText) {
|
||||
rawTextVars := extractBracesContent(node.Data.Inputs.Body.BodyData.Json)
|
||||
rawTextVars := extractBracesContent(node.Data.Inputs.Body.BodyData.RawText)
|
||||
err = extractDependenciesFromVars(rawTextVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
nodeID2ParentID := make(map[string]string)
|
||||
for _, n := range canvas.Nodes {
|
||||
if len(n.Blocks) > 0 {
|
||||
for _, block := range n.Blocks {
|
||||
nodeID2ParentID[block.ID] = n.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nodeFinder func(nodes []*vo.Node, nodeID string) *vo.Node
|
||||
nodeFinder = func(nodes []*vo.Node, nodeID string) *vo.Node {
|
||||
for i := range nodes {
|
||||
@@ -111,23 +124,78 @@ func extractImplicitDependency(node *vo.Node, canvas *vo.Canvas) ([]*ImplicitNod
|
||||
if fNode == nil {
|
||||
continue
|
||||
}
|
||||
tInfoMap := make(map[string]*vo.TypeInfo, len(node.Data.Outputs))
|
||||
for _, vAny := range fNode.Data.Outputs {
|
||||
v, err := vo.ParseVariable(vAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
intermediateVars := map[string]bool{}
|
||||
tInfoMap := make(map[string]*vo.TypeInfo)
|
||||
parentID, ok := nodeID2ParentID[node.ID]
|
||||
if ok && parentID == fNode.ID {
|
||||
// when referencing a composite node, the index field type is available by default
|
||||
tInfoMap["index"] = &vo.TypeInfo{
|
||||
Type: vo.DataTypeInteger,
|
||||
}
|
||||
tInfo, err := convert.CanvasVariableToTypeInfo(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, p := range fNode.Data.Inputs.InputParameters {
|
||||
tInfo, err := convert.CanvasBlockInputToTypeInfo(p.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tInfo.Type != vo.DataTypeArray {
|
||||
return nil, fmt.Errorf("when referencing a composite node, the input parameter must be an array type")
|
||||
}
|
||||
tInfoMap[p.Name] = tInfo.ElemTypeInfo
|
||||
}
|
||||
|
||||
if fNode.Data.Inputs.Loop != nil {
|
||||
for _, vAny := range fNode.Data.Inputs.Loop.VariableParameters {
|
||||
v, err := convert.ParseParam(vAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfo, err := convert.CanvasBlockInputToTypeInfo(v.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfoMap[v.Name] = tInfo
|
||||
|
||||
intermediateVars[v.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
} else if entity.NodeTypeMetas[entity.IDStrToNodeType(fNode.Type)].IsComposite {
|
||||
for _, vAny := range fNode.Data.Outputs {
|
||||
v, err := convert.ParseParam(vAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfo, err := convert.CanvasBlockInputToTypeInfo(v.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfoMap[v.Name] = tInfo
|
||||
}
|
||||
} else {
|
||||
for _, vAny := range fNode.Data.Outputs {
|
||||
v, err := vo.ParseVariable(vAny)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfo, err := convert.CanvasVariableToTypeInfo(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfoMap[v.Name] = tInfo
|
||||
}
|
||||
tInfoMap[v.Name] = tInfo
|
||||
}
|
||||
|
||||
tInfo, ok := getTypeInfoByPath(ds.FieldPath[0], ds.FieldPath[1:], tInfoMap)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot find type info for dependency: %s", ds.FieldPath)
|
||||
}
|
||||
|
||||
ds.TypeInfo = tInfo
|
||||
if len(intermediateVars) > 0 {
|
||||
ds.IsIntermediateVar = intermediateVars[strings.Join(ds.FieldPath, ".")]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return dependencies, nil
|
||||
@@ -152,7 +220,7 @@ var globalVariableRegex = regexp.MustCompile(`global_variable_\w+\s*\["(.*?)"]`)
|
||||
func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, implicitNodeDependencies []*ImplicitNodeDependency) (err error) {
|
||||
inputs := n.Data.Inputs
|
||||
implicitPathVars := make(map[string]bool)
|
||||
addImplicitVarsSources := func(prefix string, vars []string) error {
|
||||
addImplicitVarsSources := func(prefix string, vars []string, parent *vo.Node) error {
|
||||
for _, v := range vars {
|
||||
if strings.HasPrefix(v, "block_output_") {
|
||||
paths := strings.Split(strings.TrimPrefix(v, "block_output_"), ".")
|
||||
@@ -167,7 +235,8 @@ func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, impl
|
||||
}
|
||||
implicitPathVars[pathValue] = true
|
||||
ns.SetInputType(pathValue, dep.TypeInfo)
|
||||
ns.AddInputSource(&vo.FieldInfo{
|
||||
|
||||
filedInfo := &vo.FieldInfo{
|
||||
Path: []string{pathValue},
|
||||
Source: vo.FieldSource{
|
||||
Ref: &vo.Reference{
|
||||
@@ -175,7 +244,12 @@ func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, impl
|
||||
FromPath: dep.FieldPath,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if dep.IsIntermediateVar && parent != nil {
|
||||
filedInfo.Source.Ref.VariableType = ptr.Of(vo.ParentIntermediate)
|
||||
}
|
||||
ns.AddInputSource(filedInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,7 +289,7 @@ func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, impl
|
||||
}
|
||||
|
||||
urlVars := extractBracesContent(inputs.APIInfo.URL)
|
||||
err = addImplicitVarsSources("__apiInfo_url_", urlVars)
|
||||
err = addImplicitVarsSources("__apiInfo_url_", urlVars, n.Parent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -304,13 +378,13 @@ func setHttpRequesterInputsForNodeSchema(n *vo.Node, ns *schema.NodeSchema, impl
|
||||
ns.AddInputSource(sources...)
|
||||
case BodyTypeJSON:
|
||||
jsonVars := extractBracesContent(inputs.Body.BodyData.Json)
|
||||
err = addImplicitVarsSources("__body_bodyData_json_", jsonVars)
|
||||
err = addImplicitVarsSources("__body_bodyData_json_", jsonVars, n.Parent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case BodyTypeRawText:
|
||||
rawTextVars := extractBracesContent(inputs.Body.BodyData.RawText)
|
||||
err = addImplicitVarsSources("__body_bodyData_rawText_", rawTextVars)
|
||||
err = addImplicitVarsSources("__body_bodyData_rawText_", rawTextVars, n.Parent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -360,6 +360,11 @@ type HTTPRequester struct {
|
||||
md5FieldMapping MD5FieldMapping
|
||||
}
|
||||
|
||||
func adaptTemplate(template string) string {
|
||||
return globalVariableReplaceRegexp.ReplaceAllString(template, "global_variable_$1.$2")
|
||||
|
||||
}
|
||||
|
||||
func (hg *HTTPRequester) Invoke(ctx context.Context, input map[string]any) (output map[string]any, err error) {
|
||||
var (
|
||||
req = &Request{}
|
||||
@@ -380,7 +385,7 @@ func (hg *HTTPRequester) Invoke(ctx context.Context, input map[string]any) (outp
|
||||
Header: http.Header{},
|
||||
}
|
||||
|
||||
httpURL, err := nodes.TemplateRender(hg.urlConfig.Tpl, req.URLVars)
|
||||
httpURL, err := nodes.TemplateRender(adaptTemplate(hg.urlConfig.Tpl), req.URLVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -524,7 +529,7 @@ func (b *BodyConfig) getBodyAndContentType(ctx context.Context, req *Request) (i
|
||||
|
||||
switch b.BodyType {
|
||||
case BodyTypeJSON:
|
||||
jsonString, err := nodes.TemplateRender(b.TextJsonConfig.Tpl, req.JsonVars)
|
||||
jsonString, err := nodes.TemplateRender(adaptTemplate(b.TextJsonConfig.Tpl), req.JsonVars)
|
||||
if err != nil {
|
||||
return nil, contentType, err
|
||||
}
|
||||
@@ -539,7 +544,7 @@ func (b *BodyConfig) getBodyAndContentType(ctx context.Context, req *Request) (i
|
||||
body = strings.NewReader(form.Encode())
|
||||
contentType = ContentTypeFormURLEncoded
|
||||
case BodyTypeRawText:
|
||||
textString, err := nodes.TemplateRender(b.TextPlainConfig.Tpl, req.TextPlainVars)
|
||||
textString, err := nodes.TemplateRender(adaptTemplate(b.TextPlainConfig.Tpl), req.TextPlainVars)
|
||||
if err != nil {
|
||||
return nil, contentType, err
|
||||
}
|
||||
@@ -632,7 +637,7 @@ func (hg *HTTPRequester) ToCallbackInput(_ context.Context, input map[string]any
|
||||
result := make(map[string]any)
|
||||
result["method"] = hg.method
|
||||
|
||||
u, err := nodes.TemplateRender(hg.urlConfig.Tpl, request.URLVars)
|
||||
u, err := nodes.TemplateRender(adaptTemplate(hg.urlConfig.Tpl), request.URLVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -666,7 +671,7 @@ func (hg *HTTPRequester) ToCallbackInput(_ context.Context, input map[string]any
|
||||
result["body"] = nil
|
||||
switch hg.bodyConfig.BodyType {
|
||||
case BodyTypeJSON:
|
||||
js, err := nodes.TemplateRender(hg.bodyConfig.TextJsonConfig.Tpl, request.JsonVars)
|
||||
js, err := nodes.TemplateRender(adaptTemplate(hg.bodyConfig.TextJsonConfig.Tpl), request.JsonVars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -677,7 +682,7 @@ func (hg *HTTPRequester) ToCallbackInput(_ context.Context, input map[string]any
|
||||
}
|
||||
result["body"] = ret
|
||||
case BodyTypeRawText:
|
||||
tx, err := nodes.TemplateRender(hg.bodyConfig.TextPlainConfig.Tpl, request.TextPlainVars)
|
||||
tx, err := nodes.TemplateRender(adaptTemplate(hg.bodyConfig.TextPlainConfig.Tpl), request.TextPlainVars)
|
||||
if err != nil {
|
||||
|
||||
return nil, err
|
||||
@@ -729,7 +734,7 @@ func (hg *HTTPRequester) parserToRequest(input map[string]any) (*Request, error)
|
||||
if strings.HasPrefix(urlKey, "global_variable_") {
|
||||
urlKey = globalVariableReplaceRegexp.ReplaceAllString(urlKey, "global_variable_$1.$2")
|
||||
}
|
||||
nodes.SetMapValue(request.URLVars, strings.Split(urlKey, "."), value.(string))
|
||||
nodes.SetMapValue(request.URLVars, strings.Split(urlKey, "."), value)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(key, headersPrefix) {
|
||||
@@ -777,7 +782,6 @@ func (hg *HTTPRequester) parserToRequest(input map[string]any) (*Request, error)
|
||||
if formDataKey, ok := hg.md5FieldMapping.BodyMD5Mapping[formDataMd5Key]; ok {
|
||||
request.FormDataVars[formDataKey] = value.(string)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if strings.HasPrefix(bodyKey, bodyFormURLEncodedPrefix) {
|
||||
|
||||
Reference in New Issue
Block a user