@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,16 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ArrowRight,
|
|
3
|
+
ArrowUpRight,
|
|
4
|
+
CheckCircle,
|
|
5
|
+
ChevronDown,
|
|
6
|
+
ChevronRight,
|
|
7
|
+
Database,
|
|
8
|
+
Hammer,
|
|
9
|
+
Settings,
|
|
10
|
+
Sparkles,
|
|
11
|
+
User,
|
|
12
|
+
} from 'lucide-react';
|
|
13
|
+
import { Streamdown } from 'streamdown';
|
|
14
|
+
import { formatDateTime } from '@/app/utils/format-date';
|
|
15
|
+
import { Bubble, CodeBubble } from '@/components/traces/timeline/bubble';
|
|
16
|
+
import { Flow } from '@/components/traces/timeline/flow';
|
|
17
|
+
import { TagRow } from '@/components/traces/timeline/tag-row';
|
|
18
|
+
import {
|
|
19
|
+
ACTIVITY_TYPES,
|
|
2
20
|
type ActivityItem,
|
|
3
21
|
type ActivityKind,
|
|
4
|
-
|
|
22
|
+
TOOL_TYPES,
|
|
5
23
|
} from '@/components/traces/timeline/types';
|
|
6
|
-
import { User, Settings, Database, Hammer, Sparkles, ArrowRight, ArrowUpRight, ChevronDown, ChevronRight } from 'lucide-react';
|
|
7
|
-
import { Bubble, CodeBubble } from '@/components/traces/timeline/bubble';
|
|
8
|
-
import { TagRow } from '@/components/traces/timeline/tag-row';
|
|
9
|
-
import { TOOL_TYPES } from '@/components/traces/timeline/types';
|
|
10
|
-
import { CheckCircle } from 'lucide-react';
|
|
11
|
-
import { Flow } from '@/components/traces/timeline/flow';
|
|
12
|
-
import { Streamdown } from 'streamdown';
|
|
13
|
-
import { formatDateTime } from '@/app/utils/format-date';
|
|
14
24
|
|
|
15
25
|
function truncateWords(s: string, nWords: number) {
|
|
16
26
|
const words = s.split(/\s+/);
|
|
@@ -21,8 +31,10 @@ function truncateChars(s: string, n: number) {
|
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
function isAiMessage(activity: ActivityItem): boolean {
|
|
24
|
-
return
|
|
25
|
-
|
|
34
|
+
return (
|
|
35
|
+
activity.type === ACTIVITY_TYPES.AI_ASSISTANT_MESSAGE ||
|
|
36
|
+
activity.type === ACTIVITY_TYPES.AI_MODEL_STREAMED_TEXT
|
|
37
|
+
);
|
|
26
38
|
}
|
|
27
39
|
|
|
28
40
|
function statusIcon(
|
|
@@ -64,12 +76,12 @@ interface TimelineItemProps {
|
|
|
64
76
|
onToggleAiMessageCollapse?: (activityId: string) => void;
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
export function TimelineItem({
|
|
68
|
-
activity,
|
|
69
|
-
isLast,
|
|
70
|
-
onSelect,
|
|
71
|
-
isAiMessageCollapsed = false,
|
|
72
|
-
onToggleAiMessageCollapse
|
|
79
|
+
export function TimelineItem({
|
|
80
|
+
activity,
|
|
81
|
+
isLast,
|
|
82
|
+
onSelect,
|
|
83
|
+
isAiMessageCollapsed = false,
|
|
84
|
+
onToggleAiMessageCollapse,
|
|
73
85
|
}: TimelineItemProps) {
|
|
74
86
|
const typeForIcon =
|
|
75
87
|
activity.type === ACTIVITY_TYPES.TOOL_CALL && activity.toolType === TOOL_TYPES.TRANSFER
|
|
@@ -128,7 +140,7 @@ export function TimelineItem({
|
|
|
128
140
|
type="button"
|
|
129
141
|
onClick={() => onToggleAiMessageCollapse(activity.id)}
|
|
130
142
|
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
131
|
-
title={isAiMessageCollapsed ?
|
|
143
|
+
title={isAiMessageCollapsed ? 'Expand AI response' : 'Collapse AI response'}
|
|
132
144
|
>
|
|
133
145
|
{isAiMessageCollapsed ? (
|
|
134
146
|
<ChevronRight className="h-3 w-3" />
|
|
@@ -154,7 +166,11 @@ export function TimelineItem({
|
|
|
154
166
|
type="button"
|
|
155
167
|
onClick={() => onToggleAiMessageCollapse(activity.id)}
|
|
156
168
|
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
|
157
|
-
title={
|
|
169
|
+
title={
|
|
170
|
+
isAiMessageCollapsed
|
|
171
|
+
? 'Expand AI streaming response'
|
|
172
|
+
: 'Collapse AI streaming response'
|
|
173
|
+
}
|
|
158
174
|
>
|
|
159
175
|
{isAiMessageCollapsed ? (
|
|
160
176
|
<ChevronRight className="h-3 w-3" />
|
|
@@ -165,9 +181,7 @@ export function TimelineItem({
|
|
|
165
181
|
</button>
|
|
166
182
|
)}
|
|
167
183
|
{!isAiMessageCollapsed && (
|
|
168
|
-
<Bubble>
|
|
169
|
-
{truncateWords(activity.aiStreamTextContent, 100)}
|
|
170
|
-
</Bubble>
|
|
184
|
+
<Bubble>{truncateWords(activity.aiStreamTextContent, 100)}</Bubble>
|
|
171
185
|
)}
|
|
172
186
|
</div>
|
|
173
187
|
)}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
+
import { AlertTriangle, ChevronDown, ChevronUp, Loader2, RefreshCw } from 'lucide-react';
|
|
1
2
|
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { toast } from 'sonner';
|
|
4
|
+
import { StickToBottom } from 'use-stick-to-bottom';
|
|
5
|
+
import { ConversationTracesLink } from '@/components/traces/signoz-link';
|
|
6
|
+
import { ActivityDetailsSidePane } from '@/components/traces/timeline/activity-details-sidepane';
|
|
2
7
|
import { ActivityTimeline } from '@/components/traces/timeline/activity-timeline';
|
|
8
|
+
import { renderPanelContent } from '@/components/traces/timeline/render-panel-content';
|
|
3
9
|
import type {
|
|
4
10
|
ActivityItem,
|
|
5
|
-
SelectedPanel,
|
|
6
|
-
PanelType,
|
|
7
11
|
ConversationDetail,
|
|
12
|
+
PanelType,
|
|
13
|
+
SelectedPanel,
|
|
8
14
|
} from '@/components/traces/timeline/types';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { TOOL_TYPES } from '@/components/traces/timeline/types';
|
|
12
|
-
import { renderPanelContent } from '@/components/traces/timeline/render-panel-content';
|
|
13
|
-
import { StickToBottom } from 'use-stick-to-bottom';
|
|
14
|
-
import { ResizableHandle, ResizablePanel } from '@/components/ui/resizable';
|
|
15
|
-
import { Loader2, ChevronDown, ChevronUp } from 'lucide-react';
|
|
16
|
-
import { ConversationTracesLink } from '@/components/traces/signoz-link';
|
|
15
|
+
import { ACTIVITY_TYPES, TOOL_TYPES } from '@/components/traces/timeline/types';
|
|
16
|
+
import { Alert, AlertTitle } from '@/components/ui/alert';
|
|
17
17
|
import { Button } from '@/components/ui/button';
|
|
18
|
+
import { ResizableHandle, ResizablePanel } from '@/components/ui/resizable';
|
|
18
19
|
|
|
19
20
|
function panelTitle(selected: SelectedPanel) {
|
|
20
21
|
switch (selected.type) {
|
|
@@ -49,9 +50,42 @@ interface TimelineWrapperProps {
|
|
|
49
50
|
conversation?: ConversationDetail | null;
|
|
50
51
|
enableAutoScroll?: boolean;
|
|
51
52
|
isPolling?: boolean;
|
|
53
|
+
error?: string | null;
|
|
54
|
+
retryConnection?: () => void;
|
|
55
|
+
refreshOnce?: () => Promise<{ hasNewActivity: boolean }>;
|
|
56
|
+
showConversationTracesLink?: boolean;
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
function EmptyTimeline({
|
|
59
|
+
function EmptyTimeline({
|
|
60
|
+
isPolling,
|
|
61
|
+
error,
|
|
62
|
+
retryConnection,
|
|
63
|
+
}: {
|
|
64
|
+
isPolling: boolean;
|
|
65
|
+
error?: string | null;
|
|
66
|
+
retryConnection?: () => void;
|
|
67
|
+
}) {
|
|
68
|
+
if (error) {
|
|
69
|
+
return (
|
|
70
|
+
<div className="flex flex-col gap-4 h-full justify-center items-center px-6">
|
|
71
|
+
<Alert variant="destructive" className="max-w-md">
|
|
72
|
+
<AlertTriangle className="h-4 w-4" />
|
|
73
|
+
<AlertTitle>{error}</AlertTitle>
|
|
74
|
+
</Alert>
|
|
75
|
+
{retryConnection && (
|
|
76
|
+
<Button
|
|
77
|
+
variant="outline"
|
|
78
|
+
size="sm"
|
|
79
|
+
onClick={retryConnection}
|
|
80
|
+
className="flex items-center gap-2"
|
|
81
|
+
>
|
|
82
|
+
<RefreshCw className="h-4 w-4" />
|
|
83
|
+
Retry Connection
|
|
84
|
+
</Button>
|
|
85
|
+
)}
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
55
89
|
return (
|
|
56
90
|
<div className="flex flex-col gap-2 h-full justify-center items-center">
|
|
57
91
|
{isPolling ? (
|
|
@@ -72,13 +106,19 @@ export function TimelineWrapper({
|
|
|
72
106
|
conversation,
|
|
73
107
|
enableAutoScroll = false,
|
|
74
108
|
isPolling = false,
|
|
109
|
+
error,
|
|
110
|
+
retryConnection,
|
|
111
|
+
refreshOnce,
|
|
112
|
+
showConversationTracesLink = false,
|
|
75
113
|
}: TimelineWrapperProps) {
|
|
76
114
|
const [selected, setSelected] = useState<SelectedPanel | null>(null);
|
|
77
115
|
const [panelVisible, setPanelVisible] = useState(false);
|
|
78
|
-
|
|
116
|
+
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
117
|
+
|
|
79
118
|
// State for collapsible AI messages
|
|
80
119
|
const [collapsedAiMessages, setCollapsedAiMessages] = useState<Set<string>>(new Set());
|
|
81
|
-
const [aiMessagesGloballyCollapsed, setAiMessagesGloballyCollapsed] =
|
|
120
|
+
const [aiMessagesGloballyCollapsed, setAiMessagesGloballyCollapsed] =
|
|
121
|
+
useState<boolean>(enableAutoScroll);
|
|
82
122
|
|
|
83
123
|
useEffect(() => {
|
|
84
124
|
if (selected) {
|
|
@@ -117,12 +157,13 @@ export function TimelineWrapper({
|
|
|
117
157
|
// Initialize AI messages based on view type when activities change
|
|
118
158
|
useEffect(() => {
|
|
119
159
|
const aiMessageIds = sortedActivities
|
|
120
|
-
.filter(
|
|
121
|
-
activity
|
|
122
|
-
|
|
160
|
+
.filter(
|
|
161
|
+
(activity) =>
|
|
162
|
+
activity.type === ACTIVITY_TYPES.AI_ASSISTANT_MESSAGE ||
|
|
163
|
+
activity.type === ACTIVITY_TYPES.AI_MODEL_STREAMED_TEXT
|
|
123
164
|
)
|
|
124
|
-
.map(activity => activity.id);
|
|
125
|
-
|
|
165
|
+
.map((activity) => activity.id);
|
|
166
|
+
|
|
126
167
|
if (enableAutoScroll) {
|
|
127
168
|
// Live trace view: default collapsed
|
|
128
169
|
setCollapsedAiMessages(new Set(aiMessageIds));
|
|
@@ -133,7 +174,7 @@ export function TimelineWrapper({
|
|
|
133
174
|
setAiMessagesGloballyCollapsed(false);
|
|
134
175
|
}
|
|
135
176
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
136
|
-
}, [sortedActivities.length, enableAutoScroll]); //
|
|
177
|
+
}, [sortedActivities.length, enableAutoScroll]); // Run when activities count or view type changes
|
|
137
178
|
|
|
138
179
|
// Functions to handle expand/collapse all
|
|
139
180
|
const expandAllAiMessages = () => {
|
|
@@ -143,11 +184,12 @@ export function TimelineWrapper({
|
|
|
143
184
|
|
|
144
185
|
const collapseAllAiMessages = () => {
|
|
145
186
|
const aiMessageIds = sortedActivities
|
|
146
|
-
.filter(
|
|
147
|
-
activity
|
|
148
|
-
|
|
187
|
+
.filter(
|
|
188
|
+
(activity) =>
|
|
189
|
+
activity.type === ACTIVITY_TYPES.AI_ASSISTANT_MESSAGE ||
|
|
190
|
+
activity.type === ACTIVITY_TYPES.AI_MODEL_STREAMED_TEXT
|
|
149
191
|
)
|
|
150
|
-
.map(activity => activity.id);
|
|
192
|
+
.map((activity) => activity.id);
|
|
151
193
|
setCollapsedAiMessages(new Set(aiMessageIds));
|
|
152
194
|
setAiMessagesGloballyCollapsed(true);
|
|
153
195
|
};
|
|
@@ -160,15 +202,16 @@ export function TimelineWrapper({
|
|
|
160
202
|
newCollapsed.add(activityId);
|
|
161
203
|
}
|
|
162
204
|
setCollapsedAiMessages(newCollapsed);
|
|
163
|
-
|
|
205
|
+
|
|
164
206
|
// Update global state based on current state
|
|
165
207
|
const aiMessageIds = sortedActivities
|
|
166
|
-
.filter(
|
|
167
|
-
activity
|
|
168
|
-
|
|
208
|
+
.filter(
|
|
209
|
+
(activity) =>
|
|
210
|
+
activity.type === ACTIVITY_TYPES.AI_ASSISTANT_MESSAGE ||
|
|
211
|
+
activity.type === ACTIVITY_TYPES.AI_MODEL_STREAMED_TEXT
|
|
169
212
|
)
|
|
170
|
-
.map(activity => activity.id);
|
|
171
|
-
const allCollapsed = aiMessageIds.every(id => newCollapsed.has(id));
|
|
213
|
+
.map((activity) => activity.id);
|
|
214
|
+
const allCollapsed = aiMessageIds.every((id) => newCollapsed.has(id));
|
|
172
215
|
setAiMessagesGloballyCollapsed(allCollapsed);
|
|
173
216
|
};
|
|
174
217
|
|
|
@@ -183,7 +226,8 @@ export function TimelineWrapper({
|
|
|
183
226
|
);
|
|
184
227
|
|
|
185
228
|
const determinePanelType = (a: ActivityItem): Exclude<PanelType, 'mcp_tool_error'> => {
|
|
186
|
-
if (a.type === ACTIVITY_TYPES.TOOL_CALL && a.toolType === TOOL_TYPES.TRANSFER)
|
|
229
|
+
if (a.type === ACTIVITY_TYPES.TOOL_CALL && a.toolType === TOOL_TYPES.TRANSFER)
|
|
230
|
+
return 'transfer';
|
|
187
231
|
if (a.type === ACTIVITY_TYPES.TOOL_CALL && a.toolName?.includes('delegate'))
|
|
188
232
|
return 'delegation';
|
|
189
233
|
if (
|
|
@@ -196,6 +240,21 @@ export function TimelineWrapper({
|
|
|
196
240
|
return a.type;
|
|
197
241
|
};
|
|
198
242
|
|
|
243
|
+
const handleRefresh = async () => {
|
|
244
|
+
if (!refreshOnce || isRefreshing) return;
|
|
245
|
+
setIsRefreshing(true);
|
|
246
|
+
try {
|
|
247
|
+
const result = await refreshOnce();
|
|
248
|
+
if (!result.hasNewActivity) {
|
|
249
|
+
toast.info('No new activity found');
|
|
250
|
+
}
|
|
251
|
+
setIsRefreshing(false);
|
|
252
|
+
} catch {
|
|
253
|
+
toast.error('Failed to refresh activities');
|
|
254
|
+
setIsRefreshing(false);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
199
258
|
return (
|
|
200
259
|
<>
|
|
201
260
|
<ResizablePanel order={2}>
|
|
@@ -205,17 +264,24 @@ export function TimelineWrapper({
|
|
|
205
264
|
<div className="text-foreground text-md font-medium">Activity Timeline</div>
|
|
206
265
|
<div className="flex items-center gap-2">
|
|
207
266
|
{/* Expand/Collapse AI Messages Buttons */}
|
|
208
|
-
{sortedActivities.some(
|
|
209
|
-
activity
|
|
210
|
-
|
|
267
|
+
{sortedActivities.some(
|
|
268
|
+
(activity) =>
|
|
269
|
+
activity.type === ACTIVITY_TYPES.AI_ASSISTANT_MESSAGE ||
|
|
270
|
+
activity.type === ACTIVITY_TYPES.AI_MODEL_STREAMED_TEXT
|
|
211
271
|
) && (
|
|
212
272
|
<div className="flex items-center gap-1">
|
|
213
273
|
<Button
|
|
214
274
|
variant="ghost"
|
|
215
275
|
size="sm"
|
|
216
|
-
onClick={
|
|
276
|
+
onClick={
|
|
277
|
+
aiMessagesGloballyCollapsed ? expandAllAiMessages : collapseAllAiMessages
|
|
278
|
+
}
|
|
217
279
|
className="h-7 px-2 text-xs text-muted-foreground hover:text-foreground"
|
|
218
|
-
title={
|
|
280
|
+
title={
|
|
281
|
+
aiMessagesGloballyCollapsed
|
|
282
|
+
? 'Expand all AI messages'
|
|
283
|
+
: 'Collapse all AI messages'
|
|
284
|
+
}
|
|
219
285
|
>
|
|
220
286
|
{aiMessagesGloballyCollapsed ? (
|
|
221
287
|
<>
|
|
@@ -231,7 +297,7 @@ export function TimelineWrapper({
|
|
|
231
297
|
</Button>
|
|
232
298
|
</div>
|
|
233
299
|
)}
|
|
234
|
-
{conversation?.conversationId && (
|
|
300
|
+
{showConversationTracesLink && conversation?.conversationId && (
|
|
235
301
|
<ConversationTracesLink conversationId={conversation.conversationId} />
|
|
236
302
|
)}
|
|
237
303
|
</div>
|
|
@@ -239,7 +305,11 @@ export function TimelineWrapper({
|
|
|
239
305
|
</div>
|
|
240
306
|
<div className="p-0 flex-1 min-h-0">
|
|
241
307
|
{sortedActivities.length === 0 ? (
|
|
242
|
-
<EmptyTimeline
|
|
308
|
+
<EmptyTimeline
|
|
309
|
+
isPolling={isPolling}
|
|
310
|
+
error={error}
|
|
311
|
+
retryConnection={retryConnection}
|
|
312
|
+
/>
|
|
243
313
|
) : enableAutoScroll ? (
|
|
244
314
|
<StickToBottom
|
|
245
315
|
className="h-full [&>div]:overflow-y-auto [&>div]:scrollbar-thin [&>div]:scrollbar-thumb-muted-foreground/30 [&>div]:scrollbar-track-transparent dark:[&>div]:scrollbar-thumb-muted-foreground/50"
|
|
@@ -255,6 +325,24 @@ export function TimelineWrapper({
|
|
|
255
325
|
collapsedAiMessages={collapsedAiMessages}
|
|
256
326
|
onToggleAiMessageCollapse={toggleAiMessageCollapse}
|
|
257
327
|
/>
|
|
328
|
+
{!isPolling && sortedActivities.length > 0 && !error && refreshOnce && (
|
|
329
|
+
<div className="flex justify-center items-center z-10">
|
|
330
|
+
<Button
|
|
331
|
+
variant="outline"
|
|
332
|
+
size="sm"
|
|
333
|
+
onClick={handleRefresh}
|
|
334
|
+
disabled={isRefreshing}
|
|
335
|
+
className=" text-xs bg-background/80 backdrop-blur-sm hover:bg-background/90 transition-all duration-200 opacity-70 hover:opacity-100"
|
|
336
|
+
>
|
|
337
|
+
{isRefreshing ? (
|
|
338
|
+
<Loader2 className="h-3 w-3 mr-1.5 animate-spin" />
|
|
339
|
+
) : (
|
|
340
|
+
<RefreshCw className="h-3 w-3 mr-1.5" />
|
|
341
|
+
)}
|
|
342
|
+
{isRefreshing ? 'Refreshing...' : 'Refresh'}
|
|
343
|
+
</Button>
|
|
344
|
+
</div>
|
|
345
|
+
)}
|
|
258
346
|
</StickToBottom.Content>
|
|
259
347
|
</StickToBottom>
|
|
260
348
|
) : (
|
|
@@ -26,10 +26,10 @@ import {
|
|
|
26
26
|
import { useAggregateStats, useConversationStats } from '@/hooks/use-traces';
|
|
27
27
|
import { type TimeRange, useTracesQueryState } from '@/hooks/use-traces-query-state';
|
|
28
28
|
import { getSigNozStatsClient, type SpanFilterOptions } from '@/lib/api/signoz-stats';
|
|
29
|
-
import { ConversationStatsCard } from './conversation-stats/conversation-stats-card';
|
|
30
|
-
import { StatCard } from './charts/stat-card';
|
|
31
29
|
import { AreaChartCard } from './charts/area-chart-card';
|
|
32
|
-
import {
|
|
30
|
+
import { StatCard } from './charts/stat-card';
|
|
31
|
+
import { ConversationStatsCard } from './conversation-stats/conversation-stats-card';
|
|
32
|
+
import { CUSTOM, DatePickerWithPresets } from './filters/date-picker';
|
|
33
33
|
import { GraphFilter } from './filters/graph-filter';
|
|
34
34
|
|
|
35
35
|
// Time range options
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
+
import type * as React from 'react';
|
|
3
|
+
|
|
4
|
+
import { cn } from '@/lib/utils';
|
|
5
|
+
|
|
6
|
+
const alertVariants = cva(
|
|
7
|
+
'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: 'bg-card text-card-foreground',
|
|
12
|
+
destructive:
|
|
13
|
+
'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
variant: 'default',
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
function Alert({
|
|
23
|
+
className,
|
|
24
|
+
variant,
|
|
25
|
+
...props
|
|
26
|
+
}: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
data-slot="alert"
|
|
30
|
+
role="alert"
|
|
31
|
+
className={cn(alertVariants({ variant }), className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
|
38
|
+
return (
|
|
39
|
+
<div
|
|
40
|
+
data-slot="alert-title"
|
|
41
|
+
className={cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', className)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function AlertDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
data-slot="alert-description"
|
|
51
|
+
className={cn(
|
|
52
|
+
'text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed',
|
|
53
|
+
className
|
|
54
|
+
)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { Alert, AlertTitle, AlertDescription };
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import * as React from 'react';
|
|
4
3
|
import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-react';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
import { cn } from '@/lib/utils';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { type DayButton, DayPicker, getDefaultClassNames } from 'react-day-picker';
|
|
8
6
|
import { Button, buttonVariants } from '@/components/ui/button';
|
|
7
|
+
import { cn } from '@/lib/utils';
|
|
9
8
|
|
|
10
9
|
function Calendar({
|
|
11
10
|
className,
|
|
@@ -10,13 +10,13 @@ export function ExternalLink({ href, children, ...props }: ComponentProps<typeof
|
|
|
10
10
|
target="_blank"
|
|
11
11
|
rel="noreferrer noopener"
|
|
12
12
|
className={cn(
|
|
13
|
-
'text-sm text-muted-foreground underline underline-offset-2 inline-flex items-center gap-1 hover:text-primary ml-1 group font-mono',
|
|
13
|
+
'text-sm text-muted-foreground underline underline-offset-2 inline-flex items-center gap-1 hover:text-primary ml-1 group/link font-mono uppercase transition-colors',
|
|
14
14
|
props.className
|
|
15
15
|
)}
|
|
16
16
|
{...props}
|
|
17
17
|
>
|
|
18
18
|
{children}
|
|
19
|
-
<ArrowUpRight className="size-3.5 text-muted-foreground/60 group-hover:text-primary" />
|
|
19
|
+
<ArrowUpRight className="size-3.5 text-muted-foreground/60 group-hover/link:text-primary" />
|
|
20
20
|
</Link>
|
|
21
21
|
);
|
|
22
22
|
}
|
|
@@ -78,17 +78,24 @@ function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
|
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function FormLabel({
|
|
81
|
+
function FormLabel({
|
|
82
|
+
className,
|
|
83
|
+
isRequired,
|
|
84
|
+
children,
|
|
85
|
+
...props
|
|
86
|
+
}: React.ComponentProps<typeof LabelPrimitive.Root> & { isRequired?: boolean }) {
|
|
82
87
|
const { error, formItemId } = useFormField();
|
|
83
|
-
|
|
84
88
|
return (
|
|
85
89
|
<Label
|
|
86
90
|
data-slot="form-label"
|
|
87
91
|
data-error={!!error}
|
|
88
|
-
className={cn('data-[error=true]:text-destructive', className)}
|
|
92
|
+
className={cn('data-[error=true]:text-destructive gap-1', className)}
|
|
89
93
|
htmlFor={formItemId}
|
|
90
94
|
{...props}
|
|
91
|
-
|
|
95
|
+
>
|
|
96
|
+
{children}
|
|
97
|
+
{isRequired && <span className="text-red-500">*</span>}
|
|
98
|
+
</Label>
|
|
92
99
|
);
|
|
93
100
|
}
|
|
94
101
|
|
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { ArrowDown, Check, Info } from 'lucide-react';
|
|
4
4
|
import { Badge } from '@/components/ui/badge';
|
|
5
|
-
import {
|
|
6
|
-
Tooltip,
|
|
7
|
-
TooltipContent,
|
|
8
|
-
TooltipProvider,
|
|
9
|
-
TooltipTrigger
|
|
10
|
-
} from '@/components/ui/tooltip';
|
|
5
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
|
11
6
|
|
|
12
7
|
export interface InheritanceIndicatorProps {
|
|
13
8
|
/** Whether this value is explicitly set (not inherited) */
|
|
@@ -34,8 +29,8 @@ export function InheritanceIndicator({
|
|
|
34
29
|
<TooltipProvider>
|
|
35
30
|
<Tooltip>
|
|
36
31
|
<TooltipTrigger asChild>
|
|
37
|
-
<Badge
|
|
38
|
-
variant="outline"
|
|
32
|
+
<Badge
|
|
33
|
+
variant="outline"
|
|
39
34
|
className={`
|
|
40
35
|
${size === 'sm' ? 'h-5 px-1.5 text-xs' : 'h-6 px-2 text-sm'}
|
|
41
36
|
${position === 'absolute' ? 'absolute -top-2 -right-2' : 'inline-flex'}
|
|
@@ -60,8 +55,8 @@ export function InheritanceIndicator({
|
|
|
60
55
|
<TooltipProvider>
|
|
61
56
|
<Tooltip>
|
|
62
57
|
<TooltipTrigger asChild>
|
|
63
|
-
<Badge
|
|
64
|
-
variant="outline"
|
|
58
|
+
<Badge
|
|
59
|
+
variant="outline"
|
|
65
60
|
className={`
|
|
66
61
|
${size === 'sm' ? 'h-5 px-1.5 text-xs' : 'h-6 px-2 text-sm'}
|
|
67
62
|
${position === 'absolute' ? 'absolute -top-2 -right-2' : 'inline-flex'}
|
|
@@ -86,8 +81,8 @@ export function InheritanceIndicator({
|
|
|
86
81
|
<TooltipProvider>
|
|
87
82
|
<Tooltip>
|
|
88
83
|
<TooltipTrigger asChild>
|
|
89
|
-
<Badge
|
|
90
|
-
variant="outline"
|
|
84
|
+
<Badge
|
|
85
|
+
variant="outline"
|
|
91
86
|
className={`
|
|
92
87
|
${size === 'sm' ? 'h-5 px-1.5 text-xs' : 'h-6 px-2 text-sm'}
|
|
93
88
|
${position === 'absolute' ? 'absolute -top-2 -right-2' : 'inline-flex'}
|
|
@@ -118,9 +113,11 @@ export function getModelInheritanceStatus(
|
|
|
118
113
|
inheritedFrom?: string;
|
|
119
114
|
tooltip?: string;
|
|
120
115
|
} {
|
|
121
|
-
const hasCurrentValue =
|
|
116
|
+
const hasCurrentValue =
|
|
117
|
+
currentValue !== undefined && currentValue !== null && currentValue !== '';
|
|
122
118
|
const hasParentValue = parentValue !== undefined && parentValue !== null && parentValue !== '';
|
|
123
|
-
const hasGrandparentValue =
|
|
119
|
+
const hasGrandparentValue =
|
|
120
|
+
grandparentValue !== undefined && grandparentValue !== null && grandparentValue !== '';
|
|
124
121
|
|
|
125
122
|
// For non-project levels: if current value matches parent value exactly, it's likely inherited
|
|
126
123
|
// This handles the case where the builder resolves inheritance and stores the actual values
|
|
@@ -133,9 +130,15 @@ export function getModelInheritanceStatus(
|
|
|
133
130
|
tooltip: `This model is inherited from the ${inheritedFromLevel.toLowerCase()} level`,
|
|
134
131
|
};
|
|
135
132
|
}
|
|
136
|
-
|
|
133
|
+
|
|
137
134
|
// For agent level: also check if it matches grandparent (when graph doesn't have it set)
|
|
138
|
-
if (
|
|
135
|
+
if (
|
|
136
|
+
currentLevel === 'agent' &&
|
|
137
|
+
hasCurrentValue &&
|
|
138
|
+
!hasParentValue &&
|
|
139
|
+
hasGrandparentValue &&
|
|
140
|
+
currentValue === grandparentValue
|
|
141
|
+
) {
|
|
139
142
|
return {
|
|
140
143
|
isExplicit: false,
|
|
141
144
|
inheritedFrom: 'Project',
|
|
@@ -261,4 +264,4 @@ export function getExecutionLimitInheritanceStatus(
|
|
|
261
264
|
isExplicit: false,
|
|
262
265
|
tooltip: `Using system default (${defaultValue})`,
|
|
263
266
|
};
|
|
264
|
-
}
|
|
267
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import * as ResizablePrimitive from
|
|
3
|
+
import { GripVerticalIcon } from 'lucide-react';
|
|
4
|
+
import type * as React from 'react';
|
|
5
|
+
import * as ResizablePrimitive from 'react-resizable-panels';
|
|
6
6
|
|
|
7
|
-
import { cn } from
|
|
7
|
+
import { cn } from '@/lib/utils';
|
|
8
8
|
|
|
9
9
|
function ResizablePanelGroup({
|
|
10
10
|
className,
|
|
@@ -13,19 +13,14 @@ function ResizablePanelGroup({
|
|
|
13
13
|
return (
|
|
14
14
|
<ResizablePrimitive.PanelGroup
|
|
15
15
|
data-slot="resizable-panel-group"
|
|
16
|
-
className={cn(
|
|
17
|
-
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
|
|
18
|
-
className
|
|
19
|
-
)}
|
|
16
|
+
className={cn('flex h-full w-full data-[panel-group-direction=vertical]:flex-col', className)}
|
|
20
17
|
{...props}
|
|
21
18
|
/>
|
|
22
|
-
)
|
|
19
|
+
);
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
function ResizablePanel({
|
|
26
|
-
...props
|
|
27
|
-
}: React.ComponentProps<typeof ResizablePrimitive.Panel>) {
|
|
28
|
-
return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />
|
|
22
|
+
function ResizablePanel({ ...props }: React.ComponentProps<typeof ResizablePrimitive.Panel>) {
|
|
23
|
+
return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />;
|
|
29
24
|
}
|
|
30
25
|
|
|
31
26
|
function ResizableHandle({
|
|
@@ -33,13 +28,13 @@ function ResizableHandle({
|
|
|
33
28
|
className,
|
|
34
29
|
...props
|
|
35
30
|
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
|
|
36
|
-
withHandle?: boolean
|
|
31
|
+
withHandle?: boolean;
|
|
37
32
|
}) {
|
|
38
33
|
return (
|
|
39
34
|
<ResizablePrimitive.PanelResizeHandle
|
|
40
35
|
data-slot="resizable-handle"
|
|
41
36
|
className={cn(
|
|
42
|
-
|
|
37
|
+
'bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90',
|
|
43
38
|
className
|
|
44
39
|
)}
|
|
45
40
|
{...props}
|
|
@@ -50,7 +45,7 @@ function ResizableHandle({
|
|
|
50
45
|
</div>
|
|
51
46
|
)}
|
|
52
47
|
</ResizablePrimitive.PanelResizeHandle>
|
|
53
|
-
)
|
|
48
|
+
);
|
|
54
49
|
}
|
|
55
50
|
|
|
56
|
-
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
|
|
51
|
+
export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
|