@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,7 +1,6 @@
|
|
|
1
1
|
import { ExternalLink } from '@/components/ui/external-link';
|
|
2
2
|
|
|
3
|
-
export const DOCS_BASE_URL =
|
|
4
|
-
process.env.NEXT_PUBLIC_DOCS_BASE_URL || 'https://docs.inkeep.com';
|
|
3
|
+
export const DOCS_BASE_URL = process.env.NEXT_PUBLIC_DOCS_BASE_URL || 'https://docs.inkeep.com';
|
|
5
4
|
|
|
6
5
|
export const artifactDescription = (
|
|
7
6
|
<>
|
|
@@ -18,9 +17,7 @@ export const dataComponentDescription = (
|
|
|
18
17
|
<>
|
|
19
18
|
Data components are structured components that agents can use to display rich data.
|
|
20
19
|
{'\n'}
|
|
21
|
-
<ExternalLink href={`${DOCS_BASE_URL}/visual-builder/data-components`}>
|
|
22
|
-
Learn more
|
|
23
|
-
</ExternalLink>
|
|
20
|
+
<ExternalLink href={`${DOCS_BASE_URL}/visual-builder/data-components`}>Learn more</ExternalLink>
|
|
24
21
|
</>
|
|
25
22
|
);
|
|
26
23
|
|
package/src/constants/signoz.ts
CHANGED
|
@@ -21,13 +21,13 @@ export const SPAN_KEYS = {
|
|
|
21
21
|
HAS_ERROR: 'hasError',
|
|
22
22
|
STATUS_MESSAGE: 'status_message',
|
|
23
23
|
OTEL_STATUS_DESCRIPTION: 'otel.status_description',
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
// Graph attributes
|
|
26
26
|
GRAPH_ID: 'graph.id',
|
|
27
27
|
GRAPH_NAME: 'graph.name',
|
|
28
28
|
TENANT_ID: 'tenant.id',
|
|
29
29
|
PROJECT_ID: 'project.id',
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
// AI/Agent attributes
|
|
32
32
|
AI_AGENT_NAME: 'ai.agentName',
|
|
33
33
|
AI_AGENT_NAME_ALT: 'ai.agent.name',
|
|
@@ -39,7 +39,7 @@ export const SPAN_KEYS = {
|
|
|
39
39
|
AI_MODEL_PROVIDER: 'ai.model.provider',
|
|
40
40
|
AI_TELEMETRY_FUNCTION_ID: 'ai.telemetry.functionId',
|
|
41
41
|
AI_MODEL_ID: 'ai.model.id',
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
// Tool attributes
|
|
44
44
|
AI_TOOL_CALL_NAME: 'ai.toolCall.name',
|
|
45
45
|
AI_TOOL_CALL_RESULT: 'ai.toolCall.result',
|
|
@@ -47,37 +47,37 @@ export const SPAN_KEYS = {
|
|
|
47
47
|
AI_TOOL_CALL_ID: 'ai.toolCall.id',
|
|
48
48
|
AI_TOOL_TYPE: 'ai.toolType',
|
|
49
49
|
TOOL_PURPOSE: 'tool.purpose',
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
// Agent attributes
|
|
52
52
|
AGENT_ID: 'agent.id',
|
|
53
53
|
AGENT_NAME: 'agent.name',
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
// Token usage
|
|
56
56
|
GEN_AI_USAGE_INPUT_TOKENS: 'gen_ai.usage.input_tokens',
|
|
57
57
|
GEN_AI_USAGE_OUTPUT_TOKENS: 'gen_ai.usage.output_tokens',
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
// Context attributes
|
|
60
60
|
CONTEXT_URL: 'context.url',
|
|
61
61
|
CONTEXT_CONFIG_ID: 'context.context_config_id',
|
|
62
62
|
CONTEXT_AGENT_GRAPH_ID: 'context.agent_graph_id',
|
|
63
63
|
CONTEXT_REQUEST_KEYS: 'context.request_context_keys',
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
// Message attributes
|
|
66
66
|
MESSAGE_CONTENT: 'message.content',
|
|
67
67
|
MESSAGE_TIMESTAMP: 'message.timestamp',
|
|
68
68
|
MCP_TOOL_DESCRIPTION: 'mcp.tool.description',
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
// Delegation/Transfer attributes
|
|
71
71
|
DELEGATION_FROM_AGENT_ID: 'delegation.from_agent_id',
|
|
72
72
|
DELEGATION_TO_AGENT_ID: 'delegation.to_agent_id',
|
|
73
73
|
TRANSFER_FROM_AGENT_ID: 'transfer.from_agent_id',
|
|
74
74
|
TRANSFER_TO_AGENT_ID: 'transfer.to_agent_id',
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
// HTTP attributes
|
|
77
77
|
HTTP_URL: 'http.url',
|
|
78
78
|
HTTP_STATUS_CODE: 'http.status_code',
|
|
79
79
|
HTTP_RESPONSE_BODY_SIZE: 'http.response.body_size',
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
// Core attributes
|
|
82
82
|
NAME: 'name',
|
|
83
83
|
PARENT_SPAN_ID: 'parentSpanID',
|
|
@@ -103,19 +103,18 @@ export const QUERY_FIELD_CONFIGS = {
|
|
|
103
103
|
// String tag fields
|
|
104
104
|
STRING_TAG: { dataType: DATA_TYPES.STRING, type: FIELD_TYPES.TAG, isColumn: false },
|
|
105
105
|
STRING_TAG_COLUMN: { dataType: DATA_TYPES.STRING, type: FIELD_TYPES.TAG, isColumn: true },
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
// Numeric tag fields
|
|
108
108
|
INT64_TAG: { dataType: DATA_TYPES.INT64, type: FIELD_TYPES.TAG, isColumn: false },
|
|
109
109
|
INT64_TAG_COLUMN: { dataType: DATA_TYPES.INT64, type: FIELD_TYPES.TAG, isColumn: true },
|
|
110
110
|
FLOAT64_TAG: { dataType: DATA_TYPES.FLOAT64, type: FIELD_TYPES.TAG, isColumn: false },
|
|
111
111
|
FLOAT64_TAG_COLUMN: { dataType: DATA_TYPES.FLOAT64, type: FIELD_TYPES.TAG, isColumn: true },
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
// Boolean tag fields
|
|
114
114
|
BOOL_TAG: { dataType: DATA_TYPES.BOOL, type: FIELD_TYPES.TAG, isColumn: false },
|
|
115
115
|
BOOL_TAG_COLUMN: { dataType: DATA_TYPES.BOOL, type: FIELD_TYPES.TAG, isColumn: true },
|
|
116
116
|
} as const;
|
|
117
117
|
|
|
118
|
-
|
|
119
118
|
export const UNKNOWN_VALUE = 'unknown' as const;
|
|
120
119
|
|
|
121
120
|
/** Activity Types */
|
|
@@ -174,15 +173,15 @@ export const OPERATORS = {
|
|
|
174
173
|
GREATER_THAN: '>',
|
|
175
174
|
LESS_THAN_OR_EQUAL: '<=',
|
|
176
175
|
GREATER_THAN_OR_EQUAL: '>=',
|
|
177
|
-
|
|
176
|
+
|
|
178
177
|
// String operators
|
|
179
178
|
LIKE: 'like',
|
|
180
179
|
NOT_LIKE: 'nlike',
|
|
181
|
-
|
|
180
|
+
|
|
182
181
|
// Existence operators
|
|
183
182
|
EXISTS: 'exists',
|
|
184
183
|
NOT_EXISTS: 'nexists',
|
|
185
|
-
|
|
184
|
+
|
|
186
185
|
// Logical operators
|
|
187
186
|
AND: 'AND',
|
|
188
187
|
OR: 'OR',
|
|
@@ -276,5 +275,3 @@ export const QUERY_DEFAULTS = {
|
|
|
276
275
|
LIMIT_1000: 1000,
|
|
277
276
|
EMPTY_GROUP_BY: [],
|
|
278
277
|
} as const;
|
|
279
|
-
|
|
280
|
-
|
|
@@ -46,16 +46,16 @@ describe('graph serialize/deserialize', () => {
|
|
|
46
46
|
} as Edge,
|
|
47
47
|
];
|
|
48
48
|
|
|
49
|
-
const serialized = serializeGraphData(nodes, edges, {
|
|
50
|
-
id: 'g1',
|
|
51
|
-
name: 'G',
|
|
49
|
+
const serialized = serializeGraphData(nodes, edges, {
|
|
50
|
+
id: 'g1',
|
|
51
|
+
name: 'G',
|
|
52
52
|
description: 'D',
|
|
53
53
|
contextConfig: {
|
|
54
54
|
name: 'Context',
|
|
55
55
|
description: 'Context description',
|
|
56
56
|
contextVariables: '{}',
|
|
57
|
-
requestContextSchema: '{}'
|
|
58
|
-
}
|
|
57
|
+
requestContextSchema: '{}',
|
|
58
|
+
},
|
|
59
59
|
});
|
|
60
60
|
expect(serialized.id).toBe('g1');
|
|
61
61
|
expect(serialized.agents['a1']).toBeDefined();
|
|
@@ -44,7 +44,6 @@ function safeJsonParse(jsonString: string | undefined | null): any {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
47
|
/**
|
|
49
48
|
* Transforms React Flow nodes and edges back into the API data structure
|
|
50
49
|
*/
|
|
@@ -71,13 +70,13 @@ export function serializeGraphData(
|
|
|
71
70
|
agentArtifactComponents.forEach((componentId) => usedArtifactComponents.add(componentId));
|
|
72
71
|
// Process models - only include if it has non-empty, non-whitespace values
|
|
73
72
|
const modelsData = node.data.models as GraphMetadata['models'] | undefined;
|
|
74
|
-
let processedModels: GraphMetadata['models'] | undefined
|
|
75
|
-
|
|
73
|
+
let processedModels: GraphMetadata['models'] | undefined;
|
|
74
|
+
|
|
76
75
|
if (modelsData && typeof modelsData === 'object') {
|
|
77
|
-
const hasNonEmptyValue = Object.values(modelsData).some(
|
|
78
|
-
value !== null && value !== undefined && String(value).trim() !== ''
|
|
76
|
+
const hasNonEmptyValue = Object.values(modelsData).some(
|
|
77
|
+
(value) => value !== null && value !== undefined && String(value).trim() !== ''
|
|
79
78
|
);
|
|
80
|
-
|
|
79
|
+
|
|
81
80
|
if (hasNonEmptyValue) {
|
|
82
81
|
processedModels = modelsData;
|
|
83
82
|
}
|
|
@@ -247,15 +246,15 @@ export function serializeGraphData(
|
|
|
247
246
|
if (metadata?.models) {
|
|
248
247
|
(result as any).models = metadata.models;
|
|
249
248
|
}
|
|
250
|
-
|
|
249
|
+
|
|
251
250
|
if (metadata?.stopWhen) {
|
|
252
251
|
(result as any).stopWhen = metadata.stopWhen;
|
|
253
252
|
}
|
|
254
|
-
|
|
253
|
+
|
|
255
254
|
if (metadata?.graphPrompt) {
|
|
256
255
|
(result as any).graphPrompt = metadata.graphPrompt;
|
|
257
256
|
}
|
|
258
|
-
|
|
257
|
+
|
|
259
258
|
if (metadata?.statusUpdates) {
|
|
260
259
|
(result as any).statusUpdates = metadata.statusUpdates;
|
|
261
260
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { generateId } from '@/lib/utils/generate-id';
|
|
3
|
+
|
|
4
|
+
interface UseAutoPrefillIdZustandOptions {
|
|
5
|
+
nameValue: string | undefined;
|
|
6
|
+
idValue: string | undefined;
|
|
7
|
+
onIdChange: (id: string) => void;
|
|
8
|
+
isEditing?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Custom hook to auto-prefill an ID field based on a name field for Zustand state
|
|
13
|
+
* Only prefills when creating new items (not editing) and when the ID field hasn't been manually edited
|
|
14
|
+
*/
|
|
15
|
+
export function useAutoPrefillIdZustand({
|
|
16
|
+
nameValue,
|
|
17
|
+
idValue,
|
|
18
|
+
onIdChange,
|
|
19
|
+
isEditing = false,
|
|
20
|
+
}: UseAutoPrefillIdZustandOptions) {
|
|
21
|
+
// Track if user has manually edited the ID field
|
|
22
|
+
const hasManuallyEditedId = useRef(false);
|
|
23
|
+
const lastGeneratedId = useRef('');
|
|
24
|
+
const onIdChangeRef = useRef(onIdChange);
|
|
25
|
+
const currentIdValue = useRef(idValue);
|
|
26
|
+
|
|
27
|
+
// Keep refs up to date
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
onIdChangeRef.current = onIdChange;
|
|
30
|
+
currentIdValue.current = idValue;
|
|
31
|
+
}, [onIdChange, idValue]);
|
|
32
|
+
|
|
33
|
+
// Reset manual edit tracking when switching between graphs
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (isEditing) {
|
|
36
|
+
hasManuallyEditedId.current = false;
|
|
37
|
+
lastGeneratedId.current = '';
|
|
38
|
+
}
|
|
39
|
+
}, [isEditing]);
|
|
40
|
+
|
|
41
|
+
// Track manual edits to ID field
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!isEditing) {
|
|
44
|
+
if (!idValue) {
|
|
45
|
+
// If ID field is empty, reset manual edit flag to allow auto-generation
|
|
46
|
+
hasManuallyEditedId.current = false;
|
|
47
|
+
lastGeneratedId.current = '';
|
|
48
|
+
} else if (idValue !== lastGeneratedId.current) {
|
|
49
|
+
// Consider it a manual edit if user typed something different from our generated value
|
|
50
|
+
// This includes typing into an empty field (when lastGeneratedId is empty)
|
|
51
|
+
hasManuallyEditedId.current = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}, [idValue, isEditing]);
|
|
55
|
+
|
|
56
|
+
// Auto-prefill ID based on name field
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!isEditing && nameValue && !hasManuallyEditedId.current) {
|
|
59
|
+
const generatedId = generateId(nameValue);
|
|
60
|
+
|
|
61
|
+
// Only update if the generated ID is different from current ID
|
|
62
|
+
if (generatedId !== currentIdValue.current) {
|
|
63
|
+
lastGeneratedId.current = generatedId;
|
|
64
|
+
onIdChangeRef.current(generatedId);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}, [nameValue, isEditing]);
|
|
68
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { type UseFormReturn, type FieldValues, useWatch } from 'react-hook-form';
|
|
3
|
+
import { generateId } from '@/lib/utils/generate-id';
|
|
4
|
+
|
|
5
|
+
interface UseAutoPrefillIdOptions<T extends FieldValues> {
|
|
6
|
+
form: UseFormReturn<T>;
|
|
7
|
+
nameField: keyof T;
|
|
8
|
+
idField: keyof T;
|
|
9
|
+
isEditing?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Custom hook to auto-prefill an ID field based on a name field
|
|
14
|
+
* Only prefills when creating new items (not editing) and when the ID field hasn't been manually edited
|
|
15
|
+
*/
|
|
16
|
+
export function useAutoPrefillId<T extends FieldValues>({
|
|
17
|
+
form,
|
|
18
|
+
nameField,
|
|
19
|
+
idField,
|
|
20
|
+
isEditing = false,
|
|
21
|
+
}: UseAutoPrefillIdOptions<T>) {
|
|
22
|
+
const nameValue = useWatch({
|
|
23
|
+
control: form.control,
|
|
24
|
+
name: nameField as any,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const isIdFieldModified = (form.formState.dirtyFields as any)[idField];
|
|
28
|
+
|
|
29
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: we don't want to re-run this effect when the isIdFieldModified changes since that means the user has manually edited the ID field
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!isEditing && nameValue && !isIdFieldModified) {
|
|
32
|
+
const generatedId = generateId(nameValue);
|
|
33
|
+
form.setValue(idField as any, generatedId as any, { shouldValidate: true });
|
|
34
|
+
}
|
|
35
|
+
}, [nameValue, idField, isEditing]);
|
|
36
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import type { ConversationDetail } from '@/components/traces/timeline/types';
|
|
3
3
|
|
|
4
4
|
interface UseChatActivitiesPollingOptions {
|
|
@@ -12,6 +12,8 @@ interface UseChatActivitiesPollingReturn {
|
|
|
12
12
|
error: string | null;
|
|
13
13
|
startPolling: () => void;
|
|
14
14
|
stopPolling: () => void;
|
|
15
|
+
retryConnection: () => void;
|
|
16
|
+
refreshOnce: () => Promise<{ hasNewActivity: boolean }>;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export const useChatActivitiesPolling = ({
|
|
@@ -27,15 +29,10 @@ export const useChatActivitiesPolling = ({
|
|
|
27
29
|
const isComponentMountedRef = useRef(true);
|
|
28
30
|
const abortControllerRef = useRef<AbortController | null>(null);
|
|
29
31
|
|
|
30
|
-
const fetchChatActivities = useCallback(async () => {
|
|
32
|
+
const fetchChatActivities = useCallback(async (): Promise<ConversationDetail | null> => {
|
|
31
33
|
try {
|
|
32
34
|
setError(null);
|
|
33
35
|
|
|
34
|
-
// Cancel any previous request
|
|
35
|
-
if (abortControllerRef.current) {
|
|
36
|
-
abortControllerRef.current.abort();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
36
|
// Create new abort controller for this request
|
|
40
37
|
abortControllerRef.current = new AbortController();
|
|
41
38
|
const currentConversationId = conversationId; // Capture current ID
|
|
@@ -46,7 +43,9 @@ export const useChatActivitiesPolling = ({
|
|
|
46
43
|
|
|
47
44
|
if (!response.ok) {
|
|
48
45
|
// If conversation doesn't exist yet, that's fine - just return
|
|
49
|
-
if (response.status === 404)
|
|
46
|
+
if (response.status === 404) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
50
49
|
throw new Error('Failed to fetch chat activities');
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -61,16 +60,29 @@ export const useChatActivitiesPolling = ({
|
|
|
61
60
|
setLastActivityCount(newCount);
|
|
62
61
|
}
|
|
63
62
|
}
|
|
63
|
+
|
|
64
|
+
return data;
|
|
64
65
|
} catch (err) {
|
|
65
66
|
// Don't log abort errors as they are expected when cancelling requests
|
|
66
67
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
67
|
-
return;
|
|
68
|
+
return null;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
if (isComponentMountedRef.current) {
|
|
71
|
-
console.warn('Error fetching chat activities:', err);
|
|
72
72
|
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
73
|
+
// Stop polling on error to prevent repeated failed requests
|
|
74
|
+
setIsPolling(false);
|
|
75
|
+
if (pollingIntervalRef.current) {
|
|
76
|
+
clearInterval(pollingIntervalRef.current);
|
|
77
|
+
pollingIntervalRef.current = null;
|
|
78
|
+
}
|
|
79
|
+
// Cancel any pending requests
|
|
80
|
+
if (abortControllerRef.current) {
|
|
81
|
+
abortControllerRef.current.abort();
|
|
82
|
+
abortControllerRef.current = null;
|
|
83
|
+
}
|
|
73
84
|
}
|
|
85
|
+
throw err;
|
|
74
86
|
}
|
|
75
87
|
}, [conversationId, lastActivityCount]);
|
|
76
88
|
|
|
@@ -81,11 +93,15 @@ export const useChatActivitiesPolling = ({
|
|
|
81
93
|
setIsPolling(true);
|
|
82
94
|
|
|
83
95
|
// Initial fetch
|
|
84
|
-
fetchChatActivities()
|
|
96
|
+
fetchChatActivities().catch(() => {
|
|
97
|
+
// Error handling is already done in fetchChatActivities
|
|
98
|
+
});
|
|
85
99
|
|
|
86
100
|
// Set up polling interval
|
|
87
101
|
pollingIntervalRef.current = setInterval(() => {
|
|
88
|
-
fetchChatActivities()
|
|
102
|
+
fetchChatActivities().catch(() => {
|
|
103
|
+
// Error handling is already done in fetchChatActivities
|
|
104
|
+
});
|
|
89
105
|
}, pollingInterval);
|
|
90
106
|
}, [fetchChatActivities, pollingInterval]);
|
|
91
107
|
|
|
@@ -103,6 +119,21 @@ export const useChatActivitiesPolling = ({
|
|
|
103
119
|
}
|
|
104
120
|
}, []);
|
|
105
121
|
|
|
122
|
+
// Retry connection - clears error and restarts polling
|
|
123
|
+
const retryConnection = useCallback(() => {
|
|
124
|
+
setError(null);
|
|
125
|
+
stopPolling();
|
|
126
|
+
startPolling();
|
|
127
|
+
}, [startPolling, stopPolling]);
|
|
128
|
+
|
|
129
|
+
// Refresh once - makes a single request without starting polling
|
|
130
|
+
const refreshOnce = useCallback(async (): Promise<{ hasNewActivity: boolean }> => {
|
|
131
|
+
const currentCount = chatActivities?.activities?.length || 0;
|
|
132
|
+
const data = await fetchChatActivities();
|
|
133
|
+
const newCount = data?.activities?.length || 0;
|
|
134
|
+
return { hasNewActivity: newCount > currentCount };
|
|
135
|
+
}, [fetchChatActivities, chatActivities?.activities?.length]);
|
|
136
|
+
|
|
106
137
|
// Cleanup on unmount
|
|
107
138
|
useEffect(() => {
|
|
108
139
|
isComponentMountedRef.current = true;
|
|
@@ -141,5 +172,7 @@ export const useChatActivitiesPolling = ({
|
|
|
141
172
|
error,
|
|
142
173
|
startPolling,
|
|
143
174
|
stopPolling,
|
|
175
|
+
retryConnection,
|
|
176
|
+
refreshOnce,
|
|
144
177
|
};
|
|
145
178
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Hook for accessing graph error state and utilities
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
|
+
import { useGraphStore } from '@/features/graph/state/use-graph-store';
|
|
6
7
|
|
|
7
8
|
export interface ErrorHelpers {
|
|
8
9
|
hasFieldError: (fieldName: string) => boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
3
|
import { useParams } from 'next/navigation';
|
|
5
|
-
import
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
6
5
|
import { fetchProjectAction } from '@/lib/actions/projects';
|
|
6
|
+
import type { Project } from '@/lib/types/project';
|
|
7
7
|
|
|
8
8
|
export function useProjectData() {
|
|
9
9
|
const params = useParams();
|
|
@@ -12,11 +12,15 @@ import {
|
|
|
12
12
|
ApiError,
|
|
13
13
|
createFullGraph as apiCreateFullGraph,
|
|
14
14
|
deleteFullGraph as apiDeleteFullGraph,
|
|
15
|
+
fetchGraphs as apiFetchGraphs,
|
|
15
16
|
getFullGraph as apiGetFullGraph,
|
|
16
17
|
updateFullGraph as apiUpdateFullGraph,
|
|
17
|
-
fetchGraphs as apiFetchGraphs,
|
|
18
18
|
} from '../api/graph-full-client';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
type FullGraphDefinition,
|
|
21
|
+
FullGraphDefinitionSchema,
|
|
22
|
+
type Graph,
|
|
23
|
+
} from '../types/graph-full';
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Result type for server actions - follows a consistent pattern
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use server';
|
|
2
2
|
|
|
3
3
|
import { revalidatePath } from 'next/cache';
|
|
4
|
+
import type { ProjectFormData } from '@/components/projects/form/validation';
|
|
4
5
|
import {
|
|
5
6
|
createProject,
|
|
6
7
|
deleteProject,
|
|
@@ -11,7 +12,6 @@ import {
|
|
|
11
12
|
import { ApiError } from '../types/errors';
|
|
12
13
|
import type { Project } from '../types/project';
|
|
13
14
|
import type { ActionResult } from './types';
|
|
14
|
-
import type { ProjectFormData } from '@/components/projects/form/validation';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Fetch all projects
|
|
@@ -10,23 +10,23 @@ const DEFAULT_MANAGEMENT_API_BASE_URL = 'http://localhost:3002';
|
|
|
10
10
|
const DEFAULT_EXECUTION_API_BASE_URL = 'http://localhost:3003';
|
|
11
11
|
|
|
12
12
|
// Management API (CRUD operations, configuration)
|
|
13
|
-
if (!process.env.
|
|
13
|
+
if (!process.env.NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL) {
|
|
14
14
|
console.warn(
|
|
15
|
-
`
|
|
15
|
+
`NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL is not set, falling back to: ${DEFAULT_MANAGEMENT_API_BASE_URL}`
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// Execution API (chat completions, agent execution)
|
|
20
|
-
if (!process.env.
|
|
20
|
+
if (!process.env.NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL) {
|
|
21
21
|
console.warn(
|
|
22
|
-
`
|
|
22
|
+
`NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL is not set, falling back to: ${DEFAULT_EXECUTION_API_BASE_URL}`
|
|
23
23
|
);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export const MANAGEMENT_API_BASE_URL =
|
|
27
|
-
process.env.
|
|
27
|
+
process.env.NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL || DEFAULT_MANAGEMENT_API_BASE_URL;
|
|
28
28
|
export const EXECUTION_API_BASE_URL =
|
|
29
|
-
process.env.
|
|
29
|
+
process.env.NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL || DEFAULT_EXECUTION_API_BASE_URL;
|
|
30
30
|
|
|
31
31
|
async function makeApiRequestInternal<T>(
|
|
32
32
|
baseUrl: string,
|
package/src/lib/api/api-keys.ts
CHANGED
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
'use server';
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
ApiKeyApiCreationResponse,
|
|
12
|
+
ApiKeyApiSelect,
|
|
13
|
+
} from '@inkeep/agents-core/client-exports';
|
|
11
14
|
import type { ListResponse, SingleResponse } from '../types/response';
|
|
12
15
|
import { makeManagementApiRequest } from './api-config';
|
|
13
16
|
import { validateProjectId, validateTenantId } from './resource-validation';
|
|
@@ -100,9 +100,12 @@ export async function deleteFullGraph(
|
|
|
100
100
|
validateTenantId(tenantId);
|
|
101
101
|
validateProjectId(projectId);
|
|
102
102
|
|
|
103
|
-
await makeManagementApiRequest(
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
await makeManagementApiRequest(
|
|
104
|
+
`tenants/${tenantId}/crud/projects/${projectId}/graph/${graphId}`,
|
|
105
|
+
{
|
|
106
|
+
method: 'DELETE',
|
|
107
|
+
}
|
|
108
|
+
);
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
// Export the error class for use in server actions
|
package/src/lib/api/projects.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use server';
|
|
2
2
|
|
|
3
|
+
import type { ProjectFormData } from '@/components/projects/form/validation';
|
|
3
4
|
import type { Project } from '../types/project';
|
|
4
5
|
import type { ListResponse, SingleResponse } from '../types/response';
|
|
5
|
-
import type { ProjectFormData } from '@/components/projects/form/validation';
|
|
6
6
|
import { makeManagementApiRequest } from './api-config';
|
|
7
7
|
import { validateTenantId } from './resource-validation';
|
|
8
8
|
|