diff --git a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx index 94d272b5f..f79dd0f22 100644 --- a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx @@ -31,12 +31,10 @@ import * as ReactDOM from 'react-dom'; import { $createVariableNode } from './variable-node'; import { - useFilterStructuredOutputByValue, useFindAgentStructuredOutputLabel, useShowSecondaryMenu, } from '@/pages/agent/hooks/use-build-structured-output'; import { useBuildQueryVariableOptions } from '@/pages/agent/hooks/use-get-begin-query'; -import { hasJsonSchemaChild } from '@/pages/agent/utils/filter-agent-structured-output'; import { PromptIdentity } from '../../agent-form/use-build-prompt-options'; import { StructuredOutputSecondaryMenu } from '../structured-output-secondary-menu'; import { ProgrammaticTag } from './constant'; @@ -89,8 +87,6 @@ function VariablePickerMenuItem({ option: VariableOption | VariableInnerOption, ) => void; }) { - const filterStructuredOutput = useFilterStructuredOutputByValue(); - const showSecondaryMenu = useShowSecondaryMenu(); return ( @@ -108,12 +104,6 @@ function VariablePickerMenuItem({ const shouldShowSecondary = showSecondaryMenu(x.value, x.label); if (shouldShowSecondary) { - const filteredStructuredOutput = filterStructuredOutput(x.value); - - if (!hasJsonSchemaChild(filteredStructuredOutput)) { - return null; - } - return ( ); } diff --git a/web/src/pages/agent/form/components/select-with-secondary-menu.tsx b/web/src/pages/agent/form/components/select-with-secondary-menu.tsx index ca56a823e..237d29e35 100644 --- a/web/src/pages/agent/form/components/select-with-secondary-menu.tsx +++ b/web/src/pages/agent/form/components/select-with-secondary-menu.tsx @@ -25,11 +25,9 @@ import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { VariableType } from '../../constant'; import { - useFilterStructuredOutputByValue, useFindAgentStructuredOutputLabel, useShowSecondaryMenu, } from '../../hooks/use-build-structured-output'; -import { hasJsonSchemaChild } from '../../utils/filter-agent-structured-output'; import { StructuredOutputSecondaryMenu } from './structured-output-secondary-menu'; type Item = { @@ -68,7 +66,6 @@ export function GroupedSelectWithSecondaryMenu({ const [open, setOpen] = React.useState(false); const showSecondaryMenu = useShowSecondaryMenu(); - const filterStructuredOutput = useFilterStructuredOutputByValue(); const findAgentStructuredOutputLabel = useFindAgentStructuredOutputLabel(); // Find the label of the selected item @@ -155,21 +152,11 @@ export function GroupedSelectWithSecondaryMenu({ ); if (shouldShowSecondary) { - const filteredStructuredOutput = filterStructuredOutput( - option.value, - type, - ); - - if (!hasJsonSchemaChild(filteredStructuredOutput)) { - return null; - } - return ( ); diff --git a/web/src/pages/agent/form/components/structured-output-secondary-menu.tsx b/web/src/pages/agent/form/components/structured-output-secondary-menu.tsx index 861c02479..4c18fd7b1 100644 --- a/web/src/pages/agent/form/components/structured-output-secondary-menu.tsx +++ b/web/src/pages/agent/form/components/structured-output-secondary-menu.tsx @@ -1,4 +1,3 @@ -import { JSONSchema } from '@/components/jsonjoy-builder'; import { HoverCard, HoverCardContent, @@ -9,22 +8,28 @@ import { get, isEmpty, isPlainObject } from 'lodash'; import { ChevronRight } from 'lucide-react'; import { PropsWithChildren, ReactNode, useCallback } from 'react'; import { JsonSchemaDataType, VariableType } from '../../constant'; +import { useGetStructuredOutputByValue } from '../../hooks/use-build-structured-output'; +import { + hasJsonSchemaChild, + hasSpecificTypeChild, +} from '../../utils/filter-agent-structured-output'; type DataItem = { label: ReactNode; value: string; parentLabel?: ReactNode }; type StructuredOutputSecondaryMenuProps = { data: DataItem; click(option: { label: ReactNode; value: string }): void; - filteredStructuredOutput: JSONSchema; type?: VariableType | JsonSchemaDataType; } & PropsWithChildren; export function StructuredOutputSecondaryMenu({ data, click, - filteredStructuredOutput, type, }: StructuredOutputSecondaryMenuProps) { + const filterStructuredOutput = useGetStructuredOutputByValue(); + const structuredOutput = filterStructuredOutput(data.value); + const handleSubMenuClick = useCallback( (option: { label: ReactNode; value: string }, dataType?: string) => () => { // The query variable of the iteration operator can only select array type data. @@ -54,19 +59,28 @@ export function StructuredOutputSecondaryMenu({ const dataType = get(value, 'type'); - return ( -
  • -
    - {key} - {dataType} -
    - {dataType === JsonSchemaDataType.Object && - renderAgentStructuredOutput(value, nextOption)} -
  • - ); + if ( + !type || + (type && + (dataType === type || + hasSpecificTypeChild(value ?? {}, type))) + ) { + return ( +
  • +
    + {key} + {dataType} +
    + {dataType === JsonSchemaDataType.Object && + renderAgentStructuredOutput(value, nextOption)} +
  • + ); + } + + return null; })} ); @@ -74,9 +88,13 @@ export function StructuredOutputSecondaryMenu({ return
    ; }, - [handleSubMenuClick], + [handleSubMenuClick, type], ); + if (!hasJsonSchemaChild(structuredOutput)) { + return null; + } + return ( @@ -96,7 +114,7 @@ export function StructuredOutputSecondaryMenu({ >
    {data?.parentLabel} structured output:
    - {renderAgentStructuredOutput(filteredStructuredOutput, data)} + {renderAgentStructuredOutput(structuredOutput, data)}
    diff --git a/web/src/pages/agent/hooks/use-build-structured-output.ts b/web/src/pages/agent/hooks/use-build-structured-output.ts index ba5f6b873..489b8cecc 100644 --- a/web/src/pages/agent/hooks/use-build-structured-output.ts +++ b/web/src/pages/agent/hooks/use-build-structured-output.ts @@ -2,7 +2,6 @@ import { get } from 'lodash'; import { ReactNode, useCallback } from 'react'; import { AgentStructuredOutputField, Operator } from '../constant'; import useGraphStore from '../store'; -import { filterAgentStructuredOutput } from '../utils/filter-agent-structured-output'; function getNodeId(value: string) { return value.split('@').at(0); @@ -25,28 +24,23 @@ export function useShowSecondaryMenu() { return showSecondaryMenu; } -export function useFilterStructuredOutputByValue() { +export function useGetStructuredOutputByValue() { const { getNode } = useGraphStore((state) => state); - const filterStructuredOutput = useCallback( - (value: string, type?: string) => { + const getStructuredOutput = useCallback( + (value: string) => { const node = getNode(getNodeId(value)); const structuredOutput = get( node, `data.form.outputs.${AgentStructuredOutputField}`, ); - const filteredStructuredOutput = filterAgentStructuredOutput( - structuredOutput, - type, - ); - - return filteredStructuredOutput; + return structuredOutput; }, [getNode], ); - return filterStructuredOutput; + return getStructuredOutput; } export function useFindAgentStructuredOutputLabel() { diff --git a/web/src/pages/agent/utils/filter-agent-structured-output.ts b/web/src/pages/agent/utils/filter-agent-structured-output.ts index 107598a4a..190d15333 100644 --- a/web/src/pages/agent/utils/filter-agent-structured-output.ts +++ b/web/src/pages/agent/utils/filter-agent-structured-output.ts @@ -2,64 +2,6 @@ import { JSONSchema } from '@/components/jsonjoy-builder'; import { get, isPlainObject } from 'lodash'; import { JsonSchemaDataType } from '../constant'; -// Loop operators can only accept variables of type list. - -// Recursively traverse the JSON schema, keeping attributes with type "array" and discarding others. - -export function filterLoopOperatorInput( - structuredOutput: JSONSchema, - type: string, - path = [], -) { - if (typeof structuredOutput === 'boolean') { - return structuredOutput; - } - if ( - structuredOutput.properties && - isPlainObject(structuredOutput.properties) - ) { - const properties = Object.entries({ - ...structuredOutput.properties, - }).reduce( - (pre, [key, value]) => { - if ( - typeof value !== 'boolean' && - (value.type === type || hasArrayChild(value)) - ) { - pre[key] = filterLoopOperatorInput(value, type, path); - } - return pre; - }, - {} as Record, - ); - - return { ...structuredOutput, properties }; - } - - return structuredOutput; -} - -export function filterAgentStructuredOutput( - structuredOutput: JSONSchema, - type?: string, -) { - if (typeof structuredOutput === 'boolean') { - return structuredOutput; - } - if ( - structuredOutput.properties && - isPlainObject(structuredOutput.properties) - ) { - if (type) { - return filterLoopOperatorInput(structuredOutput, type); - } - - return structuredOutput; - } - - return structuredOutput; -} - export function hasSpecificTypeChild( data: Record | Array, type: string,