@tanstack/ai-devtools-core 0.5.10 → 0.5.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 +11 -15
- package/dist/Shell/{2X67PB7B.js → DCUFLPG6.js} +382 -77
- package/dist/{production/Shell/BJP2PZMD.js → Shell/JT2B6WKX.js} +591 -161
- package/dist/Shell/{YJTEAPIF.js → YBXRSRRK.js} +591 -161
- package/dist/dev.js +1 -1
- package/dist/index.js +1 -1
- package/dist/production/Shell/{2X67PB7B.js → DCUFLPG6.js} +382 -77
- package/dist/{Shell/BJP2PZMD.js → production/Shell/JT2B6WKX.js} +591 -161
- package/dist/production/Shell/{YJTEAPIF.js → YBXRSRRK.js} +591 -161
- 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 -3
- package/src/components/conversation/IterationCard.tsx +11 -5
- package/src/components/hooks/CompactionPanel.tsx +223 -0
- package/src/components/hooks/HookDashboard.tsx +3 -2
- package/src/components/hooks/HookDetails.tsx +22 -3
- package/src/components/hooks/index.ts +1 -0
- package/src/store/ai-context.tsx +113 -7
- package/src/store/compaction-registry.ts +121 -0
- package/src/styles/use-styles.ts +1 -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/JT2B6WKX.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/YBXRSRRK.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/DCUFLPG6.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.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"description": "Core TanStack AI Devtools plugin for inspecting chat messages, tool calls, streams, and errors.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
"@tanstack/devtools-utils": "^0.7.0",
|
|
85
85
|
"goober": "^2.1.18",
|
|
86
86
|
"solid-js": "^1.9.10",
|
|
87
|
-
"@tanstack/ai": "^0.
|
|
88
|
-
"@tanstack/ai-event-client": "^0.11.
|
|
87
|
+
"@tanstack/ai": "^0.53.0",
|
|
88
|
+
"@tanstack/ai-event-client": "^0.11.2"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
91
|
"@vitest/coverage-v8": "4.1.10",
|
|
@@ -169,6 +169,9 @@ const MiddlewareStep: Component<{
|
|
|
169
169
|
if (ev().wasDropped) return 'DROP'
|
|
170
170
|
if (ev().hookName === 'onChunk' && ev().hasTransform) return 'TRANSFORM'
|
|
171
171
|
if (ev().hookName === 'onConfig' && ev().hasTransform) return 'TRANSFORM'
|
|
172
|
+
if (ev().hookName === 'onCompactStart') return 'START'
|
|
173
|
+
if (ev().hookName === 'onCompact') return 'COMPACT'
|
|
174
|
+
if (ev().hookName === 'onCompactEnd') return 'END'
|
|
172
175
|
if (ev().hookName === 'onBeforeToolCall' && ev().hasTransform)
|
|
173
176
|
return 'DECISION'
|
|
174
177
|
return null
|
|
@@ -181,7 +184,13 @@ const MiddlewareStep: Component<{
|
|
|
181
184
|
|
|
182
185
|
return (
|
|
183
186
|
<>
|
|
184
|
-
<div
|
|
187
|
+
<div
|
|
188
|
+
class={s().step}
|
|
189
|
+
style={hasChanges() ? { cursor: 'pointer' } : undefined}
|
|
190
|
+
onClick={() => {
|
|
191
|
+
if (hasChanges()) setExpanded(!expanded())
|
|
192
|
+
}}
|
|
193
|
+
>
|
|
185
194
|
<span class={`${s().stepPrefix} ${s().stepPrefixMiddleware}`}>
|
|
186
195
|
Middleware
|
|
187
196
|
</span>
|
|
@@ -196,10 +205,7 @@ const MiddlewareStep: Component<{
|
|
|
196
205
|
<span class={s().mwSuffix}>{suffix()}</span>
|
|
197
206
|
</Show>
|
|
198
207
|
<Show when={hasChanges()}>
|
|
199
|
-
<span
|
|
200
|
-
class={s().stepExpandToggle}
|
|
201
|
-
onClick={() => setExpanded(!expanded())}
|
|
202
|
-
>
|
|
208
|
+
<span class={s().stepExpandToggle}>
|
|
203
209
|
{expanded() ? 'hide changes' : 'show changes'}
|
|
204
210
|
</span>
|
|
205
211
|
</Show>
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { For, Show, createMemo, createSignal } from 'solid-js'
|
|
2
|
+
import { JsonTree } from '@tanstack/devtools-ui'
|
|
3
|
+
import { useAIStore } from '../../store/ai-context'
|
|
4
|
+
import { useStyles } from '../../styles/use-styles'
|
|
5
|
+
import { compactionEventsForHook } from '../../store/compaction-registry'
|
|
6
|
+
import type { Component } from 'solid-js'
|
|
7
|
+
import type {
|
|
8
|
+
CompactionEventRecord,
|
|
9
|
+
CompactionMessagePreview,
|
|
10
|
+
} from '../../store/compaction-registry'
|
|
11
|
+
import type { HookRecord } from '../../store/hook-registry'
|
|
12
|
+
|
|
13
|
+
function formatTime(value: number): string {
|
|
14
|
+
const date = new Date(value)
|
|
15
|
+
if (Number.isNaN(date.getTime())) return ''
|
|
16
|
+
return date.toLocaleTimeString()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function shortStrategy(event: CompactionEventRecord): string {
|
|
20
|
+
const key = event.strategyKey
|
|
21
|
+
if (!key) return event.reusedCheckpoint ? 'checkpoint' : 'compaction'
|
|
22
|
+
const cut = key.indexOf(':maxTokens=')
|
|
23
|
+
return cut === -1 ? key : key.slice(0, cut)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const PreviewMessage: Component<{
|
|
27
|
+
preview: CompactionMessagePreview
|
|
28
|
+
}> = (props) => {
|
|
29
|
+
const styles = useStyles()
|
|
30
|
+
return (
|
|
31
|
+
<div class={styles().hookDetails.message}>
|
|
32
|
+
<div class={styles().hookDetails.messageRole}>
|
|
33
|
+
{props.preview.role}
|
|
34
|
+
<Show when={props.preview.tokens > 0}>
|
|
35
|
+
{` · ${props.preview.tokens} tok`}
|
|
36
|
+
</Show>
|
|
37
|
+
</div>
|
|
38
|
+
<div class={styles().hookDetails.messageContent}>
|
|
39
|
+
{props.preview.text}
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const PreviewColumn: Component<{
|
|
46
|
+
title: string
|
|
47
|
+
previews: Array<CompactionMessagePreview>
|
|
48
|
+
empty: string
|
|
49
|
+
}> = (props) => {
|
|
50
|
+
const styles = useStyles()
|
|
51
|
+
const s = () => styles().iterationTimeline
|
|
52
|
+
return (
|
|
53
|
+
<div class={s().stepJsonItem}>
|
|
54
|
+
<div class={s().stepJsonItemLabel}>
|
|
55
|
+
{props.title} ({props.previews.length})
|
|
56
|
+
</div>
|
|
57
|
+
<Show
|
|
58
|
+
when={props.previews.length > 0}
|
|
59
|
+
fallback={
|
|
60
|
+
<div class={styles().hookDetails.emptySmall}>{props.empty}</div>
|
|
61
|
+
}
|
|
62
|
+
>
|
|
63
|
+
<div class={styles().memoryPanel.list}>
|
|
64
|
+
<For each={props.previews}>
|
|
65
|
+
{(preview) => <PreviewMessage preview={preview} />}
|
|
66
|
+
</For>
|
|
67
|
+
</div>
|
|
68
|
+
</Show>
|
|
69
|
+
</div>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function kindLabel(kind: CompactionEventRecord['kind']): string {
|
|
74
|
+
if (kind === 'started') return 'Started'
|
|
75
|
+
if (kind === 'ended') return 'Ended'
|
|
76
|
+
return 'State'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const CompactEvent: Component<{
|
|
80
|
+
event: CompactionEventRecord
|
|
81
|
+
defaultOpen?: boolean
|
|
82
|
+
}> = (props) => {
|
|
83
|
+
const styles = useStyles()
|
|
84
|
+
const s = () => styles().iterationTimeline
|
|
85
|
+
const [expanded, setExpanded] = createSignal(props.defaultOpen === true)
|
|
86
|
+
const event = () => props.event
|
|
87
|
+
const isState = () => event().kind === 'state'
|
|
88
|
+
|
|
89
|
+
const stats = () => ({
|
|
90
|
+
kind: event().kind,
|
|
91
|
+
before: event().before,
|
|
92
|
+
after: event().after,
|
|
93
|
+
messagesBefore: event().messagesBefore,
|
|
94
|
+
messagesAfter: event().messagesAfter,
|
|
95
|
+
maxTokens: event().maxTokens,
|
|
96
|
+
strategyKey: event().strategyKey,
|
|
97
|
+
reusedCheckpoint: event().reusedCheckpoint,
|
|
98
|
+
durationMs: event().durationMs,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const countLabel = () => {
|
|
102
|
+
if (event().kind === 'started') {
|
|
103
|
+
return `${event().messagesBefore ?? 0} msgs · ${event().before ?? 0} tok`
|
|
104
|
+
}
|
|
105
|
+
if (event().kind === 'ended') {
|
|
106
|
+
return `${event().messagesAfter ?? 0} msgs · ${event().after ?? 0} tok`
|
|
107
|
+
}
|
|
108
|
+
return `${event().messagesBefore ?? 0} → ${event().messagesAfter ?? 0} msgs`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<>
|
|
113
|
+
<div
|
|
114
|
+
class={s().step}
|
|
115
|
+
data-testid="ai-devtools-compaction-event"
|
|
116
|
+
data-kind={event().kind}
|
|
117
|
+
style={{ cursor: 'pointer' }}
|
|
118
|
+
onClick={() => setExpanded(!expanded())}
|
|
119
|
+
>
|
|
120
|
+
<span class={`${s().stepPrefix} ${s().stepPrefixMiddleware}`}>
|
|
121
|
+
{kindLabel(event().kind)}
|
|
122
|
+
</span>
|
|
123
|
+
<span class={`${s().mwBadge} ${s().mwBadgeTransform}`}>
|
|
124
|
+
{shortStrategy(event())}
|
|
125
|
+
</span>
|
|
126
|
+
<span class={s().mwHook}>{countLabel()}</span>
|
|
127
|
+
<Show when={event().kind === 'state'}>
|
|
128
|
+
<span class={s().stepDuration}>
|
|
129
|
+
{event().before} → {event().after} tok
|
|
130
|
+
</span>
|
|
131
|
+
</Show>
|
|
132
|
+
<Show when={event().durationMs !== undefined}>
|
|
133
|
+
<span class={s().stepDuration}>{event().durationMs}ms</span>
|
|
134
|
+
</Show>
|
|
135
|
+
<Show when={event().reusedCheckpoint}>
|
|
136
|
+
<span class={s().mwSuffix}>checkpoint</span>
|
|
137
|
+
</Show>
|
|
138
|
+
<span class={s().stepDuration}>{formatTime(event().timestamp)}</span>
|
|
139
|
+
<span class={`${s().chevron} ${expanded() ? s().chevronOpen : ''}`}>
|
|
140
|
+
{'\u25B6'}
|
|
141
|
+
</span>
|
|
142
|
+
</div>
|
|
143
|
+
<Show when={expanded()}>
|
|
144
|
+
<div class={s().mwChangesContainer}>
|
|
145
|
+
<JsonTree value={stats()} defaultExpansionDepth={1} copyable />
|
|
146
|
+
</div>
|
|
147
|
+
<Show when={isState()}>
|
|
148
|
+
<div class={s().stepJsonItemsCompare}>
|
|
149
|
+
<PreviewColumn
|
|
150
|
+
title="Dropped"
|
|
151
|
+
previews={event().dropped ?? []}
|
|
152
|
+
empty="Nothing was dropped."
|
|
153
|
+
/>
|
|
154
|
+
<PreviewColumn
|
|
155
|
+
title="Sent to model"
|
|
156
|
+
previews={event().result ?? []}
|
|
157
|
+
empty="No compacted transcript on this event."
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
</Show>
|
|
161
|
+
</Show>
|
|
162
|
+
</>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const CompactionPanel: Component<{ hook: HookRecord }> = (props) => {
|
|
167
|
+
const { state, clearCompaction } = useAIStore()
|
|
168
|
+
const styles = useStyles()
|
|
169
|
+
const s = () => styles().iterationTimeline
|
|
170
|
+
|
|
171
|
+
const events = createMemo(() =>
|
|
172
|
+
compactionEventsForHook(state.compaction, props.hook),
|
|
173
|
+
)
|
|
174
|
+
const lastStateIndex = createMemo(() => {
|
|
175
|
+
const list = events()
|
|
176
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
177
|
+
if (list[i]?.kind === 'state') return i
|
|
178
|
+
}
|
|
179
|
+
return -1
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<div
|
|
184
|
+
class={styles().memoryPanel.container}
|
|
185
|
+
data-testid="ai-devtools-compaction-panel"
|
|
186
|
+
>
|
|
187
|
+
<Show
|
|
188
|
+
when={events().length > 0}
|
|
189
|
+
fallback={
|
|
190
|
+
<div class={styles().hookDetails.emptySmall}>
|
|
191
|
+
No compaction yet. When the transcript passes maxTokens, each
|
|
192
|
+
compact appears here.
|
|
193
|
+
</div>
|
|
194
|
+
}
|
|
195
|
+
>
|
|
196
|
+
<div class={styles().memoryPanel.toolbar}>
|
|
197
|
+
<span class={styles().memoryPanel.badge}>
|
|
198
|
+
{events().length} event{events().length === 1 ? '' : 's'}
|
|
199
|
+
</span>
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
class={styles().memoryPanel.clearButton}
|
|
203
|
+
onClick={() => clearCompaction()}
|
|
204
|
+
>
|
|
205
|
+
Clear
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
<div class={s().iterCard}>
|
|
209
|
+
<For each={events()}>
|
|
210
|
+
{(event, index) => (
|
|
211
|
+
<CompactEvent
|
|
212
|
+
event={event}
|
|
213
|
+
defaultOpen={
|
|
214
|
+
event.kind === 'state' && index() === lastStateIndex()
|
|
215
|
+
}
|
|
216
|
+
/>
|
|
217
|
+
)}
|
|
218
|
+
</For>
|
|
219
|
+
</div>
|
|
220
|
+
</Show>
|
|
221
|
+
</div>
|
|
222
|
+
)
|
|
223
|
+
}
|
|
@@ -55,8 +55,9 @@ export const HookDashboard: Component = () => {
|
|
|
55
55
|
|
|
56
56
|
const handleSelect = (hook: HookRecord) => {
|
|
57
57
|
selectHook(hook.id)
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const conversationId = hook.id || hook.clientId || hook.threadId
|
|
59
|
+
if (conversationId && state.conversations[conversationId]) {
|
|
60
|
+
selectConversation(conversationId)
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
} from './preview-messages'
|
|
40
40
|
import { GenerationPanel, GenerationPreview } from './GenerationPanel'
|
|
41
41
|
import { MemoryPanel } from './MemoryPanel'
|
|
42
|
+
import { CompactionPanel } from './CompactionPanel'
|
|
42
43
|
import { SkillsPanel } from './SkillsPanel'
|
|
43
44
|
import type { HoverOrigin, HoverTarget, PreviewJsonItem } from './preview-model'
|
|
44
45
|
import type {
|
|
@@ -50,7 +51,13 @@ import type {
|
|
|
50
51
|
import type { Conversation, Message, ToolCall } from '../../store/ai-store'
|
|
51
52
|
import type { Component, Setter } from 'solid-js'
|
|
52
53
|
|
|
53
|
-
type DetailTab =
|
|
54
|
+
type DetailTab =
|
|
55
|
+
| 'conversation'
|
|
56
|
+
| 'tools'
|
|
57
|
+
| 'state'
|
|
58
|
+
| 'memory'
|
|
59
|
+
| 'compaction'
|
|
60
|
+
| 'skills'
|
|
54
61
|
type MessagePart = NonNullable<Message['parts']>[number]
|
|
55
62
|
const scrollAnimations = new WeakMap<HTMLElement, number>()
|
|
56
63
|
|
|
@@ -157,6 +164,7 @@ export const HookDetails: Component = () => {
|
|
|
157
164
|
isGenerationHook() &&
|
|
158
165
|
(activeTab() === 'tools' ||
|
|
159
166
|
activeTab() === 'memory' ||
|
|
167
|
+
activeTab() === 'compaction' ||
|
|
160
168
|
activeTab() === 'skills')
|
|
161
169
|
) {
|
|
162
170
|
setActiveTab('conversation')
|
|
@@ -214,8 +222,10 @@ export const HookDetails: Component = () => {
|
|
|
214
222
|
<HookOverview
|
|
215
223
|
onSelectHook={(selectedHook) => {
|
|
216
224
|
selectHook(selectedHook.id)
|
|
217
|
-
|
|
218
|
-
|
|
225
|
+
const conversationId =
|
|
226
|
+
selectedHook.id || selectedHook.clientId || selectedHook.threadId
|
|
227
|
+
if (conversationId && state.conversations[conversationId]) {
|
|
228
|
+
selectConversation(conversationId)
|
|
219
229
|
}
|
|
220
230
|
}}
|
|
221
231
|
/>
|
|
@@ -265,6 +275,12 @@ export const HookDetails: Component = () => {
|
|
|
265
275
|
activeTab={activeTab()}
|
|
266
276
|
onSelect={setActiveTab}
|
|
267
277
|
/>
|
|
278
|
+
<TabButton
|
|
279
|
+
label="Compaction"
|
|
280
|
+
tab="compaction"
|
|
281
|
+
activeTab={activeTab()}
|
|
282
|
+
onSelect={setActiveTab}
|
|
283
|
+
/>
|
|
268
284
|
<TabButton
|
|
269
285
|
label="Skills"
|
|
270
286
|
tab="skills"
|
|
@@ -318,6 +334,9 @@ export const HookDetails: Component = () => {
|
|
|
318
334
|
<Show when={activeTab() === 'memory'}>
|
|
319
335
|
<MemoryPanel />
|
|
320
336
|
</Show>
|
|
337
|
+
<Show when={activeTab() === 'compaction'}>
|
|
338
|
+
<CompactionPanel hook={activeHook()} />
|
|
339
|
+
</Show>
|
|
321
340
|
<Show when={activeTab() === 'skills'}>
|
|
322
341
|
<SkillsPanel />
|
|
323
342
|
</Show>
|
|
@@ -2,5 +2,6 @@ export { HookDashboard } from './HookDashboard'
|
|
|
2
2
|
export { HookDetails } from './HookDetails'
|
|
3
3
|
export { GenerationPanel, GenerationPreview } from './GenerationPanel'
|
|
4
4
|
export { MemoryPanel } from './MemoryPanel'
|
|
5
|
+
export { CompactionPanel } from './CompactionPanel'
|
|
5
6
|
export { SkillsPanel } from './SkillsPanel'
|
|
6
7
|
export { ToolFixtureForm } from './ToolFixtureForm'
|
package/src/store/ai-context.tsx
CHANGED
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
applyHookEvent,
|
|
7
7
|
clearHookRegistry,
|
|
8
8
|
createHookRegistryState,
|
|
9
|
+
markHookViewed,
|
|
9
10
|
removeSavedFixture,
|
|
10
11
|
replaceSavedFixtures,
|
|
11
|
-
setActiveHook,
|
|
12
12
|
} from './hook-registry'
|
|
13
13
|
import {
|
|
14
14
|
createClientToolCallMessage,
|
|
@@ -20,6 +20,11 @@ import {
|
|
|
20
20
|
clearMemoryRegistry,
|
|
21
21
|
createMemoryRegistryState,
|
|
22
22
|
} from './memory-registry'
|
|
23
|
+
import {
|
|
24
|
+
applyCompactionEvent,
|
|
25
|
+
clearCompactionRegistry,
|
|
26
|
+
createCompactionRegistryState,
|
|
27
|
+
} from './compaction-registry'
|
|
23
28
|
import {
|
|
24
29
|
applySkillsSnapshot,
|
|
25
30
|
clearSkillsRegistry,
|
|
@@ -33,6 +38,7 @@ import type {
|
|
|
33
38
|
} from '@tanstack/ai-event-client'
|
|
34
39
|
import type { HookRegistryState, ToolFixtureRecord } from './hook-registry'
|
|
35
40
|
import type { MemoryRegistryState } from './memory-registry'
|
|
41
|
+
import type { CompactionRegistryState } from './compaction-registry'
|
|
36
42
|
import type { SkillsRegistryState } from './skills-registry'
|
|
37
43
|
import type { ParentComponent } from 'solid-js'
|
|
38
44
|
|
|
@@ -242,6 +248,7 @@ interface AIStoreState {
|
|
|
242
248
|
activeConversationId: string | null
|
|
243
249
|
hooks: HookRegistryState
|
|
244
250
|
memory: MemoryRegistryState
|
|
251
|
+
compaction: CompactionRegistryState
|
|
245
252
|
skills: SkillsRegistryState
|
|
246
253
|
}
|
|
247
254
|
|
|
@@ -251,6 +258,7 @@ interface AIContextValue {
|
|
|
251
258
|
selectConversation: (id: string) => void
|
|
252
259
|
clearHooks: () => void
|
|
253
260
|
clearMemory: () => void
|
|
261
|
+
clearCompaction: () => void
|
|
254
262
|
clearSkills: () => void
|
|
255
263
|
selectHook: (id: string | null) => void
|
|
256
264
|
saveToolFixture: (fixture: ToolFixtureRecord) => void
|
|
@@ -274,6 +282,7 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
274
282
|
activeConversationId: null,
|
|
275
283
|
hooks: createHookRegistryState(),
|
|
276
284
|
memory: createMemoryRegistryState(),
|
|
285
|
+
compaction: createCompactionRegistryState(),
|
|
277
286
|
skills: createSkillsRegistryState(),
|
|
278
287
|
})
|
|
279
288
|
|
|
@@ -670,6 +679,15 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
670
679
|
)
|
|
671
680
|
}
|
|
672
681
|
|
|
682
|
+
function clearCompaction() {
|
|
683
|
+
setState(
|
|
684
|
+
'compaction',
|
|
685
|
+
produce((compaction: CompactionRegistryState) => {
|
|
686
|
+
clearCompactionRegistry(compaction)
|
|
687
|
+
}),
|
|
688
|
+
)
|
|
689
|
+
}
|
|
690
|
+
|
|
673
691
|
function clearSkills() {
|
|
674
692
|
setState(
|
|
675
693
|
'skills',
|
|
@@ -680,12 +698,15 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
680
698
|
}
|
|
681
699
|
|
|
682
700
|
function selectHook(id: string | null) {
|
|
683
|
-
setState(
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
701
|
+
setState('hooks', 'activeHookId', id)
|
|
702
|
+
if (id) {
|
|
703
|
+
setState(
|
|
704
|
+
'hooks',
|
|
705
|
+
produce((hooks: HookRegistryState) => {
|
|
706
|
+
markHookViewed(hooks, id)
|
|
707
|
+
}),
|
|
708
|
+
)
|
|
709
|
+
}
|
|
689
710
|
}
|
|
690
711
|
|
|
691
712
|
function saveToolFixture(fixture: ToolFixtureRecord) {
|
|
@@ -2984,6 +3005,90 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
2984
3005
|
}),
|
|
2985
3006
|
)
|
|
2986
3007
|
|
|
3008
|
+
const recordCompactionLifecycle = (
|
|
3009
|
+
kind: 'started' | 'state' | 'ended',
|
|
3010
|
+
hookName: 'onCompactStart' | 'onCompact' | 'onCompactEnd',
|
|
3011
|
+
payload: {
|
|
3012
|
+
timestamp: number
|
|
3013
|
+
requestId?: string
|
|
3014
|
+
streamId?: string
|
|
3015
|
+
clientId?: string
|
|
3016
|
+
hookId?: string
|
|
3017
|
+
threadId?: string
|
|
3018
|
+
runId?: string
|
|
3019
|
+
eventId?: string
|
|
3020
|
+
before?: number
|
|
3021
|
+
after?: number
|
|
3022
|
+
messagesBefore?: number
|
|
3023
|
+
messagesAfter?: number
|
|
3024
|
+
reusedCheckpoint?: boolean
|
|
3025
|
+
maxTokens?: number
|
|
3026
|
+
strategyKey?: string
|
|
3027
|
+
durationMs?: number
|
|
3028
|
+
dropped?: CompactionRegistryState['events'][number]['dropped']
|
|
3029
|
+
result?: CompactionRegistryState['events'][number]['result']
|
|
3030
|
+
},
|
|
3031
|
+
) => {
|
|
3032
|
+
setState(
|
|
3033
|
+
'compaction',
|
|
3034
|
+
produce((compaction: CompactionRegistryState) => {
|
|
3035
|
+
applyCompactionEvent(compaction, kind, payload)
|
|
3036
|
+
}),
|
|
3037
|
+
)
|
|
3038
|
+
|
|
3039
|
+
const { requestId, streamId, clientId } = payload
|
|
3040
|
+
const conversationId =
|
|
3041
|
+
(clientId && state.conversations[clientId] ? clientId : undefined) ||
|
|
3042
|
+
(streamId ? streamToConversation.get(streamId) : undefined) ||
|
|
3043
|
+
(requestId ? requestToConversation.get(requestId) : undefined)
|
|
3044
|
+
if (!conversationId || !state.conversations[conversationId]) return
|
|
3045
|
+
|
|
3046
|
+
const conv = state.conversations[conversationId]
|
|
3047
|
+
const iterIndex = findLatestIterationIndex(conv, requestId)
|
|
3048
|
+
if (iterIndex < 0) return
|
|
3049
|
+
|
|
3050
|
+
const mwEvent: MiddlewareEvent = {
|
|
3051
|
+
id: `mw-cmp-${kind}-${Date.now()}-${Math.random()}`,
|
|
3052
|
+
middlewareName: 'compaction',
|
|
3053
|
+
hookName,
|
|
3054
|
+
timestamp: payload.timestamp,
|
|
3055
|
+
hasTransform: kind === 'state',
|
|
3056
|
+
configChanges: {
|
|
3057
|
+
before: payload.before,
|
|
3058
|
+
after: payload.after,
|
|
3059
|
+
messagesBefore: payload.messagesBefore,
|
|
3060
|
+
messagesAfter: payload.messagesAfter,
|
|
3061
|
+
reusedCheckpoint: payload.reusedCheckpoint,
|
|
3062
|
+
maxTokens: payload.maxTokens,
|
|
3063
|
+
strategyKey: payload.strategyKey,
|
|
3064
|
+
durationMs: payload.durationMs,
|
|
3065
|
+
},
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
setState(
|
|
3069
|
+
'conversations',
|
|
3070
|
+
conversationId,
|
|
3071
|
+
'iterations',
|
|
3072
|
+
iterIndex,
|
|
3073
|
+
'middlewareEvents',
|
|
3074
|
+
produce((arr: Array<MiddlewareEvent>) => {
|
|
3075
|
+
arr.push(mwEvent)
|
|
3076
|
+
}),
|
|
3077
|
+
)
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
cleanupFns.push(
|
|
3081
|
+
aiEventClient.on('compaction:started', (e) => {
|
|
3082
|
+
recordCompactionLifecycle('started', 'onCompactStart', e.payload)
|
|
3083
|
+
}),
|
|
3084
|
+
aiEventClient.on('compaction:state', (e) => {
|
|
3085
|
+
recordCompactionLifecycle('state', 'onCompact', e.payload)
|
|
3086
|
+
}),
|
|
3087
|
+
aiEventClient.on('compaction:ended', (e) => {
|
|
3088
|
+
recordCompactionLifecycle('ended', 'onCompactEnd', e.payload)
|
|
3089
|
+
}),
|
|
3090
|
+
)
|
|
3091
|
+
|
|
2987
3092
|
cleanupFns.push(
|
|
2988
3093
|
aiEventClient.on('summarize:request:started', (e) => {
|
|
2989
3094
|
const { requestId, model, inputLength, timestamp, clientId } = e.payload
|
|
@@ -3491,6 +3596,7 @@ export const AIProvider: ParentComponent = (props) => {
|
|
|
3491
3596
|
selectConversation,
|
|
3492
3597
|
clearHooks,
|
|
3493
3598
|
clearMemory,
|
|
3599
|
+
clearCompaction,
|
|
3494
3600
|
clearSkills,
|
|
3495
3601
|
selectHook,
|
|
3496
3602
|
saveToolFixture,
|