mirror of https://github.com/langgenius/dify.git
feat(trigger): add PluginTrigger node support and enhance output variable handling
This commit is contained in:
parent
90fc06a494
commit
bf42386c5b
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue