feat(trigger): add PluginTrigger node support and enhance output variable handling

This commit is contained in:
zhsama 2025-10-14 11:55:12 +08:00
parent 90fc06a494
commit bf42386c5b
3 changed files with 224 additions and 3 deletions

View File

@ -56,7 +56,7 @@ export const RETRIEVAL_OUTPUT_STRUCT = `{
}`
export const SUPPORT_OUTPUT_VARS_NODE = [
BlockEnum.Start, BlockEnum.TriggerWebhook, BlockEnum.LLM, BlockEnum.KnowledgeRetrieval, BlockEnum.Code, BlockEnum.TemplateTransform,
BlockEnum.Start, BlockEnum.TriggerWebhook, BlockEnum.TriggerPlugin, BlockEnum.LLM, BlockEnum.KnowledgeRetrieval, BlockEnum.Code, BlockEnum.TemplateTransform,
BlockEnum.HttpRequest, BlockEnum.Tool, BlockEnum.VariableAssigner, BlockEnum.VariableAggregator, BlockEnum.QuestionClassifier,
BlockEnum.ParameterExtractor, BlockEnum.Iteration, BlockEnum.Loop,
BlockEnum.DocExtractor, BlockEnum.ListFilter,

View File

@ -40,6 +40,8 @@ import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/v
import type { Field as StructField } from '@/app/components/workflow/nodes/llm/types'
import type { RAGPipelineVariable } from '@/models/pipeline'
import type { WebhookTriggerNodeType } from '@/app/components/workflow/nodes/trigger-webhook/types'
import type { PluginTriggerNodeType } from '@/app/components/workflow/nodes/trigger-plugin/types'
import PluginTriggerNodeDefault from '@/app/components/workflow/nodes/trigger-plugin/default'
import {
AGENT_OUTPUT_STRUCT,
@ -633,6 +635,17 @@ const formatItem = (
break
}
case BlockEnum.TriggerPlugin: {
const outputSchema = PluginTriggerNodeDefault.getOutputVars?.(
data as PluginTriggerNodeType,
allPluginInfoList,
[],
{ schemaTypeDefinitions },
) || []
res.vars = outputSchema
break
}
case 'env': {
res.vars = data.envList.map((env: EnvironmentVariable) => {
return {

View File

@ -1,7 +1,211 @@
import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import { BlockEnum, VarType } from '../../types'
import type { NodeDefault, Var } from '../../types'
import { genNodeMetaData } from '../../utils'
import type { PluginTriggerNodeType } from './types'
import { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type'
import type { SchemaTypeDefinition } from '@/service/use-common'
import { type Field, type StructuredOutput, Type } from '../llm/types'
const normalizeJsonSchemaType = (schema: any): string | undefined => {
if (!schema) return undefined
const { type, properties, items, oneOf, anyOf, allOf } = schema
if (Array.isArray(type))
return type.find((item: string | null) => item && item !== 'null') || type[0]
if (typeof type === 'string')
return type
const compositeCandidates = [oneOf, anyOf, allOf]
.filter((entry): entry is any[] => Array.isArray(entry))
.flat()
for (const candidate of compositeCandidates) {
const normalized = normalizeJsonSchemaType(candidate)
if (normalized)
return normalized
}
if (properties)
return 'object'
if (items)
return 'array'
return undefined
}
const pickItemSchema = (schema: any) => {
if (!schema || !schema.items)
return undefined
return Array.isArray(schema.items) ? schema.items[0] : schema.items
}
const resolveVarType = (schema: any, schemaTypeDefinitions?: SchemaTypeDefinition[]): { type: VarType; schemaType: string } => {
const schemaType = getMatchedSchemaType(schema, schemaTypeDefinitions)
const normalizedType = normalizeJsonSchemaType(schema)
if (schemaType === 'file') {
if (normalizedType === 'array')
return { type: VarType.arrayFile, schemaType }
return { type: VarType.file, schemaType }
}
switch (normalizedType) {
case 'string':
return { type: VarType.string, schemaType }
case 'number':
return { type: VarType.number, schemaType }
case 'integer':
return { type: VarType.integer, schemaType }
case 'boolean':
return { type: VarType.boolean, schemaType }
case 'object':
return { type: VarType.object, schemaType }
case 'array': {
const itemSchema = pickItemSchema(schema)
if (!itemSchema)
return { type: VarType.array, schemaType }
const { type: itemType, schemaType: itemSchemaType } = resolveVarType(itemSchema, schemaTypeDefinitions)
if (itemSchemaType === 'file')
return { type: VarType.arrayFile, schemaType }
switch (itemType) {
case VarType.string:
return { type: VarType.arrayString, schemaType }
case VarType.number:
case VarType.integer:
return { type: VarType.arrayNumber, schemaType }
case VarType.boolean:
return { type: VarType.arrayBoolean, schemaType }
case VarType.object:
return { type: VarType.arrayObject, schemaType }
case VarType.file:
return { type: VarType.arrayFile, schemaType }
default:
return { type: VarType.array, schemaType }
}
}
default:
return { type: VarType.any, schemaType }
}
}
const toFieldType = (normalizedType: string | undefined, schemaType?: string): Type => {
if (schemaType === 'file')
return normalizedType === 'array' ? Type.array : Type.file
switch (normalizedType) {
case 'number':
case 'integer':
return Type.number
case 'boolean':
return Type.boolean
case 'object':
return Type.object
case 'array':
return Type.array
case 'string':
default:
return Type.string
}
}
const toArrayItemType = (type: Type): Exclude<Type, Type.array> => {
if (type === Type.array)
return Type.object
return type as Exclude<Type, Type.array>
}
const convertJsonSchemaToField = (schema: any, schemaTypeDefinitions?: SchemaTypeDefinition[]): Field => {
const schemaType = getMatchedSchemaType(schema, schemaTypeDefinitions)
const normalizedType = normalizeJsonSchemaType(schema)
const fieldType = toFieldType(normalizedType, schemaType)
const field: Field = {
type: fieldType,
}
if (schema?.description)
field.description = schema.description
if (schemaType)
field.schemaType = schemaType
if (Array.isArray(schema?.enum))
field.enum = schema.enum
if (fieldType === Type.object) {
const properties = schema?.properties || {}
field.properties = Object.entries(properties).reduce((acc, [key, value]) => {
acc[key] = convertJsonSchemaToField(value, schemaTypeDefinitions)
return acc
}, {} as Record<string, Field>)
const required = Array.isArray(schema?.required) ? schema.required.filter(Boolean) : undefined
field.required = required && required.length > 0 ? required : undefined
field.additionalProperties = false
}
if (fieldType === Type.array) {
const itemSchema = pickItemSchema(schema)
if (itemSchema) {
const itemField = convertJsonSchemaToField(itemSchema, schemaTypeDefinitions)
const { type, ...rest } = itemField
field.items = {
...rest,
type: toArrayItemType(type),
}
}
}
return field
}
const buildOutputVars = (schema: Record<string, any>, schemaTypeDefinitions?: SchemaTypeDefinition[]): Var[] => {
if (!schema || typeof schema !== 'object')
return []
const properties = schema.properties as Record<string, any> | undefined
if (!properties)
return []
return Object.entries(properties).map(([name, propertySchema]) => {
const { type, schemaType } = resolveVarType(propertySchema, schemaTypeDefinitions)
const normalizedType = normalizeJsonSchemaType(propertySchema)
const varItem: Var = {
variable: name,
type,
des: propertySchema?.description,
schemaType,
}
if (normalizedType === 'object') {
const childProperties = propertySchema?.properties
? Object.entries(propertySchema.properties).reduce((acc, [key, value]) => {
acc[key] = convertJsonSchemaToField(value, schemaTypeDefinitions)
return acc
}, {} as Record<string, Field>)
: {}
const required = Array.isArray(propertySchema?.required) ? propertySchema.required.filter(Boolean) : undefined
varItem.children = {
schema: {
type: Type.object,
properties: childProperties,
required: required && required.length > 0 ? required : undefined,
additionalProperties: false,
},
} as StructuredOutput
}
return varItem
})
}
const metaData = genNodeMetaData({
sort: 1,
@ -23,6 +227,10 @@ const nodeDefault: NodeDefault<PluginTriggerNodeType> = {
errorMessage: '',
}
},
getOutputVars(payload, _allPluginInfoList, _ragVars, { schemaTypeDefinitions } = { schemaTypeDefinitions: [] }) {
const schema = payload.output_schema || {}
return buildOutputVars(schema, schemaTypeDefinitions)
},
}
export default nodeDefault