@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,472 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ChevronRight,
|
|
4
|
+
ChevronDown,
|
|
5
|
+
Wrench,
|
|
6
|
+
MessageSquare,
|
|
7
|
+
AlertCircle,
|
|
8
|
+
Brain,
|
|
9
|
+
Loader2,
|
|
10
|
+
} from 'lucide-react';
|
|
11
|
+
import { Card } from '@/components/ui/card';
|
|
12
|
+
import {
|
|
13
|
+
Collapsible,
|
|
14
|
+
CollapsibleContent,
|
|
15
|
+
CollapsibleTrigger,
|
|
16
|
+
} from '@/components/ui/collapsible';
|
|
17
|
+
import {
|
|
18
|
+
Tooltip,
|
|
19
|
+
TooltipContent,
|
|
20
|
+
TooltipTrigger,
|
|
21
|
+
} from '@/components/ui/tooltip';
|
|
22
|
+
import type {
|
|
23
|
+
Step,
|
|
24
|
+
ChildRun,
|
|
25
|
+
ParseJson,
|
|
26
|
+
ParsedInput,
|
|
27
|
+
ParsedOutput,
|
|
28
|
+
ParsedUsage,
|
|
29
|
+
PromptMessage,
|
|
30
|
+
ContentPart,
|
|
31
|
+
} from '../types';
|
|
32
|
+
import {
|
|
33
|
+
getStepSummary,
|
|
34
|
+
getStepInputSummary,
|
|
35
|
+
getInputTokenBreakdown,
|
|
36
|
+
getOutputTokenBreakdown,
|
|
37
|
+
formatInputTokens,
|
|
38
|
+
formatOutputTokens,
|
|
39
|
+
} from '../utils';
|
|
40
|
+
import {
|
|
41
|
+
StepConfigBar,
|
|
42
|
+
RawDataSection,
|
|
43
|
+
TokenBreakdownTooltip,
|
|
44
|
+
} from './shared-components';
|
|
45
|
+
import { InputPanel } from './message-components';
|
|
46
|
+
import { OutputDisplay } from './output-components';
|
|
47
|
+
|
|
48
|
+
export function StepCard({
|
|
49
|
+
step,
|
|
50
|
+
index,
|
|
51
|
+
steps,
|
|
52
|
+
isRunInProgress,
|
|
53
|
+
expandedSteps,
|
|
54
|
+
toggleStep,
|
|
55
|
+
parseJson,
|
|
56
|
+
formatDuration,
|
|
57
|
+
childRuns,
|
|
58
|
+
depth,
|
|
59
|
+
}: {
|
|
60
|
+
step: Step;
|
|
61
|
+
index: number;
|
|
62
|
+
steps: Step[];
|
|
63
|
+
isRunInProgress: boolean;
|
|
64
|
+
expandedSteps: Set<string>;
|
|
65
|
+
toggleStep: (id: string) => void;
|
|
66
|
+
parseJson: ParseJson;
|
|
67
|
+
formatDuration: (ms: number | null) => string;
|
|
68
|
+
childRuns: ChildRun[];
|
|
69
|
+
depth: number;
|
|
70
|
+
}) {
|
|
71
|
+
const isExpanded = expandedSteps.has(step.id);
|
|
72
|
+
const isLastStep = index === steps.length - 1;
|
|
73
|
+
const isActiveStep = isLastStep && isRunInProgress;
|
|
74
|
+
const input = parseJson(step.input) as ParsedInput | null;
|
|
75
|
+
const output = parseJson(step.output) as ParsedOutput | null;
|
|
76
|
+
const usage = parseJson(step.usage) as ParsedUsage | null;
|
|
77
|
+
|
|
78
|
+
const summary = getStepSummary(output, step.error);
|
|
79
|
+
const isFirstStep = index === 0;
|
|
80
|
+
const inputSummary = getStepInputSummary(input, isFirstStep);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Collapsible open={isExpanded} onOpenChange={() => toggleStep(step.id)}>
|
|
84
|
+
<Card
|
|
85
|
+
className={`overflow-hidden py-0 gap-0 ${isActiveStep ? 'ring-2 ring-blue-500/50 ring-offset-1 ring-offset-background' : ''}`}
|
|
86
|
+
>
|
|
87
|
+
<CollapsibleTrigger asChild>
|
|
88
|
+
<button
|
|
89
|
+
className={`w-full flex items-center justify-between px-4 py-3 text-left transition-colors hover:bg-accent/50 ${
|
|
90
|
+
isExpanded ? 'border-b border-border' : ''
|
|
91
|
+
}`}
|
|
92
|
+
>
|
|
93
|
+
<div className="flex items-center gap-3">
|
|
94
|
+
<span className="text-xs text-muted-foreground font-mono w-4">
|
|
95
|
+
{step.step_number}
|
|
96
|
+
</span>
|
|
97
|
+
<div className="flex items-center gap-2">
|
|
98
|
+
{inputSummary && (
|
|
99
|
+
<>
|
|
100
|
+
{inputSummary.type === 'user' ? (
|
|
101
|
+
<MessageSquare className="size-3.5 text-muted-foreground" />
|
|
102
|
+
) : (
|
|
103
|
+
<Wrench className="size-3.5 text-muted-foreground" />
|
|
104
|
+
)}
|
|
105
|
+
{inputSummary.fullText || inputSummary.toolDetails ? (
|
|
106
|
+
<Tooltip>
|
|
107
|
+
<TooltipTrigger asChild>
|
|
108
|
+
<span
|
|
109
|
+
className={`text-sm text-muted-foreground ${inputSummary.type === 'tool' ? 'font-mono' : ''}`}
|
|
110
|
+
>
|
|
111
|
+
{inputSummary.label}
|
|
112
|
+
</span>
|
|
113
|
+
</TooltipTrigger>
|
|
114
|
+
<TooltipContent
|
|
115
|
+
side="bottom"
|
|
116
|
+
className="max-w-sm max-h-40 overflow-hidden"
|
|
117
|
+
>
|
|
118
|
+
<span className="line-clamp-6">
|
|
119
|
+
{inputSummary.fullText || inputSummary.toolDetails}
|
|
120
|
+
</span>
|
|
121
|
+
</TooltipContent>
|
|
122
|
+
</Tooltip>
|
|
123
|
+
) : (
|
|
124
|
+
<span
|
|
125
|
+
className={`text-sm text-muted-foreground ${inputSummary.type === 'tool' ? 'font-mono' : ''}`}
|
|
126
|
+
>
|
|
127
|
+
{inputSummary.label}
|
|
128
|
+
</span>
|
|
129
|
+
)}
|
|
130
|
+
<span className="text-muted-foreground/50 mx-1">→</span>
|
|
131
|
+
</>
|
|
132
|
+
)}
|
|
133
|
+
|
|
134
|
+
{summary.icon === 'wrench' && (
|
|
135
|
+
<Wrench className="size-3.5 text-muted-foreground" />
|
|
136
|
+
)}
|
|
137
|
+
{summary.icon === 'message' && (
|
|
138
|
+
<MessageSquare className="size-3.5 text-muted-foreground" />
|
|
139
|
+
)}
|
|
140
|
+
{summary.icon === 'alert' && (
|
|
141
|
+
<AlertCircle className="size-3.5 text-destructive" />
|
|
142
|
+
)}
|
|
143
|
+
|
|
144
|
+
{summary.toolDetails ? (
|
|
145
|
+
<Tooltip>
|
|
146
|
+
<TooltipTrigger asChild>
|
|
147
|
+
<span className="text-sm font-medium font-mono">
|
|
148
|
+
{summary.label}
|
|
149
|
+
</span>
|
|
150
|
+
</TooltipTrigger>
|
|
151
|
+
<TooltipContent>{summary.toolDetails}</TooltipContent>
|
|
152
|
+
</Tooltip>
|
|
153
|
+
) : (
|
|
154
|
+
<span
|
|
155
|
+
className={`text-sm font-medium ${
|
|
156
|
+
summary.icon === 'wrench' ? 'font-mono' : ''
|
|
157
|
+
}`}
|
|
158
|
+
>
|
|
159
|
+
{summary.label}
|
|
160
|
+
</span>
|
|
161
|
+
)}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div className="flex items-center gap-4">
|
|
166
|
+
{isActiveStep ? (
|
|
167
|
+
<span className="text-[11px] text-blue-400 font-medium flex items-center gap-1.5">
|
|
168
|
+
<Loader2 className="size-3 animate-spin" />
|
|
169
|
+
streaming
|
|
170
|
+
</span>
|
|
171
|
+
) : (
|
|
172
|
+
<span className="text-[11px] text-muted-foreground font-mono">
|
|
173
|
+
{formatDuration(step.duration_ms)}
|
|
174
|
+
</span>
|
|
175
|
+
)}
|
|
176
|
+
{usage && (
|
|
177
|
+
<Tooltip>
|
|
178
|
+
<TooltipTrigger asChild>
|
|
179
|
+
<span className="text-[11px] font-mono text-muted-foreground cursor-help">
|
|
180
|
+
{formatInputTokens(
|
|
181
|
+
getInputTokenBreakdown(usage.inputTokens),
|
|
182
|
+
)}{' '}
|
|
183
|
+
<span className="text-muted-foreground/50">→</span>{' '}
|
|
184
|
+
{formatOutputTokens(
|
|
185
|
+
getOutputTokenBreakdown(usage.outputTokens),
|
|
186
|
+
)}
|
|
187
|
+
</span>
|
|
188
|
+
</TooltipTrigger>
|
|
189
|
+
<TooltipContent>
|
|
190
|
+
<TokenBreakdownTooltip
|
|
191
|
+
input={getInputTokenBreakdown(usage.inputTokens)}
|
|
192
|
+
output={getOutputTokenBreakdown(usage.outputTokens)}
|
|
193
|
+
raw={usage.raw}
|
|
194
|
+
/>
|
|
195
|
+
</TooltipContent>
|
|
196
|
+
</Tooltip>
|
|
197
|
+
)}
|
|
198
|
+
<ChevronDown
|
|
199
|
+
className={`size-4 text-muted-foreground transition-transform ${
|
|
200
|
+
isExpanded ? 'rotate-180' : ''
|
|
201
|
+
}`}
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
</button>
|
|
205
|
+
</CollapsibleTrigger>
|
|
206
|
+
|
|
207
|
+
<CollapsibleContent>
|
|
208
|
+
<StepDetailContent
|
|
209
|
+
step={step}
|
|
210
|
+
index={index}
|
|
211
|
+
steps={steps}
|
|
212
|
+
isRunInProgress={isRunInProgress}
|
|
213
|
+
parseJson={parseJson}
|
|
214
|
+
formatDuration={formatDuration}
|
|
215
|
+
childRuns={childRuns}
|
|
216
|
+
expandedSteps={expandedSteps}
|
|
217
|
+
toggleStep={toggleStep}
|
|
218
|
+
depth={depth}
|
|
219
|
+
/>
|
|
220
|
+
</CollapsibleContent>
|
|
221
|
+
</Card>
|
|
222
|
+
</Collapsible>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function StepDetailContent({
|
|
227
|
+
step,
|
|
228
|
+
index,
|
|
229
|
+
steps,
|
|
230
|
+
isRunInProgress,
|
|
231
|
+
parseJson,
|
|
232
|
+
formatDuration,
|
|
233
|
+
childRuns,
|
|
234
|
+
expandedSteps,
|
|
235
|
+
toggleStep,
|
|
236
|
+
depth,
|
|
237
|
+
}: {
|
|
238
|
+
step: Step;
|
|
239
|
+
index: number;
|
|
240
|
+
steps: Step[];
|
|
241
|
+
isRunInProgress: boolean;
|
|
242
|
+
parseJson: ParseJson;
|
|
243
|
+
formatDuration: (ms: number | null) => string;
|
|
244
|
+
childRuns: ChildRun[];
|
|
245
|
+
expandedSteps?: Set<string>;
|
|
246
|
+
toggleStep?: (id: string) => void;
|
|
247
|
+
depth: number;
|
|
248
|
+
}) {
|
|
249
|
+
const isLastStep = index === steps.length - 1;
|
|
250
|
+
const isActiveStep = isLastStep && isRunInProgress;
|
|
251
|
+
const input = parseJson(step.input) as ParsedInput | null;
|
|
252
|
+
const output = parseJson(step.output) as ParsedOutput | null;
|
|
253
|
+
const usage = parseJson(step.usage) as ParsedUsage | null;
|
|
254
|
+
|
|
255
|
+
const nextStep = steps[index + 1];
|
|
256
|
+
const nextInput = nextStep
|
|
257
|
+
? (parseJson(nextStep.input) as ParsedInput | null)
|
|
258
|
+
: null;
|
|
259
|
+
const toolResults: ContentPart[] =
|
|
260
|
+
nextInput?.prompt
|
|
261
|
+
?.filter((msg: PromptMessage) => msg.role === 'tool')
|
|
262
|
+
?.flatMap((msg: PromptMessage) =>
|
|
263
|
+
Array.isArray(msg.content) ? msg.content : [],
|
|
264
|
+
) ?? [];
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<>
|
|
268
|
+
<StepConfigBar
|
|
269
|
+
modelId={step.model_id}
|
|
270
|
+
provider={step.provider}
|
|
271
|
+
input={input}
|
|
272
|
+
providerOptions={
|
|
273
|
+
parseJson(step.provider_options) as Record<string, unknown> | null
|
|
274
|
+
}
|
|
275
|
+
usage={usage}
|
|
276
|
+
/>
|
|
277
|
+
|
|
278
|
+
<div className="grid grid-cols-2 divide-x divide-border">
|
|
279
|
+
<div className="bg-card/50">
|
|
280
|
+
<InputPanel input={input} />
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<div className="p-4 bg-background min-h-full">
|
|
284
|
+
<h3 className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-3">
|
|
285
|
+
Output
|
|
286
|
+
</h3>
|
|
287
|
+
|
|
288
|
+
{step.error ? (
|
|
289
|
+
<div className="p-3 rounded-md bg-destructive/10 border border-destructive/30 text-sm text-destructive-foreground font-mono">
|
|
290
|
+
{step.error}
|
|
291
|
+
</div>
|
|
292
|
+
) : output ? (
|
|
293
|
+
<OutputDisplay output={output} toolResults={toolResults} />
|
|
294
|
+
) : isActiveStep ? (
|
|
295
|
+
<div className="flex items-center gap-2 text-sm text-blue-400">
|
|
296
|
+
<Loader2 className="size-4 animate-spin" />
|
|
297
|
+
<span>Waiting for response...</span>
|
|
298
|
+
</div>
|
|
299
|
+
) : (
|
|
300
|
+
<p className="text-sm text-muted-foreground">No output</p>
|
|
301
|
+
)}
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
{childRuns.length > 0 && expandedSteps && toggleStep && (
|
|
306
|
+
<NestedChildRuns
|
|
307
|
+
childRuns={childRuns}
|
|
308
|
+
expandedSteps={expandedSteps}
|
|
309
|
+
toggleStep={toggleStep}
|
|
310
|
+
parseJson={parseJson}
|
|
311
|
+
formatDuration={formatDuration}
|
|
312
|
+
depth={depth}
|
|
313
|
+
/>
|
|
314
|
+
)}
|
|
315
|
+
|
|
316
|
+
<RawDataSection
|
|
317
|
+
rawRequest={step.raw_request}
|
|
318
|
+
rawResponse={step.raw_response}
|
|
319
|
+
rawChunks={step.raw_chunks}
|
|
320
|
+
isStream={step.type === 'stream'}
|
|
321
|
+
/>
|
|
322
|
+
</>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function NestedChildRuns({
|
|
327
|
+
childRuns,
|
|
328
|
+
expandedSteps,
|
|
329
|
+
toggleStep,
|
|
330
|
+
parseJson,
|
|
331
|
+
formatDuration,
|
|
332
|
+
depth,
|
|
333
|
+
}: {
|
|
334
|
+
childRuns: ChildRun[];
|
|
335
|
+
expandedSteps: Set<string>;
|
|
336
|
+
toggleStep: (id: string) => void;
|
|
337
|
+
parseJson: ParseJson;
|
|
338
|
+
formatDuration: (ms: number | null) => string;
|
|
339
|
+
depth: number;
|
|
340
|
+
}) {
|
|
341
|
+
return (
|
|
342
|
+
<div className="border-t border-border bg-muted/10">
|
|
343
|
+
<div className="px-4 py-2 flex items-center gap-2">
|
|
344
|
+
<div className="h-px flex-1 bg-border" />
|
|
345
|
+
<span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground shrink-0">
|
|
346
|
+
Sub-agent {childRuns.length === 1 ? 'call' : 'calls'} (
|
|
347
|
+
{childRuns.length})
|
|
348
|
+
</span>
|
|
349
|
+
<div className="h-px flex-1 bg-border" />
|
|
350
|
+
</div>
|
|
351
|
+
<div className="px-4 pb-3 flex flex-col gap-2">
|
|
352
|
+
{childRuns.map(childRun => (
|
|
353
|
+
<NestedRunCard
|
|
354
|
+
key={childRun.run.id}
|
|
355
|
+
childRun={childRun}
|
|
356
|
+
expandedSteps={expandedSteps}
|
|
357
|
+
toggleStep={toggleStep}
|
|
358
|
+
parseJson={parseJson}
|
|
359
|
+
formatDuration={formatDuration}
|
|
360
|
+
depth={depth + 1}
|
|
361
|
+
/>
|
|
362
|
+
))}
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function NestedRunCard({
|
|
369
|
+
childRun,
|
|
370
|
+
expandedSteps,
|
|
371
|
+
toggleStep,
|
|
372
|
+
parseJson,
|
|
373
|
+
formatDuration,
|
|
374
|
+
depth,
|
|
375
|
+
}: {
|
|
376
|
+
childRun: ChildRun;
|
|
377
|
+
expandedSteps: Set<string>;
|
|
378
|
+
toggleStep: (id: string) => void;
|
|
379
|
+
parseJson: ParseJson;
|
|
380
|
+
formatDuration: (ms: number | null) => string;
|
|
381
|
+
depth: number;
|
|
382
|
+
}) {
|
|
383
|
+
const [expanded, setExpanded] = useState(false);
|
|
384
|
+
|
|
385
|
+
const firstStep = childRun.steps[0];
|
|
386
|
+
const firstInput = firstStep
|
|
387
|
+
? (parseJson(firstStep.input) as ParsedInput | null)
|
|
388
|
+
: null;
|
|
389
|
+
const promptMessages = firstInput?.prompt ?? [];
|
|
390
|
+
const userMsg = [...promptMessages]
|
|
391
|
+
.reverse()
|
|
392
|
+
.find((m: PromptMessage) => m.role === 'user');
|
|
393
|
+
let label = 'Sub-agent call';
|
|
394
|
+
if (userMsg) {
|
|
395
|
+
const content =
|
|
396
|
+
typeof userMsg.content === 'string'
|
|
397
|
+
? userMsg.content
|
|
398
|
+
: Array.isArray(userMsg.content)
|
|
399
|
+
? userMsg.content.find(
|
|
400
|
+
(p): p is { type: 'text'; text: string } => p.type === 'text',
|
|
401
|
+
)?.text || ''
|
|
402
|
+
: '';
|
|
403
|
+
label = content.length > 60 ? content.slice(0, 60) + '...' : content;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const totalDuration = childRun.steps.reduce(
|
|
407
|
+
(acc, s) => acc + (s.duration_ms || 0),
|
|
408
|
+
0,
|
|
409
|
+
);
|
|
410
|
+
const modelId = firstStep?.model_id;
|
|
411
|
+
|
|
412
|
+
return (
|
|
413
|
+
<div className="rounded-md border border-cyan-500/30 overflow-hidden">
|
|
414
|
+
<button
|
|
415
|
+
className="w-full flex items-center gap-2 px-3 py-2 bg-cyan-500/10 hover:bg-cyan-500/15 transition-colors"
|
|
416
|
+
onClick={() => setExpanded(!expanded)}
|
|
417
|
+
>
|
|
418
|
+
<ChevronRight
|
|
419
|
+
className={`size-3 text-cyan-400 transition-transform shrink-0 ${
|
|
420
|
+
expanded ? 'rotate-90' : ''
|
|
421
|
+
}`}
|
|
422
|
+
/>
|
|
423
|
+
<Brain className="size-3 text-cyan-400 shrink-0" />
|
|
424
|
+
<span className="text-xs font-medium text-cyan-400 truncate">
|
|
425
|
+
{label}
|
|
426
|
+
</span>
|
|
427
|
+
<span className="ml-auto flex items-center gap-3 shrink-0">
|
|
428
|
+
{modelId && (
|
|
429
|
+
<span className="text-[10px] font-mono text-muted-foreground">
|
|
430
|
+
{modelId}
|
|
431
|
+
</span>
|
|
432
|
+
)}
|
|
433
|
+
<span className="text-[10px] text-muted-foreground">
|
|
434
|
+
{childRun.steps.length}{' '}
|
|
435
|
+
{childRun.steps.length === 1 ? 'step' : 'steps'}
|
|
436
|
+
</span>
|
|
437
|
+
<span className="text-[10px] font-mono text-muted-foreground">
|
|
438
|
+
{formatDuration(totalDuration)}
|
|
439
|
+
</span>
|
|
440
|
+
{childRun.run.isInProgress && (
|
|
441
|
+
<Loader2 className="size-3 text-blue-400 animate-spin" />
|
|
442
|
+
)}
|
|
443
|
+
</span>
|
|
444
|
+
</button>
|
|
445
|
+
|
|
446
|
+
{expanded && (
|
|
447
|
+
<div className="border-t border-cyan-500/20 bg-background/50 p-3 flex flex-col gap-2">
|
|
448
|
+
{childRun.steps.map((step, index) => {
|
|
449
|
+
const nestedChildRuns = (childRun.childRuns ?? []).filter(
|
|
450
|
+
cr => cr.run.parent_step_id === step.id,
|
|
451
|
+
);
|
|
452
|
+
return (
|
|
453
|
+
<StepCard
|
|
454
|
+
key={step.id}
|
|
455
|
+
step={step}
|
|
456
|
+
index={index}
|
|
457
|
+
steps={childRun.steps}
|
|
458
|
+
isRunInProgress={childRun.run.isInProgress ?? false}
|
|
459
|
+
expandedSteps={expandedSteps}
|
|
460
|
+
toggleStep={toggleStep}
|
|
461
|
+
parseJson={parseJson}
|
|
462
|
+
formatDuration={formatDuration}
|
|
463
|
+
childRuns={nestedChildRuns}
|
|
464
|
+
depth={depth}
|
|
465
|
+
/>
|
|
466
|
+
);
|
|
467
|
+
})}
|
|
468
|
+
</div>
|
|
469
|
+
)}
|
|
470
|
+
</div>
|
|
471
|
+
);
|
|
472
|
+
}
|