@inkeep/agents-manage-ui 0.1.1 → 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 +10 -0
- package/.turbo/turbo-build.log +48 -54
- package/.turbo/turbo-test.log +7 -7
- package/.turbo/turbo-typecheck.log +1 -1
- package/LICENSE.md +22 -17
- package/README.md +3 -3
- package/biome.json +3 -0
- package/package.json +10 -9
- package/src/app/api/signoz/conversations/[conversationId]/route.ts +95 -34
- package/src/app/api/signoz/route.ts +8 -6
- package/src/components/api-keys/form/api-key-form.tsx +2 -0
- package/src/components/api-keys/form/validation.ts +1 -1
- package/src/components/artifact-components/form/artifact-component-form.tsx +20 -6
- package/src/components/artifact-components/form/validation.ts +3 -3
- package/src/components/credentials/views/credential-form-validation.ts +1 -1
- package/src/components/credentials/views/credential-form.tsx +2 -0
- package/src/components/credentials/views/edit-credential-form.tsx +1 -1
- package/src/components/credentials/views/generic-auth-form.tsx +1 -1
- package/src/components/data-components/form/data-component-form.tsx +19 -1
- package/src/components/data-components/form/validation.ts +3 -2
- package/src/components/form/expandable-field.tsx +6 -1
- package/src/components/form/form-field-wrapper.tsx +3 -1
- package/src/components/form/generic-combo-box.tsx +3 -1
- package/src/components/form/generic-input.tsx +9 -1
- package/src/components/form/generic-select.tsx +3 -1
- package/src/components/form/generic-textarea.tsx +3 -1
- package/src/components/form/json-schema-input.tsx +9 -1
- 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 +31 -2
- package/src/components/graph/playground/ikp-message.tsx +16 -16
- package/src/components/graph/playground/playground.tsx +12 -6
- package/src/components/graph/sidepane/metadata/metadata-editor.tsx +62 -45
- package/src/components/graph/sidepane/nodes/agent-node-editor.tsx +56 -27
- package/src/components/graph/sidepane/nodes/expandable-text-area.tsx +3 -1
- package/src/components/graph/sidepane/nodes/external-agent-node-editor.tsx +31 -11
- package/src/components/graph/sidepane/nodes/form-fields.tsx +10 -1
- 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/mcp-servers/form/mcp-server-form.tsx +8 -1
- package/src/components/mcp-servers/form/validation.ts +1 -1
- package/src/components/projects/edit-project-dialog.tsx +1 -1
- package/src/components/projects/form/project-form.tsx +20 -8
- 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/projects/form/validation.ts +14 -10
- package/src/components/projects/new-project-dialog.tsx +1 -1
- 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 +125 -37
- package/src/components/traces/traces-overview.tsx +3 -3
- package/src/components/ui/alert.tsx +60 -0
- package/src/components/ui/calendar.tsx +3 -4
- package/src/components/ui/external-link.tsx +2 -2
- package/src/components/ui/form.tsx +11 -4
- 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 +45 -12
- 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/api/signoz-stats.ts +958 -304
- package/src/lib/index.ts +1 -1
- package/src/lib/logger.ts +1 -2
- package/src/lib/types/graph-full.ts +1 -1
- package/src/lib/utils/generate-id.ts +14 -0
- package/src/lib/validation.ts +1 -1
- package/tsconfig.json +2 -2
- package/.env.sample +0 -5
- package/eslint.config.mjs +0 -14
|
@@ -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;
|
|
@@ -25,9 +25,11 @@ export const Playground = ({
|
|
|
25
25
|
const {
|
|
26
26
|
chatActivities,
|
|
27
27
|
isPolling,
|
|
28
|
-
error
|
|
28
|
+
error,
|
|
29
29
|
startPolling,
|
|
30
30
|
stopPolling,
|
|
31
|
+
retryConnection,
|
|
32
|
+
refreshOnce,
|
|
31
33
|
} = useChatActivitiesPolling({
|
|
32
34
|
conversationId,
|
|
33
35
|
});
|
|
@@ -58,6 +60,10 @@ export const Playground = ({
|
|
|
58
60
|
isPolling={isPolling}
|
|
59
61
|
conversation={chatActivities}
|
|
60
62
|
enableAutoScroll={true}
|
|
63
|
+
error={error}
|
|
64
|
+
retryConnection={retryConnection}
|
|
65
|
+
refreshOnce={refreshOnce}
|
|
66
|
+
showConversationTracesLink={true}
|
|
61
67
|
/>
|
|
62
68
|
</ResizablePanelGroup>
|
|
63
69
|
</div>
|
|
@@ -2,26 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
import { ChevronRight, Info } from 'lucide-react';
|
|
4
4
|
import { useParams } from 'next/navigation';
|
|
5
|
-
import { useCallback
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
6
|
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
28
|
|
|
26
29
|
function MetadataEditor() {
|
|
27
30
|
const params = useParams();
|
|
@@ -44,6 +47,21 @@ function MetadataEditor() {
|
|
|
44
47
|
[setMetadata, markUnsaved]
|
|
45
48
|
);
|
|
46
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
|
+
|
|
47
65
|
return (
|
|
48
66
|
<div className="space-y-8">
|
|
49
67
|
{graphId && (
|
|
@@ -64,45 +82,44 @@ function MetadataEditor() {
|
|
|
64
82
|
<CopyableSingleLineCode code={graphUrl} />
|
|
65
83
|
</div>
|
|
66
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
|
+
/>
|
|
94
|
+
<InputField
|
|
95
|
+
id="id"
|
|
96
|
+
name="id"
|
|
97
|
+
label="Id"
|
|
98
|
+
value={id || ''}
|
|
99
|
+
onChange={(e) => updateMetadata('id', e.target.value)}
|
|
100
|
+
disabled={!!graphId} // only editable if no graphId is set (i.e. new graph)
|
|
101
|
+
placeholder="my-graph"
|
|
102
|
+
description={
|
|
103
|
+
!graphId
|
|
104
|
+
? 'Choose a unique identifier for this graph. Using an existing id will replace that graph.'
|
|
105
|
+
: undefined
|
|
106
|
+
}
|
|
107
|
+
isRequired
|
|
108
|
+
/>
|
|
109
|
+
<TextareaField
|
|
110
|
+
id="description"
|
|
111
|
+
name="description"
|
|
112
|
+
label="Description"
|
|
113
|
+
value={description}
|
|
114
|
+
onChange={(e) => updateMetadata('description', e.target.value)}
|
|
115
|
+
placeholder="This graph is used to..."
|
|
116
|
+
className="max-h-96"
|
|
117
|
+
/>
|
|
67
118
|
<div className="space-y-2">
|
|
68
|
-
<
|
|
69
|
-
<Input
|
|
70
|
-
id="id"
|
|
71
|
-
value={id || ''}
|
|
72
|
-
onChange={(e) => updateMetadata('id', e.target.value)}
|
|
73
|
-
disabled={!!graphId} // only editable if no graphId is set (i.e. new graph)
|
|
74
|
-
placeholder="my-graph"
|
|
75
|
-
/>
|
|
76
|
-
{!graphId && (
|
|
77
|
-
<p className="text-sm text-muted-foreground">
|
|
78
|
-
Choose a unique identifier for this graph. Using an existing id will replace that graph.
|
|
79
|
-
</p>
|
|
80
|
-
)}
|
|
81
|
-
</div>
|
|
82
|
-
<div className="space-y-2">
|
|
83
|
-
<Label htmlFor="name">Name</Label>
|
|
84
|
-
<Input
|
|
85
|
-
id="name"
|
|
86
|
-
value={name}
|
|
87
|
-
onChange={(e) => updateMetadata('name', e.target.value)}
|
|
88
|
-
placeholder="My graph"
|
|
89
|
-
/>
|
|
90
|
-
</div>
|
|
91
|
-
<div className="space-y-2">
|
|
92
|
-
<Label htmlFor="description">Description</Label>
|
|
93
|
-
<Textarea
|
|
94
|
-
id="description"
|
|
95
|
-
value={description}
|
|
96
|
-
onChange={(e) => updateMetadata('description', e.target.value)}
|
|
97
|
-
placeholder="This graph is used to..."
|
|
98
|
-
className="max-h-96"
|
|
99
|
-
/>
|
|
100
|
-
</div>
|
|
101
|
-
|
|
102
|
-
<div className="space-y-2">
|
|
103
|
-
<Label htmlFor="graph-prompt">Graph Prompt</Label>
|
|
104
|
-
<Textarea
|
|
119
|
+
<ExpandableTextArea
|
|
105
120
|
id="graph-prompt"
|
|
121
|
+
name="graph-prompt"
|
|
122
|
+
label="Graph Prompt"
|
|
106
123
|
value={graphPrompt || ''}
|
|
107
124
|
onChange={(e) => updateMetadata('graphPrompt', e.target.value)}
|
|
108
125
|
placeholder="System-level instructions for this graph..."
|
|
@@ -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';
|
|
@@ -35,7 +36,7 @@ export function AgentNodeEditor({
|
|
|
35
36
|
const { tenantId, projectId } = useParams<{ tenantId: string; projectId: string }>();
|
|
36
37
|
const selectedDataComponents = selectedNode.data?.dataComponents || [];
|
|
37
38
|
const selectedArtifactComponents = selectedNode.data?.artifactComponents || [];
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
// Get project and graph data for inheritance indicators
|
|
40
41
|
const { project } = useProjectData();
|
|
41
42
|
const metadata = useGraphStore((state) => state.metadata);
|
|
@@ -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,19 +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."
|
|
95
|
+
isRequired
|
|
68
96
|
/>
|
|
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
|
-
/>
|
|
80
|
-
|
|
81
97
|
<TextareaField
|
|
82
98
|
ref={(el) => setFieldRef('description', el)}
|
|
83
99
|
id="description"
|
|
@@ -100,14 +116,15 @@ export function AgentNodeEditor({
|
|
|
100
116
|
data-invalid={errorHelpers?.hasFieldError('prompt') ? '' : undefined}
|
|
101
117
|
className="w-full max-h-96 data-invalid:border-red-300 data-invalid:focus-visible:border-red-300 data-invalid:focus-visible:ring-red-300"
|
|
102
118
|
label="Prompt"
|
|
119
|
+
isRequired
|
|
103
120
|
/>
|
|
104
121
|
{getFieldError('prompt') && (
|
|
105
122
|
<p className="text-sm text-red-600">{getFieldError('prompt')}</p>
|
|
106
123
|
)}
|
|
107
124
|
</div>
|
|
108
125
|
|
|
109
|
-
<ModelSection
|
|
110
|
-
models={selectedNode.data.models}
|
|
126
|
+
<ModelSection
|
|
127
|
+
models={selectedNode.data.models}
|
|
111
128
|
updatePath={updateModelPath}
|
|
112
129
|
projectModels={project?.models}
|
|
113
130
|
graphModels={metadata?.models}
|
|
@@ -156,12 +173,24 @@ export function AgentNodeEditor({
|
|
|
156
173
|
</div>
|
|
157
174
|
|
|
158
175
|
<div className="text-xs text-muted-foreground p-3 bg-blue-50 dark:bg-blue-950/20 rounded-md border border-blue-200 dark:border-blue-800">
|
|
159
|
-
<p className="font-medium text-blue-900 dark:text-blue-100 mb-2">
|
|
176
|
+
<p className="font-medium text-blue-900 dark:text-blue-100 mb-2">
|
|
177
|
+
How execution limit inheritance works:
|
|
178
|
+
</p>
|
|
160
179
|
<ul className="space-y-1 text-blue-800 dark:text-blue-200">
|
|
161
|
-
<li
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
<li
|
|
180
|
+
<li>
|
|
181
|
+
• <strong>stepCountIs</strong>: Project → Agent only (agent-level execution limit)
|
|
182
|
+
</li>
|
|
183
|
+
<li>
|
|
184
|
+
• <strong>Explicit settings</strong> always take precedence over inherited values
|
|
185
|
+
</li>
|
|
186
|
+
<li>
|
|
187
|
+
• <strong>Agent scope</strong>: This limit applies only to this specific agent's
|
|
188
|
+
execution steps
|
|
189
|
+
</li>
|
|
190
|
+
<li>
|
|
191
|
+
• <strong>Independent from transfers</strong>: Steps are counted per agent, transfers
|
|
192
|
+
are counted per conversation
|
|
193
|
+
</li>
|
|
165
194
|
</ul>
|
|
166
195
|
</div>
|
|
167
196
|
</div>
|
|
@@ -17,12 +17,14 @@ function ExpandedTextArea({ ...props }) {
|
|
|
17
17
|
|
|
18
18
|
export function ExpandableTextArea({
|
|
19
19
|
label,
|
|
20
|
+
isRequired = false,
|
|
20
21
|
...props
|
|
21
|
-
}: { label: string } & React.ComponentProps<typeof Textarea>) {
|
|
22
|
+
}: { label: string; isRequired?: boolean } & React.ComponentProps<typeof Textarea>) {
|
|
22
23
|
return (
|
|
23
24
|
<ExpandableField
|
|
24
25
|
name={props.id || 'expandable-textarea'}
|
|
25
26
|
label={label}
|
|
27
|
+
isRequired={isRequired}
|
|
26
28
|
compactView={<Textarea {...props} />}
|
|
27
29
|
expandedView={<ExpandedTextArea {...props} />}
|
|
28
30
|
/>
|
|
@@ -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
|
|
@@ -4,6 +4,7 @@ import { Input } from '@/components/ui/input';
|
|
|
4
4
|
import { Label } from '@/components/ui/label';
|
|
5
5
|
import { Textarea } from '@/components/ui/textarea';
|
|
6
6
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
|
7
|
+
import { cn } from '@/lib/utils';
|
|
7
8
|
|
|
8
9
|
interface BaseFieldProps {
|
|
9
10
|
id: string;
|
|
@@ -16,6 +17,8 @@ interface BaseFieldProps {
|
|
|
16
17
|
className?: string;
|
|
17
18
|
description?: string;
|
|
18
19
|
tooltip?: string;
|
|
20
|
+
isRequired?: boolean;
|
|
21
|
+
disabled?: boolean;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
interface InputFieldProps extends BaseFieldProps {
|
|
@@ -40,13 +43,16 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(
|
|
|
40
43
|
description,
|
|
41
44
|
tooltip,
|
|
42
45
|
type = 'text',
|
|
46
|
+
isRequired = false,
|
|
47
|
+
disabled = false,
|
|
43
48
|
},
|
|
44
49
|
ref
|
|
45
50
|
) => {
|
|
46
51
|
return (
|
|
47
52
|
<div className="space-y-2">
|
|
48
|
-
<Label htmlFor={id} className={error ? 'text-red-600' : ''}>
|
|
53
|
+
<Label htmlFor={id} className={cn(error ? 'text-red-600' : '', 'gap-1')}>
|
|
49
54
|
{label}
|
|
55
|
+
{isRequired && <span className="text-red-500">*</span>}
|
|
50
56
|
{tooltip && (
|
|
51
57
|
<Tooltip>
|
|
52
58
|
<TooltipTrigger>
|
|
@@ -66,6 +72,7 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(
|
|
|
66
72
|
placeholder={placeholder}
|
|
67
73
|
data-invalid={error ? '' : undefined}
|
|
68
74
|
className={`w-full data-invalid:border-red-300 data-invalid:focus-visible:border-red-300 data-invalid:focus-visible:ring-red-300 ${className}`}
|
|
75
|
+
disabled={disabled}
|
|
69
76
|
/>
|
|
70
77
|
{error && <p className="text-sm text-red-600">{error}</p>}
|
|
71
78
|
{description && <p className="text-sm text-muted-foreground">{description}</p>}
|
|
@@ -89,6 +96,7 @@ export const TextareaField = forwardRef<HTMLTextAreaElement, TextareaFieldProps>
|
|
|
89
96
|
className = '',
|
|
90
97
|
description,
|
|
91
98
|
maxHeight = 'max-h-96',
|
|
99
|
+
disabled = false,
|
|
92
100
|
},
|
|
93
101
|
ref
|
|
94
102
|
) => {
|
|
@@ -106,6 +114,7 @@ export const TextareaField = forwardRef<HTMLTextAreaElement, TextareaFieldProps>
|
|
|
106
114
|
placeholder={placeholder}
|
|
107
115
|
data-invalid={error ? '' : undefined}
|
|
108
116
|
className={`w-full ${maxHeight} data-invalid:border-red-300 data-invalid:focus-visible:border-red-300 data-invalid:focus-visible:ring-red-300 ${className}`}
|
|
117
|
+
disabled={disabled}
|
|
109
118
|
/>
|
|
110
119
|
{error && <p className="text-sm text-red-600">{error}</p>}
|
|
111
120
|
{description && <p className="text-sm text-muted-foreground">{description}</p>}
|
|
@@ -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
|
}
|
|
@@ -105,12 +105,19 @@ export function MCPServerForm({
|
|
|
105
105
|
return (
|
|
106
106
|
<Form {...form}>
|
|
107
107
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
108
|
-
<GenericInput
|
|
108
|
+
<GenericInput
|
|
109
|
+
control={form.control}
|
|
110
|
+
name="name"
|
|
111
|
+
label="Name"
|
|
112
|
+
placeholder="MCP Server"
|
|
113
|
+
isRequired
|
|
114
|
+
/>
|
|
109
115
|
<GenericInput
|
|
110
116
|
control={form.control}
|
|
111
117
|
name="config.mcp.server.url"
|
|
112
118
|
label="URL"
|
|
113
119
|
placeholder="https://api.example.com/mcp"
|
|
120
|
+
isRequired
|
|
114
121
|
/>
|
|
115
122
|
<GenericSelect
|
|
116
123
|
control={form.control}
|
|
@@ -33,7 +33,7 @@ export function EditProjectDialog({
|
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
35
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
36
|
-
<DialogContent className="max-w-2xl">
|
|
36
|
+
<DialogContent className="!max-w-2xl">
|
|
37
37
|
<DialogHeader>
|
|
38
38
|
<DialogTitle>Edit project</DialogTitle>
|
|
39
39
|
<DialogDescription className="sr-only">Edit project details.</DialogDescription>
|