@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
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { type NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { z } from 'zod';
|
|
3
1
|
import axios from 'axios';
|
|
4
2
|
import axiosRetry from 'axios-retry';
|
|
3
|
+
import { type NextRequest, NextResponse } from 'next/server';
|
|
4
|
+
import { z } from 'zod/v4';
|
|
5
5
|
import { getLogger } from '@/lib/logger';
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
// Configure axios retry
|
|
9
|
-
axiosRetry(axios, {
|
|
8
|
+
axiosRetry(axios, {
|
|
10
9
|
retries: 3,
|
|
11
|
-
retryDelay: axiosRetry.exponentialDelay
|
|
10
|
+
retryDelay: axiosRetry.exponentialDelay,
|
|
12
11
|
});
|
|
13
12
|
|
|
14
13
|
const SIGNOZ_URL = process.env.SIGNOZ_URL || 'http://localhost:3080';
|
|
@@ -96,7 +95,10 @@ export async function POST(request: NextRequest) {
|
|
|
96
95
|
|
|
97
96
|
return NextResponse.json(data);
|
|
98
97
|
} catch (error) {
|
|
99
|
-
logger.error(
|
|
98
|
+
logger.error(
|
|
99
|
+
{ error, stack: error instanceof Error ? error.stack : undefined },
|
|
100
|
+
'Error proxying to SigNoz'
|
|
101
|
+
);
|
|
100
102
|
return NextResponse.json(
|
|
101
103
|
{
|
|
102
104
|
error: 'Failed to connect to SigNoz',
|
|
@@ -100,6 +100,7 @@ export function ApiKeyForm({
|
|
|
100
100
|
placeholder="Select expiration date"
|
|
101
101
|
options={EXPIRATION_DATE_OPTIONS}
|
|
102
102
|
selectTriggerClassName="w-full"
|
|
103
|
+
isRequired
|
|
103
104
|
/>
|
|
104
105
|
<GenericComboBox
|
|
105
106
|
control={form.control}
|
|
@@ -108,6 +109,7 @@ export function ApiKeyForm({
|
|
|
108
109
|
options={graphsOptions}
|
|
109
110
|
placeholder="Select a graph"
|
|
110
111
|
searchPlaceholder="Search graphs..."
|
|
112
|
+
isRequired
|
|
111
113
|
/>
|
|
112
114
|
<div className="flex justify-end">
|
|
113
115
|
<Button type="submit" disabled={isSubmitting}>
|
|
@@ -9,6 +9,7 @@ import { GenericTextarea } from '@/components/form/generic-textarea';
|
|
|
9
9
|
import { JsonSchemaInput } from '@/components/form/json-schema-input';
|
|
10
10
|
import { Button } from '@/components/ui/button';
|
|
11
11
|
import { Form } from '@/components/ui/form';
|
|
12
|
+
import { useAutoPrefillId } from '@/hooks/use-auto-prefill-id';
|
|
12
13
|
import {
|
|
13
14
|
createArtifactComponentAction,
|
|
14
15
|
updateArtifactComponentAction,
|
|
@@ -52,6 +53,14 @@ export function ArtifactComponentForm({
|
|
|
52
53
|
const { isSubmitting } = form.formState;
|
|
53
54
|
const router = useRouter();
|
|
54
55
|
|
|
56
|
+
// Auto-prefill ID based on name field (only for new components)
|
|
57
|
+
useAutoPrefillId({
|
|
58
|
+
form,
|
|
59
|
+
nameField: 'name',
|
|
60
|
+
idField: 'id',
|
|
61
|
+
isEditing: !!id,
|
|
62
|
+
});
|
|
63
|
+
|
|
55
64
|
const onSubmit = async (data: ArtifactComponentFormData) => {
|
|
56
65
|
try {
|
|
57
66
|
const payload = { ...data } as ArtifactComponent;
|
|
@@ -81,42 +90,47 @@ export function ArtifactComponentForm({
|
|
|
81
90
|
return (
|
|
82
91
|
<Form {...form}>
|
|
83
92
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
93
|
+
<GenericInput
|
|
94
|
+
control={form.control}
|
|
95
|
+
name="name"
|
|
96
|
+
label="Name"
|
|
97
|
+
placeholder="Document Artifact"
|
|
98
|
+
isRequired
|
|
99
|
+
/>
|
|
84
100
|
<GenericInput
|
|
85
101
|
control={form.control}
|
|
86
102
|
name="id"
|
|
87
103
|
label="Id"
|
|
88
104
|
placeholder="my-artifact-component"
|
|
89
105
|
disabled={!!id}
|
|
106
|
+
isRequired
|
|
90
107
|
description={
|
|
91
108
|
id
|
|
92
109
|
? ''
|
|
93
110
|
: 'Choose a unique identifier for this component. Using an existing id will replace that component.'
|
|
94
111
|
}
|
|
95
112
|
/>
|
|
96
|
-
<GenericInput
|
|
97
|
-
control={form.control}
|
|
98
|
-
name="name"
|
|
99
|
-
label="Name"
|
|
100
|
-
placeholder="Document Artifact"
|
|
101
|
-
/>
|
|
102
113
|
<GenericTextarea
|
|
103
114
|
control={form.control}
|
|
104
115
|
name="description"
|
|
105
116
|
label="Description"
|
|
106
117
|
placeholder="Structured factual information extracted from search results"
|
|
107
118
|
className="min-h-[80px]"
|
|
119
|
+
isRequired
|
|
108
120
|
/>
|
|
109
121
|
<JsonSchemaInput
|
|
110
122
|
control={form.control}
|
|
111
123
|
name="summaryProps"
|
|
112
124
|
label="Summary props (JSON Schema)"
|
|
113
125
|
placeholder="Enter a valid JSON Schema..."
|
|
126
|
+
isRequired
|
|
114
127
|
/>
|
|
115
128
|
<JsonSchemaInput
|
|
116
129
|
control={form.control}
|
|
117
130
|
name="fullProps"
|
|
118
131
|
label="Full props (JSON Schema)"
|
|
119
132
|
placeholder="Enter a valid JSON Schema..."
|
|
133
|
+
isRequired
|
|
120
134
|
/>
|
|
121
135
|
<Button type="submit" disabled={isSubmitting}>
|
|
122
136
|
Save
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
2
|
import { getJsonParseError, validateJsonSchemaForLlm } from '@/lib/json-schema-validation';
|
|
3
3
|
import { idSchema } from '@/lib/validation';
|
|
4
4
|
|
|
@@ -35,8 +35,8 @@ export const artifactComponentSchema = z.object({
|
|
|
35
35
|
id: idSchema,
|
|
36
36
|
name: z.string().min(1, 'Name is required.'),
|
|
37
37
|
description: z.string().min(1, 'Description is required.'),
|
|
38
|
-
summaryProps: jsonSchemaValidation('Summary props'),
|
|
39
|
-
fullProps: jsonSchemaValidation('Full props'),
|
|
38
|
+
summaryProps: jsonSchemaValidation('Summary props').optional(),
|
|
39
|
+
fullProps: jsonSchemaValidation('Full props').optional(),
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
export type ArtifactComponentFormData = z.infer<typeof artifactComponentSchema>;
|
|
@@ -115,6 +115,7 @@ export function CredentialForm({ onCreateCredential, tenantId, projectId }: Cred
|
|
|
115
115
|
name="name"
|
|
116
116
|
label="Name"
|
|
117
117
|
placeholder="e.g., production-api-key"
|
|
118
|
+
isRequired
|
|
118
119
|
/>
|
|
119
120
|
|
|
120
121
|
<div className="space-y-3">
|
|
@@ -123,6 +124,7 @@ export function CredentialForm({ onCreateCredential, tenantId, projectId }: Cred
|
|
|
123
124
|
name="apiKeyToSet"
|
|
124
125
|
label="API Key"
|
|
125
126
|
placeholder="e.g., sk-1234567890abcdef1234567890abcdef"
|
|
127
|
+
isRequired
|
|
126
128
|
/>
|
|
127
129
|
<div className="text-xs text-muted-foreground p-3 bg-muted/30 rounded-md">
|
|
128
130
|
<p className="mb-2">
|
|
@@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
4
4
|
import { useRouter } from 'next/navigation';
|
|
5
5
|
import { useForm } from 'react-hook-form';
|
|
6
6
|
import { toast } from 'sonner';
|
|
7
|
-
import { z } from 'zod';
|
|
7
|
+
import { z } from 'zod/v4';
|
|
8
8
|
import { CredentialToolsList } from '@/components/credentials/credential-tools-list';
|
|
9
9
|
import { GenericInput } from '@/components/form/generic-input';
|
|
10
10
|
import { GenericKeyValueInput } from '@/components/form/generic-key-value-input';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
4
4
|
import type { ApiProvider } from '@nangohq/types';
|
|
5
5
|
import { useForm } from 'react-hook-form';
|
|
6
|
-
import * as z from 'zod';
|
|
6
|
+
import * as z from 'zod/v4';
|
|
7
7
|
import { GenericInput } from '@/components/form/generic-input';
|
|
8
8
|
import { GenericTextarea } from '@/components/form/generic-textarea';
|
|
9
9
|
import { ProviderIcon } from '@/components/icons/provider-icon';
|
|
@@ -9,6 +9,7 @@ import { GenericTextarea } from '@/components/form/generic-textarea';
|
|
|
9
9
|
import { JsonSchemaInput } from '@/components/form/json-schema-input';
|
|
10
10
|
import { Button } from '@/components/ui/button';
|
|
11
11
|
import { Form } from '@/components/ui/form';
|
|
12
|
+
import { useAutoPrefillId } from '@/hooks/use-auto-prefill-id';
|
|
12
13
|
import {
|
|
13
14
|
createDataComponentAction,
|
|
14
15
|
updateDataComponentAction,
|
|
@@ -49,6 +50,14 @@ export function DataComponentForm({
|
|
|
49
50
|
const { isSubmitting } = form.formState;
|
|
50
51
|
const router = useRouter();
|
|
51
52
|
|
|
53
|
+
// Auto-prefill ID based on name field (only for new components)
|
|
54
|
+
useAutoPrefillId({
|
|
55
|
+
form,
|
|
56
|
+
nameField: 'name',
|
|
57
|
+
idField: 'id',
|
|
58
|
+
isEditing: !!id,
|
|
59
|
+
});
|
|
60
|
+
|
|
52
61
|
const onSubmit = async (data: DataComponentFormData) => {
|
|
53
62
|
try {
|
|
54
63
|
const payload = { ...data } as DataComponent;
|
|
@@ -78,6 +87,13 @@ export function DataComponentForm({
|
|
|
78
87
|
return (
|
|
79
88
|
<Form {...form}>
|
|
80
89
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
|
90
|
+
<GenericInput
|
|
91
|
+
control={form.control}
|
|
92
|
+
name="name"
|
|
93
|
+
label="Name"
|
|
94
|
+
placeholder="List orders"
|
|
95
|
+
isRequired
|
|
96
|
+
/>
|
|
81
97
|
<GenericInput
|
|
82
98
|
control={form.control}
|
|
83
99
|
name="id"
|
|
@@ -89,20 +105,22 @@ export function DataComponentForm({
|
|
|
89
105
|
? ''
|
|
90
106
|
: 'Choose a unique identifier for this component. Using an existing id will replace that component.'
|
|
91
107
|
}
|
|
108
|
+
isRequired
|
|
92
109
|
/>
|
|
93
|
-
<GenericInput control={form.control} name="name" label="Name" placeholder="List orders" />
|
|
94
110
|
<GenericTextarea
|
|
95
111
|
control={form.control}
|
|
96
112
|
name="description"
|
|
97
113
|
label="Description"
|
|
98
114
|
placeholder="Display a list of user orders with interactive options"
|
|
99
115
|
className="min-h-[80px]"
|
|
116
|
+
isRequired
|
|
100
117
|
/>
|
|
101
118
|
<JsonSchemaInput
|
|
102
119
|
control={form.control}
|
|
103
120
|
name="props"
|
|
104
121
|
label="Props (JSON Schema)"
|
|
105
122
|
placeholder="Enter a valid JSON Schema..."
|
|
123
|
+
isRequired
|
|
106
124
|
/>
|
|
107
125
|
<Button type="submit" disabled={isSubmitting}>
|
|
108
126
|
Save
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
2
|
import { getJsonParseError, validateJsonSchemaForLlm } from '@/lib/json-schema-validation';
|
|
3
3
|
import { idSchema } from '@/lib/validation';
|
|
4
4
|
|
|
@@ -32,7 +32,8 @@ export const dataComponentSchema = z.object({
|
|
|
32
32
|
});
|
|
33
33
|
return z.NEVER;
|
|
34
34
|
}
|
|
35
|
-
})
|
|
35
|
+
})
|
|
36
|
+
.optional(),
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
export type DataComponentFormData = z.infer<typeof dataComponentSchema>;
|
|
@@ -20,6 +20,7 @@ interface ExpandableFieldProps {
|
|
|
20
20
|
expandedView: ReactNode;
|
|
21
21
|
actions?: ReactNode;
|
|
22
22
|
expandButtonLabel?: string;
|
|
23
|
+
isRequired?: boolean;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function ExpandableField({
|
|
@@ -30,13 +31,17 @@ export function ExpandableField({
|
|
|
30
31
|
expandedView,
|
|
31
32
|
actions,
|
|
32
33
|
expandButtonLabel = 'Expand to full screen',
|
|
34
|
+
isRequired = false,
|
|
33
35
|
}: ExpandableFieldProps) {
|
|
34
36
|
return (
|
|
35
37
|
<Dialog>
|
|
36
38
|
<div className={className}>
|
|
37
39
|
<div className="space-y-2">
|
|
38
40
|
<div className="flex items-center justify-between">
|
|
39
|
-
<Label htmlFor={name}>
|
|
41
|
+
<Label className="gap-1" htmlFor={name}>
|
|
42
|
+
{label}
|
|
43
|
+
{isRequired && <span className="text-red-500">*</span>}
|
|
44
|
+
</Label>
|
|
40
45
|
{actions && <div className="flex gap-2">{actions}</div>}
|
|
41
46
|
</div>
|
|
42
47
|
<div className="relative">
|
|
@@ -19,6 +19,7 @@ interface FormFieldWrapperProps<T extends FieldValues> {
|
|
|
19
19
|
children: (field: FieldValues) => React.ReactNode;
|
|
20
20
|
description?: string;
|
|
21
21
|
rules?: RegisterOptions<T, FieldPath<T>>;
|
|
22
|
+
isRequired?: boolean;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export function FormFieldWrapper<T extends FieldValues>({
|
|
@@ -28,6 +29,7 @@ export function FormFieldWrapper<T extends FieldValues>({
|
|
|
28
29
|
children,
|
|
29
30
|
description,
|
|
30
31
|
rules,
|
|
32
|
+
isRequired,
|
|
31
33
|
}: FormFieldWrapperProps<T>) {
|
|
32
34
|
return (
|
|
33
35
|
<FormField
|
|
@@ -36,7 +38,7 @@ export function FormFieldWrapper<T extends FieldValues>({
|
|
|
36
38
|
rules={rules}
|
|
37
39
|
render={({ field }) => (
|
|
38
40
|
<FormItem className="relative">
|
|
39
|
-
<FormLabel>{label}</FormLabel>
|
|
41
|
+
<FormLabel isRequired={isRequired}>{label}</FormLabel>
|
|
40
42
|
<FormControl>{children(field)}</FormControl>
|
|
41
43
|
{description && <FormDescription>{description}</FormDescription>}
|
|
42
44
|
<FormMessage />
|
|
@@ -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,6 +1,4 @@
|
|
|
1
|
-
import type { Node } from '@xyflow/react';
|
|
2
1
|
import { Bot, BotMessageSquare, Hammer } from 'lucide-react';
|
|
3
|
-
import { nanoid } from 'nanoid';
|
|
4
2
|
import type { MCPTool } from '@/lib/api/tools';
|
|
5
3
|
import { AgentNode } from '../nodes/agent-node';
|
|
6
4
|
import { ExternalAgentNode } from '../nodes/external-agent-node';
|
|
@@ -58,10 +56,10 @@ export const externalAgentNodeTargetHandleId = 'target-external-agent';
|
|
|
58
56
|
|
|
59
57
|
export const newNodeDefaults: Record<keyof typeof nodeTypes, NodeData> = {
|
|
60
58
|
[NodeType.Agent]: {
|
|
61
|
-
name: '
|
|
59
|
+
name: '',
|
|
62
60
|
},
|
|
63
61
|
[NodeType.ExternalAgent]: {
|
|
64
|
-
name: '
|
|
62
|
+
name: '',
|
|
65
63
|
},
|
|
66
64
|
[NodeType.MCP]: {
|
|
67
65
|
name: 'MCP',
|
|
@@ -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,9 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { InkeepEmbeddedChat } from '@inkeep/cxkit-react-oss';
|
|
3
3
|
import type { ComponentsConfig, InkeepCallbackEvent } from '@inkeep/cxkit-react-oss/types';
|
|
4
|
+
import { nanoid } from 'nanoid';
|
|
5
|
+
import { useEffect, useRef } from 'react';
|
|
4
6
|
import { EXECUTION_API_BASE_URL } from '@/lib/api/api-config';
|
|
5
7
|
import { IkpMessage as IkpMessageComponent } from './ikp-message';
|
|
6
|
-
import { nanoid } from 'nanoid';
|
|
7
8
|
|
|
8
9
|
interface ChatWidgetProps {
|
|
9
10
|
graphId?: string;
|
|
@@ -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>
|