Skip to content
This repository was archived by the owner on Mar 18, 2026. It is now read-only.

Commit 403bc47

Browse files
authored
Merge pull request #261 from X2bee/main
fix: Agent Xgen
2 parents 7652535 + dc70054 commit 403bc47

13 files changed

Lines changed: 121 additions & 67 deletions

File tree

src/app/canvas/components/Canvas/components/CanvasNodes.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ interface CanvasNodesProps {
2525
onOutputAdd?: (nodeId: string, output: any) => void;
2626
onOutputDelete?: (nodeId: string, outputId: string) => void;
2727
onOutputNameChange?: (nodeId: string, outputId: string, newName: string) => void;
28-
// AgentXgen specific props
29-
onOutputsUpdate?: (nodeId: string, outputs: any[]) => void;
3028
currentEdges: any[];
3129
onToggleExpanded: (nodeId: string) => void;
3230
}
@@ -51,7 +49,6 @@ export const CanvasNodes: React.FC<CanvasNodesProps> = ({
5149
onOutputAdd,
5250
onOutputDelete,
5351
onOutputNameChange,
54-
onOutputsUpdate,
5552
currentEdges,
5653
onToggleExpanded
5754
}) => {
@@ -101,11 +98,6 @@ export const CanvasNodes: React.FC<CanvasNodesProps> = ({
10198
if (propName === 'onOutputNameChange') additionalProps.onOutputNameChange = onOutputNameChange;
10299
});
103100

104-
// AgentXgen specific prop
105-
if (specialNodeConfig.name === 'AgentXgenNode') {
106-
additionalProps.onOutputsUpdate = onOutputsUpdate;
107-
}
108-
109101
return <SpecialComponent key={node.id} {...commonProps} {...additionalProps} />;
110102
}
111103

src/app/canvas/components/Canvas/hooks/useNodeManagement.ts

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ interface UseNodeManagementReturn {
2727
addOutput: (nodeId: string, newOutput: Port) => void;
2828
deleteOutput: (nodeId: string, outputId: string) => void;
2929
updateOutputName: (nodeId: string, outputId: string, newName: string) => void;
30-
// AgentXgen specific functions
31-
updateNodeOutputs: (nodeId: string, outputs: Port[]) => void;
3230
}
3331

3432
export const useNodeManagement = ({ historyHelpers }: UseNodeManagementProps = {}): UseNodeManagementReturn => {
@@ -361,32 +359,6 @@ export const useNodeManagement = ({ historyHelpers }: UseNodeManagementProps = {
361359
});
362360
}, []);
363361

