@inkeep/agents-manage-ui 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -2
- package/.turbo/turbo-build.log +30 -30
- package/.turbo/turbo-test.log +10 -43
- package/.turbo/turbo-typecheck.log +1 -1
- package/README.md +3 -3
- package/biome.json +3 -0
- package/package.json +8 -7
- package/src/app/api/signoz/conversations/[conversationId]/route.ts +56 -34
- package/src/components/artifact-components/form/artifact-component-form.tsx +16 -7
- package/src/components/data-components/form/data-component-form.tsx +16 -7
- package/src/components/graph/configuration/node-types.tsx +2 -4
- package/src/components/graph/graph.tsx +4 -26
- package/src/components/graph/nodes/agent-node.tsx +1 -1
- package/src/components/graph/nodes/external-agent-node.tsx +1 -1
- package/src/components/graph/playground/chat-widget.tsx +2 -2
- package/src/components/graph/playground/ikp-message.tsx +16 -16
- package/src/components/graph/playground/playground.tsx +5 -5
- package/src/components/graph/sidepane/metadata/metadata-editor.tsx +34 -17
- package/src/components/graph/sidepane/nodes/agent-node-editor.tsx +34 -19
- package/src/components/graph/sidepane/nodes/external-agent-node-editor.tsx +31 -11
- package/src/components/graph/sidepane/nodes/mcp-node-editor.tsx +4 -9
- package/src/components/graph/sidepane/nodes/model-section.tsx +1 -1
- package/src/components/graph/toolbar/toolbar.tsx +1 -1
- package/src/components/mcp-servers/form/active-tools-selector.tsx +4 -2
- package/src/components/projects/form/project-form.tsx +18 -9
- package/src/components/projects/form/project-models-section.tsx +51 -23
- package/src/components/projects/form/project-stopwhen-section.tsx +25 -11
- package/src/components/traces/ai-calls-breakdown.tsx +1 -1
- package/src/components/traces/charts/area-chart-card.tsx +2 -2
- package/src/components/traces/charts/area-chart.tsx +2 -2
- package/src/components/traces/charts/chart-card.tsx +4 -4
- package/src/components/traces/conversation-detail.tsx +10 -8
- package/src/components/traces/conversation-stats/conversation-list-item.tsx +48 -37
- package/src/components/traces/conversation-stats/conversation-stats-card.tsx +1 -1
- package/src/components/traces/filters/date-picker.tsx +6 -6
- package/src/components/traces/filters/filter-trigger.tsx +1 -1
- package/src/components/traces/filters/graph-filter.tsx +3 -3
- package/src/components/traces/timeline/activity-details-sidepane.tsx +1 -1
- package/src/components/traces/timeline/activity-timeline.tsx +1 -1
- package/src/components/traces/timeline/blocks.tsx +2 -2
- package/src/components/traces/timeline/render-panel-content.tsx +11 -11
- package/src/components/traces/timeline/timeline-item.tsx +36 -22
- package/src/components/traces/timeline/timeline-wrapper.tsx +11 -12
- package/src/components/traces/traces-overview.tsx +3 -3
- package/src/components/ui/alert.tsx +17 -23
- package/src/components/ui/calendar.tsx +3 -4
- package/src/components/ui/external-link.tsx +2 -2
- package/src/components/ui/inheritance-indicator.tsx +20 -17
- package/src/components/ui/resizable.tsx +13 -18
- package/src/constants/page-descriptions.tsx +2 -5
- package/src/constants/signoz.ts +15 -18
- package/src/features/graph/domain/__tests__/roundtrip.test.ts +5 -5
- package/src/features/graph/domain/serialize.ts +8 -9
- package/src/hooks/use-auto-prefill-id-zustand.ts +68 -0
- package/src/hooks/use-auto-prefill-id.ts +36 -0
- package/src/hooks/use-chat-activities-polling.ts +1 -1
- package/src/hooks/use-graph-errors.ts +2 -1
- package/src/hooks/use-project-data.ts +2 -2
- package/src/lib/actions/graph-full.ts +6 -2
- package/src/lib/actions/projects.ts +1 -1
- package/src/lib/api/api-config.ts +6 -6
- package/src/lib/api/api-keys.ts +4 -1
- package/src/lib/api/credentials.ts +1 -1
- package/src/lib/api/data-components.ts +1 -1
- package/src/lib/api/graph-full-client.ts +6 -3
- package/src/lib/api/projects.ts +1 -1
- package/src/lib/api/signoz-sql.ts +1 -1
- package/src/lib/index.ts +1 -1
- package/src/lib/logger.ts +1 -2
- package/src/lib/utils/generate-id.ts +14 -0
- package/tsconfig.json +2 -2
- package/eslint.config.mjs +0 -14
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from '@xyflow/react';
|
|
17
17
|
import { nanoid } from 'nanoid';
|
|
18
18
|
import { useParams, useRouter } from 'next/navigation';
|
|
19
|
-
import { useCallback,
|
|
19
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
20
20
|
import { toast } from 'sonner';
|
|
21
21
|
import { commandManager } from '@/features/graph/commands/command-manager';
|
|
22
22
|
import { AddNodeCommand, AddPreparedEdgeCommand } from '@/features/graph/commands/commands';
|
|
@@ -31,7 +31,6 @@ import { saveGraph } from '@/lib/services/save-graph';
|
|
|
31
31
|
import type { FullGraphDefinition } from '@/lib/types/graph-full';
|
|
32
32
|
import { formatJsonField } from '@/lib/utils';
|
|
33
33
|
import { getErrorSummaryMessage, parseGraphValidationErrors } from '@/lib/utils/graph-error-parser';
|
|
34
|
-
import { Playground } from './playground/playground';
|
|
35
34
|
import { EdgeType, edgeTypes, initialEdges } from './configuration/edge-types';
|
|
36
35
|
import type { ContextConfig } from './configuration/graph-types';
|
|
37
36
|
import {
|
|
@@ -47,10 +46,10 @@ import { GraphErrorSummary } from './error-display/graph-error-summary';
|
|
|
47
46
|
import { DefaultMarker } from './markers/default-marker';
|
|
48
47
|
import { SelectedMarker } from './markers/selected-marker';
|
|
49
48
|
import NodeLibrary from './node-library/node-library';
|
|
49
|
+
import { Playground } from './playground/playground';
|
|
50
50
|
import { SidePane } from './sidepane/sidepane';
|
|
51
51
|
import { Toolbar } from './toolbar/toolbar';
|
|
52
52
|
|
|
53
|
-
|
|
54
53
|
function getEdgeId(a: string, b: string) {
|
|
55
54
|
const [low, high] = [a, b].sort();
|
|
56
55
|
return `edge-${low}-${high}`;
|
|
@@ -76,7 +75,7 @@ function Flow({ graph, dataComponentLookup = {}, artifactComponentLookup = {} }:
|
|
|
76
75
|
id: nanoid(),
|
|
77
76
|
type: NodeType.Agent,
|
|
78
77
|
position: { x: 0, y: 0 },
|
|
79
|
-
data: { name: '
|
|
78
|
+
data: { name: '', isDefault: true },
|
|
80
79
|
deletable: false,
|
|
81
80
|
},
|
|
82
81
|
],
|
|
@@ -179,25 +178,6 @@ function Flow({ graph, dataComponentLookup = {}, artifactComponentLookup = {} }:
|
|
|
179
178
|
}
|
|
180
179
|
}, []);
|
|
181
180
|
|
|
182
|
-
const generateUniqueName = useCallback(
|
|
183
|
-
(type: string) => {
|
|
184
|
-
if (type !== NodeType.Agent && type !== NodeType.ExternalAgent)
|
|
185
|
-
return newNodeDefaults[type as keyof typeof newNodeDefaults].name;
|
|
186
|
-
|
|
187
|
-
const agentNodes = nodes.filter(
|
|
188
|
-
(node) => node.type === NodeType.Agent || node.type === NodeType.ExternalAgent
|
|
189
|
-
);
|
|
190
|
-
const existingNames = new Set(agentNodes.map((node) => node.data.name));
|
|
191
|
-
let counter = 1;
|
|
192
|
-
|
|
193
|
-
while (existingNames.has(`Agent ${counter}`)) {
|
|
194
|
-
counter++;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return `Agent ${counter}`;
|
|
198
|
-
},
|
|
199
|
-
[nodes]
|
|
200
|
-
);
|
|
201
181
|
|
|
202
182
|
// biome-ignore lint/correctness/useExhaustiveDependencies: we only want to add/connect edges once
|
|
203
183
|
const onConnectWrapped = useCallback((params: Connection) => {
|
|
@@ -280,14 +260,13 @@ function Flow({ graph, dataComponentLookup = {}, artifactComponentLookup = {} }:
|
|
|
280
260
|
selected: true,
|
|
281
261
|
data: {
|
|
282
262
|
...newNodeDefaults[nodeData.type as keyof typeof newNodeDefaults],
|
|
283
|
-
name: generateUniqueName(nodeData.type),
|
|
284
263
|
},
|
|
285
264
|
};
|
|
286
265
|
|
|
287
266
|
clearSelection();
|
|
288
267
|
commandManager.execute(new AddNodeCommand(newNode as Node));
|
|
289
268
|
},
|
|
290
|
-
[screenToFlowPosition,
|
|
269
|
+
[screenToFlowPosition, clearSelection]
|
|
291
270
|
);
|
|
292
271
|
|
|
293
272
|
const onSelectionChange = useCallback(
|
|
@@ -401,7 +380,6 @@ function Flow({ graph, dataComponentLookup = {}, artifactComponentLookup = {} }:
|
|
|
401
380
|
artifactComponentLookup
|
|
402
381
|
);
|
|
403
382
|
|
|
404
|
-
|
|
405
383
|
const res = await saveGraph(
|
|
406
384
|
tenantId,
|
|
407
385
|
projectId,
|
|
@@ -79,7 +79,7 @@ export function AgentNode(props: NodeProps & { data: AgentNodeData }) {
|
|
|
79
79
|
<BaseNodeHeader className="flex items-center justify-between gap-2">
|
|
80
80
|
<div className="flex items-center gap-2">
|
|
81
81
|
<Bot className="size-4 text-muted-foreground" />
|
|
82
|
-
<BaseNodeHeaderTitle>{name}</BaseNodeHeaderTitle>
|
|
82
|
+
<BaseNodeHeaderTitle>{name || 'Agent'}</BaseNodeHeaderTitle>
|
|
83
83
|
</div>
|
|
84
84
|
{hasErrors && (
|
|
85
85
|
<ErrorIndicator errors={nodeErrors} className="absolute -top-2 -right-2 w-6 h-6" />
|
|
@@ -30,7 +30,7 @@ export function ExternalAgentNode(props: NodeProps & { data: AgentNodeData }) {
|
|
|
30
30
|
<BaseNodeHeader className="flex items-center justify-between gap-2">
|
|
31
31
|
<div className="flex items-center gap-2">
|
|
32
32
|
<BotMessageSquare className="size-4 text-muted-foreground" />
|
|
33
|
-
<BaseNodeHeaderTitle>{name}</BaseNodeHeaderTitle>
|
|
33
|
+
<BaseNodeHeaderTitle>{name || 'External agent'}</BaseNodeHeaderTitle>
|
|
34
34
|
</div>
|
|
35
35
|
{hasErrors && (
|
|
36
36
|
<ErrorIndicator errors={nodeErrors} className="absolute -top-2 -right-2 w-6 h-6" />
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useRef, useEffect } from 'react';
|
|
3
2
|
import { InkeepEmbeddedChat } from '@inkeep/cxkit-react-oss';
|
|
4
3
|
import type { ComponentsConfig, InkeepCallbackEvent } from '@inkeep/cxkit-react-oss/types';
|
|
4
|
+
import { nanoid } from 'nanoid';
|
|
5
|
+
import { useEffect, useRef } from 'react';
|
|
5
6
|
import { EXECUTION_API_BASE_URL } from '@/lib/api/api-config';
|
|
6
7
|
import { IkpMessage as IkpMessageComponent } from './ikp-message';
|
|
7
|
-
import { nanoid } from 'nanoid';
|
|
8
8
|
|
|
9
9
|
interface ChatWidgetProps {
|
|
10
10
|
graphId?: string;
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
Sparkles,
|
|
16
16
|
Users,
|
|
17
17
|
} from 'lucide-react';
|
|
18
|
-
import { type FC, useEffect, useMemo,
|
|
18
|
+
import { type FC, useEffect, useMemo, useRef, useState } from 'react';
|
|
19
19
|
import supersub from 'remark-supersub';
|
|
20
20
|
import { Streamdown } from 'streamdown';
|
|
21
21
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
@@ -278,17 +278,17 @@ const OperationStep: FC<{ operation: any; isLast: boolean }> = ({ operation, isL
|
|
|
278
278
|
const renderStructuredLabel = (operationType: string, context: any) => {
|
|
279
279
|
// Convert snake_case to readable format
|
|
280
280
|
const readableType = operationType.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
|
|
281
|
-
|
|
281
|
+
|
|
282
282
|
// Try to find the most meaningful fields to display
|
|
283
283
|
const meaningfulFields = Object.entries(context).filter(
|
|
284
284
|
([key, value]) => typeof value === 'string' && value.length > 0 && value.length < 100
|
|
285
285
|
);
|
|
286
|
-
|
|
286
|
+
|
|
287
287
|
if (meaningfulFields.length > 0) {
|
|
288
288
|
const [firstKey, firstValue] = meaningfulFields[0];
|
|
289
289
|
return `${readableType}: ${firstValue}`;
|
|
290
290
|
}
|
|
291
|
-
|
|
291
|
+
|
|
292
292
|
return readableType;
|
|
293
293
|
};
|
|
294
294
|
|
|
@@ -315,20 +315,20 @@ const OperationStep: FC<{ operation: any; isLast: boolean }> = ({ operation, isL
|
|
|
315
315
|
|
|
316
316
|
return (
|
|
317
317
|
<div className="relative">
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
318
|
+
<div className="flex items-center gap-2 relative">
|
|
319
|
+
{/* Connection line */}
|
|
320
|
+
{!isLast && (
|
|
321
|
+
<div className="absolute left-1.5 top-6 bottom-0 w-px bg-gray-200 dark:bg-border" />
|
|
322
|
+
)}
|
|
323
323
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
324
|
+
{/* Step indicator */}
|
|
325
|
+
<div className={cn('flex items-center justify-center w-3 h-3 z-10', getStepColor())}>
|
|
326
|
+
{getStepIcon()}
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
{/* Step label */}
|
|
330
|
+
<span className={cn('text-xs font-medium', getStepColor())}>{getStepLabel()}</span>
|
|
328
331
|
|
|
329
|
-
{/* Step label */}
|
|
330
|
-
<span className={cn('text-xs font-medium', getStepColor())}>{getStepLabel()}</span>
|
|
331
|
-
|
|
332
332
|
{/* Expand button for structured operations */}
|
|
333
333
|
{isStructuredOperation && Object.keys(ctx).length > 1 && (
|
|
334
334
|
<button
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { ArrowLeft } from 'lucide-react';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
1
3
|
import { useState } from 'react';
|
|
2
|
-
import { ChatWidget } from './chat-widget';
|
|
3
|
-
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable';
|
|
4
4
|
import { TimelineWrapper } from '@/components/traces/timeline/timeline-wrapper';
|
|
5
|
-
import { useChatActivitiesPolling } from '@/hooks/use-chat-activities-polling';
|
|
6
|
-
import { nanoid } from 'nanoid';
|
|
7
5
|
import { Button } from '@/components/ui/button';
|
|
8
|
-
import {
|
|
6
|
+
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable';
|
|
7
|
+
import { useChatActivitiesPolling } from '@/hooks/use-chat-activities-polling';
|
|
8
|
+
import { ChatWidget } from './chat-widget';
|
|
9
9
|
|
|
10
10
|
interface PlaygroundProps {
|
|
11
11
|
graphId: string;
|
|
@@ -7,23 +7,24 @@ import { ExpandableJsonEditor } from '@/components/form/expandable-json-editor';
|
|
|
7
7
|
import { Button } from '@/components/ui/button';
|
|
8
8
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
|
9
9
|
import { CopyableSingleLineCode } from '@/components/ui/copyable-single-line-code';
|
|
10
|
+
import {
|
|
11
|
+
getExecutionLimitInheritanceStatus,
|
|
12
|
+
getModelInheritanceStatus,
|
|
13
|
+
InheritanceIndicator,
|
|
14
|
+
} from '@/components/ui/inheritance-indicator';
|
|
10
15
|
import { Input } from '@/components/ui/input';
|
|
11
16
|
import { Label } from '@/components/ui/label';
|
|
12
17
|
import { Separator } from '@/components/ui/separator';
|
|
13
18
|
import { Textarea } from '@/components/ui/textarea';
|
|
14
|
-
import {
|
|
15
|
-
InheritanceIndicator,
|
|
16
|
-
getModelInheritanceStatus,
|
|
17
|
-
getExecutionLimitInheritanceStatus,
|
|
18
|
-
} from '@/components/ui/inheritance-indicator';
|
|
19
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
19
20
|
import { useGraphStore } from '@/features/graph/state/use-graph-store';
|
|
20
|
-
import { EXECUTION_API_BASE_URL } from '@/lib/api/api-config';
|
|
21
21
|
import { useProjectData } from '@/hooks/use-project-data';
|
|
22
|
+
import { useAutoPrefillIdZustand } from '@/hooks/use-auto-prefill-id-zustand';
|
|
23
|
+
import { EXECUTION_API_BASE_URL } from '@/lib/api/api-config';
|
|
24
|
+
import { ExpandableTextArea } from '../nodes/expandable-text-area';
|
|
25
|
+
import { InputField, TextareaField } from '../nodes/form-fields';
|
|
22
26
|
import { ModelSelector } from '../nodes/model-selector';
|
|
23
27
|
import { ContextConfigForm } from './context-config';
|
|
24
|
-
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
25
|
-
import { InputField, TextareaField } from '../nodes/form-fields';
|
|
26
|
-
import { ExpandableTextArea } from '../nodes/expandable-text-area';
|
|
27
28
|
|
|
28
29
|
function MetadataEditor() {
|
|
29
30
|
const params = useParams();
|
|
@@ -46,6 +47,21 @@ function MetadataEditor() {
|
|
|
46
47
|
[setMetadata, markUnsaved]
|
|
47
48
|
);
|
|
48
49
|
|
|
50
|
+
const handleIdChange = useCallback(
|
|
51
|
+
(generatedId: string) => {
|
|
52
|
+
updateMetadata('id', generatedId);
|
|
53
|
+
},
|
|
54
|
+
[updateMetadata]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Auto-prefill ID based on name field (only for new graphs)
|
|
58
|
+
useAutoPrefillIdZustand({
|
|
59
|
+
nameValue: name,
|
|
60
|
+
idValue: id,
|
|
61
|
+
onIdChange: handleIdChange,
|
|
62
|
+
isEditing: !!graphId,
|
|
63
|
+
});
|
|
64
|
+
|
|
49
65
|
return (
|
|
50
66
|
<div className="space-y-8">
|
|
51
67
|
{graphId && (
|
|
@@ -66,6 +82,15 @@ function MetadataEditor() {
|
|
|
66
82
|
<CopyableSingleLineCode code={graphUrl} />
|
|
67
83
|
</div>
|
|
68
84
|
)}
|
|
85
|
+
<InputField
|
|
86
|
+
id="name"
|
|
87
|
+
name="name"
|
|
88
|
+
label="Name"
|
|
89
|
+
value={name}
|
|
90
|
+
onChange={(e) => updateMetadata('name', e.target.value)}
|
|
91
|
+
placeholder="My graph"
|
|
92
|
+
isRequired
|
|
93
|
+
/>
|
|
69
94
|
<InputField
|
|
70
95
|
id="id"
|
|
71
96
|
name="id"
|
|
@@ -79,14 +104,6 @@ function MetadataEditor() {
|
|
|
79
104
|
? 'Choose a unique identifier for this graph. Using an existing id will replace that graph.'
|
|
80
105
|
: undefined
|
|
81
106
|
}
|
|
82
|
-
/>
|
|
83
|
-
<InputField
|
|
84
|
-
id="name"
|
|
85
|
-
name="name"
|
|
86
|
-
label="Name"
|
|
87
|
-
value={name}
|
|
88
|
-
onChange={(e) => updateMetadata('name', e.target.value)}
|
|
89
|
-
placeholder="My graph"
|
|
90
107
|
isRequired
|
|
91
108
|
/>
|
|
92
109
|
<TextareaField
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import type { Node } from '@xyflow/react';
|
|
2
2
|
import { useParams } from 'next/navigation';
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
|
+
import {
|
|
5
|
+
getExecutionLimitInheritanceStatus,
|
|
6
|
+
InheritanceIndicator,
|
|
7
|
+
} from '@/components/ui/inheritance-indicator';
|
|
8
|
+
import { Input } from '@/components/ui/input';
|
|
9
|
+
import { Label } from '@/components/ui/label';
|
|
10
|
+
import { useGraphStore } from '@/features/graph/state/use-graph-store';
|
|
4
11
|
import type { ErrorHelpers } from '@/hooks/use-graph-errors';
|
|
5
12
|
import { useNodeEditor } from '@/hooks/use-node-editor';
|
|
6
13
|
import { useProjectData } from '@/hooks/use-project-data';
|
|
7
|
-
import {
|
|
14
|
+
import { useAutoPrefillIdZustand } from '@/hooks/use-auto-prefill-id-zustand';
|
|
8
15
|
import type { ArtifactComponent } from '@/lib/api/artifact-components';
|
|
9
16
|
import type { DataComponent } from '@/lib/api/data-components';
|
|
10
|
-
import { Input } from '@/components/ui/input';
|
|
11
|
-
import { Label } from '@/components/ui/label';
|
|
12
|
-
import {
|
|
13
|
-
InheritanceIndicator,
|
|
14
|
-
getExecutionLimitInheritanceStatus,
|
|
15
|
-
} from '@/components/ui/inheritance-indicator';
|
|
16
17
|
import type { AgentNodeData } from '../../configuration/node-types';
|
|
17
18
|
import { ComponentSelector } from './component-selector/component-selector';
|
|
18
19
|
import { ExpandableTextArea } from './expandable-text-area';
|
|
@@ -53,8 +54,34 @@ export function AgentNodeEditor({
|
|
|
53
54
|
[updateNestedPath, selectedNode.data]
|
|
54
55
|
);
|
|
55
56
|
|
|
57
|
+
const handleIdChange = useCallback(
|
|
58
|
+
(generatedId: string) => {
|
|
59
|
+
updatePath('id', generatedId);
|
|
60
|
+
},
|
|
61
|
+
[updatePath]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Auto-prefill ID based on name field (always enabled for agent nodes)
|
|
65
|
+
useAutoPrefillIdZustand({
|
|
66
|
+
nameValue: selectedNode.data.name,
|
|
67
|
+
idValue: selectedNode.data.id,
|
|
68
|
+
onIdChange: handleIdChange,
|
|
69
|
+
isEditing: false,
|
|
70
|
+
});
|
|
71
|
+
|
|
56
72
|
return (
|
|
57
73
|
<div className="space-y-8 flex flex-col">
|
|
74
|
+
<InputField
|
|
75
|
+
ref={(el) => setFieldRef('name', el)}
|
|
76
|
+
id="name"
|
|
77
|
+
name="name"
|
|
78
|
+
label="Name"
|
|
79
|
+
value={selectedNode.data.name || ''}
|
|
80
|
+
onChange={(e) => updatePath('name', e.target.value)}
|
|
81
|
+
placeholder="Support agent"
|
|
82
|
+
error={getFieldError('name')}
|
|
83
|
+
isRequired
|
|
84
|
+
/>
|
|
58
85
|
<InputField
|
|
59
86
|
ref={(el) => setFieldRef('id', el)}
|
|
60
87
|
id="id"
|
|
@@ -65,20 +92,8 @@ export function AgentNodeEditor({
|
|
|
65
92
|
placeholder="my-agent"
|
|
66
93
|
error={getFieldError('id')}
|
|
67
94
|
description="Choose a unique identifier for this agent. Using an existing id will replace that agent."
|
|
68
|
-
/>
|
|
69
|
-
|
|
70
|
-
<InputField
|
|
71
|
-
ref={(el) => setFieldRef('name', el)}
|
|
72
|
-
id="name"
|
|
73
|
-
name="name"
|
|
74
|
-
label="Name"
|
|
75
|
-
value={selectedNode.data.name || ''}
|
|
76
|
-
onChange={(e) => updatePath('name', e.target.value)}
|
|
77
|
-
placeholder="Support agent"
|
|
78
|
-
error={getFieldError('name')}
|
|
79
95
|
isRequired
|
|
80
96
|
/>
|
|
81
|
-
|
|
82
97
|
<TextareaField
|
|
83
98
|
ref={(el) => setFieldRef('description', el)}
|
|
84
99
|
id="description"
|
|
@@ -3,6 +3,8 @@ import type { ErrorHelpers } from '@/hooks/use-graph-errors';
|
|
|
3
3
|
import { useNodeEditor } from '@/hooks/use-node-editor';
|
|
4
4
|
import type { ExternalAgentNodeData } from '../../configuration/node-types';
|
|
5
5
|
import { InputField, TextareaField } from './form-fields';
|
|
6
|
+
import { useCallback } from 'react';
|
|
7
|
+
import { useAutoPrefillIdZustand } from '@/hooks/use-auto-prefill-id-zustand';
|
|
6
8
|
|
|
7
9
|
interface ExternalAgentNodeEditorProps {
|
|
8
10
|
selectedNode: Node<ExternalAgentNodeData>;
|
|
@@ -18,6 +20,23 @@ export function ExternalAgentNodeEditor({
|
|
|
18
20
|
errorHelpers,
|
|
19
21
|
});
|
|
20
22
|
|
|
23
|
+
const handleIdChange = useCallback(
|
|
24
|
+
(generatedId: string) => {
|
|
25
|
+
handleInputChange({
|
|
26
|
+
target: { name: 'id', value: generatedId },
|
|
27
|
+
} as React.ChangeEvent<HTMLInputElement>);
|
|
28
|
+
},
|
|
29
|
+
[handleInputChange]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Auto-prefill ID based on name field (always enabled for agent nodes)
|
|
33
|
+
useAutoPrefillIdZustand({
|
|
34
|
+
nameValue: selectedNode.data.name,
|
|
35
|
+
idValue: selectedNode.data.id,
|
|
36
|
+
onIdChange: handleIdChange,
|
|
37
|
+
isEditing: false,
|
|
38
|
+
});
|
|
39
|
+
|
|
21
40
|
return (
|
|
22
41
|
<div className="space-y-8 flex flex-col">
|
|
23
42
|
<p className="text-sm text-muted-foreground">
|
|
@@ -26,6 +45,17 @@ export function ExternalAgentNodeEditor({
|
|
|
26
45
|
within the agent framework or to third-party services.
|
|
27
46
|
</p>
|
|
28
47
|
|
|
48
|
+
<InputField
|
|
49
|
+
ref={(el) => setFieldRef('name', el)}
|
|
50
|
+
id="name"
|
|
51
|
+
name="name"
|
|
52
|
+
label="Name"
|
|
53
|
+
value={selectedNode.data.name || ''}
|
|
54
|
+
onChange={handleInputChange}
|
|
55
|
+
placeholder="Support agent"
|
|
56
|
+
error={getFieldError('name')}
|
|
57
|
+
/>
|
|
58
|
+
|
|
29
59
|
<InputField
|
|
30
60
|
ref={(el) => setFieldRef('id', el)}
|
|
31
61
|
id="id"
|
|
@@ -36,17 +66,7 @@ export function ExternalAgentNodeEditor({
|
|
|
36
66
|
placeholder="my-external-agent"
|
|
37
67
|
error={getFieldError('id')}
|
|
38
68
|
description="Choose a unique identifier for this agent. Using an existing id will replace that agent."
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<InputField
|
|
42
|
-
ref={(el) => setFieldRef('name', el)}
|
|
43
|
-
id="name"
|
|
44
|
-
name="name"
|
|
45
|
-
label="Name"
|
|
46
|
-
value={selectedNode.data.name || ''}
|
|
47
|
-
onChange={handleInputChange}
|
|
48
|
-
placeholder="Support agent"
|
|
49
|
-
error={getFieldError('name')}
|
|
69
|
+
isRequired
|
|
50
70
|
/>
|
|
51
71
|
|
|
52
72
|
<TextareaField
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { type Node, useReactFlow } from '@xyflow/react';
|
|
2
|
-
import { ExternalLink } from 'lucide-react';
|
|
3
|
-
import Link from 'next/link';
|
|
4
2
|
import { useParams } from 'next/navigation';
|
|
5
3
|
import { getActiveTools } from '@/app/utils/active-tools';
|
|
6
4
|
import { MCPToolImage } from '@/components/mcp-servers/mcp-tool-image';
|
|
7
5
|
import { Badge } from '@/components/ui/badge';
|
|
6
|
+
import { ExternalLink } from '@/components/ui/external-link';
|
|
8
7
|
import { Input } from '@/components/ui/input';
|
|
9
8
|
import { Label } from '@/components/ui/label';
|
|
10
9
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
@@ -126,15 +125,11 @@ export function MCPServerNodeEditor({ selectedNode }: MCPServerNodeEditorProps)
|
|
|
126
125
|
)}
|
|
127
126
|
</div>
|
|
128
127
|
|
|
129
|
-
<
|
|
128
|
+
<ExternalLink
|
|
130
129
|
href={`/${tenantId}/projects/${projectId}/mcp-servers/${selectedNode.data.id}/edit`}
|
|
131
|
-
target="_blank"
|
|
132
|
-
rel="noopener noreferrer"
|
|
133
|
-
className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
134
130
|
>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
</Link>
|
|
131
|
+
Edit MCP Server
|
|
132
|
+
</ExternalLink>
|
|
138
133
|
</div>
|
|
139
134
|
);
|
|
140
135
|
}
|
|
@@ -4,8 +4,8 @@ import type { AgentNodeData } from '@/components/graph/configuration/node-types'
|
|
|
4
4
|
import { Button } from '@/components/ui/button';
|
|
5
5
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
|
6
6
|
import {
|
|
7
|
-
InheritanceIndicator,
|
|
8
7
|
getModelInheritanceStatus,
|
|
8
|
+
InheritanceIndicator,
|
|
9
9
|
} from '@/components/ui/inheritance-indicator';
|
|
10
10
|
import { ModelSelector } from './model-selector';
|
|
11
11
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Play, Settings } from 'lucide-react';
|
|
2
2
|
import { Button } from '@/components/ui/button';
|
|
3
3
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
4
4
|
import { useGraphStore } from '@/features/graph/state/use-graph-store';
|
|
@@ -104,8 +104,10 @@ export function ActiveToolsSelector<
|
|
|
104
104
|
return availableTools.some((tool) => tool.name === toolName);
|
|
105
105
|
case 'selective':
|
|
106
106
|
// Only return true if tool is selected AND still exists in availableTools
|
|
107
|
-
return
|
|
108
|
-
|
|
107
|
+
return (
|
|
108
|
+
safeToolsConfig.tools.includes(toolName) &&
|
|
109
|
+
availableTools.some((tool) => tool.name === toolName)
|
|
110
|
+
);
|
|
109
111
|
default:
|
|
110
112
|
return false;
|
|
111
113
|
}
|
|
@@ -9,11 +9,12 @@ import { GenericTextarea } from '@/components/form/generic-textarea';
|
|
|
9
9
|
import { Button } from '@/components/ui/button';
|
|
10
10
|
import { Form } from '@/components/ui/form';
|
|
11
11
|
import { Separator } from '@/components/ui/separator';
|
|
12
|
+
import { useAutoPrefillId } from '@/hooks/use-auto-prefill-id';
|
|
12
13
|
import { createProjectAction, updateProjectAction } from '@/lib/actions/projects';
|
|
13
14
|
import { defaultValues } from './form-configuration';
|
|
14
|
-
import { type ProjectFormData, projectSchema } from './validation';
|
|
15
15
|
import { ProjectModelsSection } from './project-models-section';
|
|
16
16
|
import { ProjectStopWhenSection } from './project-stopwhen-section';
|
|
17
|
+
import { type ProjectFormData, projectSchema } from './validation';
|
|
17
18
|
|
|
18
19
|
interface ProjectFormProps {
|
|
19
20
|
tenantId: string;
|
|
@@ -38,6 +39,14 @@ export function ProjectForm({
|
|
|
38
39
|
const { isSubmitting } = form.formState;
|
|
39
40
|
const router = useRouter();
|
|
40
41
|
|
|
42
|
+
// Auto-prefill ID based on name field (only for new components)
|
|
43
|
+
useAutoPrefillId({
|
|
44
|
+
form,
|
|
45
|
+
nameField: 'name',
|
|
46
|
+
idField: 'id',
|
|
47
|
+
isEditing: !!projectId,
|
|
48
|
+
});
|
|
49
|
+
|
|
41
50
|
const onSubmit = async (data: ProjectFormData) => {
|
|
42
51
|
try {
|
|
43
52
|
if (projectId) {
|
|
@@ -75,6 +84,14 @@ export function ProjectForm({
|
|
|
75
84
|
return (
|
|
76
85
|
<Form {...form}>
|
|
77
86
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
87
|
+
<GenericInput
|
|
88
|
+
control={form.control}
|
|
89
|
+
name="name"
|
|
90
|
+
label="Project Name"
|
|
91
|
+
placeholder="My Project"
|
|
92
|
+
description="A friendly name for your project"
|
|
93
|
+
isRequired
|
|
94
|
+
/>
|
|
78
95
|
<GenericInput
|
|
79
96
|
control={form.control}
|
|
80
97
|
name="id"
|
|
@@ -84,14 +101,6 @@ export function ProjectForm({
|
|
|
84
101
|
disabled={!!projectId}
|
|
85
102
|
isRequired
|
|
86
103
|
/>
|
|
87
|
-
<GenericInput
|
|
88
|
-
control={form.control}
|
|
89
|
-
name="name"
|
|
90
|
-
label="Project Name"
|
|
91
|
-
placeholder="My Project"
|
|
92
|
-
description="A friendly name for your project"
|
|
93
|
-
isRequired
|
|
94
|
-
/>
|
|
95
104
|
<GenericTextarea
|
|
96
105
|
control={form.control}
|
|
97
106
|
name="description"
|