@ai-sdk/devtools 1.0.0-beta.8 → 1.0.0-canary.20
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/dist/client/assets/index-B1quREL_.css +1 -0
- package/dist/client/assets/index-DYruJ8X0.js +181 -0
- package/dist/client/index.html +2 -2
- package/dist/index.d.ts +19 -1
- package/dist/index.js +342 -3
- package/dist/viewer/server.js +42 -5
- package/package.json +28 -4
- package/src/db.ts +32 -5
- package/src/index.ts +1 -0
- package/src/integration.ts +509 -0
- package/src/middleware.ts +5 -5
- package/src/viewer/client/app.tsx +97 -1878
- package/src/viewer/client/components/message-components.tsx +342 -0
- package/src/viewer/client/components/output-components.tsx +145 -0
- package/src/viewer/client/components/shared-components.tsx +691 -0
- package/src/viewer/client/components/step-card.tsx +472 -0
- package/src/viewer/client/components/trace-timeline.tsx +529 -0
- package/src/viewer/client/components/ui/badge.tsx +0 -1
- package/src/viewer/client/components/ui/button.tsx +0 -1
- package/src/viewer/client/types.ts +183 -0
- package/src/viewer/client/utils.ts +708 -0
- package/src/viewer/server.ts +49 -5
- package/dist/client/assets/index-BkyOIjbt.css +0 -1
- package/dist/client/assets/index-BwqXGbSV.js +0 -176
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import React, { useState, useMemo, useCallback, useRef } from 'react';
|
|
2
|
+
import { Wrench, MessageSquare, AlertCircle, Brain, Zap } from 'lucide-react';
|
|
3
|
+
import { Badge } from '@/components/ui/badge';
|
|
4
|
+
import { Card } from '@/components/ui/card';
|
|
5
|
+
import {
|
|
6
|
+
Tooltip,
|
|
7
|
+
TooltipContent,
|
|
8
|
+
TooltipTrigger,
|
|
9
|
+
} from '@/components/ui/tooltip';
|
|
10
|
+
import type {
|
|
11
|
+
RunDetail,
|
|
12
|
+
Step,
|
|
13
|
+
ChildRun,
|
|
14
|
+
SpanKind,
|
|
15
|
+
TraceSpan,
|
|
16
|
+
ParseJson,
|
|
17
|
+
ParsedOutput,
|
|
18
|
+
ContentPart,
|
|
19
|
+
ToolCallContentPart,
|
|
20
|
+
ToolResultContentPart,
|
|
21
|
+
} from '../types';
|
|
22
|
+
import {
|
|
23
|
+
buildTraceSpans,
|
|
24
|
+
safeParseJson,
|
|
25
|
+
SPAN_COLORS,
|
|
26
|
+
SPAN_COLORS_MUTED,
|
|
27
|
+
} from '../utils';
|
|
28
|
+
import { JsonBlock } from './shared-components';
|
|
29
|
+
import { StepDetailContent } from './step-card';
|
|
30
|
+
|
|
31
|
+
export function TraceTimeline({
|
|
32
|
+
runDetail,
|
|
33
|
+
parseJson,
|
|
34
|
+
formatDuration,
|
|
35
|
+
}: {
|
|
36
|
+
runDetail: RunDetail;
|
|
37
|
+
parseJson: ParseJson;
|
|
38
|
+
formatDuration: (ms: number | null) => string;
|
|
39
|
+
}) {
|
|
40
|
+
const [selectedSpanId, setSelectedSpanId] = useState<string | null>(null);
|
|
41
|
+
const [labelWidth, setLabelWidth] = useState(280);
|
|
42
|
+
const isResizingLabel = useRef(false);
|
|
43
|
+
|
|
44
|
+
const spans = useMemo(
|
|
45
|
+
() => buildTraceSpans(runDetail, parseJson),
|
|
46
|
+
[runDetail, parseJson],
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const allSteps = useMemo(() => {
|
|
50
|
+
const steps: Step[] = [];
|
|
51
|
+
function collectSteps(runSteps: Step[], children: ChildRun[]) {
|
|
52
|
+
steps.push(...runSteps);
|
|
53
|
+
for (const cr of children) {
|
|
54
|
+
collectSteps(cr.steps, cr.childRuns ?? []);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
collectSteps(runDetail.steps, runDetail.childRuns ?? []);
|
|
58
|
+
return steps;
|
|
59
|
+
}, [runDetail]);
|
|
60
|
+
|
|
61
|
+
const allChildRuns = useMemo(() => {
|
|
62
|
+
const runs: ChildRun[] = [];
|
|
63
|
+
function collectRuns(children: ChildRun[]) {
|
|
64
|
+
for (const cr of children) {
|
|
65
|
+
runs.push(cr);
|
|
66
|
+
collectRuns(cr.childRuns ?? []);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
collectRuns(runDetail.childRuns ?? []);
|
|
70
|
+
return runs;
|
|
71
|
+
}, [runDetail]);
|
|
72
|
+
|
|
73
|
+
const handleLabelResizeStart = useCallback(
|
|
74
|
+
(e: React.MouseEvent) => {
|
|
75
|
+
e.preventDefault();
|
|
76
|
+
e.stopPropagation();
|
|
77
|
+
isResizingLabel.current = true;
|
|
78
|
+
const startX = e.clientX;
|
|
79
|
+
const startWidth = labelWidth;
|
|
80
|
+
|
|
81
|
+
const onMouseMove = (moveEvent: MouseEvent) => {
|
|
82
|
+
if (!isResizingLabel.current) return;
|
|
83
|
+
const newWidth = Math.min(
|
|
84
|
+
Math.max(startWidth + (moveEvent.clientX - startX), 120),
|
|
85
|
+
600,
|
|
86
|
+
);
|
|
87
|
+
setLabelWidth(newWidth);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const onMouseUp = () => {
|
|
91
|
+
isResizingLabel.current = false;
|
|
92
|
+
document.removeEventListener('mousemove', onMouseMove);
|
|
93
|
+
document.removeEventListener('mouseup', onMouseUp);
|
|
94
|
+
document.body.style.cursor = '';
|
|
95
|
+
document.body.style.userSelect = '';
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
99
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
100
|
+
document.body.style.cursor = 'col-resize';
|
|
101
|
+
document.body.style.userSelect = 'none';
|
|
102
|
+
},
|
|
103
|
+
[labelWidth],
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if (spans.length === 0) return null;
|
|
107
|
+
|
|
108
|
+
const totalDurationMs = Math.max(
|
|
109
|
+
...spans.map(s => s.startMs + s.durationMs),
|
|
110
|
+
1,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const tickCount = 5;
|
|
114
|
+
const ticks = Array.from({ length: tickCount + 1 }, (_, i) => {
|
|
115
|
+
const ms = (totalDurationMs / tickCount) * i;
|
|
116
|
+
return {
|
|
117
|
+
ms,
|
|
118
|
+
label: ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const ROW_HEIGHT = 28;
|
|
123
|
+
|
|
124
|
+
const handleSpanClick = (spanId: string) => {
|
|
125
|
+
setSelectedSpanId(prev => (prev === spanId ? null : spanId));
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const selectedSpan = selectedSpanId
|
|
129
|
+
? spans.find(s => s.id === selectedSpanId)
|
|
130
|
+
: null;
|
|
131
|
+
|
|
132
|
+
const resolvedStepId = selectedSpan?.stepId ?? selectedSpanId;
|
|
133
|
+
|
|
134
|
+
const selectedStep = resolvedStepId
|
|
135
|
+
? allSteps.find(s => s.id === resolvedStepId)
|
|
136
|
+
: null;
|
|
137
|
+
|
|
138
|
+
const selectedStepIndex = selectedStep
|
|
139
|
+
? (() => {
|
|
140
|
+
if (runDetail.steps.find(s => s.id === selectedStep.id)) {
|
|
141
|
+
return runDetail.steps.findIndex(s => s.id === selectedStep.id);
|
|
142
|
+
}
|
|
143
|
+
const cr = allChildRuns.find(cr =>
|
|
144
|
+
cr.steps.some(s => s.id === selectedStep.id),
|
|
145
|
+
);
|
|
146
|
+
return cr?.steps.findIndex(s => s.id === selectedStep.id) ?? 0;
|
|
147
|
+
})()
|
|
148
|
+
: 0;
|
|
149
|
+
|
|
150
|
+
const selectedStepSiblings = selectedStep
|
|
151
|
+
? (() => {
|
|
152
|
+
if (runDetail.steps.find(s => s.id === selectedStep.id)) {
|
|
153
|
+
return runDetail.steps;
|
|
154
|
+
}
|
|
155
|
+
const cr = allChildRuns.find(cr =>
|
|
156
|
+
cr.steps.some(s => s.id === selectedStep.id),
|
|
157
|
+
);
|
|
158
|
+
return cr?.steps ?? runDetail.steps;
|
|
159
|
+
})()
|
|
160
|
+
: runDetail.steps;
|
|
161
|
+
|
|
162
|
+
const selectedStepChildRuns = selectedStep
|
|
163
|
+
? allChildRuns.filter(cr => cr.run.parent_step_id === selectedStep.id)
|
|
164
|
+
: [];
|
|
165
|
+
|
|
166
|
+
const spanIcon = (kind: SpanKind) => {
|
|
167
|
+
switch (kind) {
|
|
168
|
+
case 'child-run':
|
|
169
|
+
return <Brain className="size-3 text-cyan-400 shrink-0" />;
|
|
170
|
+
case 'thinking':
|
|
171
|
+
return <Brain className="size-3 text-amber-500 shrink-0" />;
|
|
172
|
+
case 'tool-call':
|
|
173
|
+
return <Wrench className="size-3 text-purple-400 shrink-0" />;
|
|
174
|
+
case 'text':
|
|
175
|
+
return <MessageSquare className="size-3 text-emerald-400 shrink-0" />;
|
|
176
|
+
case 'error':
|
|
177
|
+
return <AlertCircle className="size-3 text-red-400 shrink-0" />;
|
|
178
|
+
default:
|
|
179
|
+
return <Zap className="size-3 text-blue-400 shrink-0" />;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div className="space-y-4">
|
|
185
|
+
<div className="rounded-lg border border-border bg-card overflow-hidden">
|
|
186
|
+
<div className="px-4 py-2 border-b border-border bg-muted/20 flex items-center justify-between">
|
|
187
|
+
<span className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
|
|
188
|
+
Trace Timeline
|
|
189
|
+
</span>
|
|
190
|
+
<span className="text-[11px] font-mono text-muted-foreground">
|
|
191
|
+
{formatDuration(totalDurationMs)}
|
|
192
|
+
</span>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div className="overflow-x-auto">
|
|
196
|
+
<div style={{ minWidth: labelWidth + 400 }}>
|
|
197
|
+
{/* Time axis */}
|
|
198
|
+
<div
|
|
199
|
+
className="flex border-b border-border/50"
|
|
200
|
+
style={{ height: 22 }}
|
|
201
|
+
>
|
|
202
|
+
<div
|
|
203
|
+
style={{ width: labelWidth, minWidth: labelWidth }}
|
|
204
|
+
className="shrink-0"
|
|
205
|
+
/>
|
|
206
|
+
<div
|
|
207
|
+
className="w-1 shrink-0 cursor-col-resize hover:bg-primary/30"
|
|
208
|
+
onMouseDown={handleLabelResizeStart}
|
|
209
|
+
/>
|
|
210
|
+
<div className="flex-1 relative">
|
|
211
|
+
{ticks.map((tick, i) => (
|
|
212
|
+
<span
|
|
213
|
+
key={i}
|
|
214
|
+
className="absolute text-[9px] font-mono text-muted-foreground/60 -translate-x-1/2"
|
|
215
|
+
style={{
|
|
216
|
+
left: `${(tick.ms / totalDurationMs) * 100}%`,
|
|
217
|
+
top: 5,
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
{tick.label}
|
|
221
|
+
</span>
|
|
222
|
+
))}
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{/* Span rows */}
|
|
227
|
+
{spans.map(span => {
|
|
228
|
+
const leftPct = (span.startMs / totalDurationMs) * 100;
|
|
229
|
+
const widthPct = Math.max(
|
|
230
|
+
(span.durationMs / totalDurationMs) * 100,
|
|
231
|
+
0.3,
|
|
232
|
+
);
|
|
233
|
+
const isSelected = selectedSpanId === span.id;
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<div
|
|
237
|
+
key={span.id}
|
|
238
|
+
className={`flex items-center border-b border-border/30 transition-colors cursor-pointer ${
|
|
239
|
+
isSelected ? 'bg-accent' : 'hover:bg-accent/30'
|
|
240
|
+
}`}
|
|
241
|
+
style={{ height: ROW_HEIGHT }}
|
|
242
|
+
onClick={() => handleSpanClick(span.id)}
|
|
243
|
+
>
|
|
244
|
+
{/* Label column */}
|
|
245
|
+
<div
|
|
246
|
+
className="shrink-0 flex items-center gap-1.5 px-2 overflow-hidden"
|
|
247
|
+
style={{
|
|
248
|
+
width: labelWidth,
|
|
249
|
+
minWidth: labelWidth,
|
|
250
|
+
paddingLeft: 8 + span.depth * 16,
|
|
251
|
+
}}
|
|
252
|
+
>
|
|
253
|
+
{spanIcon(span.kind)}
|
|
254
|
+
<span className="text-[11px] font-mono text-foreground truncate">
|
|
255
|
+
{span.label}
|
|
256
|
+
</span>
|
|
257
|
+
{span.sublabel && (
|
|
258
|
+
<span className="text-[10px] text-muted-foreground truncate">
|
|
259
|
+
{span.sublabel}
|
|
260
|
+
</span>
|
|
261
|
+
)}
|
|
262
|
+
<span className="ml-auto text-[10px] font-mono text-muted-foreground shrink-0 pl-1">
|
|
263
|
+
{formatDuration(span.durationMs || null)}
|
|
264
|
+
</span>
|
|
265
|
+
{span.tokens && (
|
|
266
|
+
<span className="text-[9px] font-mono text-muted-foreground/60 shrink-0">
|
|
267
|
+
{span.tokens.input}→{span.tokens.output}
|
|
268
|
+
</span>
|
|
269
|
+
)}
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
{/* Resize handle */}
|
|
273
|
+
<div
|
|
274
|
+
className="w-1 shrink-0 cursor-col-resize hover:bg-primary/30 self-stretch"
|
|
275
|
+
onMouseDown={handleLabelResizeStart}
|
|
276
|
+
/>
|
|
277
|
+
|
|
278
|
+
{/* Bar column */}
|
|
279
|
+
<div className="flex-1 relative h-full">
|
|
280
|
+
{ticks.map((tick, i) => (
|
|
281
|
+
<div
|
|
282
|
+
key={i}
|
|
283
|
+
className="absolute top-0 bottom-0 border-l border-border/20"
|
|
284
|
+
style={{
|
|
285
|
+
left: `${(tick.ms / totalDurationMs) * 100}%`,
|
|
286
|
+
}}
|
|
287
|
+
/>
|
|
288
|
+
))}
|
|
289
|
+
<Tooltip>
|
|
290
|
+
<TooltipTrigger asChild>
|
|
291
|
+
<div
|
|
292
|
+
className="absolute top-1 bottom-1 flex items-center cursor-pointer"
|
|
293
|
+
style={{
|
|
294
|
+
left: `${leftPct}%`,
|
|
295
|
+
width: `${widthPct}%`,
|
|
296
|
+
minWidth: 3,
|
|
297
|
+
}}
|
|
298
|
+
>
|
|
299
|
+
<div
|
|
300
|
+
className={`h-full w-full rounded-sm ${
|
|
301
|
+
isSelected
|
|
302
|
+
? 'ring-2 ring-foreground/50 ring-offset-1 ring-offset-background'
|
|
303
|
+
: ''
|
|
304
|
+
} ${
|
|
305
|
+
span.isInProgress
|
|
306
|
+
? `${SPAN_COLORS_MUTED[span.kind]} animate-pulse`
|
|
307
|
+
: SPAN_COLORS[span.kind]
|
|
308
|
+
}`}
|
|
309
|
+
/>
|
|
310
|
+
</div>
|
|
311
|
+
</TooltipTrigger>
|
|
312
|
+
<TooltipContent side="top" className="max-w-xs">
|
|
313
|
+
<div className="text-xs space-y-1">
|
|
314
|
+
<div className="font-medium">{span.label}</div>
|
|
315
|
+
{span.sublabel && (
|
|
316
|
+
<div className="text-muted-foreground">
|
|
317
|
+
{span.sublabel}
|
|
318
|
+
</div>
|
|
319
|
+
)}
|
|
320
|
+
{span.modelId && span.modelId !== span.label && (
|
|
321
|
+
<div className="text-muted-foreground font-mono">
|
|
322
|
+
{span.modelId}
|
|
323
|
+
</div>
|
|
324
|
+
)}
|
|
325
|
+
<div className="text-muted-foreground font-mono">
|
|
326
|
+
{formatDuration(span.durationMs || null)}
|
|
327
|
+
{span.tokens && (
|
|
328
|
+
<span className="ml-2">
|
|
329
|
+
{span.tokens.input} in → {span.tokens.output}{' '}
|
|
330
|
+
out
|
|
331
|
+
</span>
|
|
332
|
+
)}
|
|
333
|
+
</div>
|
|
334
|
+
<div className="text-muted-foreground/60 pt-0.5">
|
|
335
|
+
Click to {isSelected ? 'deselect' : 'view details'}
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</TooltipContent>
|
|
339
|
+
</Tooltip>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
);
|
|
343
|
+
})}
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
{/* Selected span detail */}
|
|
349
|
+
{selectedStep && selectedSpan && (
|
|
350
|
+
<Card className="overflow-hidden py-0 gap-0">
|
|
351
|
+
<div className="flex items-center justify-between px-4 py-2.5 bg-muted/20 border-b border-border">
|
|
352
|
+
<div className="flex items-center gap-2">
|
|
353
|
+
{spanIcon(selectedSpan.kind)}
|
|
354
|
+
<span className="text-xs text-muted-foreground font-mono">
|
|
355
|
+
Step {selectedStepIndex + 1}
|
|
356
|
+
</span>
|
|
357
|
+
{selectedSpan.kind !== 'step' && (
|
|
358
|
+
<Badge
|
|
359
|
+
variant="secondary"
|
|
360
|
+
className="text-[10px] h-5 capitalize"
|
|
361
|
+
>
|
|
362
|
+
{selectedSpan.kind}
|
|
363
|
+
</Badge>
|
|
364
|
+
)}
|
|
365
|
+
<span className="text-xs font-medium font-mono">
|
|
366
|
+
{selectedSpan.label}
|
|
367
|
+
</span>
|
|
368
|
+
{selectedStep.provider && (
|
|
369
|
+
<span className="px-1.5 py-0.5 rounded bg-sidebar-primary/10 text-sidebar-primary text-[10px] font-medium">
|
|
370
|
+
{selectedStep.provider}
|
|
371
|
+
</span>
|
|
372
|
+
)}
|
|
373
|
+
</div>
|
|
374
|
+
<button
|
|
375
|
+
onClick={() => setSelectedSpanId(null)}
|
|
376
|
+
className="text-[11px] text-muted-foreground hover:text-foreground transition-colors"
|
|
377
|
+
>
|
|
378
|
+
Deselect
|
|
379
|
+
</button>
|
|
380
|
+
</div>
|
|
381
|
+
<SpanDetailPanel
|
|
382
|
+
span={selectedSpan}
|
|
383
|
+
step={selectedStep}
|
|
384
|
+
stepIndex={selectedStepIndex}
|
|
385
|
+
steps={selectedStepSiblings}
|
|
386
|
+
runDetail={runDetail}
|
|
387
|
+
parseJson={parseJson}
|
|
388
|
+
formatDuration={formatDuration}
|
|
389
|
+
childRuns={selectedStepChildRuns}
|
|
390
|
+
/>
|
|
391
|
+
</Card>
|
|
392
|
+
)}
|
|
393
|
+
</div>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function SpanDetailPanel({
|
|
398
|
+
span,
|
|
399
|
+
step,
|
|
400
|
+
stepIndex,
|
|
401
|
+
steps,
|
|
402
|
+
runDetail,
|
|
403
|
+
parseJson,
|
|
404
|
+
formatDuration,
|
|
405
|
+
childRuns,
|
|
406
|
+
}: {
|
|
407
|
+
span: TraceSpan;
|
|
408
|
+
step: Step;
|
|
409
|
+
stepIndex: number;
|
|
410
|
+
steps: Step[];
|
|
411
|
+
runDetail: RunDetail;
|
|
412
|
+
parseJson: ParseJson;
|
|
413
|
+
formatDuration: (ms: number | null) => string;
|
|
414
|
+
childRuns: ChildRun[];
|
|
415
|
+
}) {
|
|
416
|
+
const output = parseJson(step.output) as ParsedOutput | null;
|
|
417
|
+
const contentParts: ContentPart[] =
|
|
418
|
+
(output?.content as ContentPart[] | undefined) ?? [];
|
|
419
|
+
|
|
420
|
+
if (span.kind === 'tool-call' && span.toolCallId) {
|
|
421
|
+
const toolCall = contentParts.find(
|
|
422
|
+
(p): p is ToolCallContentPart =>
|
|
423
|
+
p.type === 'tool-call' &&
|
|
424
|
+
'toolCallId' in p &&
|
|
425
|
+
p.toolCallId === span.toolCallId,
|
|
426
|
+
);
|
|
427
|
+
const toolResult = contentParts.find(
|
|
428
|
+
(p): p is ToolResultContentPart =>
|
|
429
|
+
p.type === 'tool-result' &&
|
|
430
|
+
'toolCallId' in p &&
|
|
431
|
+
p.toolCallId === span.toolCallId,
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
const args = toolCall?.input ?? toolCall?.args;
|
|
435
|
+
const parsedArgs = typeof args === 'string' ? safeParseJson(args) : args;
|
|
436
|
+
const resultData = toolResult?.output ?? toolResult?.result;
|
|
437
|
+
const parsedResult =
|
|
438
|
+
typeof resultData === 'string' ? safeParseJson(resultData) : resultData;
|
|
439
|
+
|
|
440
|
+
return (
|
|
441
|
+
<div className="p-4 space-y-4">
|
|
442
|
+
<div className="flex items-center gap-2 mb-1">
|
|
443
|
+
<Wrench className="size-4 text-purple-400" />
|
|
444
|
+
<span className="text-sm font-mono font-medium text-purple-400">
|
|
445
|
+
{span.label}
|
|
446
|
+
</span>
|
|
447
|
+
</div>
|
|
448
|
+
{parsedArgs != null && (
|
|
449
|
+
<div>
|
|
450
|
+
<h4 className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-2">
|
|
451
|
+
Input
|
|
452
|
+
</h4>
|
|
453
|
+
<JsonBlock data={parsedArgs} />
|
|
454
|
+
</div>
|
|
455
|
+
)}
|
|
456
|
+
{parsedResult != null && (
|
|
457
|
+
<div>
|
|
458
|
+
<h4 className="text-[11px] font-semibold uppercase tracking-wider text-success mb-2">
|
|
459
|
+
Output
|
|
460
|
+
</h4>
|
|
461
|
+
<JsonBlock data={parsedResult} />
|
|
462
|
+
</div>
|
|
463
|
+
)}
|
|
464
|
+
{!parsedArgs && !parsedResult && (
|
|
465
|
+
<p className="text-sm text-muted-foreground">
|
|
466
|
+
No data available for this tool call.
|
|
467
|
+
</p>
|
|
468
|
+
)}
|
|
469
|
+
</div>
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (span.kind === 'thinking' && span.thinkingText) {
|
|
474
|
+
return (
|
|
475
|
+
<div className="p-4 space-y-2">
|
|
476
|
+
<div className="flex items-center gap-2 mb-1">
|
|
477
|
+
<Brain className="size-4 text-amber-500" />
|
|
478
|
+
<span className="text-sm font-medium text-amber-500">Thinking</span>
|
|
479
|
+
</div>
|
|
480
|
+
<div className="text-sm text-foreground/80 leading-relaxed whitespace-pre-wrap rounded-md border border-amber-500/20 bg-amber-500/5 p-3">
|
|
481
|
+
{span.thinkingText}
|
|
482
|
+
</div>
|
|
483
|
+
</div>
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (span.kind === 'text' && span.textContent) {
|
|
488
|
+
return (
|
|
489
|
+
<div className="p-4 space-y-2">
|
|
490
|
+
<div className="flex items-center gap-2 mb-1">
|
|
491
|
+
<MessageSquare className="size-4 text-emerald-400" />
|
|
492
|
+
<span className="text-sm font-medium text-emerald-400">
|
|
493
|
+
Text Response
|
|
494
|
+
</span>
|
|
495
|
+
</div>
|
|
496
|
+
<div className="text-sm text-foreground/80 leading-relaxed whitespace-pre-wrap rounded-md border border-border bg-background p-3">
|
|
497
|
+
{span.textContent}
|
|
498
|
+
</div>
|
|
499
|
+
</div>
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (span.kind === 'error') {
|
|
504
|
+
return (
|
|
505
|
+
<div className="p-4 space-y-2">
|
|
506
|
+
<div className="flex items-center gap-2 mb-1">
|
|
507
|
+
<AlertCircle className="size-4 text-red-400" />
|
|
508
|
+
<span className="text-sm font-medium text-red-400">Error</span>
|
|
509
|
+
</div>
|
|
510
|
+
<div className="text-sm text-destructive-foreground font-mono whitespace-pre-wrap rounded-md border border-destructive/30 bg-destructive/10 p-3">
|
|
511
|
+
{step.error}
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return (
|
|
518
|
+
<StepDetailContent
|
|
519
|
+
step={step}
|
|
520
|
+
index={stepIndex}
|
|
521
|
+
steps={steps}
|
|
522
|
+
isRunInProgress={runDetail.run.isInProgress ?? false}
|
|
523
|
+
parseJson={parseJson}
|
|
524
|
+
formatDuration={formatDuration}
|
|
525
|
+
childRuns={childRuns}
|
|
526
|
+
depth={0}
|
|
527
|
+
/>
|
|
528
|
+
);
|
|
529
|
+
}
|