Skip to content

Commit a52bdf0

Browse files
authored
Feat: The structured output of the variable query can also be clicked. #10866 (#10952)
### What problem does this PR solve? Feat: The structured output of the variable query can also be clicked. #10866 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
1 parent b473614 commit a52bdf0

File tree

6 files changed

+61
-23
lines changed

6 files changed

+61
-23
lines changed

web/src/pages/agent/constant/index.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,3 +929,11 @@ export const HALF_PLACEHOLDER_NODE_HEIGHT =
929929
export const DROPDOWN_HORIZONTAL_OFFSET = 28;
930930
export const DROPDOWN_VERTICAL_OFFSET = 74;
931931
export const PREVENT_CLOSE_DELAY = 300;
932+
933+
export enum JsonSchemaDataType {
934+
String = 'string',
935+
Number = 'number',
936+
Boolean = 'boolean',
937+
Array = 'array',
938+
Object = 'object',
939+
}

web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
useShowSecondaryMenu,
3737
} from '@/pages/agent/hooks/use-build-structured-output';
3838
import { useBuildQueryVariableOptions } from '@/pages/agent/hooks/use-get-begin-query';
39+
import { hasJsonSchemaChild } from '@/pages/agent/utils/filter-agent-structured-output';
3940
import { PromptIdentity } from '../../agent-form/use-build-prompt-options';
4041
import { StructuredOutputSecondaryMenu } from '../structured-output-secondary-menu';
4142
import { ProgrammaticTag } from './constant';
@@ -109,6 +110,10 @@ function VariablePickerMenuItem({
109110
if (shouldShowSecondary) {
110111
const filteredStructuredOutput = filterStructuredOutput(x.value);
111112

113+
if (!hasJsonSchemaChild(filteredStructuredOutput)) {
114+
return null;
115+
}
116+
112117
return (
113118
<StructuredOutputSecondaryMenu
114119
key={x.value}

web/src/pages/agent/form/components/select-with-secondary-menu.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
useFindAgentStructuredOutputLabel,
3030
useShowSecondaryMenu,
3131
} from '../../hooks/use-build-structured-output';
32+
import { hasJsonSchemaChild } from '../../utils/filter-agent-structured-output';
3233
import { StructuredOutputSecondaryMenu } from './structured-output-secondary-menu';
3334

3435
type Item = {
@@ -156,7 +157,13 @@ export function GroupedSelectWithSecondaryMenu({
156157
if (shouldShowSecondary) {
157158
const filteredStructuredOutput = filterStructuredOutput(
158159
option.value,
160+
type,
159161
);
162+
163+
if (!hasJsonSchemaChild(filteredStructuredOutput)) {
164+
return null;
165+
}
166+
160167
return (
161168
<StructuredOutputSecondaryMenu
162169
key={option.value}

web/src/pages/agent/form/components/structured-output-secondary-menu.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,20 @@ import {
55
HoverCardTrigger,
66
} from '@/components/ui/hover-card';
77
import { cn } from '@/lib/utils';
8-
import { get, isPlainObject } from 'lodash';
8+
import { get, isEmpty, isPlainObject } from 'lodash';
99
import { ChevronRight } from 'lucide-react';
1010
import { PropsWithChildren, ReactNode, useCallback } from 'react';
11-
import { VariableType } from '../../constant';
11+
import { JsonSchemaDataType, VariableType } from '../../constant';
1212

1313
type DataItem = { label: ReactNode; value: string; parentLabel?: ReactNode };
1414

1515
type StructuredOutputSecondaryMenuProps = {
1616
data: DataItem;
1717
click(option: { label: ReactNode; value: string }): void;
1818
filteredStructuredOutput: JSONSchema;
19-
type?: VariableType;
19+
type?: VariableType | JsonSchemaDataType;
2020
} & PropsWithChildren;
21+
2122
export function StructuredOutputSecondaryMenu({
2223
data,
2324
click,
@@ -34,6 +35,12 @@ export function StructuredOutputSecondaryMenu({
3435
[click, type],
3536
);
3637

38+
const handleMenuClick = useCallback(() => {
39+
if (isEmpty(type) || type === JsonSchemaDataType.Object) {
40+
click(data);
41+
}
42+
}, [click, data, type]);
43+
3744
const renderAgentStructuredOutput = useCallback(
3845
(values: any, option: { label: ReactNode; value: string }) => {
3946
if (isPlainObject(values) && 'properties' in values) {
@@ -56,7 +63,7 @@ export function StructuredOutputSecondaryMenu({
5663
{key}
5764
<span className="text-text-secondary">{dataType}</span>
5865
</div>
59-
{dataType === 'object' &&
66+
{dataType === JsonSchemaDataType.Object &&
6067
renderAgentStructuredOutput(value, nextOption)}
6168
</li>
6269
);
@@ -74,7 +81,7 @@ export function StructuredOutputSecondaryMenu({
7481
<HoverCard key={data.value} openDelay={100} closeDelay={100}>
7582
<HoverCardTrigger asChild>
7683
<li
77-
onClick={() => click(data)}
84+
onClick={handleMenuClick}
7885
className="hover:bg-bg-card py-1 px-2 text-text-primary rounded-sm text-sm flex justify-between items-center"
7986
>
8087
{data.label} <ChevronRight className="size-3.5 text-text-secondary" />

web/src/pages/agent/hooks/use-build-structured-output.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,10 @@ export function useShowSecondaryMenu() {
2626
}
2727

2828
export function useFilterStructuredOutputByValue() {
29-
const { getOperatorTypeFromId, getNode, clickedNodeId } = useGraphStore(
30-
(state) => state,
31-
);
29+
const { getNode } = useGraphStore((state) => state);
3230

3331
const filterStructuredOutput = useCallback(
34-
(value: string) => {
32+
(value: string, type?: string) => {
3533
const node = getNode(getNodeId(value));
3634
const structuredOutput = get(
3735
node,
@@ -40,12 +38,12 @@ export function useFilterStructuredOutputByValue() {
4038

4139
const filteredStructuredOutput = filterAgentStructuredOutput(
4240
structuredOutput,
43-
getOperatorTypeFromId(clickedNodeId),
41+
type,
4442
);
4543

4644
return filteredStructuredOutput;
4745
},
48-
[clickedNodeId, getNode, getOperatorTypeFromId],
46+
[getNode],
4947
);
5048

5149
return filterStructuredOutput;

web/src/pages/agent/utils/filter-agent-structured-output.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { JSONSchema } from '@/components/jsonjoy-builder';
2-
import { Operator } from '@/constants/agent';
3-
import { isPlainObject } from 'lodash';
2+
import { get, isPlainObject } from 'lodash';
3+
import { JsonSchemaDataType } from '../constant';
44

55
// Loop operators can only accept variables of type list.
66

77
// Recursively traverse the JSON schema, keeping attributes with type "array" and discarding others.
88

99
export function filterLoopOperatorInput(
1010
structuredOutput: JSONSchema,
11+
type: string,
1112
path = [],
1213
) {
1314
if (typeof structuredOutput === 'boolean') {
@@ -23,9 +24,9 @@ export function filterLoopOperatorInput(
2324
(pre, [key, value]) => {
2425
if (
2526
typeof value !== 'boolean' &&
26-
(value.type === 'array' || hasArrayChild(value))
27+
(value.type === type || hasArrayChild(value))
2728
) {
28-
pre[key] = filterLoopOperatorInput(value, path);
29+
pre[key] = filterLoopOperatorInput(value, type, path);
2930
}
3031
return pre;
3132
},
@@ -40,7 +41,7 @@ export function filterLoopOperatorInput(
4041

4142
export function filterAgentStructuredOutput(
4243
structuredOutput: JSONSchema,
43-
operator?: string,
44+
type?: string,
4445
) {
4546
if (typeof structuredOutput === 'boolean') {
4647
return structuredOutput;
@@ -49,8 +50,8 @@ export function filterAgentStructuredOutput(
4950
structuredOutput.properties &&
5051
isPlainObject(structuredOutput.properties)
5152
) {
52-
if (operator === Operator.Iteration) {
53-
return filterLoopOperatorInput(structuredOutput);
53+
if (type) {
54+
return filterLoopOperatorInput(structuredOutput, type);
5455
}
5556

5657
return structuredOutput;
@@ -59,29 +60,41 @@ export function filterAgentStructuredOutput(
5960
return structuredOutput;
6061
}
6162

62-
export function hasArrayChild(data: Record<string, any> | Array<any>) {
63+
export function hasSpecificTypeChild(
64+
data: Record<string, any> | Array<any>,
65+
type: string,
66+
) {
6367
if (Array.isArray(data)) {
6468
for (const value of data) {
65-
if (isPlainObject(value) && value.type === 'array') {
69+
if (isPlainObject(value) && value.type === type) {
6670
return true;
6771
}
68-
if (hasArrayChild(value)) {
72+
if (hasSpecificTypeChild(value, type)) {
6973
return true;
7074
}
7175
}
7276
}
7377

7478
if (isPlainObject(data)) {
7579
for (const value of Object.values(data)) {
76-
if (isPlainObject(value) && value.type === 'array') {
80+
if (isPlainObject(value) && value.type === type) {
7781
return true;
7882
}
7983

80-
if (hasArrayChild(value)) {
84+
if (hasSpecificTypeChild(value, type)) {
8185
return true;
8286
}
8387
}
8488
}
8589

8690
return false;
8791
}
92+
93+
export function hasArrayChild(data: Record<string, any> | Array<any>) {
94+
return hasSpecificTypeChild(data, JsonSchemaDataType.Array);
95+
}
96+
97+
export function hasJsonSchemaChild(data: JSONSchema) {
98+
const properties = get(data, 'properties') ?? {};
99+
return isPlainObject(properties) && Object.keys(properties).length > 0;
100+
}

0 commit comments

Comments
 (0)