@tanstack/ai-devtools-core 0.3.9 → 0.3.12
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/README.md +6 -6
- package/dist/Shell/{MUFU35SY.js → SHJAKIOY.js} +3873 -1906
- package/dist/Shell/{GHXECDJV.js → TWTNN4SQ.js} +3909 -1427
- package/dist/dev.js +1 -1
- package/dist/index.js +1 -1
- package/dist/production/Shell/{MUFU35SY.js → SHJAKIOY.js} +3873 -1906
- package/dist/production/Shell/{GHXECDJV.js → TWTNN4SQ.js} +3909 -1427
- 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 +3 -2
- package/src/components/ConversationDetails.tsx +166 -99
- package/src/components/conversation/ConversationHeader.tsx +57 -98
- package/src/components/conversation/ConversationTabs.tsx +12 -14
- package/src/components/conversation/IterationCard.tsx +745 -0
- package/src/components/conversation/IterationTimeline.tsx +424 -0
- package/src/components/conversation/index.ts +3 -0
- package/src/store/ai-context.tsx +485 -25
- package/src/store/ai-store.ts +8 -1
- package/src/styles/use-styles.ts +707 -0
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/TWTNN4SQ.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/TWTNN4SQ.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/SHJAKIOY.js')
|
|
6
6
|
);
|
|
7
7
|
|
|
8
8
|
export { AiDevtoolsCore };
|
package/dist/server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-devtools-core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"description": "Core TanStack AI Devtools",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"@tanstack/devtools-utils": "0.3.2",
|
|
73
73
|
"goober": "^2.1.18",
|
|
74
74
|
"solid-js": "^1.9.10",
|
|
75
|
-
"@tanstack/ai": "0.
|
|
75
|
+
"@tanstack/ai": "0.8.1",
|
|
76
|
+
"@tanstack/ai-event-client": "0.1.1"
|
|
76
77
|
},
|
|
77
78
|
"devDependencies": {
|
|
78
79
|
"@vitest/coverage-v8": "4.0.14",
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ChunksTab,
|
|
7
7
|
ConversationHeader,
|
|
8
8
|
ConversationTabs,
|
|
9
|
+
IterationTimeline,
|
|
9
10
|
MessagesTab,
|
|
10
11
|
SummariesTab,
|
|
11
12
|
} from './conversation'
|
|
@@ -23,69 +24,83 @@ export const ConversationDetails: Component = () => {
|
|
|
23
24
|
return state.conversations[state.activeConversationId]
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
const hasIterations = () => {
|
|
28
|
+
const conv = activeConversation()
|
|
29
|
+
return conv && conv.iterations.length > 0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const hasActivityTabs = () => {
|
|
33
|
+
const conv = activeConversation()
|
|
34
|
+
if (!conv) return false
|
|
35
|
+
return (
|
|
36
|
+
conv.hasSummarize ||
|
|
37
|
+
conv.hasImage ||
|
|
38
|
+
conv.hasSpeech ||
|
|
39
|
+
conv.hasTranscription ||
|
|
40
|
+
conv.hasVideo
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Update active tab when conversation changes (only for non-iteration views)
|
|
27
45
|
createEffect(() => {
|
|
28
46
|
const conv = activeConversation()
|
|
29
|
-
if (conv)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
47
|
+
if (!conv) return
|
|
48
|
+
|
|
49
|
+
// If iterations exist, the timeline is the primary view — only set tab for activity
|
|
50
|
+
if (conv.iterations.length > 0) {
|
|
51
|
+
if (conv.hasSummarize || (conv.summaries && conv.summaries.length > 0)) {
|
|
52
|
+
setActiveTab('summaries')
|
|
53
|
+
} else if (
|
|
54
|
+
conv.hasImage ||
|
|
55
|
+
(conv.imageEvents && conv.imageEvents.length > 0)
|
|
56
|
+
) {
|
|
57
|
+
setActiveTab('image')
|
|
58
|
+
}
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// No iterations — use flat message/chunk view
|
|
63
|
+
if (conv.type === 'server') {
|
|
64
|
+
if (conv.chunks.length > 0) {
|
|
65
|
+
setActiveTab('chunks')
|
|
66
|
+
} else if (
|
|
67
|
+
conv.hasSummarize ||
|
|
68
|
+
(conv.summaries && conv.summaries.length > 0)
|
|
69
|
+
) {
|
|
70
|
+
setActiveTab('summaries')
|
|
71
|
+
} else if (
|
|
72
|
+
conv.hasImage ||
|
|
73
|
+
(conv.imageEvents && conv.imageEvents.length > 0)
|
|
74
|
+
) {
|
|
75
|
+
setActiveTab('image')
|
|
76
|
+
} else if (
|
|
77
|
+
conv.hasSpeech ||
|
|
78
|
+
(conv.speechEvents && conv.speechEvents.length > 0)
|
|
79
|
+
) {
|
|
80
|
+
setActiveTab('speech')
|
|
81
|
+
} else if (
|
|
82
|
+
conv.hasTranscription ||
|
|
83
|
+
(conv.transcriptionEvents && conv.transcriptionEvents.length > 0)
|
|
84
|
+
) {
|
|
85
|
+
setActiveTab('transcription')
|
|
86
|
+
} else if (
|
|
87
|
+
conv.hasVideo ||
|
|
88
|
+
(conv.videoEvents && conv.videoEvents.length > 0)
|
|
89
|
+
) {
|
|
90
|
+
setActiveTab('video')
|
|
91
|
+
} else {
|
|
92
|
+
setActiveTab('chunks')
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
if (conv.messages.length > 0) {
|
|
96
|
+
setActiveTab('messages')
|
|
97
|
+
} else if (
|
|
98
|
+
conv.hasImage ||
|
|
99
|
+
(conv.imageEvents && conv.imageEvents.length > 0)
|
|
100
|
+
) {
|
|
101
|
+
setActiveTab('image')
|
|
62
102
|
} else {
|
|
63
|
-
|
|
64
|
-
if (conv.messages.length > 0) {
|
|
65
|
-
setActiveTab('messages')
|
|
66
|
-
} else if (
|
|
67
|
-
conv.hasImage ||
|
|
68
|
-
(conv.imageEvents && conv.imageEvents.length > 0)
|
|
69
|
-
) {
|
|
70
|
-
setActiveTab('image')
|
|
71
|
-
} else if (
|
|
72
|
-
conv.hasSpeech ||
|
|
73
|
-
(conv.speechEvents && conv.speechEvents.length > 0)
|
|
74
|
-
) {
|
|
75
|
-
setActiveTab('speech')
|
|
76
|
-
} else if (
|
|
77
|
-
conv.hasTranscription ||
|
|
78
|
-
(conv.transcriptionEvents && conv.transcriptionEvents.length > 0)
|
|
79
|
-
) {
|
|
80
|
-
setActiveTab('transcription')
|
|
81
|
-
} else if (
|
|
82
|
-
conv.hasVideo ||
|
|
83
|
-
(conv.videoEvents && conv.videoEvents.length > 0)
|
|
84
|
-
) {
|
|
85
|
-
setActiveTab('video')
|
|
86
|
-
} else {
|
|
87
|
-
setActiveTab('messages')
|
|
88
|
-
}
|
|
103
|
+
setActiveTab('messages')
|
|
89
104
|
}
|
|
90
105
|
}
|
|
91
106
|
})
|
|
@@ -102,46 +117,98 @@ export const ConversationDetails: Component = () => {
|
|
|
102
117
|
{(conv) => (
|
|
103
118
|
<div class={styles().conversationDetails.container}>
|
|
104
119
|
<ConversationHeader conversation={conv()} />
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<MessagesTab messages={conv().messages} />
|
|
113
|
-
</Show>
|
|
114
|
-
<Show when={activeTab() === 'chunks'}>
|
|
115
|
-
<ChunksTab chunks={conv().chunks} messages={conv().messages} />
|
|
116
|
-
</Show>
|
|
117
|
-
<Show when={activeTab() === 'summaries'}>
|
|
118
|
-
<SummariesTab summaries={conv().summaries ?? []} />
|
|
119
|
-
</Show>
|
|
120
|
-
<Show when={activeTab() === 'image'}>
|
|
121
|
-
<ActivityEventsTab
|
|
122
|
-
title="Image Activity"
|
|
123
|
-
events={conv().imageEvents ?? []}
|
|
124
|
-
/>
|
|
125
|
-
</Show>
|
|
126
|
-
<Show when={activeTab() === 'speech'}>
|
|
127
|
-
<ActivityEventsTab
|
|
128
|
-
title="Speech Activity"
|
|
129
|
-
events={conv().speechEvents ?? []}
|
|
130
|
-
/>
|
|
131
|
-
</Show>
|
|
132
|
-
<Show when={activeTab() === 'transcription'}>
|
|
133
|
-
<ActivityEventsTab
|
|
134
|
-
title="Transcription Activity"
|
|
135
|
-
events={conv().transcriptionEvents ?? []}
|
|
136
|
-
/>
|
|
137
|
-
</Show>
|
|
138
|
-
<Show when={activeTab() === 'video'}>
|
|
139
|
-
<ActivityEventsTab
|
|
140
|
-
title="Video Activity"
|
|
141
|
-
events={conv().videoEvents ?? []}
|
|
120
|
+
|
|
121
|
+
{/* Primary view: iteration timeline when iterations exist */}
|
|
122
|
+
<Show when={hasIterations()}>
|
|
123
|
+
<div class={styles().conversationDetails.contentArea}>
|
|
124
|
+
<IterationTimeline
|
|
125
|
+
iterations={conv().iterations}
|
|
126
|
+
messages={conv().messages}
|
|
142
127
|
/>
|
|
143
|
-
</
|
|
144
|
-
</
|
|
128
|
+
</div>
|
|
129
|
+
</Show>
|
|
130
|
+
|
|
131
|
+
{/* Fallback: flat message/chunk view when no iterations */}
|
|
132
|
+
<Show when={!hasIterations()}>
|
|
133
|
+
<ConversationTabs
|
|
134
|
+
conversation={conv()}
|
|
135
|
+
activeTab={activeTab()}
|
|
136
|
+
onTabChange={setActiveTab}
|
|
137
|
+
/>
|
|
138
|
+
<div class={styles().conversationDetails.contentArea}>
|
|
139
|
+
<Show when={activeTab() === 'messages'}>
|
|
140
|
+
<MessagesTab messages={conv().messages} />
|
|
141
|
+
</Show>
|
|
142
|
+
<Show when={activeTab() === 'chunks'}>
|
|
143
|
+
<ChunksTab chunks={conv().chunks} messages={conv().messages} />
|
|
144
|
+
</Show>
|
|
145
|
+
<Show when={activeTab() === 'summaries'}>
|
|
146
|
+
<SummariesTab summaries={conv().summaries ?? []} />
|
|
147
|
+
</Show>
|
|
148
|
+
<Show when={activeTab() === 'image'}>
|
|
149
|
+
<ActivityEventsTab
|
|
150
|
+
title="Image Activity"
|
|
151
|
+
events={conv().imageEvents ?? []}
|
|
152
|
+
/>
|
|
153
|
+
</Show>
|
|
154
|
+
<Show when={activeTab() === 'speech'}>
|
|
155
|
+
<ActivityEventsTab
|
|
156
|
+
title="Speech Activity"
|
|
157
|
+
events={conv().speechEvents ?? []}
|
|
158
|
+
/>
|
|
159
|
+
</Show>
|
|
160
|
+
<Show when={activeTab() === 'transcription'}>
|
|
161
|
+
<ActivityEventsTab
|
|
162
|
+
title="Transcription Activity"
|
|
163
|
+
events={conv().transcriptionEvents ?? []}
|
|
164
|
+
/>
|
|
165
|
+
</Show>
|
|
166
|
+
<Show when={activeTab() === 'video'}>
|
|
167
|
+
<ActivityEventsTab
|
|
168
|
+
title="Video Activity"
|
|
169
|
+
events={conv().videoEvents ?? []}
|
|
170
|
+
/>
|
|
171
|
+
</Show>
|
|
172
|
+
</div>
|
|
173
|
+
</Show>
|
|
174
|
+
|
|
175
|
+
{/* Activity tabs shown below iterations when relevant */}
|
|
176
|
+
<Show when={hasIterations() && hasActivityTabs()}>
|
|
177
|
+
<ConversationTabs
|
|
178
|
+
conversation={conv()}
|
|
179
|
+
activeTab={activeTab()}
|
|
180
|
+
onTabChange={setActiveTab}
|
|
181
|
+
/>
|
|
182
|
+
<div class={styles().conversationDetails.contentArea}>
|
|
183
|
+
<Show when={activeTab() === 'summaries'}>
|
|
184
|
+
<SummariesTab summaries={conv().summaries ?? []} />
|
|
185
|
+
</Show>
|
|
186
|
+
<Show when={activeTab() === 'image'}>
|
|
187
|
+
<ActivityEventsTab
|
|
188
|
+
title="Image Activity"
|
|
189
|
+
events={conv().imageEvents ?? []}
|
|
190
|
+
/>
|
|
191
|
+
</Show>
|
|
192
|
+
<Show when={activeTab() === 'speech'}>
|
|
193
|
+
<ActivityEventsTab
|
|
194
|
+
title="Speech Activity"
|
|
195
|
+
events={conv().speechEvents ?? []}
|
|
196
|
+
/>
|
|
197
|
+
</Show>
|
|
198
|
+
<Show when={activeTab() === 'transcription'}>
|
|
199
|
+
<ActivityEventsTab
|
|
200
|
+
title="Transcription Activity"
|
|
201
|
+
events={conv().transcriptionEvents ?? []}
|
|
202
|
+
/>
|
|
203
|
+
</Show>
|
|
204
|
+
<Show when={activeTab() === 'video'}>
|
|
205
|
+
<ActivityEventsTab
|
|
206
|
+
title="Video Activity"
|
|
207
|
+
events={conv().videoEvents ?? []}
|
|
208
|
+
/>
|
|
209
|
+
</Show>
|
|
210
|
+
</div>
|
|
211
|
+
</Show>
|
|
145
212
|
</div>
|
|
146
213
|
)}
|
|
147
214
|
</Show>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { JsonTree } from '@tanstack/devtools-ui'
|
|
1
|
+
import { Show } from 'solid-js'
|
|
3
2
|
import { useStyles } from '../../styles/use-styles'
|
|
4
3
|
import { formatDuration } from '../utils'
|
|
5
4
|
import type { Component } from 'solid-js'
|
|
@@ -15,11 +14,41 @@ export const ConversationHeader: Component<ConversationHeaderProps> = (
|
|
|
15
14
|
const styles = useStyles()
|
|
16
15
|
const conv = () => props.conversation
|
|
17
16
|
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
const iterationCount = () => conv().iterationCount ?? conv().iterations.length
|
|
18
|
+
const totalDuration = () => {
|
|
19
|
+
if (!conv().completedAt) return undefined
|
|
20
|
+
return conv().completedAt! - conv().startedAt
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const totalMessages = () => conv().messages.length
|
|
24
|
+
|
|
25
|
+
const totalToolCalls = () => {
|
|
26
|
+
let count = 0
|
|
27
|
+
for (const iter of conv().iterations) {
|
|
28
|
+
if (iter.finishReason === 'tool_calls') count++
|
|
29
|
+
}
|
|
30
|
+
return count
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Sum usage across all iterations
|
|
34
|
+
const totalUsage = () => {
|
|
35
|
+
if (conv().usage) return conv().usage
|
|
36
|
+
if (conv().iterations.length === 0) return undefined
|
|
37
|
+
let promptTokens = 0
|
|
38
|
+
let completionTokens = 0
|
|
39
|
+
for (const iter of conv().iterations) {
|
|
40
|
+
if (iter.usage) {
|
|
41
|
+
promptTokens += iter.usage.promptTokens
|
|
42
|
+
completionTokens += iter.usage.completionTokens
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (promptTokens === 0 && completionTokens === 0) return undefined
|
|
46
|
+
return {
|
|
47
|
+
promptTokens,
|
|
48
|
+
completionTokens,
|
|
49
|
+
totalTokens: promptTokens + completionTokens,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
23
52
|
|
|
24
53
|
return (
|
|
25
54
|
<div class={styles().panelHeader}>
|
|
@@ -39,107 +68,37 @@ export const ConversationHeader: Component<ConversationHeaderProps> = (
|
|
|
39
68
|
>
|
|
40
69
|
{conv().status}
|
|
41
70
|
</div>
|
|
42
|
-
<Show when={iterationCount() !== undefined && iterationCount()! > 1}>
|
|
43
|
-
<div class={styles().conversationDetails.iterationBadge}>
|
|
44
|
-
🔄 {iterationCount()} iterations
|
|
45
|
-
</div>
|
|
46
|
-
</Show>
|
|
47
71
|
</div>
|
|
48
72
|
<div class={styles().conversationDetails.metaInfo}>
|
|
49
|
-
{
|
|
50
|
-
{
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
{totalDuration() !== undefined && formatDuration(totalDuration())}
|
|
74
|
+
<Show when={iterationCount() > 0}>
|
|
75
|
+
{totalDuration() !== undefined && ' · '}
|
|
76
|
+
{iterationCount()}{' '}
|
|
77
|
+
{iterationCount() === 1 ? 'iteration' : 'iterations'}
|
|
78
|
+
</Show>
|
|
79
|
+
<Show when={totalMessages() > 0}>
|
|
80
|
+
{(totalDuration() !== undefined || iterationCount() > 0) && ' · '}
|
|
81
|
+
{totalMessages()} {totalMessages() === 1 ? 'message' : 'messages'}
|
|
82
|
+
</Show>
|
|
83
|
+
<Show when={totalToolCalls() > 0}>
|
|
84
|
+
{' · '}
|
|
85
|
+
{totalToolCalls()} tool {totalToolCalls() === 1 ? 'call' : 'calls'}
|
|
86
|
+
</Show>
|
|
53
87
|
</div>
|
|
54
|
-
{
|
|
55
|
-
<Show when={toolNames().length > 0}>
|
|
56
|
-
<div class={styles().conversationDetails.toolsRow}>
|
|
57
|
-
<span class={styles().conversationDetails.toolsLabel}>🔧</span>
|
|
58
|
-
<div class={styles().conversationDetails.toolsList}>
|
|
59
|
-
<For each={toolNames()}>
|
|
60
|
-
{(toolName) => (
|
|
61
|
-
<span class={styles().conversationDetails.toolBadge}>
|
|
62
|
-
{toolName}
|
|
63
|
-
</span>
|
|
64
|
-
)}
|
|
65
|
-
</For>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
</Show>
|
|
69
|
-
{/* Options - always visible in compact form */}
|
|
70
|
-
<Show when={options() && Object.keys(options()!).length > 0}>
|
|
71
|
-
<div class={styles().conversationDetails.optionsRow}>
|
|
72
|
-
<span class={styles().conversationDetails.optionsLabel}>
|
|
73
|
-
⚙️ Options:
|
|
74
|
-
</span>
|
|
75
|
-
<div class={styles().conversationDetails.optionsCompact}>
|
|
76
|
-
<For each={Object.entries(options()!)}>
|
|
77
|
-
{([key, value]) => (
|
|
78
|
-
<span class={styles().conversationDetails.optionBadge}>
|
|
79
|
-
{key}:{' '}
|
|
80
|
-
{typeof value === 'object'
|
|
81
|
-
? JSON.stringify(value)
|
|
82
|
-
: String(value)}
|
|
83
|
-
</span>
|
|
84
|
-
)}
|
|
85
|
-
</For>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
</Show>
|
|
89
|
-
<Show when={conv().usage}>
|
|
88
|
+
<Show when={totalUsage()}>
|
|
90
89
|
<div class={styles().conversationDetails.usageInfo}>
|
|
91
|
-
<span class={styles().conversationDetails.usageLabel}>
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
<span class={styles().conversationDetails.usageLabel}>Tokens:</span>
|
|
91
|
+
<span>{totalUsage()?.promptTokens.toLocaleString() || 0} in</span>
|
|
92
|
+
<span>·</span>
|
|
94
93
|
<span>
|
|
95
|
-
|
|
94
|
+
{totalUsage()?.completionTokens.toLocaleString() || 0} out
|
|
96
95
|
</span>
|
|
97
|
-
<span
|
|
98
|
-
<span>
|
|
99
|
-
Completion: {conv().usage?.completionTokens.toLocaleString() || 0}
|
|
100
|
-
</span>
|
|
101
|
-
<span>•</span>
|
|
96
|
+
<span>·</span>
|
|
102
97
|
<span class={styles().conversationDetails.usageBold}>
|
|
103
|
-
|
|
98
|
+
{totalUsage()?.totalTokens.toLocaleString() || 0} total
|
|
104
99
|
</span>
|
|
105
100
|
</div>
|
|
106
101
|
</Show>
|
|
107
|
-
{/* Model options - collapsible */}
|
|
108
|
-
<Show when={modelOptions() && Object.keys(modelOptions()!).length > 0}>
|
|
109
|
-
<details class={styles().conversationDetails.collapsibleSection}>
|
|
110
|
-
<summary class={styles().conversationDetails.collapsibleSummary}>
|
|
111
|
-
🧪 Model options
|
|
112
|
-
</summary>
|
|
113
|
-
<div class={styles().conversationDetails.collapsibleContent}>
|
|
114
|
-
<JsonTree
|
|
115
|
-
value={modelOptions() as Record<string, unknown>}
|
|
116
|
-
defaultExpansionDepth={2}
|
|
117
|
-
/>
|
|
118
|
-
</div>
|
|
119
|
-
</details>
|
|
120
|
-
</Show>
|
|
121
|
-
{/* System prompts - collapsible */}
|
|
122
|
-
<Show when={systemPrompts().length > 0}>
|
|
123
|
-
<details class={styles().conversationDetails.collapsibleSection}>
|
|
124
|
-
<summary class={styles().conversationDetails.collapsibleSummary}>
|
|
125
|
-
🧩 System prompts ({systemPrompts().length})
|
|
126
|
-
</summary>
|
|
127
|
-
<div class={styles().conversationDetails.collapsibleContent}>
|
|
128
|
-
<For each={systemPrompts()}>
|
|
129
|
-
{(prompt, index) => (
|
|
130
|
-
<div class={styles().conversationDetails.systemPromptItem}>
|
|
131
|
-
<div class={styles().conversationDetails.systemPromptIndex}>
|
|
132
|
-
#{index() + 1}
|
|
133
|
-
</div>
|
|
134
|
-
<div class={styles().conversationDetails.systemPromptText}>
|
|
135
|
-
{prompt}
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
)}
|
|
139
|
-
</For>
|
|
140
|
-
</div>
|
|
141
|
-
</details>
|
|
142
|
-
</Show>
|
|
143
102
|
</div>
|
|
144
103
|
</div>
|
|
145
104
|
)
|
|
@@ -21,6 +21,7 @@ interface ConversationTabsProps {
|
|
|
21
21
|
export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
22
22
|
const styles = useStyles()
|
|
23
23
|
const conv = () => props.conversation
|
|
24
|
+
const hasIterations = () => conv().iterations.length > 0
|
|
24
25
|
|
|
25
26
|
// Total raw chunks = sum of all chunkCounts
|
|
26
27
|
const totalRawChunks = () =>
|
|
@@ -79,11 +80,11 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
79
80
|
previousVideoCount = count
|
|
80
81
|
})
|
|
81
82
|
|
|
82
|
-
//
|
|
83
|
-
// For server conversations, don't show messages tab - only chunks
|
|
83
|
+
// When iterations exist, only show activity tabs (no messages/chunks)
|
|
84
84
|
const hasMessages = () =>
|
|
85
|
-
conv().type === 'client' && conv().messages.length > 0
|
|
86
|
-
const hasChunks = () =>
|
|
85
|
+
!hasIterations() && conv().type === 'client' && conv().messages.length > 0
|
|
86
|
+
const hasChunks = () =>
|
|
87
|
+
!hasIterations() && (conv().chunks.length > 0 || conv().type === 'server')
|
|
87
88
|
const hasSummaries = () => conv().hasSummarize || summariesCount() > 0
|
|
88
89
|
const hasImage = () => conv().hasImage || imageCount() > 0
|
|
89
90
|
const hasSpeech = () => conv().hasSpeech || speechCount() > 0
|
|
@@ -111,7 +112,6 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
111
112
|
|
|
112
113
|
return (
|
|
113
114
|
<div class={styles().conversationDetails.tabsContainer}>
|
|
114
|
-
{/* Show messages tab for client conversations or when there are messages */}
|
|
115
115
|
<Show when={hasMessages()}>
|
|
116
116
|
<button
|
|
117
117
|
class={`${styles().actionButton} ${
|
|
@@ -121,10 +121,9 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
121
121
|
}`}
|
|
122
122
|
onClick={() => props.onTabChange('messages')}
|
|
123
123
|
>
|
|
124
|
-
|
|
124
|
+
Messages ({conv().messages.length})
|
|
125
125
|
</button>
|
|
126
126
|
</Show>
|
|
127
|
-
{/* Show chunks tab for server conversations or when there are chunks */}
|
|
128
127
|
<Show when={hasChunks() && conv().type === 'server'}>
|
|
129
128
|
<button
|
|
130
129
|
class={`${styles().actionButton} ${
|
|
@@ -134,10 +133,9 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
134
133
|
}`}
|
|
135
134
|
onClick={() => props.onTabChange('chunks')}
|
|
136
135
|
>
|
|
137
|
-
|
|
136
|
+
Chunks ({totalRawChunks()})
|
|
138
137
|
</button>
|
|
139
138
|
</Show>
|
|
140
|
-
{/* Show summaries tab if there are summarize operations */}
|
|
141
139
|
<Show when={hasSummaries()}>
|
|
142
140
|
<button
|
|
143
141
|
class={`${styles().actionButton} ${
|
|
@@ -147,7 +145,7 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
147
145
|
}`}
|
|
148
146
|
onClick={() => props.onTabChange('summaries')}
|
|
149
147
|
>
|
|
150
|
-
|
|
148
|
+
Summaries ({summariesCount()})
|
|
151
149
|
</button>
|
|
152
150
|
</Show>
|
|
153
151
|
<Show when={hasImage()}>
|
|
@@ -159,7 +157,7 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
159
157
|
} ${imagePulse() ? styles().conversationDetails.tabButtonPulse : ''}`}
|
|
160
158
|
onClick={() => props.onTabChange('image')}
|
|
161
159
|
>
|
|
162
|
-
|
|
160
|
+
Image ({imageCount()})
|
|
163
161
|
</button>
|
|
164
162
|
</Show>
|
|
165
163
|
<Show when={hasSpeech()}>
|
|
@@ -171,7 +169,7 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
171
169
|
} ${speechPulse() ? styles().conversationDetails.tabButtonPulse : ''}`}
|
|
172
170
|
onClick={() => props.onTabChange('speech')}
|
|
173
171
|
>
|
|
174
|
-
|
|
172
|
+
Speech ({speechCount()})
|
|
175
173
|
</button>
|
|
176
174
|
</Show>
|
|
177
175
|
<Show when={hasTranscription()}>
|
|
@@ -187,7 +185,7 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
187
185
|
}`}
|
|
188
186
|
onClick={() => props.onTabChange('transcription')}
|
|
189
187
|
>
|
|
190
|
-
|
|
188
|
+
Transcription ({transcriptionCount()})
|
|
191
189
|
</button>
|
|
192
190
|
</Show>
|
|
193
191
|
<Show when={hasVideo()}>
|
|
@@ -199,7 +197,7 @@ export const ConversationTabs: Component<ConversationTabsProps> = (props) => {
|
|
|
199
197
|
} ${videoPulse() ? styles().conversationDetails.tabButtonPulse : ''}`}
|
|
200
198
|
onClick={() => props.onTabChange('video')}
|
|
201
199
|
>
|
|
202
|
-
|
|
200
|
+
Video ({videoCount()})
|
|
203
201
|
</button>
|
|
204
202
|
</Show>
|
|
205
203
|
</div>
|