Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 36 additions & 23 deletions backend/application/workflow/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -3631,7 +3631,25 @@ func toWorkflowAPIParameterAssistType(ty vo.FileSubType) workflow.AssistParamete
}
}

func toVariableSlice(params []*workflow.APIParameter) ([]*vo.Variable, error) {
if len(params) == 0 {
return nil, nil
}
res := make([]*vo.Variable, 0, len(params))
for _, p := range params {
v, err := toVariable(p)
if err != nil {
return nil, err
}
res = append(res, v)
}
return res, nil
}

func toVariable(p *workflow.APIParameter) (*vo.Variable, error) {
if p == nil {
return nil, nil
}
v := &vo.Variable{
Name: p.Name,
Description: p.Desc,
Expand All @@ -3653,38 +3671,33 @@ func toVariable(p *workflow.APIParameter) (*vo.Variable, error) {
v.Type = vo.VariableTypeBoolean
case workflow.ParameterType_Array:
v.Type = vo.VariableTypeList
if len(p.SubParameters) == 1 && p.SubType != nil && *p.SubType != workflow.ParameterType_Object {
av, err := toVariable(p.SubParameters[0])
if p.SubType == nil {
return nil, fmt.Errorf("array parameter '%s' is missing a SubType", p.Name)
}
// The schema of a list variable is a single variable describing the items.
itemSchema := &vo.Variable{
Type: vo.VariableType(strings.ToLower(p.SubType.String())),
}
// If the items in the array are objects, describe their structure.
if *p.SubType == workflow.ParameterType_Object {
itemFields, err := toVariableSlice(p.SubParameters)
if err != nil {
return nil, err
}
v.Schema = &av
itemSchema.Schema = itemFields
} else {
subVs := make([]any, 0)
for _, ap := range p.SubParameters {
av, err := toVariable(ap)
if err != nil {
return nil, err
}
subVs = append(subVs, av)
}
v.Schema = &vo.Variable{
Type: vo.VariableTypeObject,
Schema: subVs,
if len(p.SubParameters) > 0 && p.SubParameters[0].AssistType != nil {
itemSchema.AssistType = vo.AssistType(*p.SubParameters[0].AssistType)
}
}
v.Schema = itemSchema
case workflow.ParameterType_Object:
v.Type = vo.VariableTypeObject
vs := make([]*vo.Variable, 0)
for _, v := range p.SubParameters {
objV, err := toVariable(v)
if err != nil {
return nil, err
}
vs = append(vs, objV)

subVars, err := toVariableSlice(p.SubParameters)
if err != nil {
return nil, err
}
v.Schema = vs
v.Schema = subVars
default:
return nil, fmt.Errorf("unknown workflow api parameter type: %v", p.Type)
}
Expand Down
158 changes: 158 additions & 0 deletions backend/application/workflow/workflow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package workflow

import (
"fmt"
"strings"
"testing"

"github.com/coze-dev/coze-studio/backend/api/model/workflow"
"github.com/coze-dev/coze-studio/backend/domain/workflow/entity/vo"
"github.com/coze-dev/coze-studio/backend/pkg/lang/ptr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestToVariable(t *testing.T) {
fileAssistType := workflow.AssistParameterType_DEFAULT

testCases := []struct {
name string
input *workflow.APIParameter
expected *vo.Variable
expectErr bool
expectedErrAs any
}{
{
name: "Nil Input",
input: nil,
expected: nil,
},
{
name: "Simple String",
input: &workflow.APIParameter{
Name: "prompt", Type: workflow.ParameterType_String, IsRequired: true,
},
expected: &vo.Variable{
Name: "prompt", Type: vo.VariableTypeString, Required: true,
},
},
{
name: "Simple Object",
input: &workflow.APIParameter{
Name: "user",
Type: workflow.ParameterType_Object,
SubParameters: []*workflow.APIParameter{
{Name: "name", Type: workflow.ParameterType_String},
{Name: "age", Type: workflow.ParameterType_Integer},
},
},
expected: &vo.Variable{
Name: "user",
Type: vo.VariableTypeObject,
Schema: []*vo.Variable{
{Name: "name", Type: vo.VariableTypeString},
{Name: "age", Type: vo.VariableTypeInteger},
},
},
},
{
name: "Array of Objects",
input: &workflow.APIParameter{
Name: "items",
Type: workflow.ParameterType_Array,
SubType: ptr.Of(workflow.ParameterType_Object),
SubParameters: []*workflow.APIParameter{
{Name: "id", Type: workflow.ParameterType_String},
{Name: "price", Type: workflow.ParameterType_Number},
},
},
expected: &vo.Variable{
Name: "items",
Type: vo.VariableTypeList,
Schema: &vo.Variable{
Type: vo.VariableTypeObject,
Schema: []*vo.Variable{
{Name: "id", Type: vo.VariableTypeString},
{Name: "price", Type: vo.VariableTypeFloat},
},
},
},
},
{
name: "Array of Primitives (File)",
input: &workflow.APIParameter{
Name: "attachments",
Type: workflow.ParameterType_Array,
SubType: ptr.Of(workflow.ParameterType_String),
SubParameters: []*workflow.APIParameter{
{AssistType: &fileAssistType},
},
},
expected: &vo.Variable{
Name: "attachments",
Type: vo.VariableTypeList,
Schema: &vo.Variable{
Type: vo.VariableTypeString,
AssistType: vo.AssistType(fileAssistType),
},
},
},
{
name: "Array of Primitives (String)",
input: &workflow.APIParameter{
Name: "tags",
Type: workflow.ParameterType_Array,
SubType: ptr.Of(workflow.ParameterType_String),
},
expected: &vo.Variable{
Name: "tags",
Type: vo.VariableTypeList,
Schema: &vo.Variable{
Type: vo.VariableTypeString,
},
},
},
{
name: "Array with missing SubType",
input: &workflow.APIParameter{
Name: "bad_array",
Type: workflow.ParameterType_Array,
},
expectErr: true,
expectedErrAs: "missing a SubType",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual, err := toVariable(tc.input)

if tc.expectErr {
require.Error(t, err)
if tc.expectedErrAs != nil {
assert.True(t, strings.Contains(err.Error(), fmt.Sprint(tc.expectedErrAs)))
}
return
}

require.NoError(t, err)
assert.Equal(t, tc.expected, actual)
})
}
}
10 changes: 4 additions & 6 deletions backend/crossdomain/impl/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,31 +571,29 @@ func toWorkflowAPIParameter(parameter *common.APIParameter) *workflow3.APIParame
if parameter.SubType != nil {
p.SubType = ptr.Of(workflow3.ParameterType(*parameter.SubType))
}

if parameter.DefaultParamSource != nil {
p.DefaultParamSource = ptr.Of(workflow3.DefaultParamSource(*parameter.DefaultParamSource))
}
if parameter.AssistType != nil {
p.AssistType = ptr.Of(workflow3.AssistParameterType(*parameter.AssistType))
}

// Check if it's an array that needs unwrapping.
// Check if it's a specially wrapped array that needs unwrapping.
if parameter.Type == common.ParameterType_Array && len(parameter.SubParameters) == 1 && parameter.SubParameters[0].Name == "[Array Item]" {
arrayItem := parameter.SubParameters[0]
// The actual type of array elements is the type of the "[Array Item]".
p.SubType = ptr.Of(workflow3.ParameterType(arrayItem.Type))
// If the "[Array Item]" is an object, its sub-parameters become the array's sub-parameters.
// If the array elements are objects, their sub-parameters (fields) are lifted up.
if arrayItem.Type == common.ParameterType_Object {
p.SubParameters = make([]*workflow3.APIParameter, 0, len(arrayItem.SubParameters))
for _, subParam := range arrayItem.SubParameters {
p.SubParameters = append(p.SubParameters, toWorkflowAPIParameter(subParam))
}
} else {
// The array's SubType is the Type of the "[Array Item]".
p.SubParameters = make([]*workflow3.APIParameter, 0, 1)
p.SubParameters = append(p.SubParameters, toWorkflowAPIParameter(arrayItem))
p.SubParameters[0].Name = "" // Remove the "[Array Item]" name.
}
} else if len(parameter.SubParameters) > 0 { // A simple object or a non-wrapped array.
} else if len(parameter.SubParameters) > 0 {
p.SubParameters = make([]*workflow3.APIParameter, 0, len(parameter.SubParameters))
for _, subParam := range parameter.SubParameters {
p.SubParameters = append(p.SubParameters, toWorkflowAPIParameter(subParam))
Expand Down
Loading
Loading