feat: set var inspect schema type

This commit is contained in:
Joel 2025-09-02 18:44:40 +08:00
parent 32a009654f
commit 60da4c9048
13 changed files with 128 additions and 48 deletions

View File

@ -10,6 +10,7 @@ import { HooksStoreContext } from './provider'
import type {
BlockEnum,
NodeDefault,
ToolWithProvider,
} from '@/app/components/workflow/types'
import type { IOtherOptions } from '@/service/base'
import type { VarInInspect } from '@/types/workflow'
@ -19,6 +20,7 @@ import type {
} from '@/app/components/workflow/types'
import type { FlowType } from '@/types/common'
import type { FileUpload } from '../../base/features/types'
import type { SchemaTypeDefinition } from '@/service/use-common'
export type AvailableNodesMetaData = {
nodes: NodeDefault[]
@ -47,7 +49,7 @@ export type CommonHooksFnMap = {
getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string; traceUrl: string }
exportCheck?: () => Promise<void>
handleExportDSL?: (include?: boolean) => Promise<void>
fetchInspectVars: () => Promise<void>
fetchInspectVars: (params: { passInVars?: boolean, vars?: VarInInspect[], passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>, passedInSchemaTypeDefinitions?: SchemaTypeDefinition[] }) => Promise<void>
hasNodeInspectVars: (nodeId: string) => boolean
hasSetInspectVar: (nodeId: string, name: string, sysVars: VarInInspect[], conversationVars: VarInInspect[]) => boolean
fetchInspectVarValue: (selector: ValueSelector) => Promise<void>

View File

@ -1,12 +1,16 @@
import { useCallback } from 'react'
import type { NodeWithVar, VarInInspect } from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
import { useStoreApi } from 'reactflow'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import type { Node } from '@/app/components/workflow/types'
import { fetchAllInspectVars } from '@/service/workflow'
import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow'
import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync'
import type { FlowType } from '@/types/common'
import useMatchSchemaType, { getMatchedSchemaType } from '../nodes/_base/components/variable/use-match-schema-type'
import { toNodeOutputVars } from '../nodes/_base/components/variable/utils'
import type { SchemaTypeDefinition } from '@/service/use-common'
import { useCallback } from 'react'
type Params = {
flowType: FlowType
@ -22,11 +26,27 @@ export const useSetWorkflowVarsWithValue = ({
const invalidateConversationVarValues = useInvalidateConversationVarValues(flowType, flowId)
const invalidateSysVarValues = useInvalidateSysVarValues(flowType, flowId)
const { handleCancelAllNodeSuccessStatus } = useNodesInteractionsWithoutSync()
const { schemaTypeDefinitions } = useMatchSchemaType()
const buildInTools = useStore(s => s.buildInTools)
const customTools = useStore(s => s.customTools)
const workflowTools = useStore(s => s.workflowTools)
const mcpTools = useStore(s => s.mcpTools)
const dataSourceList = useStore(s => s.dataSourceList)
const allPluginInfoList = {
buildInTools,
customTools,
workflowTools,
mcpTools,
dataSourceList: dataSourceList ?? [],
}
const setInspectVarsToStore = useCallback((inspectVars: VarInInspect[]) => {
const setInspectVarsToStore = (inspectVars: VarInInspect[], passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>, passedInSchemaTypeDefinitions?: SchemaTypeDefinition[]) => {
const { setNodesWithInspectVars } = workflowStore.getState()
const { getNodes } = store.getState()
const nodeArr = getNodes()
const allNodesOutputVars = toNodeOutputVars(nodeArr, false, () => true, [], [], [], passedInAllPluginInfoList || allPluginInfoList, passedInSchemaTypeDefinitions || schemaTypeDefinitions)
const nodesKeyValue: Record<string, Node> = {}
nodeArr.forEach((node) => {
nodesKeyValue[node.id] = node
@ -50,27 +70,41 @@ export const useSetWorkflowVarsWithValue = ({
const varsUnderTheNode = inspectVars.filter((varItem) => {
return varItem.selector[0] === nodeId
})
const nodeVar = allNodesOutputVars.find(item => item.nodeId === nodeId)
const nodeWithVar = {
nodeId,
nodePayload: node.data,
nodeType: node.data.type,
title: node.data.title,
vars: varsUnderTheNode,
vars: varsUnderTheNode.map((item) => {
const schemaType = nodeVar ? nodeVar.vars.find(v => v.variable === item.name)?.schemaType : ''
return {
...item,
schemaType,
}
}),
isSingRunRunning: false,
isValueFetched: false,
}
return nodeWithVar
})
setNodesWithInspectVars(res)
}, [workflowStore, store])
}
const fetchInspectVars = useCallback(async () => {
const fetchInspectVars = useCallback(async (params: {
passInVars?: boolean,
vars?: VarInInspect[],
passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>,
passedInSchemaTypeDefinitions?: SchemaTypeDefinition[]
}) => {
const { passInVars, vars, passedInAllPluginInfoList, passedInSchemaTypeDefinitions } = params
invalidateConversationVarValues()
invalidateSysVarValues()
const data = await fetchAllInspectVars(flowType, flowId)
setInspectVarsToStore(data)
const data = passInVars ? vars! : await fetchAllInspectVars(flowType, flowId)
setInspectVarsToStore(data, passedInAllPluginInfoList, passedInSchemaTypeDefinitions)
handleCancelAllNodeSuccessStatus() // to make sure clear node output show the unset status
}, [invalidateConversationVarValues, invalidateSysVarValues, flowType, flowId, setInspectVarsToStore, handleCancelAllNodeSuccessStatus])
}, [invalidateConversationVarValues, invalidateSysVarValues, flowType, flowId, setInspectVarsToStore, handleCancelAllNodeSuccessStatus, schemaTypeDefinitions, getMatchedSchemaType])
return {
fetchInspectVars,
}

View File

@ -17,7 +17,7 @@ import useMatchSchemaType from '../nodes/_base/components/variable/use-match-sch
export const useWorkflowVariables = () => {
const { t } = useTranslation()
const workflowStore = useWorkflowStore()
const { getMatchedSchemaType } = useMatchSchemaType()
const { schemaTypeDefinitions } = useMatchSchemaType()
const buildInTools = useStore(s => s.buildInTools)
const customTools = useStore(s => s.customTools)
@ -60,9 +60,9 @@ export const useWorkflowVariables = () => {
mcpTools,
dataSourceList: dataSourceList ?? [],
},
getMatchedSchemaType,
schemaTypeDefinitions,
})
}, [t, workflowStore, getMatchedSchemaType, buildInTools])
}, [t, workflowStore, schemaTypeDefinitions, buildInTools])
const getCurrentVariableType = useCallback(({
parentNode,
@ -111,10 +111,10 @@ export const useWorkflowVariables = () => {
mcpTools,
dataSourceList: dataSourceList ?? [],
},
getMatchedSchemaType,
schemaTypeDefinitions,
preferSchemaType,
})
}, [workflowStore, getVarType, getMatchedSchemaType])
}, [workflowStore, getVarType, schemaTypeDefinitions])
return {
getNodeAvailableVars,

View File

@ -7,6 +7,7 @@ import {
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import { setAutoFreeze } from 'immer'
import {
@ -85,9 +86,12 @@ import {
import { WorkflowHistoryProvider } from './workflow-history-store'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import DatasetsDetailProvider from './datasets-detail-store/provider'
import { HooksStoreContextProvider } from './hooks-store'
import { HooksStoreContextProvider, useHooksStore } from './hooks-store'
import type { Shape as HooksStoreShape } from './hooks-store'
import dynamic from 'next/dynamic'
import useMatchSchemaType from './nodes/_base/components/variable/use-match-schema-type'
import type { VarInInspect } from '@/types/workflow'
import { fetchAllInspectVars } from '@/service/workflow'
const Confirm = dynamic(() => import('@/app/components/base/confirm'), {
ssr: false,
@ -293,10 +297,42 @@ export const Workflow: FC<WorkflowProps> = memo(({
return setupScrollToNodeListener(nodes, reactflow)
}, [nodes, reactflow])
const { schemaTypeDefinitions } = useMatchSchemaType()
const { fetchInspectVars } = useSetWorkflowVarsWithValue()
const buildInTools = useStore(s => s.buildInTools)
const customTools = useStore(s => s.customTools)
const workflowTools = useStore(s => s.workflowTools)
const mcpTools = useStore(s => s.mcpTools)
const dataSourceList = useStore(s => s.dataSourceList)
// buildInTools, customTools, workflowTools, mcpTools, dataSourceList
const configsMap = useHooksStore(s => s.configsMap)
const [isLoadedVars, setIsLoadedVars] = useState(false)
const [vars, setVars] = useState<VarInInspect[]>([])
useEffect(() => {
fetchInspectVars()
}, [])
(async () => {
if(!configsMap?.flowType || !configsMap?.flowId)
return
const data = await fetchAllInspectVars(configsMap.flowType, configsMap.flowId)
setVars(data)
setIsLoadedVars(true)
})()
}, [configsMap?.flowType, configsMap?.flowId])
useEffect(() => {
if(schemaTypeDefinitions && isLoadedVars) {
fetchInspectVars({
passInVars: true,
vars,
passedInAllPluginInfoList: {
buildInTools,
customTools,
workflowTools,
mcpTools,
dataSourceList: dataSourceList ?? [],
},
passedInSchemaTypeDefinitions: schemaTypeDefinitions,
})
}
}, [schemaTypeDefinitions, fetchInspectVars, isLoadedVars, vars, customTools, buildInTools, workflowTools, mcpTools, dataSourceList])
const store = useStoreApi()
if (process.env.NODE_ENV === 'development') {

View File

@ -1,16 +1,20 @@
import type { SchemaTypeDefinition } from '@/service/use-common'
import { useSchemaTypeDefinitions } from '@/service/use-common'
import type { AnyObj } from './match-schema-type'
import matchTheSchemaType from './match-schema-type'
const useMatchSchemaType = () => {
const { data: schemaTypeDefinitions } = useSchemaTypeDefinitions()
const getMatchedSchemaType = (obj: AnyObj): string => {
export const getMatchedSchemaType = (obj: AnyObj, schemaTypeDefinitions?: SchemaTypeDefinition[]): string => {
if(!schemaTypeDefinitions) return ''
const matched = schemaTypeDefinitions.find(def => matchTheSchemaType(obj, def.schema))
return matched ? matched.name : ''
}
}
const useMatchSchemaType = () => {
const { data: schemaTypeDefinitions, isLoading } = useSchemaTypeDefinitions()
return {
getMatchedSchemaType,
isLoading,
schemaTypeDefinitions,
}
}

View File

@ -41,6 +41,7 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-so
import type { PromptItem } from '@/models/debug'
import { VAR_REGEX } from '@/config'
import type { AgentNodeType } from '../../../agent/types'
import type { SchemaTypeDefinition } from '@/service/use-common'
export const isSystemVar = (valueSelector: ValueSelector) => {
return valueSelector[0] === 'sys' || valueSelector[1] === 'sys'
@ -233,7 +234,7 @@ const formatItem = (
filterVar: (payload: Var, selector: ValueSelector) => boolean,
allPluginInfoList: Record<string, ToolWithProvider[]>,
ragVars?: Var[],
getMatchedSchemaType = (_obj: any) => '',
schemaTypeDefinitions: SchemaTypeDefinition[] = [],
): NodeOutPutVar => {
const { id, data } = item
@ -415,7 +416,7 @@ const formatItem = (
}
case BlockEnum.Tool: {
const toolOutputVars = ToolNodeDefault.getOutputVars?.(data as ToolNodeType, allPluginInfoList, [], { getMatchedSchemaType }) || []
const toolOutputVars = ToolNodeDefault.getOutputVars?.(data as ToolNodeType, allPluginInfoList, [], { schemaTypeDefinitions }) || []
res.vars = toolOutputVars
break
}
@ -511,7 +512,7 @@ const formatItem = (
case BlockEnum.DataSource: {
const payload = data as DataSourceNodeType
const dataSourceVars = DataSourceNodeDefault.getOutputVars?.(payload, allPluginInfoList, ragVars, { getMatchedSchemaType }) || []
const dataSourceVars = DataSourceNodeDefault.getOutputVars?.(payload, allPluginInfoList, ragVars, { schemaTypeDefinitions }) || []
res.vars = dataSourceVars
break
}
@ -641,7 +642,7 @@ export const toNodeOutputVars = (
conversationVariables: ConversationVariable[] = [],
ragVariables: RAGPipelineVariable[] = [],
allPluginInfoList: Record<string, ToolWithProvider[]>,
getMatchedSchemaType = (_obj: any) => '',
schemaTypeDefinitions?: SchemaTypeDefinition[],
): NodeOutPutVar[] => {
// ENV_NODE data format
const ENV_NODE = {
@ -699,7 +700,7 @@ export const toNodeOutputVars = (
description: ragVariable.label,
isRagVariable: true,
} as Var),
), getMatchedSchemaType),
), schemaTypeDefinitions),
isStartNode: node.data.type === BlockEnum.Start,
}
}).filter(item => item.vars.length > 0)
@ -824,7 +825,7 @@ export const getVarType = ({
conversationVariables = [],
ragVariables = [],
allPluginInfoList,
getMatchedSchemaType,
schemaTypeDefinitions,
preferSchemaType,
}: {
valueSelector: ValueSelector
@ -838,7 +839,7 @@ export const getVarType = ({
conversationVariables?: ConversationVariable[]
ragVariables?: RAGPipelineVariable[]
allPluginInfoList: Record<string, ToolWithProvider[]>
getMatchedSchemaType: (obj: any) => string
schemaTypeDefinitions?: SchemaTypeDefinition[]
preferSchemaType?: boolean
}): VarType => {
if (isConstant)
@ -852,7 +853,7 @@ export const getVarType = ({
conversationVariables,
ragVariables,
allPluginInfoList,
getMatchedSchemaType,
schemaTypeDefinitions,
)
const isIterationInnerVar = parentNode?.data.type === BlockEnum.Iteration
@ -979,7 +980,7 @@ export const toNodeAvailableVars = ({
ragVariables,
filterVar,
allPluginInfoList,
getMatchedSchemaType,
schemaTypeDefinitions,
}: {
parentNode?: Node | null
t?: any
@ -994,7 +995,7 @@ export const toNodeAvailableVars = ({
ragVariables?: RAGPipelineVariable[]
filterVar: (payload: Var, selector: ValueSelector) => boolean
allPluginInfoList: Record<string, ToolWithProvider[]>
getMatchedSchemaType: (obj: any) => string
schemaTypeDefinitions?: SchemaTypeDefinition[]
}): NodeOutPutVar[] => {
const beforeNodesOutputVars = toNodeOutputVars(
beforeNodes,
@ -1004,7 +1005,7 @@ export const toNodeAvailableVars = ({
conversationVariables,
ragVariables,
allPluginInfoList,
getMatchedSchemaType,
schemaTypeDefinitions,
)
const isInIteration = parentNode?.data.type === BlockEnum.Iteration
if (isInIteration) {
@ -1018,7 +1019,7 @@ export const toNodeAvailableVars = ({
environmentVariables,
conversationVariables,
allPluginInfoList,
getMatchedSchemaType,
schemaTypeDefinitions,
})
const itemChildren = itemType === VarType.file
? {

View File

@ -23,7 +23,7 @@ import { useStore } from '@/app/components/workflow/store'
import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import ToolForm from '../tool/components/tool-form'
import { wrapStructuredVarItem } from '@/app/components/workflow/utils/tool'
import useMatchSchemaType from '../_base/components/variable/use-match-schema-type'
import useMatchSchemaType, { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type'
const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => {
const { t } = useTranslation()
@ -50,8 +50,7 @@ const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => {
const pipelineId = useStore(s => s.pipelineId)
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
const { getMatchedSchemaType } = useMatchSchemaType()
const { schemaTypeDefinitions } = useMatchSchemaType()
return (
<div >
{
@ -137,7 +136,7 @@ const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => {
}
{
outputSchema.map((outputItem) => {
const schemaType = getMatchedSchemaType(outputItem.value)
const schemaType = getMatchedSchemaType(outputItem.value, schemaTypeDefinitions)
return (
<div key={outputItem.name}>

View File

@ -6,7 +6,7 @@ import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/typ
import { TOOL_OUTPUT_STRUCT } from '../../constants'
import { CollectionType } from '@/app/components/tools/types'
import { canFindTool } from '@/utils'
import type { AnyObj } from '../_base/components/variable/match-schema-type'
import { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type'
const i18nPrefix = 'workflow.errorMsg'
@ -66,7 +66,7 @@ const nodeDefault: NodeDefault<ToolNodeType> = {
errorMessage: errorMessages,
}
},
getOutputVars(payload: ToolNodeType, allPluginInfoList: Record<string, ToolWithProvider[]>, _ragVars: any, { getMatchedSchemaType } = { getMatchedSchemaType: (_obj: AnyObj) => '' }) {
getOutputVars(payload: ToolNodeType, allPluginInfoList: Record<string, ToolWithProvider[]>, _ragVars: any, { schemaTypeDefinitions } = { schemaTypeDefinitions: [] }) {
const { provider_id, provider_type } = payload
let currentTools: ToolWithProvider[] = []
switch (provider_type) {
@ -97,7 +97,7 @@ const nodeDefault: NodeDefault<ToolNodeType> = {
Object.keys(output_schema.properties).forEach((outputKey) => {
const output = output_schema.properties[outputKey]
const dataType = output.type
const schemaType = getMatchedSchemaType?.(output)
const schemaType = getMatchedSchemaType(output, schemaTypeDefinitions)
let type = dataType === 'array'
? `array[${output.items?.type.slice(0, 1).toLocaleLowerCase()}${output.items?.type.slice(1)}]`
: `${output.type.slice(0, 1).toLocaleLowerCase()}${output.type.slice(1)}`

View File

@ -12,7 +12,7 @@ import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/compo
import StructureOutputItem from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show'
import { useStore } from '@/app/components/workflow/store'
import { wrapStructuredVarItem } from '@/app/components/workflow/utils/tool'
import useMatchSchemaType from '../_base/components/variable/use-match-schema-type'
import useMatchSchemaType, { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type'
const i18nPrefix = 'workflow.nodes.tool'
@ -40,7 +40,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
const [collapsed, setCollapsed] = React.useState(false)
const pipelineId = useStore(s => s.pipelineId)
const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel)
const { getMatchedSchemaType } = useMatchSchemaType()
const { schemaTypeDefinitions } = useMatchSchemaType()
if (isLoading) {
return <div className='flex h-[200px] items-center justify-center'>
@ -118,7 +118,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({
isIndent={hasObjectOutput}
/>
{outputSchema.map((outputItem) => {
const schemaType = getMatchedSchemaType(outputItem.value)
const schemaType = getMatchedSchemaType(outputItem.value, schemaTypeDefinitions)
return (
<div key={outputItem.name}>
{outputItem.value?.type === 'object' ? (

View File

@ -21,6 +21,7 @@ import type { WorkflowRetryConfig } from '@/app/components/workflow/nodes/_base/
import type { StructuredOutput } from '@/app/components/workflow/nodes/llm/types'
import type { PluginMeta } from '../plugins/types'
import type { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types'
import type { SchemaTypeDefinition } from '@/service/use-common'
export enum BlockEnum {
Start = 'start',
@ -332,7 +333,9 @@ export type NodeDefault<T = {}> = {
defaultValue: Partial<T>
defaultRunInputData?: Record<string, any>
checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean; errorMessage?: string }
getOutputVars?: (payload: T, allPluginInfoList: Record<string, ToolWithProvider[]>, ragVariables?: Var[], utils?: { getMatchedSchemaType: (obj: any) => string }) => Var[]
getOutputVars?: (payload: T, allPluginInfoList: Record<string, ToolWithProvider[]>, ragVariables?: Var[], utils?: {
schemaTypeDefinitions?: SchemaTypeDefinition[]
}) => Var[]
}
export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue | DataSourceDefaultValue) => void

View File

@ -194,7 +194,7 @@ const Right = ({
)}
<div title={currentNodeVar.var.name} className='system-sm-semibold truncate text-text-secondary'>{currentNodeVar.var.name}</div>
<div className='system-xs-medium ml-1 shrink-0 space-x-2 text-text-tertiary'>
<span>{currentNodeVar.var.value_type}</span>
<span>{`${currentNodeVar.var.value_type}${currentNodeVar.var.schemaType ? (`(${currentNodeVar.var.schemaType})`) : ''}`}</span>
{isTruncated && (
<>
<span>·</span>

View File

@ -61,7 +61,7 @@ export const useFilePreview = (fileID: string) => {
})
}
type SchemaTypeDefinition = {
export type SchemaTypeDefinition = {
name: string
schema: {
properties: Record<string, any>

View File

@ -404,6 +404,7 @@ export type VarInInspect = {
visible: boolean
is_truncated: boolean
full_content: FullContent
schemaType?: string
}
export type NodeWithVar = {