@tanstack/ai-devtools-core 0.3.38 → 0.4.0
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/Shell/DKCFIT6H.js +12142 -0
- package/dist/Shell/SLOX2TPU.js +12119 -0
- package/dist/Shell/Z6HLJOVZ.js +10170 -0
- package/dist/dev.js +1 -1
- package/dist/index.js +1 -1
- package/dist/production/Shell/DKCFIT6H.js +12142 -0
- package/dist/production/Shell/SLOX2TPU.js +12119 -0
- package/dist/production/Shell/Z6HLJOVZ.js +10170 -0
- package/dist/production/dev.js +1 -1
- package/dist/production/index.js +1 -1
- package/dist/production/server.js +1 -1
- package/dist/server.js +1 -1
- package/package.json +4 -4
- package/src/components/Shell.tsx +74 -30
- package/src/components/conversation/IterationCard.tsx +261 -12
- package/src/components/conversation/IterationTimeline.tsx +66 -16
- package/src/components/conversation/index.ts +0 -9
- package/src/components/hooks/FixtureNamePopover.tsx +69 -0
- package/src/components/hooks/GenerationPanel.tsx +847 -0
- package/src/components/hooks/HookDashboard.tsx +292 -0
- package/src/components/hooks/HookDetails.tsx +1757 -0
- package/src/components/hooks/ToolFixtureForm.tsx +347 -0
- package/src/components/hooks/hook-dashboard-model.ts +134 -0
- package/src/components/hooks/index.ts +4 -0
- package/src/components/hooks/preview-messages.ts +117 -0
- package/src/components/hooks/preview-model.ts +195 -0
- package/src/components/utils/format.ts +0 -58
- package/src/store/ai-context.tsx +1165 -159
- package/src/store/ai-store.ts +0 -1
- package/src/store/hook-registry.ts +815 -0
- package/src/store/message-event-utils.ts +60 -0
- package/src/styles/use-styles.ts +1558 -99
- package/dist/Shell/MRWZEHAJ.js +0 -6225
- package/dist/Shell/RRRNZJ5E.js +0 -7560
- package/dist/production/Shell/MRWZEHAJ.js +0 -6225
- package/dist/production/Shell/RRRNZJ5E.js +0 -7560
- package/src/components/ConversationDetails.tsx +0 -216
- package/src/components/ConversationsList.tsx +0 -21
- package/src/components/conversation/ActivityEventsTab.tsx +0 -57
- package/src/components/conversation/ChunkBadges.tsx +0 -51
- package/src/components/conversation/ChunkItem.tsx +0 -273
- package/src/components/conversation/ChunksCollapsible.tsx +0 -56
- package/src/components/conversation/ChunksTab.tsx +0 -87
- package/src/components/conversation/ConversationHeader.tsx +0 -106
- package/src/components/conversation/ConversationTabs.tsx +0 -205
- package/src/components/conversation/MessageCard.tsx +0 -174
- package/src/components/conversation/MessageGroup.tsx +0 -101
- package/src/components/conversation/MessagesTab.tsx +0 -30
- package/src/components/conversation/SummariesTab.tsx +0 -91
- package/src/components/conversation/ToolCallDisplay.tsx +0 -99
- package/src/components/list/ConversationRow.tsx +0 -85
- package/src/components/list/index.ts +0 -1
package/dist/production/dev.js
CHANGED
|
@@ -2,7 +2,7 @@ import { constructCoreClass } from '@tanstack/devtools-utils/solid/class';
|
|
|
2
2
|
|
|
3
3
|
// src/core.ts
|
|
4
4
|
var [AiDevtoolsCore, AiDevtoolsCoreNoOp] = constructCoreClass(
|
|
5
|
-
() => import('./Shell/
|
|
5
|
+
() => import('./Shell/DKCFIT6H.js')
|
|
6
6
|
);
|
|
7
7
|
|
|
8
8
|
export { AiDevtoolsCore };
|
package/dist/production/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { constructCoreClass } from '@tanstack/devtools-utils/solid/class';
|
|
|
2
2
|
|
|
3
3
|
// src/core.ts
|
|
4
4
|
var [AiDevtoolsCore, AiDevtoolsCoreNoOp] = constructCoreClass(
|
|
5
|
-
() => import('./Shell/
|
|
5
|
+
() => import('./Shell/SLOX2TPU.js')
|
|
6
6
|
);
|
|
7
7
|
|
|
8
8
|
export { AiDevtoolsCore };
|
|
@@ -2,7 +2,7 @@ import { constructCoreClass } from '@tanstack/devtools-utils/solid/class';
|
|
|
2
2
|
|
|
3
3
|
// src/core.ts
|
|
4
4
|
var [AiDevtoolsCore, AiDevtoolsCoreNoOp] = constructCoreClass(
|
|
5
|
-
() => import('./Shell/
|
|
5
|
+
() => import('./Shell/Z6HLJOVZ.js')
|
|
6
6
|
);
|
|
7
7
|
|
|
8
8
|
export { AiDevtoolsCore };
|
package/dist/server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-devtools-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Core TanStack AI Devtools plugin for inspecting chat messages, tool calls, streams, and errors.",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/TanStack/ai.git",
|
|
10
|
-
"directory": "packages/
|
|
10
|
+
"directory": "packages/ai"
|
|
11
11
|
},
|
|
12
12
|
"type": "module",
|
|
13
13
|
"types": "./dist/index.d.ts",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"@tanstack/devtools-utils": "^0.4.0",
|
|
77
77
|
"goober": "^2.1.18",
|
|
78
78
|
"solid-js": "^1.9.10",
|
|
79
|
-
"@tanstack/ai": "0.22.
|
|
80
|
-
"@tanstack/ai-event-client": "0.
|
|
79
|
+
"@tanstack/ai": "0.22.1",
|
|
80
|
+
"@tanstack/ai-event-client": "0.4.0"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@vitest/coverage-v8": "4.0.14",
|
package/src/components/Shell.tsx
CHANGED
|
@@ -5,10 +5,14 @@ import {
|
|
|
5
5
|
MainPanel,
|
|
6
6
|
ThemeContextProvider,
|
|
7
7
|
} from '@tanstack/devtools-ui'
|
|
8
|
+
import {
|
|
9
|
+
aiEventClient,
|
|
10
|
+
createAIDevtoolsEventEnvelope,
|
|
11
|
+
emitAIDevtoolsEvent,
|
|
12
|
+
} from '@tanstack/ai-event-client'
|
|
8
13
|
import { useStyles } from '../styles/use-styles'
|
|
9
14
|
import { AIProvider } from '../store/ai-context'
|
|
10
|
-
import {
|
|
11
|
-
import { ConversationDetails } from './ConversationDetails'
|
|
15
|
+
import { HookDashboard, HookDetails } from './hooks'
|
|
12
16
|
|
|
13
17
|
import type { TanStackDevtoolsTheme } from '@tanstack/devtools-ui'
|
|
14
18
|
|
|
@@ -62,43 +66,83 @@ function DevtoolsContent() {
|
|
|
62
66
|
onMount(() => {
|
|
63
67
|
document.addEventListener('mousemove', handleMouseMove)
|
|
64
68
|
document.addEventListener('mouseup', handleMouseUp)
|
|
69
|
+
|
|
70
|
+
const openedAt = Date.now()
|
|
71
|
+
emitAIDevtoolsEvent('devtools:opened', {
|
|
72
|
+
...createAIDevtoolsEventEnvelope({
|
|
73
|
+
eventType: 'devtools:opened',
|
|
74
|
+
source: 'devtools',
|
|
75
|
+
visibility: 'devtools-action',
|
|
76
|
+
timestamp: openedAt,
|
|
77
|
+
}),
|
|
78
|
+
})
|
|
79
|
+
emitAIDevtoolsEvent('devtools:request-state', {
|
|
80
|
+
...createAIDevtoolsEventEnvelope({
|
|
81
|
+
eventType: 'devtools:request-state',
|
|
82
|
+
source: 'devtools',
|
|
83
|
+
visibility: 'devtools-action',
|
|
84
|
+
timestamp: openedAt + 1,
|
|
85
|
+
}),
|
|
86
|
+
})
|
|
65
87
|
})
|
|
66
88
|
|
|
67
89
|
onCleanup(() => {
|
|
68
90
|
document.removeEventListener('mousemove', handleMouseMove)
|
|
69
91
|
document.removeEventListener('mouseup', handleMouseUp)
|
|
92
|
+
// If the panel unmounts mid-drag, the mouseup handler never fires;
|
|
93
|
+
// reset the global drag styles so the host page isn't stuck with
|
|
94
|
+
// col-resize cursor / unselectable body.
|
|
95
|
+
if (isDragging()) {
|
|
96
|
+
setIsDragging(false)
|
|
97
|
+
document.body.style.cursor = ''
|
|
98
|
+
document.body.style.userSelect = ''
|
|
99
|
+
}
|
|
100
|
+
aiEventClient.emit('devtools:closed', {
|
|
101
|
+
...createAIDevtoolsEventEnvelope({
|
|
102
|
+
eventType: 'devtools:closed',
|
|
103
|
+
source: 'devtools',
|
|
104
|
+
visibility: 'devtools-action',
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
}),
|
|
107
|
+
})
|
|
70
108
|
})
|
|
71
109
|
|
|
72
110
|
return (
|
|
73
111
|
<MainPanel>
|
|
74
|
-
<
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<div
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<
|
|
112
|
+
<div class={styles().shellRoot} data-testid="ai-devtools-panel">
|
|
113
|
+
<Header>
|
|
114
|
+
<HeaderLogo flavor={{ light: '#ec4899', dark: '#ec4899' }}>
|
|
115
|
+
TanStack AI
|
|
116
|
+
</HeaderLogo>
|
|
117
|
+
</Header>
|
|
118
|
+
|
|
119
|
+
<div class={styles().mainContainer}>
|
|
120
|
+
<div
|
|
121
|
+
class={styles().leftPanel}
|
|
122
|
+
data-testid="ai-devtools-left-panel"
|
|
123
|
+
style={{
|
|
124
|
+
width: `${leftPanelWidth()}px`,
|
|
125
|
+
'min-width': '150px',
|
|
126
|
+
'max-width': '800px',
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
<div class={styles().shell.sectionHeader}>AI Hooks</div>
|
|
130
|
+
|
|
131
|
+
<HookDashboard />
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div
|
|
135
|
+
class={`${styles().dragHandle} ${isDragging() ? 'dragging' : ''}`}
|
|
136
|
+
onMouseDown={handleMouseDown}
|
|
137
|
+
/>
|
|
138
|
+
|
|
139
|
+
<div
|
|
140
|
+
class={styles().rightPanel}
|
|
141
|
+
data-testid="ai-devtools-right-panel"
|
|
142
|
+
style={{ flex: 1 }}
|
|
143
|
+
>
|
|
144
|
+
<HookDetails />
|
|
145
|
+
</div>
|
|
102
146
|
</div>
|
|
103
147
|
</div>
|
|
104
148
|
</MainPanel>
|
|
@@ -9,8 +9,18 @@ import {
|
|
|
9
9
|
} from 'solid-js'
|
|
10
10
|
import { JsonTree } from '@tanstack/devtools-ui'
|
|
11
11
|
import { useStyles } from '../../styles/use-styles'
|
|
12
|
+
import {
|
|
13
|
+
createHoverTarget,
|
|
14
|
+
getHoverDataAttributes,
|
|
15
|
+
isMessageHighlighted,
|
|
16
|
+
structuredOutputJsonItems,
|
|
17
|
+
structuredOutputPartId,
|
|
18
|
+
toolCallPartId,
|
|
19
|
+
toolResultPartId,
|
|
20
|
+
} from '../hooks/preview-model'
|
|
12
21
|
import { formatDuration } from '../utils'
|
|
13
22
|
import { SystemPromptItem } from './IterationTimeline'
|
|
23
|
+
import type { HoverTarget } from '../hooks/preview-model'
|
|
14
24
|
import type {
|
|
15
25
|
Iteration,
|
|
16
26
|
Message,
|
|
@@ -25,6 +35,12 @@ interface IterationCardProps {
|
|
|
25
35
|
messages: Array<Message>
|
|
26
36
|
index: number
|
|
27
37
|
isLast: boolean
|
|
38
|
+
hoverTarget?: HoverTarget | null
|
|
39
|
+
onHoverTarget?: (target: HoverTarget | null) => void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type StructuredOutputMessagePart = NonNullable<Message['parts']>[number] & {
|
|
43
|
+
type: 'structured-output'
|
|
28
44
|
}
|
|
29
45
|
|
|
30
46
|
// --- Step types ---
|
|
@@ -32,12 +48,37 @@ interface IterationCardProps {
|
|
|
32
48
|
type IterationStep =
|
|
33
49
|
| { kind: 'middleware'; event: MiddlewareEvent }
|
|
34
50
|
| { kind: 'thinking'; message: Message }
|
|
51
|
+
| {
|
|
52
|
+
kind: 'structured_output'
|
|
53
|
+
message: Message
|
|
54
|
+
part: StructuredOutputMessagePart
|
|
55
|
+
}
|
|
35
56
|
| { kind: 'assistant'; message: Message }
|
|
36
57
|
| { kind: 'tool_call'; toolCall: ToolCall; message: Message }
|
|
37
58
|
| { kind: 'tool_result'; message: Message }
|
|
38
59
|
|
|
39
60
|
// --- Helpers ---
|
|
40
61
|
|
|
62
|
+
function getApprovalStatus(toolCall: ToolCall): string | undefined {
|
|
63
|
+
if (toolCall.approvalApproved === true || toolCall.state === 'approved') {
|
|
64
|
+
return 'approved'
|
|
65
|
+
}
|
|
66
|
+
if (toolCall.approvalApproved === false || toolCall.state === 'denied') {
|
|
67
|
+
return 'denied'
|
|
68
|
+
}
|
|
69
|
+
if (
|
|
70
|
+
toolCall.approvalRequired ||
|
|
71
|
+
toolCall.approvalId ||
|
|
72
|
+
toolCall.state === 'approval-requested'
|
|
73
|
+
) {
|
|
74
|
+
return 'approval requested'
|
|
75
|
+
}
|
|
76
|
+
if (toolCall.state === 'approval-responded') {
|
|
77
|
+
return 'responded'
|
|
78
|
+
}
|
|
79
|
+
return undefined
|
|
80
|
+
}
|
|
81
|
+
|
|
41
82
|
function getIterationLabel(iter: Iteration, displayIndex: number): string {
|
|
42
83
|
if (!iter.completedAt) return `Iteration ${displayIndex} — Generating...`
|
|
43
84
|
if (iter.finishReason === 'error') return `Iteration ${displayIndex} — Error`
|
|
@@ -70,6 +111,15 @@ function buildSteps(
|
|
|
70
111
|
if (msg.thinkingContent) {
|
|
71
112
|
steps.push({ kind: 'thinking', message: msg })
|
|
72
113
|
}
|
|
114
|
+
for (const part of msg.parts ?? []) {
|
|
115
|
+
if (part.type === 'structured-output') {
|
|
116
|
+
steps.push({
|
|
117
|
+
kind: 'structured_output',
|
|
118
|
+
message: msg,
|
|
119
|
+
part: part as StructuredOutputMessagePart,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
}
|
|
73
123
|
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
74
124
|
for (const tc of msg.toolCalls) {
|
|
75
125
|
steps.push({ kind: 'tool_call', toolCall: tc, message: msg })
|
|
@@ -157,7 +207,7 @@ const MiddlewareStep: Component<{
|
|
|
157
207
|
<Show when={expanded() && hasChanges()}>
|
|
158
208
|
<div class={s().mwChangesContainer}>
|
|
159
209
|
<JsonTree
|
|
160
|
-
value={ev().configChanges
|
|
210
|
+
value={ev().configChanges}
|
|
161
211
|
defaultExpansionDepth={2}
|
|
162
212
|
copyable
|
|
163
213
|
/>
|
|
@@ -169,6 +219,7 @@ const MiddlewareStep: Component<{
|
|
|
169
219
|
|
|
170
220
|
const ThinkingStep: Component<{
|
|
171
221
|
step: Extract<IterationStep, { kind: 'thinking' }>
|
|
222
|
+
onHoverTarget?: (target: HoverTarget | null) => void
|
|
172
223
|
}> = (props) => {
|
|
173
224
|
const styles = useStyles()
|
|
174
225
|
const s = () => styles().iterationTimeline
|
|
@@ -180,7 +231,19 @@ const ThinkingStep: Component<{
|
|
|
180
231
|
|
|
181
232
|
return (
|
|
182
233
|
<>
|
|
183
|
-
<div
|
|
234
|
+
<div
|
|
235
|
+
{...getHoverDataAttributes({ messageIds: [msg().id] })}
|
|
236
|
+
class={s().step}
|
|
237
|
+
onMouseEnter={() =>
|
|
238
|
+
props.onHoverTarget?.(
|
|
239
|
+
createHoverTarget({
|
|
240
|
+
messageIds: [msg().id],
|
|
241
|
+
origin: 'timeline',
|
|
242
|
+
}),
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
onMouseLeave={() => props.onHoverTarget?.(null)}
|
|
246
|
+
>
|
|
184
247
|
<span class={`${s().stepPrefix} ${s().stepPrefixThinking}`}>
|
|
185
248
|
Thinking
|
|
186
249
|
</span>
|
|
@@ -201,8 +264,78 @@ const ThinkingStep: Component<{
|
|
|
201
264
|
)
|
|
202
265
|
}
|
|
203
266
|
|
|
267
|
+
const StructuredOutputStep: Component<{
|
|
268
|
+
step: Extract<IterationStep, { kind: 'structured_output' }>
|
|
269
|
+
onHoverTarget?: (target: HoverTarget | null) => void
|
|
270
|
+
}> = (props) => {
|
|
271
|
+
const styles = useStyles()
|
|
272
|
+
const s = () => styles().iterationTimeline
|
|
273
|
+
const [expanded, setExpanded] = createSignal(true)
|
|
274
|
+
const msg = () => props.step.message
|
|
275
|
+
const part = () => props.step.part
|
|
276
|
+
const partId = () => structuredOutputPartId(msg().id)
|
|
277
|
+
const jsonItems = createMemo(() => structuredOutputJsonItems(part()))
|
|
278
|
+
|
|
279
|
+
const badgeClass = () => {
|
|
280
|
+
if (part().status === 'complete') return s().mwBadgeApproved
|
|
281
|
+
if (part().status === 'error') return s().mwBadgeError
|
|
282
|
+
return s().mwBadgeDefault
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<>
|
|
287
|
+
<div
|
|
288
|
+
{...getHoverDataAttributes({
|
|
289
|
+
messageIds: [msg().id],
|
|
290
|
+
partIds: [partId()],
|
|
291
|
+
})}
|
|
292
|
+
class={s().step}
|
|
293
|
+
onClick={() => setExpanded(!expanded())}
|
|
294
|
+
onMouseEnter={() =>
|
|
295
|
+
props.onHoverTarget?.(
|
|
296
|
+
createHoverTarget({
|
|
297
|
+
messageIds: [msg().id],
|
|
298
|
+
partIds: [partId()],
|
|
299
|
+
origin: 'timeline',
|
|
300
|
+
}),
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
onMouseLeave={() => props.onHoverTarget?.(null)}
|
|
304
|
+
style={{ cursor: 'pointer' }}
|
|
305
|
+
>
|
|
306
|
+
<span class={`${s().stepPrefix} ${s().stepPrefixAssistant}`}>
|
|
307
|
+
Structured
|
|
308
|
+
</span>
|
|
309
|
+
<span class={`${s().mwBadge} ${badgeClass()}`}>{part().status}</span>
|
|
310
|
+
<span class={`${s().chevron} ${expanded() ? s().chevronOpen : ''}`}>
|
|
311
|
+
{'\u25B6'}
|
|
312
|
+
</span>
|
|
313
|
+
</div>
|
|
314
|
+
<div class={`${s().cardBody} ${expanded() ? s().cardBodyOpen : ''}`}>
|
|
315
|
+
<div class={`${s().cardBodyInner} ${s().stepJsonItemsCompare}`}>
|
|
316
|
+
<For each={jsonItems()}>
|
|
317
|
+
{(item) => (
|
|
318
|
+
<div class={s().stepJsonItem}>
|
|
319
|
+
<div class={s().stepJsonItemLabel}>{item.label}</div>
|
|
320
|
+
<div class={s().stepJsonPanel}>
|
|
321
|
+
<JsonTree
|
|
322
|
+
value={item.value}
|
|
323
|
+
defaultExpansionDepth={1}
|
|
324
|
+
copyable
|
|
325
|
+
/>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
)}
|
|
329
|
+
</For>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</>
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
204
336
|
const AssistantStep: Component<{
|
|
205
337
|
step: Extract<IterationStep, { kind: 'assistant' }>
|
|
338
|
+
onHoverTarget?: (target: HoverTarget | null) => void
|
|
206
339
|
}> = (props) => {
|
|
207
340
|
const styles = useStyles()
|
|
208
341
|
const s = () => styles().iterationTimeline
|
|
@@ -220,7 +353,19 @@ const AssistantStep: Component<{
|
|
|
220
353
|
|
|
221
354
|
return (
|
|
222
355
|
<>
|
|
223
|
-
<div
|
|
356
|
+
<div
|
|
357
|
+
{...getHoverDataAttributes({ messageIds: [msg().id] })}
|
|
358
|
+
class={`${s().step} ${isLong() ? s().stepResponseLong : ''}`}
|
|
359
|
+
onMouseEnter={() =>
|
|
360
|
+
props.onHoverTarget?.(
|
|
361
|
+
createHoverTarget({
|
|
362
|
+
messageIds: [msg().id],
|
|
363
|
+
origin: 'timeline',
|
|
364
|
+
}),
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
onMouseLeave={() => props.onHoverTarget?.(null)}
|
|
368
|
+
>
|
|
224
369
|
<span class={`${s().stepPrefix} ${s().stepPrefixAssistant}`}>
|
|
225
370
|
Response
|
|
226
371
|
</span>
|
|
@@ -245,12 +390,16 @@ const AssistantStep: Component<{
|
|
|
245
390
|
|
|
246
391
|
const ToolCallStep: Component<{
|
|
247
392
|
step: Extract<IterationStep, { kind: 'tool_call' }>
|
|
393
|
+
onHoverTarget?: (target: HoverTarget | null) => void
|
|
248
394
|
}> = (props) => {
|
|
249
395
|
const styles = useStyles()
|
|
250
396
|
const s = () => styles().iterationTimeline
|
|
251
397
|
const [argsOpen, setArgsOpen] = createSignal(false)
|
|
252
398
|
const [resultOpen, setResultOpen] = createSignal(false)
|
|
253
399
|
const tc = () => props.step.toolCall
|
|
400
|
+
const callPartId = () => toolCallPartId(tc().id)
|
|
401
|
+
const resultPartId = () => toolResultPartId(tc().id)
|
|
402
|
+
const messageId = () => props.step.message.id
|
|
254
403
|
|
|
255
404
|
const parsedArgs = () => {
|
|
256
405
|
const raw = tc().arguments || '{}'
|
|
@@ -258,6 +407,13 @@ const ToolCallStep: Component<{
|
|
|
258
407
|
}
|
|
259
408
|
|
|
260
409
|
const hasResult = () => tc().result !== undefined
|
|
410
|
+
const approvalStatus = () => getApprovalStatus(tc())
|
|
411
|
+
const approvalBadgeClass = () => {
|
|
412
|
+
const status = approvalStatus()
|
|
413
|
+
if (status === 'approved') return s().mwBadgeApproved
|
|
414
|
+
if (status === 'denied') return s().mwBadgeDenied
|
|
415
|
+
return s().mwBadgeApproval
|
|
416
|
+
}
|
|
261
417
|
|
|
262
418
|
const parsedResult = () => {
|
|
263
419
|
if (!hasResult()) return null
|
|
@@ -270,14 +426,35 @@ const ToolCallStep: Component<{
|
|
|
270
426
|
return (
|
|
271
427
|
<>
|
|
272
428
|
<div
|
|
429
|
+
{...getHoverDataAttributes({
|
|
430
|
+
messageIds: [messageId()],
|
|
431
|
+
partIds: [callPartId()],
|
|
432
|
+
})}
|
|
273
433
|
class={s().step}
|
|
274
434
|
onClick={() => setArgsOpen(!argsOpen())}
|
|
435
|
+
onMouseEnter={() =>
|
|
436
|
+
props.onHoverTarget?.(
|
|
437
|
+
createHoverTarget({
|
|
438
|
+
messageIds: [messageId()],
|
|
439
|
+
partIds: [callPartId()],
|
|
440
|
+
origin: 'timeline',
|
|
441
|
+
}),
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
onMouseLeave={() => props.onHoverTarget?.(null)}
|
|
275
445
|
style={{ cursor: 'pointer' }}
|
|
276
446
|
>
|
|
277
447
|
<span class={`${s().stepPrefix} ${s().stepPrefixToolCall}`}>
|
|
278
448
|
Tool Call
|
|
279
449
|
</span>
|
|
280
450
|
<span class={`${s().mwBadge} ${s().mwBadgeToolCall}`}>{tc().name}</span>
|
|
451
|
+
<Show when={approvalStatus()}>
|
|
452
|
+
{(status) => (
|
|
453
|
+
<span class={`${s().mwBadge} ${approvalBadgeClass()}`}>
|
|
454
|
+
{status()}
|
|
455
|
+
</span>
|
|
456
|
+
)}
|
|
457
|
+
</Show>
|
|
281
458
|
<Show when={tc().duration !== undefined}>
|
|
282
459
|
<span class={s().stepDuration}>{tc().duration}ms</span>
|
|
283
460
|
</Show>
|
|
@@ -288,18 +465,28 @@ const ToolCallStep: Component<{
|
|
|
288
465
|
<div class={`${s().cardBody} ${argsOpen() ? s().cardBodyOpen : ''}`}>
|
|
289
466
|
<div class={s().cardBodyInner}>
|
|
290
467
|
<div class={s().stepJsonPanel}>
|
|
291
|
-
<JsonTree
|
|
292
|
-
value={parsedArgs() as Record<string, unknown>}
|
|
293
|
-
defaultExpansionDepth={0}
|
|
294
|
-
copyable
|
|
295
|
-
/>
|
|
468
|
+
<JsonTree value={parsedArgs()} defaultExpansionDepth={0} copyable />
|
|
296
469
|
</div>
|
|
297
470
|
</div>
|
|
298
471
|
</div>
|
|
299
472
|
<Show when={hasResult()}>
|
|
300
473
|
<div
|
|
474
|
+
{...getHoverDataAttributes({
|
|
475
|
+
messageIds: [messageId()],
|
|
476
|
+
partIds: [callPartId(), resultPartId()],
|
|
477
|
+
})}
|
|
301
478
|
class={s().step}
|
|
302
479
|
onClick={() => setResultOpen(!resultOpen())}
|
|
480
|
+
onMouseEnter={() =>
|
|
481
|
+
props.onHoverTarget?.(
|
|
482
|
+
createHoverTarget({
|
|
483
|
+
messageIds: [messageId()],
|
|
484
|
+
partIds: [callPartId(), resultPartId()],
|
|
485
|
+
origin: 'timeline',
|
|
486
|
+
}),
|
|
487
|
+
)
|
|
488
|
+
}
|
|
489
|
+
onMouseLeave={() => props.onHoverTarget?.(null)}
|
|
303
490
|
style={{ cursor: 'pointer' }}
|
|
304
491
|
>
|
|
305
492
|
<span class={`${s().stepPrefix} ${s().stepPrefixToolResult}`}>
|
|
@@ -319,7 +506,7 @@ const ToolCallStep: Component<{
|
|
|
319
506
|
<div class={s().cardBodyInner}>
|
|
320
507
|
<div class={s().stepJsonPanel}>
|
|
321
508
|
<JsonTree
|
|
322
|
-
value={parsedResult()
|
|
509
|
+
value={parsedResult()}
|
|
323
510
|
defaultExpansionDepth={0}
|
|
324
511
|
copyable
|
|
325
512
|
/>
|
|
@@ -333,6 +520,7 @@ const ToolCallStep: Component<{
|
|
|
333
520
|
|
|
334
521
|
const ToolResultStep: Component<{
|
|
335
522
|
step: Extract<IterationStep, { kind: 'tool_result' }>
|
|
523
|
+
onHoverTarget?: (target: HoverTarget | null) => void
|
|
336
524
|
}> = (props) => {
|
|
337
525
|
const styles = useStyles()
|
|
338
526
|
const s = () => styles().iterationTimeline
|
|
@@ -347,8 +535,18 @@ const ToolResultStep: Component<{
|
|
|
347
535
|
return (
|
|
348
536
|
<>
|
|
349
537
|
<div
|
|
538
|
+
{...getHoverDataAttributes({ messageIds: [msg().id] })}
|
|
350
539
|
class={s().step}
|
|
351
540
|
onClick={() => setIsOpen(!isOpen())}
|
|
541
|
+
onMouseEnter={() =>
|
|
542
|
+
props.onHoverTarget?.(
|
|
543
|
+
createHoverTarget({
|
|
544
|
+
messageIds: [msg().id],
|
|
545
|
+
origin: 'timeline',
|
|
546
|
+
}),
|
|
547
|
+
)
|
|
548
|
+
}
|
|
549
|
+
onMouseLeave={() => props.onHoverTarget?.(null)}
|
|
352
550
|
style={{ cursor: 'pointer' }}
|
|
353
551
|
>
|
|
354
552
|
<span class={`${s().stepPrefix} ${s().stepPrefixToolResult}`}>
|
|
@@ -362,7 +560,7 @@ const ToolResultStep: Component<{
|
|
|
362
560
|
<div class={s().cardBodyInner}>
|
|
363
561
|
<div class={s().stepJsonPanel}>
|
|
364
562
|
<JsonTree
|
|
365
|
-
value={parsedContent()
|
|
563
|
+
value={parsedContent()}
|
|
366
564
|
defaultExpansionDepth={0}
|
|
367
565
|
copyable
|
|
368
566
|
/>
|
|
@@ -395,6 +593,18 @@ export const IterationCard: Component<IterationCardProps> = (props) => {
|
|
|
395
593
|
|
|
396
594
|
const label = () => getIterationLabel(iter(), props.index)
|
|
397
595
|
const steps = createMemo(() => buildSteps(iter(), props.messages))
|
|
596
|
+
const iterationPartIds = createMemo(() =>
|
|
597
|
+
steps().flatMap((step) => {
|
|
598
|
+
if (step.kind === 'structured_output') {
|
|
599
|
+
return [structuredOutputPartId(step.message.id)]
|
|
600
|
+
}
|
|
601
|
+
if (step.kind !== 'tool_call') return []
|
|
602
|
+
const callId = step.toolCall.id
|
|
603
|
+
return step.toolCall.result === undefined
|
|
604
|
+
? [toolCallPartId(callId)]
|
|
605
|
+
: [toolCallPartId(callId), toolResultPartId(callId)]
|
|
606
|
+
}),
|
|
607
|
+
)
|
|
398
608
|
|
|
399
609
|
/**
|
|
400
610
|
* Compute delta usage for display.
|
|
@@ -443,6 +653,14 @@ export const IterationCard: Component<IterationCardProps> = (props) => {
|
|
|
443
653
|
return ''
|
|
444
654
|
}
|
|
445
655
|
|
|
656
|
+
const isIterationHighlighted = () =>
|
|
657
|
+
iter().messageIds.some((messageId) =>
|
|
658
|
+
isMessageHighlighted(messageId, props.hoverTarget ?? null),
|
|
659
|
+
) ||
|
|
660
|
+
iterationPartIds().some((partId) =>
|
|
661
|
+
(props.hoverTarget?.partIds ?? []).includes(partId),
|
|
662
|
+
)
|
|
663
|
+
|
|
446
664
|
// Config data from this iteration
|
|
447
665
|
const configSubtitle = () => {
|
|
448
666
|
const { model, provider } = iter()
|
|
@@ -531,13 +749,29 @@ export const IterationCard: Component<IterationCardProps> = (props) => {
|
|
|
531
749
|
|
|
532
750
|
return (
|
|
533
751
|
<div
|
|
534
|
-
|
|
752
|
+
{...getHoverDataAttributes({
|
|
753
|
+
messageIds: iter().messageIds,
|
|
754
|
+
partIds: iterationPartIds(),
|
|
755
|
+
})}
|
|
756
|
+
class={`${s().iterCard} ${
|
|
757
|
+
isIterationHighlighted() ? s().iterCardHighlighted : ''
|
|
758
|
+
}`}
|
|
535
759
|
style={{ 'animation-delay': `${props.index * 60}ms` }}
|
|
536
760
|
>
|
|
537
761
|
{/* Header */}
|
|
538
762
|
<div
|
|
763
|
+
{...getHoverDataAttributes({ messageIds: iter().messageIds })}
|
|
539
764
|
class={`${s().iterCardHeader} ${headerAccent()}`}
|
|
540
765
|
onClick={() => setIsOpen(!isOpen())}
|
|
766
|
+
onMouseEnter={() =>
|
|
767
|
+
props.onHoverTarget?.(
|
|
768
|
+
createHoverTarget({
|
|
769
|
+
messageIds: iter().messageIds,
|
|
770
|
+
origin: 'timeline',
|
|
771
|
+
}),
|
|
772
|
+
)
|
|
773
|
+
}
|
|
774
|
+
onMouseLeave={() => props.onHoverTarget?.(null)}
|
|
541
775
|
>
|
|
542
776
|
<div class={s().cardHeaderContent}>
|
|
543
777
|
<span class={s().iterCardTitle}>{label()}</span>
|
|
@@ -690,7 +924,7 @@ export const IterationCard: Component<IterationCardProps> = (props) => {
|
|
|
690
924
|
<span class={s().configPanelLabel}>Model Options</span>
|
|
691
925
|
<div class={s().configJsonTreeContainer}>
|
|
692
926
|
<JsonTree
|
|
693
|
-
value={modelOptions()
|
|
927
|
+
value={modelOptions()}
|
|
694
928
|
defaultExpansionDepth={2}
|
|
695
929
|
copyable
|
|
696
930
|
/>
|
|
@@ -719,6 +953,18 @@ export const IterationCard: Component<IterationCardProps> = (props) => {
|
|
|
719
953
|
step={
|
|
720
954
|
step() as Extract<IterationStep, { kind: 'thinking' }>
|
|
721
955
|
}
|
|
956
|
+
onHoverTarget={props.onHoverTarget}
|
|
957
|
+
/>
|
|
958
|
+
</Match>
|
|
959
|
+
<Match when={step().kind === 'structured_output'}>
|
|
960
|
+
<StructuredOutputStep
|
|
961
|
+
step={
|
|
962
|
+
step() as Extract<
|
|
963
|
+
IterationStep,
|
|
964
|
+
{ kind: 'structured_output' }
|
|
965
|
+
>
|
|
966
|
+
}
|
|
967
|
+
onHoverTarget={props.onHoverTarget}
|
|
722
968
|
/>
|
|
723
969
|
</Match>
|
|
724
970
|
<Match when={step().kind === 'assistant'}>
|
|
@@ -726,6 +972,7 @@ export const IterationCard: Component<IterationCardProps> = (props) => {
|
|
|
726
972
|
step={
|
|
727
973
|
step() as Extract<IterationStep, { kind: 'assistant' }>
|
|
728
974
|
}
|
|
975
|
+
onHoverTarget={props.onHoverTarget}
|
|
729
976
|
/>
|
|
730
977
|
</Match>
|
|
731
978
|
<Match when={step().kind === 'tool_call'}>
|
|
@@ -733,6 +980,7 @@ export const IterationCard: Component<IterationCardProps> = (props) => {
|
|
|
733
980
|
step={
|
|
734
981
|
step() as Extract<IterationStep, { kind: 'tool_call' }>
|
|
735
982
|
}
|
|
983
|
+
onHoverTarget={props.onHoverTarget}
|
|
736
984
|
/>
|
|
737
985
|
</Match>
|
|
738
986
|
<Match when={step().kind === 'tool_result'}>
|
|
@@ -740,6 +988,7 @@ export const IterationCard: Component<IterationCardProps> = (props) => {
|
|
|
740
988
|
step={
|
|
741
989
|
step() as Extract<IterationStep, { kind: 'tool_result' }>
|
|
742
990
|
}
|
|
991
|
+
onHoverTarget={props.onHoverTarget}
|
|
743
992
|
/>
|
|
744
993
|
</Match>
|
|
745
994
|
</Switch>
|