Feat: Get the type of the output value of the begin operator

This commit is contained in:
bill 2025-12-10 14:36:34 +08:00
parent 760782276a
commit 951e41cae6
3 changed files with 129 additions and 19 deletions

View file

@ -1,6 +1,7 @@
import { omit } from 'lodash'; import { omit } from 'lodash';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { UseFormReturn, useWatch } from 'react-hook-form'; import { UseFormReturn, useWatch } from 'react-hook-form';
import { AgentDialogueMode } from '../../constant';
import { BeginQuery } from '../../interface'; import { BeginQuery } from '../../interface';
import useGraphStore from '../../store'; import useGraphStore from '../../store';
@ -20,10 +21,21 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) {
if (id) { if (id) {
values = form?.getValues() || {}; values = form?.getValues() || {};
let outputs: Record<string, any> = {};
// For webhook mode, use schema properties as direct outputs
// Each property (body, headers, query) should be able to show secondary menu
if (
values.mode === AgentDialogueMode.Webhook &&
values.schema?.properties
) {
outputs = { ...values.schema.properties };
}
const nextValues = { const nextValues = {
...values, ...values,
inputs: transferInputsArrayToObject(values.inputs), inputs: transferInputsArrayToObject(values.inputs),
outputs: values.schema, outputs,
}; };
updateNodeForm(id, nextValues); updateNodeForm(id, nextValues);

View file

@ -2,7 +2,9 @@ import { getStructuredDatatype } from '@/utils/canvas-util';
import { get, isPlainObject } from 'lodash'; import { get, isPlainObject } from 'lodash';
import { ReactNode, useCallback } from 'react'; import { ReactNode, useCallback } from 'react';
import { import {
AgentDialogueMode,
AgentStructuredOutputField, AgentStructuredOutputField,
BeginId,
JsonSchemaDataType, JsonSchemaDataType,
Operator, Operator,
} from '../constant'; } from '../constant';
@ -16,36 +18,94 @@ function getNodeId(value: string) {
} }
export function useShowSecondaryMenu() { export function useShowSecondaryMenu() {
const { getOperatorTypeFromId } = useGraphStore((state) => state); const { getOperatorTypeFromId, getNode } = useGraphStore((state) => state);
const showSecondaryMenu = useCallback( const showSecondaryMenu = useCallback(
(value: string, outputLabel: string) => { (value: string, outputLabel: string) => {
const nodeId = getNodeId(value); const nodeId = getNodeId(value);
return ( const operatorType = getOperatorTypeFromId(nodeId);
getOperatorTypeFromId(nodeId) === Operator.Agent &&
// For Agent nodes, show secondary menu for 'structured' field
if (
operatorType === Operator.Agent &&
outputLabel === AgentStructuredOutputField outputLabel === AgentStructuredOutputField
) {
return true;
}
// For Begin nodes in webhook mode, show secondary menu for schema properties (body, headers, query, etc.)
if (operatorType === Operator.Begin) {
const node = getNode(nodeId);
const mode = get(node, 'data.form.mode');
if (mode === AgentDialogueMode.Webhook) {
// Check if this output field is from the schema
const outputs = get(node, 'data.form.outputs', {});
const outputField = outputs[outputLabel];
// Show secondary menu if the field is an object or has properties
return (
outputField &&
(outputField.type === 'object' ||
(outputField.properties &&
Object.keys(outputField.properties).length > 0))
); );
}
}
return false;
}, },
[getOperatorTypeFromId], [getOperatorTypeFromId, getNode],
); );
return showSecondaryMenu; return showSecondaryMenu;
} }
function useGetBeginOutputsOrSchema() {
const { getNode } = useGraphStore((state) => state);
const getBeginOutputs = useCallback(() => {
const node = getNode(BeginId);
const outputs = get(node, 'data.form.outputs', {});
return outputs;
}, [getNode]);
const getBeginSchema = useCallback(() => {
const node = getNode(BeginId);
const outputs = get(node, 'data.form.schema', {});
return outputs;
}, [getNode]);
return { getBeginOutputs, getBeginSchema };
}
export function useGetStructuredOutputByValue() { export function useGetStructuredOutputByValue() {
const { getNode } = useGraphStore((state) => state); const { getNode, getOperatorTypeFromId } = useGraphStore((state) => state);
const { getBeginOutputs } = useGetBeginOutputsOrSchema();
const getStructuredOutput = useCallback( const getStructuredOutput = useCallback(
(value: string) => { (value: string) => {
const node = getNode(getNodeId(value)); const nodeId = getNodeId(value);
const structuredOutput = get( const node = getNode(nodeId);
const operatorType = getOperatorTypeFromId(nodeId);
const fields = splitValue(value);
const outputLabel = fields.at(1);
let structuredOutput;
if (operatorType === Operator.Agent) {
structuredOutput = get(
node, node,
`data.form.outputs.${AgentStructuredOutputField}`, `data.form.outputs.${AgentStructuredOutputField}`,
); );
} else if (operatorType === Operator.Begin) {
// For Begin nodes in webhook mode, get the specific schema property
const outputs = getBeginOutputs();
if (outputLabel) {
structuredOutput = outputs[outputLabel];
}
}
return structuredOutput; return structuredOutput;
}, },
[getNode], [getBeginOutputs, getNode, getOperatorTypeFromId],
); );
return getStructuredOutput; return getStructuredOutput;
@ -66,13 +126,14 @@ export function useFindAgentStructuredOutputLabel() {
icon?: ReactNode; icon?: ReactNode;
}>, }>,
) => { ) => {
// agent structured output
const fields = splitValue(value); const fields = splitValue(value);
const operatorType = getOperatorTypeFromId(fields.at(0));
// Handle Agent structured fields
if ( if (
getOperatorTypeFromId(fields.at(0)) === Operator.Agent && operatorType === Operator.Agent &&
fields.at(1)?.startsWith(AgentStructuredOutputField) fields.at(1)?.startsWith(AgentStructuredOutputField)
) { ) {
// is agent structured output
const agentOption = options.find((x) => value.includes(x.value)); const agentOption = options.find((x) => value.includes(x.value));
const jsonSchemaFields = fields const jsonSchemaFields = fields
.at(1) .at(1)
@ -84,6 +145,19 @@ export function useFindAgentStructuredOutputLabel() {
value: value, value: value,
}; };
} }
// Handle Begin webhook fields
if (operatorType === Operator.Begin && fields.at(1)) {
const fieldOption = options
.filter((x) => x.parentLabel === BeginId)
.find((x) => value.startsWith(x.value));
return {
...fieldOption,
label: fields.at(1),
value: value,
};
}
}, },
[getOperatorTypeFromId], [getOperatorTypeFromId],
); );
@ -94,6 +168,7 @@ export function useFindAgentStructuredOutputLabel() {
export function useFindAgentStructuredOutputTypeByValue() { export function useFindAgentStructuredOutputTypeByValue() {
const { getOperatorTypeFromId } = useGraphStore((state) => state); const { getOperatorTypeFromId } = useGraphStore((state) => state);
const filterStructuredOutput = useGetStructuredOutputByValue(); const filterStructuredOutput = useGetStructuredOutputByValue();
const { getBeginSchema } = useGetBeginOutputsOrSchema();
const findTypeByValue = useCallback( const findTypeByValue = useCallback(
( (
@ -136,10 +211,12 @@ export function useFindAgentStructuredOutputTypeByValue() {
} }
const fields = splitValue(value); const fields = splitValue(value);
const nodeId = fields.at(0); const nodeId = fields.at(0);
const operatorType = getOperatorTypeFromId(nodeId);
const jsonSchema = filterStructuredOutput(value); const jsonSchema = filterStructuredOutput(value);
// Handle Agent structured fields
if ( if (
getOperatorTypeFromId(nodeId) === Operator.Agent && operatorType === Operator.Agent &&
fields.at(1)?.startsWith(AgentStructuredOutputField) fields.at(1)?.startsWith(AgentStructuredOutputField)
) { ) {
const jsonSchemaFields = fields const jsonSchemaFields = fields
@ -151,13 +228,32 @@ export function useFindAgentStructuredOutputTypeByValue() {
return type; return type;
} }
} }
// Handle Begin webhook fields (body, headers, query, etc.)
if (operatorType === Operator.Begin) {
const outputLabel = fields.at(1);
const schema = getBeginSchema();
if (outputLabel && schema) {
const jsonSchemaFields = fields.at(1);
if (jsonSchemaFields) {
const type = findTypeByValue(schema, jsonSchemaFields);
return type;
}
}
}
}, },
[filterStructuredOutput, findTypeByValue, getOperatorTypeFromId], [
filterStructuredOutput,
findTypeByValue,
getBeginSchema,
getOperatorTypeFromId,
],
); );
return findAgentStructuredOutputTypeByValue; return findAgentStructuredOutputTypeByValue;
} }
// TODO: Consider merging with useFindAgentStructuredOutputLabel
export function useFindAgentStructuredOutputLabelByValue() { export function useFindAgentStructuredOutputLabelByValue() {
const { getNode } = useGraphStore((state) => state); const { getNode } = useGraphStore((state) => state);

View file

@ -314,10 +314,12 @@ export function useFilterQueryVariableOptionsByTypes({
? toLower(y.type).includes(toLower(x)) ? toLower(y.type).includes(toLower(x))
: toLower(y.type) === toLower(x), : toLower(y.type) === toLower(x),
) || ) ||
// agent structured output
isAgentStructured( isAgentStructured(
y.value, y.value,
y.value.slice(-AgentStructuredOutputField.length), y.value.slice(-AgentStructuredOutputField.length),
), // agent structured output ) ||
y.value.startsWith(BeginId), // begin node outputs
), ),
}; };
}) })