364-
// AgentXgen specific function - Update entire outputs array
365-
const updateNodeOutputs = useCallback((nodeId: string, outputs: Port[]): void => {
366-
setNodes(prevNodes => {
367-
const targetNodeIndex = prevNodes.findIndex(node => node.id === nodeId);
368-
if (targetNodeIndex === -1) {
369-
return prevNodes;
370-
}
371-
372-
const targetNode = prevNodes[targetNodeIndex];
373-
374-
const newNodes = [
375-
...prevNodes.slice(0, targetNodeIndex),
376-
{
377-
...targetNode,
378-
data: {
379-
...targetNode.data,
380-
outputs
381-
}
382-
},
383-
...prevNodes.slice(targetNodeIndex + 1)
384-
];
385-
386-
return newNodes;
387-
});
388-
}, []);
389-
390362
return {
391363
nodes,
392364
setNodes,
@@ -402,7 +374,6 @@ export const useNodeManagement = ({ historyHelpers }: UseNodeManagementProps = {
402374
deleteParameter,
403375
addOutput,
404376
deleteOutput,
405-
updateOutputName,
406-
updateNodeOutputs
377+
updateOutputName
407378
};
408379
};

src/app/canvas/components/Canvas/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ const Canvas = forwardRef<CanvasRef, CanvasProps>(({
8787
deleteParameter,
8888
addOutput,
8989
deleteOutput,
90-
updateOutputName,
91-
updateNodeOutputs
90+
updateOutputName
9291
} = useNodeManagement({ historyHelpers });
9392

9493
const {
@@ -757,7 +756,6 @@ const Canvas = forwardRef<CanvasRef, CanvasProps>(({
757756
onOutputAdd={addOutput}
758757
onOutputDelete={deleteOutput}
759758
onOutputNameChange={updateOutputName}
760-
onOutputsUpdate={updateNodeOutputs}
761759
onClearSelection={clearSelection}
762760
onOpenNodeModal={onOpenNodeModal}
763761
onSynchronizeSchema={handleSynchronizeSchema}

src/app/canvas/components/Node/components/NodePorts.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import React from 'react';
1+
import React, { useMemo } from 'react';
22
import { LuDownload } from 'react-icons/lu';
33
import styles from '@/app/canvas/assets/Node.module.scss';
44
import { generatePortKey, getConnectedSchemaProvider } from '../utils/nodeUtils';
5+
import { filterPortsByDependency } from '../utils/portUtils';
56
import type { NodePortsProps } from '../types';
67

78
export const NodePorts: React.FC<NodePortsProps> = ({
89
nodeId,
910
inputs,
1011
outputs,
12+
parameters,
1113
isPreview = false,
1214
isPredicted = false,
1315
isSelected,
@@ -20,8 +22,12 @@ export const NodePorts: React.FC<NodePortsProps> = ({
2022
currentEdges,
2123
onSynchronizeSchema
2224
}) => {
23-
const hasInputs = inputs && inputs.length > 0;
24-
const hasOutputs = outputs && outputs.length > 0;
25+
// Dependency를 고려하여 실제 렌더링할 포트만 필터링
26+
const filteredInputs = useMemo(() => filterPortsByDependency(inputs, parameters), [inputs, parameters]);
27+
const filteredOutputs = useMemo(() => filterPortsByDependency(outputs, parameters), [outputs, parameters]);
28+
29+
const hasInputs = filteredInputs && filteredInputs.length > 0;
30+
const hasOutputs = filteredOutputs && filteredOutputs.length > 0;
2531
const hasOnlyOutputs = hasOutputs && !hasInputs;
2632

2733
const handleSynchronizeSchema = (portId: string) => {
@@ -38,7 +44,7 @@ export const NodePorts: React.FC<NodePortsProps> = ({
3844
{hasInputs && (
3945
<div className={styles.column}>
4046
<div className={styles.sectionHeader}>INPUT</div>
41-
{inputs.map(portData => {
47+
{filteredInputs.map(portData => {
4248
const portKey = generatePortKey(nodeId, portData.id, 'input');
4349
const isSnapping = snappedPortKey === portKey;
4450

@@ -102,7 +108,7 @@ export const NodePorts: React.FC<NodePortsProps> = ({
102108
{hasOutputs && (
103109
<div className={`${styles.column} ${styles.outputColumn} ${hasOnlyOutputs ? styles.fullWidth : ''}`}>
104110
<div className={styles.sectionHeader}>OUTPUT</div>
105-
{outputs.map(portData => {
111+
{filteredOutputs.map(portData => {
106112
const portClasses = [
107113
styles.port,
108114
styles.outputPort,

src/app/canvas/components/Node/components/NodePortsCollapsed.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import React from 'react';
1+
import React, { useMemo } from 'react';
22
import styles from '@/app/canvas/assets/Node.module.scss';
33
import { generatePortKey } from '../utils/nodeUtils';
4+
import { filterPortsByDependency } from '../utils/portUtils';
45
import type { NodePortsProps } from '../types';
56

67
export const NodePortsCollapsed: React.FC<NodePortsProps> = ({
78
nodeId,
89
inputs,
910
outputs,
11+
parameters,
1012
isPreview = false,
1113
isPredicted = false,
1214
onPortMouseDown,
@@ -15,8 +17,12 @@ export const NodePortsCollapsed: React.FC<NodePortsProps> = ({
1517
snappedPortKey,
1618
isSnapTargetInvalid
1719
}) => {
18-
const hasInputs = inputs && inputs.length > 0;
19-
const hasOutputs = outputs && outputs.length > 0;
20+
// Dependency를 고려하여 실제 렌더링할 포트만 필터링
21+
const filteredInputs = useMemo(() => filterPortsByDependency(inputs, parameters), [inputs, parameters]);
22+
const filteredOutputs = useMemo(() => filterPortsByDependency(outputs, parameters), [outputs, parameters]);
23+
24+
const hasInputs = filteredInputs && filteredInputs.length > 0;
25+
const hasOutputs = filteredOutputs && filteredOutputs.length > 0;
2026

2127
if (!hasInputs && !hasOutputs) {
2228
return null;
@@ -26,7 +32,7 @@ export const NodePortsCollapsed: React.FC<NodePortsProps> = ({
2632
<div className={styles.collapsedPorts}>
2733
{/* 입력 포트들 - 왼쪽 */}
2834
<div className={styles.collapsedInputs}>
29-
{hasInputs && inputs?.map((input) => {
35+
{hasInputs && filteredInputs?.map((input) => {
3036
const portKey = generatePortKey(nodeId, input.id, 'input');
3137
const isSnapping = snappedPortKey === portKey;
3238

@@ -76,7 +82,7 @@ export const NodePortsCollapsed: React.FC<NodePortsProps> = ({
7682

7783
{/* 출력 포트들 - 오른쪽 */}
7884
<div className={styles.collapsedOutputs}>
79-
{hasOutputs && outputs?.map((output) => {
85+
{hasOutputs && filteredOutputs?.map((output) => {
8086
const portKey = generatePortKey(nodeId, output.id, 'output');
8187
const isSnapping = snappedPortKey === portKey;
8288

src/app/canvas/components/Node/components/RouterNodePorts.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useMemo } from 'react';
22
import { LuDownload, LuPlus } from 'react-icons/lu';
33
import styles from '@/app/canvas/assets/Node.module.scss';
44
import { generatePortKey, getConnectedSchemaProvider } from '../utils/nodeUtils';
5+
import { filterPortsByDependency } from '../utils/portUtils';
56
import type { NodePortsProps } from '../types';
67
import type { Port } from '@/app/canvas/types';
78

@@ -15,6 +16,7 @@ export const RouterNodePorts: React.FC<RouterNodePortsProps> = ({
1516
nodeId,
1617
inputs,
1718
outputs,
19+
parameters,
1820
isPreview = false,
1921
isPredicted = false,
2022
isSelected,
@@ -33,8 +35,12 @@ export const RouterNodePorts: React.FC<RouterNodePortsProps> = ({
3335
const [editingOutput, setEditingOutput] = useState<string | null>(null);
3436
const [editingOutputValue, setEditingOutputValue] = useState<string>('');
3537

36-
const hasInputs = inputs && inputs.length > 0;
37-
const hasOutputs = outputs && outputs.length > 0;
38+
// Filter ports based on parameter dependencies
39+
const filteredInputs = useMemo(() => filterPortsByDependency(inputs, parameters), [inputs, parameters]);
40+
const filteredOutputs = useMemo(() => filterPortsByDependency(outputs, parameters), [outputs, parameters]);
41+
42+
const hasInputs = filteredInputs && filteredInputs.length > 0;
43+
const hasOutputs = filteredOutputs && filteredOutputs.length > 0;
3844
const hasOnlyOutputs = hasOutputs && !hasInputs;
3945

4046
const handleSynchronizeSchema = (portId: string) => {
@@ -116,7 +122,7 @@ export const RouterNodePorts: React.FC<RouterNodePortsProps> = ({
116122
{hasInputs && (
117123
<div className={styles.column}>
118124
<div className={styles.sectionHeader}>INPUT</div>
119-
{inputs.map(portData => {
125+
{filteredInputs.map(portData => {
120126
const portKey = generatePortKey(nodeId, portData.id, 'input');
121127
const isSnapping = snappedPortKey === portKey;
122128

@@ -220,7 +226,7 @@ export const RouterNodePorts: React.FC<RouterNodePortsProps> = ({
220226
</button>
221227
)}
222228
</div>
223-
{hasOutputs ? outputs.map(portData => {
229+
{hasOutputs ? filteredOutputs.map(portData => {
224230
const portClasses = [
225231
styles.port,
226232
styles.outputPort,

src/app/canvas/components/Node/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ const Node: React.FC<NodeProps> = ({
137137
nodeId={id}
138138
inputs={inputs}
139139
outputs={outputs}
140+
parameters={parameters}
140141
isPreview={isPreview}
141142
isPredicted={isPredicted}
142143
isSelected={isSelected}
@@ -181,6 +182,7 @@ const Node: React.FC<NodeProps> = ({
181182
nodeId={id}
182183
inputs={inputs}
183184
outputs={outputs}
185+
parameters={parameters}
184186
isPreview={isPreview}
185187
isPredicted={isPredicted}
186188
isSelected={isSelected}

src/app/canvas/components/Node/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export interface NodePortsProps {
9999
nodeId: string;
100100
inputs?: any[];
101101
outputs?: any[];
102+
parameters?: Parameter[]; // Port dependency 체크를 위해 필요
102103
isPreview?: boolean;
103104
isPredicted?: boolean;
104105
isSelected: boolean;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import type { Port, Parameter } from '@/app/canvas/types';
2+
3+
/**
4+
* Port의 dependency를 확인하는 유틸리티 함수들
5+
* NodeParameters의 dependency 로직과 동일하게 작동
6+
*/
7+
8+
/**
9+
* Parameter 값들을 맵으로 변환
10+
*/
11+
export const createParameterValueMap = (parameters?: Parameter[]): Record<string, Parameter['value'] | undefined> => {
12+
const valueMap: Record<string, Parameter['value'] | undefined> = {};
13+
(parameters ?? []).forEach((param) => {
14+
valueMap[param.id] = param.value;
15+
});
16+
return valueMap;
17+
};
18+
19+
/**
20+
* Boolean 값을 정규화 (문자열 'true'/'false'를 boolean으로 변환)
21+
*/
22+
export const normalizeBoolean = (value: Parameter['value'] | undefined): boolean | undefined => {
23+
if (typeof value === 'boolean') return value;
24+
if (typeof value === 'string') {
25+
const normalized = value.trim().toLowerCase();
26+
if (normalized === 'true') return true;
27+
if (normalized === 'false') return false;
28+
}
29+
return undefined;
30+
};
31+
32+
/**
33+
* Port의 dependency가 만족되는지 확인
34+
* NodeParameters의 isDependencySatisfied와 동일한 로직
35+
*/
36+
export const isPortDependencySatisfied = (
37+
port: Port,
38+
parameterValueMap: Record<string, Parameter['value'] | undefined>
39+
): boolean => {
40+
// dependency가 없으면 항상 표시
41+
if (!port.dependency) return true;
42+
43+
const dependencyValue = parameterValueMap[port.dependency];
44+
const expectedValue = port.dependencyValue ?? true;
45+
46+
// Boolean 비교
47+
if (typeof expectedValue === 'boolean') {
48+
const normalized = normalizeBoolean(dependencyValue);
49+
if (normalized !== undefined) return normalized === expectedValue;
50+
return dependencyValue === expectedValue;
51+
}
52+
53+
// String 비교 (대소문자 무시)
54+
if (typeof expectedValue === 'string') {
55+
if (typeof dependencyValue === 'string') {
56+
return dependencyValue.trim().toLowerCase() === expectedValue.trim().toLowerCase();
57+
}
58+
return false;
59+
}
60+
61+
// 기타 타입 직접 비교
62+
return dependencyValue === expectedValue;
63+
};
64+
65+
/**
66+
* Port 배열을 필터링하여 dependency가 만족되는 포트만 반환
67+
*/
68+
export const filterPortsByDependency = (
69+
ports: Port[] | undefined,
70+
parameters: Parameter[] | undefined
71+
): Port[] => {
72+
if (!ports) return [];
73+
74+
const parameterValueMap = createParameterValueMap(parameters);
75+
76+
return ports.filter(port => isPortDependencySatisfied(port, parameterValueMap));
77+
};

src/app/canvas/components/SpecialNode/RouterNode.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ const RouterNode: React.FC<NodeProps & {
159159
nodeId={id}
160160
inputs={inputs}
161161
outputs={outputs}
162+
parameters={parameters}
162163
isPreview={isPreview}
163164
isPredicted={isPredicted}
164165
isSelected={isSelected}
@@ -205,6 +206,7 @@ const RouterNode: React.FC<NodeProps & {
205206
nodeId={id}
206207
inputs={inputs}
207208
outputs={outputs}
209+
parameters={parameters}
208210
isPreview={isPreview}
209211
isPredicted={isPredicted}
210212
isSelected={isSelected}

0 commit comments

Comments
 (0)