/* * 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 vo import ( model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/modelmgr" "github.com/coze-dev/coze-studio/backend/api/model/workflow" "github.com/coze-dev/coze-studio/backend/pkg/i18n" "github.com/coze-dev/coze-studio/backend/pkg/lang/ternary" ) // Canvas is the definition of FRONTEND schema for a workflow. type Canvas struct { Nodes []*Node `json:"nodes"` Edges []*Edge `json:"edges"` Versions any `json:"versions"` } // Node represents a node within a workflow canvas. type Node struct { // ID is the unique node ID within the workflow. // In normal use cases, this ID is generated by frontend. // It does NOT need to be unique between parent workflow and sub workflows. // The Entry node and Exit node of a workflow always have fixed node IDs: 100001 and 900001. ID string `json:"id"` // Type is the Node Type of this node instance. // It corresponds to the string value of 'ID' field of NodeMeta. Type string `json:"type"` // Meta holds meta data used by frontend, such as the node's position within canvas. Meta any `json:"meta"` // Data holds the actual configurations of a node, such as inputs, outputs and exception handling. // It also holds exclusive configurations for different node types, such as LLM configurations. Data *Data `json:"data"` // Blocks holds the sub nodes of this node. // It is only used when the node type is Composite, such as NodeTypeBatch and NodeTypeLoop. Blocks []*Node `json:"blocks,omitempty"` // Edges are the connections between nodes. // Strictly corresponds to connections drawn on canvas. Edges []*Edge `json:"edges,omitempty"` // Version is the version of this node type's schema. Version string `json:"version,omitempty"` parent *Node // if this node is within a composite node, coze will set this. No need to set manually } func (n *Node) SetParent(parent *Node) { n.parent = parent } func (n *Node) Parent() *Node { return n.parent } type NodeMetaFE struct { Title string `json:"title,omitempty"` Description string `json:"description,omitempty"` Icon string `json:"icon,omitempty"` SubTitle string `json:"subTitle,omitempty"` MainColor string `json:"mainColor,omitempty"` } type Edge struct { SourceNodeID string `json:"sourceNodeID"` TargetNodeID string `json:"targetNodeID"` SourcePortID string `json:"sourcePortID,omitempty"` TargetPortID string `json:"targetPortID,omitempty"` } // Data holds the actual configuration of a Node. type Data struct { // Meta is the meta data of this node. Only used by frontend. Meta *NodeMetaFE `json:"nodeMeta,omitempty"` // Outputs configures the output fields and their types. // Outputs can be either []*Variable (most of the cases, just fields and types), // or []*Param (used by composite nodes as they need to refer to outputs of sub nodes) Outputs []any `json:"outputs,omitempty"` // Inputs configures ALL input information of a node, // including fixed input fields and dynamic input fields defined by user. Inputs *Inputs `json:"inputs,omitempty"` // Size configures the size of this node in frontend. // Only used by NodeTypeComment. Size any `json:"size,omitempty"` } type Inputs struct { // InputParameters are the fields defined by user for this particular node. InputParameters []*Param `json:"inputParameters"` // SettingOnError configures common error handling strategy for nodes. // NOTE: enable in frontend node's form first. SettingOnError *SettingOnError `json:"settingOnError,omitempty"` // NodeBatchInfo configures batch mode for nodes. // NOTE: not to be confused with NodeTypeBatch. NodeBatchInfo *NodeBatch `json:"batch,omitempty"` // LLMParam may be one of the LLMParam or IntentDetectorLLMParam or SimpleLLMParam. // Shared between most nodes requiring an ChatModel to function. LLMParam any `json:"llmParam,omitempty"` *OutputEmitter // exclusive configurations for NodeTypeEmitter and NodeTypeExit in Answer mode *Exit // exclusive configurations for NodeTypeExit *LLM // exclusive configurations for NodeTypeLLM *Loop // exclusive configurations for NodeTypeLoop *Selector // exclusive configurations for NodeTypeSelector *TextProcessor // exclusive configurations for NodeTypeTextProcessor *SubWorkflow // exclusive configurations for NodeTypeSubWorkflow *IntentDetector // exclusive configurations for NodeTypeIntentDetector *DatabaseNode // exclusive configurations for various Database nodes *HttpRequestNode // exclusive configurations for NodeTypeHTTPRequester *Knowledge // exclusive configurations for various Knowledge nodes *CodeRunner // exclusive configurations for NodeTypeCodeRunner *PluginAPIParam // exclusive configurations for NodeTypePlugin *VariableAggregator // exclusive configurations for NodeTypeVariableAggregator *VariableAssigner // exclusive configurations for NodeTypeVariableAssigner *QA // exclusive configurations for NodeTypeQuestionAnswer *Batch // exclusive configurations for NodeTypeBatch *Comment // exclusive configurations for NodeTypeComment *InputReceiver // exclusive configurations for NodeTypeInputReceiver } type OutputEmitter struct { Content *BlockInput `json:"content"` StreamingOutput bool `json:"streamingOutput,omitempty"` } type Exit struct { TerminatePlan *TerminatePlan `json:"terminatePlan,omitempty"` } type LLM struct { FCParam *FCParam `json:"fcParam,omitempty"` } type Loop struct { LoopType LoopType `json:"loopType,omitempty"` LoopCount *BlockInput `json:"loopCount,omitempty"` VariableParameters []*Param `json:"variableParameters,omitempty"` } type Selector struct { Branches []*struct { Condition struct { Logic LogicType `json:"logic"` Conditions []*Condition `json:"conditions"` } `json:"condition"` } `json:"branches,omitempty"` } type Comment struct { SchemaType string `json:"schemaType,omitempty"` Note any `json:"note,omitempty"` } type TextProcessor struct { Method TextProcessingMethod `json:"method,omitempty"` ConcatParams []*Param `json:"concatParams,omitempty"` SplitParams []*Param `json:"splitParams,omitempty"` } type VariableAssigner struct { VariableTypeMap map[string]any `json:"variableTypeMap,omitempty"` } type LLMParam = []*Param type IntentDetectorLLMParam = map[string]any type SimpleLLMParam struct { GenerationDiversity string `json:"generationDiversity"` MaxTokens int `json:"maxTokens"` ModelName string `json:"modelName"` ModelType int64 `json:"modelType"` ResponseFormat model.ResponseFormat `json:"responseFormat"` SystemPrompt string `json:"systemPrompt"` Temperature float64 `json:"temperature"` TopP float64 `json:"topP"` } type QA struct { AnswerType QAAnswerType `json:"answer_type"` Limit int `json:"limit,omitempty"` ExtractOutput bool `json:"extra_output,omitempty"` OptionType QAOptionType `json:"option_type,omitempty"` Options []struct { Name string `json:"name"` } `json:"options,omitempty"` Question string `json:"question,omitempty"` DynamicOption *BlockInput `json:"dynamic_option,omitempty"` } type QAAnswerType string const ( QAAnswerTypeOption QAAnswerType = "option" QAAnswerTypeText QAAnswerType = "text" ) type QAOptionType string const ( QAOptionTypeStatic QAOptionType = "static" QAOptionTypeDynamic QAOptionType = "dynamic" ) type RequestParameter struct { Name string } type FCParam struct { WorkflowFCParam *struct { WorkflowList []struct { WorkflowID string `json:"workflow_id"` WorkflowVersion string `json:"workflow_version"` PluginID string `json:"plugin_id"` PluginVersion string `json:"plugin_version"` IsDraft bool `json:"is_draft"` FCSetting *struct { RequestParameters []*workflow.APIParameter `json:"request_params"` ResponseParameters []*workflow.APIParameter `json:"response_params"` } `json:"fc_setting,omitempty"` } `json:"workflowList,omitempty"` } `json:"workflowFCParam,omitempty"` PluginFCParam *struct { PluginList []struct { PluginID string `json:"plugin_id"` ApiId string `json:"api_id"` ApiName string `json:"api_name"` PluginVersion string `json:"plugin_version"` IsDraft bool `json:"is_draft"` FCSetting *struct { RequestParameters []*workflow.APIParameter `json:"request_params"` ResponseParameters []*workflow.APIParameter `json:"response_params"` } `json:"fc_setting,omitempty"` } `json:"pluginList,omitempty"` } `json:"pluginFCParam,omitempty"` KnowledgeFCParam *struct { GlobalSetting *struct { SearchMode int64 `json:"search_mode"` TopK int64 `json:"top_k"` MinScore float64 `json:"min_score"` UseNL2SQL bool `json:"use_nl2_sql"` UseRewrite bool `json:"use_rewrite"` UseRerank bool `json:"use_rerank"` NoRecallReplyCustomizePrompt string `json:"no_recall_reply_customize_prompt"` NoRecallReplyMode int64 `json:"no_recall_reply_mode"` } `json:"global_setting,omitempty"` KnowledgeList []*struct { ID string `json:"id"` } `json:"knowledgeList,omitempty"` } `json:"knowledgeFCParam,omitempty"` } type Batch struct { BatchSize *BlockInput `json:"batchSize,omitempty"` ConcurrentSize *BlockInput `json:"concurrentSize,omitempty"` } type NodeBatch struct { BatchEnable bool `json:"batchEnable"` BatchSize int64 `json:"batchSize"` ConcurrentSize int64 `json:"concurrentSize"` InputLists []*Param `json:"inputLists,omitempty"` } type IntentDetectorLLMConfig struct { ModelName string `json:"modelName"` ModelType int `json:"modelType"` Temperature *float64 `json:"temperature"` TopP *float64 `json:"topP"` MaxTokens int `json:"maxTokens"` ResponseFormat int64 `json:"responseFormat"` SystemPrompt BlockInput `json:"systemPrompt"` } type VariableAggregator struct { MergeGroups []*Param `json:"mergeGroups,omitempty"` } type PluginAPIParam struct { APIParams []*Param `json:"apiParam"` } type CodeRunner struct { Code string `json:"code"` Language int64 `json:"language"` } type Knowledge struct { DatasetParam []*Param `json:"datasetParam,omitempty"` StrategyParam StrategyParam `json:"strategyParam,omitempty"` } type StrategyParam struct { ParsingStrategy struct { ParsingType string `json:"parsingType,omitempty"` ImageExtraction bool `json:"imageExtraction"` TableExtraction bool `json:"tableExtraction"` ImageOcr bool `json:"imageOcr"` } `json:"parsingStrategy,omitempty"` ChunkStrategy struct { ChunkType string `json:"chunkType,omitempty"` SeparatorType string `json:"separatorType,omitempty"` Separator string `json:"separator,omitempty"` MaxToken int64 `json:"maxToken,omitempty"` Overlap float64 `json:"overlap,omitempty"` } `json:"chunkStrategy,omitempty"` IndexStrategy any `json:"indexStrategy"` } type HttpRequestNode struct { APIInfo APIInfo `json:"apiInfo,omitempty"` Body Body `json:"body,omitempty"` Headers []*Param `json:"headers"` Params []*Param `json:"params"` Auth *Auth `json:"auth"` Setting *HttpRequestSetting `json:"setting"` } type APIInfo struct { Method string `json:"method"` URL string `json:"url"` } type Body struct { BodyType string `json:"bodyType"` BodyData *BodyData `json:"bodyData"` } type BodyData struct { Json string `json:"json,omitempty"` FormData *struct { Data []*Param `json:"data"` } `json:"formData,omitempty"` FormURLEncoded []*Param `json:"formURLEncoded,omitempty"` RawText string `json:"rawText,omitempty"` Binary struct { FileURL *BlockInput `json:"fileURL"` } `json:"binary"` } type Auth struct { AuthType string `json:"authType"` AuthData struct { CustomData struct { AddTo string `json:"addTo"` Data []*Param `json:"data,omitempty"` } `json:"customData"` BearerTokenData []*Param `json:"bearerTokenData,omitempty"` } `json:"authData"` AuthOpen bool `json:"authOpen"` } type HttpRequestSetting struct { Timeout int64 `json:"timeout"` RetryTimes int64 `json:"retryTimes"` } type DatabaseNode struct { DatabaseInfoList []*DatabaseInfo `json:"databaseInfoList,omitempty"` SQL string `json:"sql,omitempty"` SelectParam *SelectParam `json:"selectParam,omitempty"` InsertParam *InsertParam `json:"insertParam,omitempty"` DeleteParam *DeleteParam `json:"deleteParam,omitempty"` UpdateParam *UpdateParam `json:"updateParam,omitempty"` } type DatabaseLogicType string const ( DatabaseLogicAnd DatabaseLogicType = "AND" DatabaseLogicOr DatabaseLogicType = "OR" ) type DBCondition struct { ConditionList [][]*Param `json:"conditionList,omitempty"` Logic DatabaseLogicType `json:"logic"` } type UpdateParam struct { Condition DBCondition `json:"condition"` FieldInfo [][]*Param `json:"fieldInfo"` } type DeleteParam struct { Condition DBCondition `json:"condition"` } type InsertParam struct { FieldInfo [][]*Param `json:"fieldInfo"` } type SelectParam struct { Condition *DBCondition `json:"condition,omitempty"` // may be nil OrderByList []struct { FieldID int64 `json:"fieldID"` IsAsc bool `json:"isAsc"` } `json:"orderByList,omitempty"` Limit int64 `json:"limit"` FieldList []struct { FieldID int64 `json:"fieldID"` IsDistinct bool `json:"isDistinct"` } `json:"fieldList,omitempty"` } type DatabaseInfo struct { DatabaseInfoID string `json:"databaseInfoID"` } type IntentDetector struct { ChatHistorySetting *ChatHistorySetting `json:"chatHistorySetting,omitempty"` Intents []*Intent `json:"intents,omitempty"` Mode string `json:"mode,omitempty"` } type ChatHistorySetting struct { EnableChatHistory bool `json:"enableChatHistory,omitempty"` ChatHistoryRound int64 `json:"chatHistoryRound,omitempty"` } type Intent struct { Name string `json:"name"` } // Param is a node's field with type and source info. type Param struct { // Name is the field's name. Name string `json:"name,omitempty"` // Input is the configurations for normal, singular field. Input *BlockInput `json:"input,omitempty"` // Left is the configurations for the left half of an expression, // such as an assignment in NodeTypeVariableAssigner. Left *BlockInput `json:"left,omitempty"` // Right is the configuration for the right half of an expression. Right *BlockInput `json:"right,omitempty"` // Variables are configurations for a group of fields. // Only used in NodeTypeVariableAggregator. Variables []*BlockInput `json:"variables,omitempty"` } // Variable is the configuration of a node's field, either input or output. type Variable struct { // Name is the field's name as defined on canvas. Name string `json:"name"` // Type is the field's data type, such as string, integer, number, object, array, etc. Type VariableType `json:"type"` // Required is set to true if you checked the 'required box' on this field Required bool `json:"required,omitempty"` // AssistType is the 'secondary' type of string fields, such as different types of file and image, or time. AssistType AssistType `json:"assistType,omitempty"` // Schema contains detailed info for sub-fields of an object field, or element type of an array. Schema any `json:"schema,omitempty"` // either []*Variable (for object) or *Variable (for list) // Description describes the field's intended use. Used on Entry node. Useful for workflow tools. Description string `json:"description,omitempty"` // ReadOnly indicates a field is not to be set by Node's business logic. // e.g. the ErrorBody field when exception strategy is configured. ReadOnly bool `json:"readOnly,omitempty"` // DefaultValue configures the 'default value' if this field is missing in input. // Effective only in Entry node. DefaultValue any `json:"defaultValue,omitempty"` } type BlockInput struct { Type VariableType `json:"type,omitempty" yaml:"Type,omitempty"` AssistType AssistType `json:"assistType,omitempty" yaml:"AssistType,omitempty"` Schema any `json:"schema,omitempty" yaml:"Schema,omitempty"` // either *BlockInput(or *Variable) for list or []*Variable (for object) Value *BlockInputValue `json:"value,omitempty" yaml:"Value,omitempty"` } type BlockInputValue struct { Type BlockInputValueType `json:"type"` Content any `json:"content,omitempty"` // either string for text such as template, or BlockInputReference RawMeta any `json:"rawMeta,omitempty"` } type BlockInputReference struct { BlockID string `json:"blockID"` Name string `json:"name,omitempty"` Path []string `json:"path,omitempty"` Source RefSourceType `json:"source"` } type Condition struct { Operator OperatorType `json:"operator"` Left *Param `json:"left"` Right *Param `json:"right,omitempty"` } type SubWorkflow struct { WorkflowID string `json:"workflowId,omitempty"` WorkflowVersion string `json:"workflowVersion,omitempty"` TerminationType int `json:"type,omitempty"` SpaceID string `json:"spaceId,omitempty"` } type VariableType string const ( VariableTypeString VariableType = "string" VariableTypeInteger VariableType = "integer" VariableTypeFloat VariableType = "float" VariableTypeBoolean VariableType = "boolean" VariableTypeObject VariableType = "object" VariableTypeList VariableType = "list" ) type AssistType = int64 const ( AssistTypeNotSet AssistType = 0 AssistTypeDefault AssistType = 1 AssistTypeImage AssistType = 2 AssistTypeDoc AssistType = 3 AssistTypeCode AssistType = 4 AssistTypePPT AssistType = 5 AssistTypeTXT AssistType = 6 AssistTypeExcel AssistType = 7 AssistTypeAudio AssistType = 8 AssistTypeZip AssistType = 9 AssistTypeVideo AssistType = 10 AssistTypeSvg AssistType = 11 AssistTypeVoice AssistType = 12 AssistTypeTime AssistType = 10000 ) type BlockInputValueType string const ( BlockInputValueTypeLiteral BlockInputValueType = "literal" BlockInputValueTypeRef BlockInputValueType = "ref" BlockInputValueTypeObjectRef BlockInputValueType = "object_ref" ) type RefSourceType string const ( RefSourceTypeBlockOutput RefSourceType = "block-output" // Represents an implicitly declared variable that references the output of a block RefSourceTypeGlobalApp RefSourceType = "global_variable_app" RefSourceTypeGlobalSystem RefSourceType = "global_variable_system" RefSourceTypeGlobalUser RefSourceType = "global_variable_user" ) type TerminatePlan string const ( ReturnVariables TerminatePlan = "returnVariables" UseAnswerContent TerminatePlan = "useAnswerContent" ) type ErrorProcessType int const ( ErrorProcessTypeThrow ErrorProcessType = 1 // throws the error as usual ErrorProcessTypeReturnDefaultData ErrorProcessType = 2 // return DataOnErr configured in SettingOnError ErrorProcessTypeExceptionBranch ErrorProcessType = 3 // executes the exception branch on error ) // SettingOnError contains common error handling strategy. type SettingOnError struct { // DataOnErr defines the JSON result to be returned on error. DataOnErr string `json:"dataOnErr,omitempty"` // Switch defines whether ANY error handling strategy is active. // If set to false, it's equivalent to set ProcessType = ErrorProcessTypeThrow Switch bool `json:"switch,omitempty"` // ProcessType determines the error handling strategy for this node. ProcessType *ErrorProcessType `json:"processType,omitempty"` // RetryTimes determines how many times to retry. 0 means no retry. // If positive, any retries will be executed immediately after error. RetryTimes int64 `json:"retryTimes,omitempty"` // TimeoutMs sets the timeout duration in millisecond. // If any retry happens, ALL retry attempts accumulates to the same timeout threshold. TimeoutMs int64 `json:"timeoutMs,omitempty"` // Ext sets any extra settings specific to NodeType Ext *struct { // BackupLLMParam is only for LLM Node, marshaled from SimpleLLMParam. // If retry happens, the backup LLM will be used instead of the main LLM. BackupLLMParam string `json:"backupLLMParam,omitempty"` } `json:"ext,omitempty"` } type LogicType int const ( _ LogicType = iota OR AND ) type OperatorType int const ( _ OperatorType = iota Equal NotEqual LengthGreaterThan LengthGreaterThanEqual LengthLessThan LengthLessThanEqual Contain NotContain Empty NotEmpty True False GreaterThan GreaterThanEqual LessThan LessThanEqual ) type TextProcessingMethod string const ( Concat TextProcessingMethod = "concat" Split TextProcessingMethod = "split" ) type LoopType string const ( LoopTypeArray LoopType = "array" LoopTypeCount LoopType = "count" LoopTypeInfinite LoopType = "infinite" ) type InputReceiver struct { OutputSchema string `json:"outputSchema,omitempty"` } func GenerateNodeIDForBatchMode(key string) string { return key + "_inner" } func IsGeneratedNodeForBatchMode(key string, parentKey string) bool { return key == GenerateNodeIDForBatchMode(parentKey) } const defaultZhCNInitCanvasJsonSchema = `{ "nodes": [ { "id": "100001", "type": "1", "meta": { "position": { "x": 0, "y": 0 } }, "data": { "nodeMeta": { "description": "工作流的起始节点,用于设定启动工作流需要的信息", "icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start.png", "subTitle": "", "title": "开始" }, "outputs": [ { "type": "string", "name": "input", "required": false } ], "trigger_parameters": [ { "type": "string", "name": "input", "required": false } ] } }, { "id": "900001", "type": "2", "meta": { "position": { "x": 1000, "y": 0 } }, "data": { "nodeMeta": { "description": "工作流的最终节点,用于返回工作流运行后的结果信息", "icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End.png", "subTitle": "", "title": "结束" }, "inputs": { "terminatePlan": "returnVariables", "inputParameters": [ { "name": "output", "input": { "type": "string", "value": { "type": "ref", "content": { "source": "block-output", "blockID": "", "name": "" } } } } ] } } } ], "edges": [], "versions": { "loop": "v2" } }` const defaultEnUSInitCanvasJsonSchema = `{ "nodes": [ { "id": "100001", "type": "1", "meta": { "position": { "x": 0, "y": 0 } }, "data": { "nodeMeta": { "description": "The starting node of the workflow, used to set the information needed to initiate the workflow.", "icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-Start.png", "subTitle": "", "title": "Start" }, "outputs": [ { "type": "string", "name": "input", "required": false } ], "trigger_parameters": [ { "type": "string", "name": "input", "required": false } ] } }, { "id": "900001", "type": "2", "meta": { "position": { "x": 1000, "y": 0 } }, "data": { "nodeMeta": { "description": "The final node of the workflow, used to return the result information after the workflow runs.", "icon": "https://lf3-static.bytednsdoc.com/obj/eden-cn/dvsmryvd_avi_dvsm/ljhwZthlaukjlkulzlp/icon/icon-End.png", "subTitle": "", "title": "End" }, "inputs": { "terminatePlan": "returnVariables", "inputParameters": [ { "name": "output", "input": { "type": "string", "value": { "type": "ref", "content": { "source": "block-output", "blockID": "", "name": "" } } } } ] } } } ], "edges": [], "versions": { "loop": "v2" } }` func GetDefaultInitCanvasJsonSchema(locale i18n.Locale) string { return ternary.IFElse(locale == i18n.LocaleEN, defaultEnUSInitCanvasJsonSchema, defaultZhCNInitCanvasJsonSchema) }