@inkeep/agents-manage-ui 0.1.1 → 0.1.2
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.sample → .env.example} +6 -1
- package/.turbo/turbo-build.log +27 -33
- package/.turbo/turbo-test.log +43 -10
- package/LICENSE.md +22 -17
- package/package.json +5 -5
- package/src/app/api/signoz/conversations/[conversationId]/route.ts +43 -4
- 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 +5 -0
- 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 +10 -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/playground/chat-widget.tsx +30 -1
- package/src/components/graph/playground/playground.tsx +7 -1
- package/src/components/graph/sidepane/metadata/metadata-editor.tsx +38 -38
- package/src/components/graph/sidepane/nodes/agent-node-editor.tsx +22 -8
- package/src/components/graph/sidepane/nodes/expandable-text-area.tsx +3 -1
- package/src/components/graph/sidepane/nodes/form-fields.tsx +10 -1
- 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 +3 -0
- package/src/components/projects/form/validation.ts +14 -10
- package/src/components/projects/new-project-dialog.tsx +1 -1
- package/src/components/traces/timeline/timeline-wrapper.tsx +117 -28
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/form.tsx +11 -4
- package/src/hooks/use-chat-activities-polling.ts +44 -11
- package/src/lib/api/signoz-stats.ts +958 -304
- package/src/lib/types/graph-full.ts +1 -1
- package/src/lib/validation.ts +1 -1
|
@@ -25,6 +25,7 @@ interface GenericComboBoxProps<T extends FieldValues> {
|
|
|
25
25
|
searchPlaceholder?: string;
|
|
26
26
|
placeholder?: string;
|
|
27
27
|
disabled?: boolean;
|
|
28
|
+
isRequired?: boolean;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export function GenericComboBox<T extends FieldValues>({
|
|
@@ -35,11 +36,12 @@ export function GenericComboBox<T extends FieldValues>({
|
|
|
35
36
|
searchPlaceholder = 'Search...',
|
|
36
37
|
placeholder,
|
|
37
38
|
disabled = false,
|
|
39
|
+
isRequired = false,
|
|
38
40
|
}: GenericComboBoxProps<T>) {
|
|
39
41
|
const [open, setOpen] = useState(false);
|
|
40
42
|
|
|
41
43
|
return (
|
|
42
|
-
<FormFieldWrapper control={control} name={name} label={label}>
|
|
44
|
+
<FormFieldWrapper control={control} name={name} label={label} isRequired={isRequired}>
|
|
43
45
|
{(field) => (
|
|
44
46
|
<Popover open={open} onOpenChange={setOpen}>
|
|
45
47
|
<PopoverTrigger asChild>
|
|
@@ -13,6 +13,7 @@ interface GenericInputProps<T extends FieldValues> {
|
|
|
13
13
|
min?: string;
|
|
14
14
|
disabled?: boolean;
|
|
15
15
|
description?: string;
|
|
16
|
+
isRequired?: boolean;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export function GenericInput<T extends FieldValues>({
|
|
@@ -24,9 +25,16 @@ export function GenericInput<T extends FieldValues>({
|
|
|
24
25
|
min,
|
|
25
26
|
disabled,
|
|
26
27
|
description,
|
|
28
|
+
isRequired = false,
|
|
27
29
|
}: GenericInputProps<T>) {
|
|
28
30
|
return (
|
|
29
|
-
<FormFieldWrapper
|
|
31
|
+
<FormFieldWrapper
|
|
32
|
+
control={control}
|
|
33
|
+
name={name}
|
|
34
|
+
label={label}
|
|
35
|
+
description={description}
|
|
36
|
+
isRequired={isRequired}
|
|
37
|
+
>
|
|
30
38
|
{(field) => (
|
|
31
39
|
<Input
|
|
32
40
|
type={type}
|
|
@@ -24,6 +24,7 @@ interface GenericSelectProps<T extends FieldValues> {
|
|
|
24
24
|
options: SelectOption[];
|
|
25
25
|
disabled?: boolean;
|
|
26
26
|
selectTriggerClassName?: string;
|
|
27
|
+
isRequired?: boolean;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export function GenericSelect<T extends FieldValues>({
|
|
@@ -34,9 +35,10 @@ export function GenericSelect<T extends FieldValues>({
|
|
|
34
35
|
options,
|
|
35
36
|
disabled = false,
|
|
36
37
|
selectTriggerClassName,
|
|
38
|
+
isRequired = false,
|
|
37
39
|
}: GenericSelectProps<T>) {
|
|
38
40
|
return (
|
|
39
|
-
<FormFieldWrapper control={control} name={name} label={label}>
|
|
41
|
+
<FormFieldWrapper control={control} name={name} label={label} isRequired={isRequired}>
|
|
40
42
|
{(field) => (
|
|
41
43
|
<Select onValueChange={field.onChange} defaultValue={field.value} disabled={disabled}>
|
|
42
44
|
<SelectTrigger disabled={disabled} className={selectTriggerClassName}>
|
|
@@ -12,6 +12,7 @@ interface GenericTextareaProps<T extends FieldValues> {
|
|
|
12
12
|
className?: string;
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
readOnly?: boolean;
|
|
15
|
+
isRequired?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function GenericTextarea<T extends FieldValues>({
|
|
@@ -22,9 +23,10 @@ export function GenericTextarea<T extends FieldValues>({
|
|
|
22
23
|
className,
|
|
23
24
|
disabled,
|
|
24
25
|
readOnly,
|
|
26
|
+
isRequired = false,
|
|
25
27
|
}: GenericTextareaProps<T>) {
|
|
26
28
|
return (
|
|
27
|
-
<FormFieldWrapper control={control} name={name} label={label}>
|
|
29
|
+
<FormFieldWrapper control={control} name={name} label={label} isRequired={isRequired}>
|
|
28
30
|
{(field) => (
|
|
29
31
|
<Textarea
|
|
30
32
|
placeholder={placeholder}
|
|
@@ -12,6 +12,7 @@ interface JsonSchemaInputProps<T extends FieldValues> {
|
|
|
12
12
|
disabled?: boolean;
|
|
13
13
|
description?: string;
|
|
14
14
|
readOnly?: boolean;
|
|
15
|
+
isRequired?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function JsonSchemaInput<T extends FieldValues>({
|
|
@@ -22,9 +23,16 @@ export function JsonSchemaInput<T extends FieldValues>({
|
|
|
22
23
|
disabled,
|
|
23
24
|
description,
|
|
24
25
|
readOnly,
|
|
26
|
+
isRequired = false,
|
|
25
27
|
}: JsonSchemaInputProps<T>) {
|
|
26
28
|
return (
|
|
27
|
-
<FormFieldWrapper
|
|
29
|
+
<FormFieldWrapper
|
|
30
|
+
control={control}
|
|
31
|
+
name={name}
|
|
32
|
+
label={label}
|
|
33
|
+
description={description}
|
|
34
|
+
isRequired={isRequired}
|
|
35
|
+
>
|
|
28
36
|
{(field) => (
|
|
29
37
|
<StandaloneJsonEditor
|
|
30
38
|
placeholder={placeholder}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { useRef, useEffect } from 'react';
|
|
2
3
|
import { InkeepEmbeddedChat } from '@inkeep/cxkit-react-oss';
|
|
3
4
|
import type { ComponentsConfig, InkeepCallbackEvent } from '@inkeep/cxkit-react-oss/types';
|
|
4
5
|
import { EXECUTION_API_BASE_URL } from '@/lib/api/api-config';
|
|
@@ -107,16 +108,44 @@ export function ChatWidget({
|
|
|
107
108
|
startPolling,
|
|
108
109
|
stopPolling,
|
|
109
110
|
}: ChatWidgetProps) {
|
|
111
|
+
const stopPollingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
return () => {
|
|
115
|
+
if (stopPollingTimeoutRef.current) {
|
|
116
|
+
clearTimeout(stopPollingTimeoutRef.current);
|
|
117
|
+
stopPollingTimeoutRef.current = null;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}, []);
|
|
121
|
+
|
|
110
122
|
return (
|
|
111
123
|
<div className="h-full flex flex-row gap-4">
|
|
112
124
|
<div className="flex-1 min-w-0 h-full">
|
|
113
125
|
<InkeepEmbeddedChat
|
|
114
126
|
baseSettings={{
|
|
115
127
|
onEvent: (event: InkeepCallbackEvent) => {
|
|
128
|
+
if (event.eventName === 'assistant_message_received') {
|
|
129
|
+
// Add a delay before stopping polling to allow activity data to be available
|
|
130
|
+
stopPollingTimeoutRef.current = setTimeout(() => {
|
|
131
|
+
stopPolling();
|
|
132
|
+
stopPollingTimeoutRef.current = null;
|
|
133
|
+
}, 5000);
|
|
134
|
+
}
|
|
116
135
|
if (event.eventName === 'user_message_submitted') {
|
|
136
|
+
// Cancel any pending stop polling timeout since we need to keep polling
|
|
137
|
+
if (stopPollingTimeoutRef.current) {
|
|
138
|
+
clearTimeout(stopPollingTimeoutRef.current);
|
|
139
|
+
stopPollingTimeoutRef.current = null;
|
|
140
|
+
}
|
|
117
141
|
startPolling();
|
|
118
142
|
}
|
|
119
143
|
if (event.eventName === 'chat_clear_button_clicked') {
|
|
144
|
+
// Cancel any pending stop polling timeout
|
|
145
|
+
if (stopPollingTimeoutRef.current) {
|
|
146
|
+
clearTimeout(stopPollingTimeoutRef.current);
|
|
147
|
+
stopPollingTimeoutRef.current = null;
|
|
148
|
+
}
|
|
120
149
|
stopPolling();
|
|
121
150
|
setConversationId(nanoid());
|
|
122
151
|
}
|
|
@@ -171,7 +200,7 @@ export function ChatWidget({
|
|
|
171
200
|
components: {
|
|
172
201
|
IkpMessage,
|
|
173
202
|
},
|
|
174
|
-
introMessage: 'Hi!'
|
|
203
|
+
introMessage: 'Hi!',
|
|
175
204
|
}}
|
|
176
205
|
/>
|
|
177
206
|
</div>
|
|
@@ -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,7 +2,7 @@
|
|
|
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';
|
|
@@ -22,6 +22,8 @@ import { useProjectData } from '@/hooks/use-project-data';
|
|
|
22
22
|
import { ModelSelector } from '../nodes/model-selector';
|
|
23
23
|
import { ContextConfigForm } from './context-config';
|
|
24
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';
|
|
25
27
|
|
|
26
28
|
function MetadataEditor() {
|
|
27
29
|
const params = useParams();
|
|
@@ -64,45 +66,43 @@ function MetadataEditor() {
|
|
|
64
66
|
<CopyableSingleLineCode code={graphUrl} />
|
|
65
67
|
</div>
|
|
66
68
|
)}
|
|
69
|
+
<InputField
|
|
70
|
+
id="id"
|
|
71
|
+
name="id"
|
|
72
|
+
label="Id"
|
|
73
|
+
value={id || ''}
|
|
74
|
+
onChange={(e) => updateMetadata('id', e.target.value)}
|
|
75
|
+
disabled={!!graphId} // only editable if no graphId is set (i.e. new graph)
|
|
76
|
+
placeholder="my-graph"
|
|
77
|
+
description={
|
|
78
|
+
!graphId
|
|
79
|
+
? 'Choose a unique identifier for this graph. Using an existing id will replace that graph.'
|
|
80
|
+
: undefined
|
|
81
|
+
}
|
|
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
|
+
isRequired
|
|
91
|
+
/>
|
|
92
|
+
<TextareaField
|
|
93
|
+
id="description"
|
|
94
|
+
name="description"
|
|
95
|
+
label="Description"
|
|
96
|
+
value={description}
|
|
97
|
+
onChange={(e) => updateMetadata('description', e.target.value)}
|
|
98
|
+
placeholder="This graph is used to..."
|
|
99
|
+
className="max-h-96"
|
|
100
|
+
/>
|
|
67
101
|
<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
|
|
102
|
+
<ExpandableTextArea
|
|
105
103
|
id="graph-prompt"
|
|
104
|
+
name="graph-prompt"
|
|
105
|
+
label="Graph Prompt"
|
|
106
106
|
value={graphPrompt || ''}
|
|
107
107
|
onChange={(e) => updateMetadata('graphPrompt', e.target.value)}
|
|
108
108
|
placeholder="System-level instructions for this graph..."
|
|
@@ -35,7 +35,7 @@ export function AgentNodeEditor({
|
|
|
35
35
|
const { tenantId, projectId } = useParams<{ tenantId: string; projectId: string }>();
|
|
36
36
|
const selectedDataComponents = selectedNode.data?.dataComponents || [];
|
|
37
37
|
const selectedArtifactComponents = selectedNode.data?.artifactComponents || [];
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
// Get project and graph data for inheritance indicators
|
|
40
40
|
const { project } = useProjectData();
|
|
41
41
|
const metadata = useGraphStore((state) => state.metadata);
|
|
@@ -76,6 +76,7 @@ export function AgentNodeEditor({
|
|
|
76
76
|
onChange={(e) => updatePath('name', e.target.value)}
|
|
77
77
|
placeholder="Support agent"
|
|
78
78
|
error={getFieldError('name')}
|
|
79
|
+
isRequired
|
|
79
80
|
/>
|
|
80
81
|
|
|
81
82
|
<TextareaField
|
|
@@ -100,14 +101,15 @@ export function AgentNodeEditor({
|
|
|
100
101
|
data-invalid={errorHelpers?.hasFieldError('prompt') ? '' : undefined}
|
|
101
102
|
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
103
|
label="Prompt"
|
|
104
|
+
isRequired
|
|
103
105
|
/>
|
|
104
106
|
{getFieldError('prompt') && (
|
|
105
107
|
<p className="text-sm text-red-600">{getFieldError('prompt')}</p>
|
|
106
108
|
)}
|
|
107
109
|
</div>
|
|
108
110
|
|
|
109
|
-
<ModelSection
|
|
110
|
-
models={selectedNode.data.models}
|
|
111
|
+
<ModelSection
|
|
112
|
+
models={selectedNode.data.models}
|
|
111
113
|
updatePath={updateModelPath}
|
|
112
114
|
projectModels={project?.models}
|
|
113
115
|
graphModels={metadata?.models}
|
|
@@ -156,12 +158,24 @@ export function AgentNodeEditor({
|
|
|
156
158
|
</div>
|
|
157
159
|
|
|
158
160
|
<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">
|
|
161
|
+
<p className="font-medium text-blue-900 dark:text-blue-100 mb-2">
|
|
162
|
+
How execution limit inheritance works:
|
|
163
|
+
</p>
|
|
160
164
|
<ul className="space-y-1 text-blue-800 dark:text-blue-200">
|
|
161
|
-
<li
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
<li
|
|
165
|
+
<li>
|
|
166
|
+
• <strong>stepCountIs</strong>: Project → Agent only (agent-level execution limit)
|
|
167
|
+
</li>
|
|
168
|
+
<li>
|
|
169
|
+
• <strong>Explicit settings</strong> always take precedence over inherited values
|
|
170
|
+
</li>
|
|
171
|
+
<li>
|
|
172
|
+
• <strong>Agent scope</strong>: This limit applies only to this specific agent's
|
|
173
|
+
execution steps
|
|
174
|
+
</li>
|
|
175
|
+
<li>
|
|
176
|
+
• <strong>Independent from transfers</strong>: Steps are counted per agent, transfers
|
|
177
|
+
are counted per conversation
|
|
178
|
+
</li>
|
|
165
179
|
</ul>
|
|
166
180
|
</div>
|
|
167
181
|
</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
|
/>
|
|
@@ -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>}
|
|
@@ -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>
|
|
@@ -82,6 +82,7 @@ export function ProjectForm({
|
|
|
82
82
|
placeholder="my-project"
|
|
83
83
|
description="Choose a unique identifier for this project. This cannot be changed later."
|
|
84
84
|
disabled={!!projectId}
|
|
85
|
+
isRequired
|
|
85
86
|
/>
|
|
86
87
|
<GenericInput
|
|
87
88
|
control={form.control}
|
|
@@ -89,6 +90,7 @@ export function ProjectForm({
|
|
|
89
90
|
label="Project Name"
|
|
90
91
|
placeholder="My Project"
|
|
91
92
|
description="A friendly name for your project"
|
|
93
|
+
isRequired
|
|
92
94
|
/>
|
|
93
95
|
<GenericTextarea
|
|
94
96
|
control={form.control}
|
|
@@ -96,6 +98,7 @@ export function ProjectForm({
|
|
|
96
98
|
label="Description"
|
|
97
99
|
placeholder="Describe what this project is for..."
|
|
98
100
|
className="min-h-[100px]"
|
|
101
|
+
isRequired
|
|
99
102
|
/>
|
|
100
103
|
|
|
101
104
|
<Separator />
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
2
|
|
|
3
3
|
const modelSettingsSchema = z.object({
|
|
4
4
|
model: z.string().optional(), // Allow empty model - system will fall back to defaults
|
|
5
5
|
providerOptions: z.record(z.string(), z.any()).optional(),
|
|
6
6
|
});
|
|
7
7
|
|
|
8
|
-
const projectModelsSchema = z
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
const projectModelsSchema = z
|
|
9
|
+
.object({
|
|
10
|
+
base: modelSettingsSchema.optional(),
|
|
11
|
+
structuredOutput: modelSettingsSchema.optional(),
|
|
12
|
+
summarizer: modelSettingsSchema.optional(),
|
|
13
|
+
})
|
|
14
|
+
.optional();
|
|
13
15
|
|
|
14
|
-
const projectStopWhenSchema = z
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const projectStopWhenSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
transferCountIs: z.number().min(1).max(100).optional(),
|
|
19
|
+
stepCountIs: z.number().min(1).max(1000).optional(),
|
|
20
|
+
})
|
|
21
|
+
.optional();
|
|
18
22
|
|
|
19
23
|
export const projectSchema = z.object({
|
|
20
24
|
id: z
|
|
@@ -52,7 +52,7 @@ export function NewProjectDialog({
|
|
|
52
52
|
)}
|
|
53
53
|
</DialogTrigger>
|
|
54
54
|
)}
|
|
55
|
-
<DialogContent className="max-w-2xl">
|
|
55
|
+
<DialogContent className="!max-w-2xl">
|
|
56
56
|
<DialogTitle>Create new project</DialogTitle>
|
|
57
57
|
<DialogDescription>
|
|
58
58
|
Create a new project to organize your agents, tools, and resources.
|