@tanstack/ai-client 0.11.8 → 0.13.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/esm/chat-client.d.ts +17 -2
- package/dist/esm/chat-client.js +223 -37
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.d.ts +20 -2
- package/dist/esm/connection-adapters.js +154 -48
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/devtools-noop.d.ts +60 -0
- package/dist/esm/devtools-noop.js +135 -0
- package/dist/esm/devtools-noop.js.map +1 -0
- package/dist/esm/devtools.d.ts +276 -0
- package/dist/esm/devtools.js +1178 -0
- package/dist/esm/devtools.js.map +1 -0
- package/dist/esm/events.d.ts +50 -7
- package/dist/esm/events.js +65 -20
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/generation-client.d.ts +14 -0
- package/dist/esm/generation-client.js +135 -5
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-types.d.ts +15 -1
- package/dist/esm/generation-types.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.d.ts +78 -10
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/video-generation-client.d.ts +15 -0
- package/dist/esm/video-generation-client.js +147 -8
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +8 -4
- package/src/chat-client.ts +285 -39
- package/src/connection-adapters.ts +226 -61
- package/src/devtools-noop.ts +205 -0
- package/src/devtools.ts +1866 -0
- package/src/events.ts +142 -11
- package/src/generation-client.ts +176 -9
- package/src/generation-types.ts +23 -4
- package/src/index.ts +15 -1
- package/src/types.ts +98 -12
- package/src/video-generation-client.ts +186 -11
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AnyClientTool, ModelMessage, StreamChunk } from '@tanstack/ai';
|
|
2
2
|
import { ConnectionAdapter } from './connection-adapters.js';
|
|
3
|
-
import { ChatClientOptions, ChatClientState, ConnectionStatus, MultimodalContent, UIMessage } from './types.js';
|
|
3
|
+
import { ChatClientOptions, ChatClientState, ChatFetcher, ConnectionStatus, MultimodalContent, UIMessage } from './types.js';
|
|
4
4
|
export declare class ChatClient {
|
|
5
5
|
private readonly processor;
|
|
6
6
|
private connection;
|
|
@@ -15,8 +15,15 @@ export declare class ChatClient {
|
|
|
15
15
|
private status;
|
|
16
16
|
private connectionStatus;
|
|
17
17
|
private abortController;
|
|
18
|
-
private readonly events;
|
|
19
18
|
private readonly clientToolsRef;
|
|
19
|
+
private readonly devtoolsBridge;
|
|
20
|
+
/**
|
|
21
|
+
* Alias for `this.events`. The bridge installs an
|
|
22
|
+
* emitter that auto-attaches run/thread context and auto-emits a
|
|
23
|
+
* snapshot after every event, so chat-client only ever calls
|
|
24
|
+
* `this.events.X(...)` exactly like it did before devtools landed.
|
|
25
|
+
*/
|
|
26
|
+
private readonly events;
|
|
20
27
|
private currentStreamId;
|
|
21
28
|
private currentMessageId;
|
|
22
29
|
private readonly postStreamActions;
|
|
@@ -30,8 +37,10 @@ export declare class ChatClient {
|
|
|
30
37
|
private draining;
|
|
31
38
|
private sessionGenerating;
|
|
32
39
|
private readonly activeRunIds;
|
|
40
|
+
private devtoolsMounted;
|
|
33
41
|
private readonly callbacksRef;
|
|
34
42
|
constructor(options: ChatClientOptions);
|
|
43
|
+
mountDevtools(): void;
|
|
35
44
|
private generateUniqueId;
|
|
36
45
|
private setIsLoading;
|
|
37
46
|
private setStatus;
|
|
@@ -40,6 +49,9 @@ export declare class ChatClient {
|
|
|
40
49
|
private setSessionGenerating;
|
|
41
50
|
private resetSessionGenerating;
|
|
42
51
|
private setError;
|
|
52
|
+
private buildDevtoolsBridgeOptions;
|
|
53
|
+
private getDevtoolsSnapshot;
|
|
54
|
+
private findMessageIdForToolCall;
|
|
43
55
|
private abortSubscriptionLoop;
|
|
44
56
|
private resolveProcessing;
|
|
45
57
|
private cancelInFlightStream;
|
|
@@ -149,6 +161,7 @@ export declare class ChatClient {
|
|
|
149
161
|
state?: 'output-available' | 'output-error';
|
|
150
162
|
errorText?: string;
|
|
151
163
|
}): Promise<void>;
|
|
164
|
+
private addToolResultInternal;
|
|
152
165
|
/**
|
|
153
166
|
* Respond to a tool approval request
|
|
154
167
|
*/
|
|
@@ -214,6 +227,7 @@ export declare class ChatClient {
|
|
|
214
227
|
*/
|
|
215
228
|
updateOptions(options: {
|
|
216
229
|
connection?: ConnectionAdapter;
|
|
230
|
+
fetcher?: ChatFetcher;
|
|
217
231
|
/** @deprecated Use `forwardedProps` instead. */
|
|
218
232
|
body?: Record<string, any>;
|
|
219
233
|
forwardedProps?: Record<string, any>;
|
|
@@ -229,4 +243,5 @@ export declare class ChatClient {
|
|
|
229
243
|
toolCallId?: string;
|
|
230
244
|
}) => void;
|
|
231
245
|
}): void;
|
|
246
|
+
dispose(): void;
|
|
232
247
|
}
|
package/dist/esm/chat-client.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { StreamProcessor, normalizeToUIMessage, generateMessageId, convertSchemaToJsonSchema } from "@tanstack/ai";
|
|
2
|
-
import {
|
|
3
|
-
import { normalizeConnectionAdapter } from "./connection-adapters.js";
|
|
2
|
+
import { createNoOpChatDevtoolsBridge } from "./devtools-noop.js";
|
|
3
|
+
import { normalizeConnectionAdapter, fetcherToConnectionAdapter } from "./connection-adapters.js";
|
|
4
|
+
function resolveTransport(transport) {
|
|
5
|
+
const { connection, fetcher } = transport;
|
|
6
|
+
if (connection && fetcher) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"ChatClient: pass either `connection` or `fetcher`, not both."
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
if (connection) return connection;
|
|
12
|
+
if (fetcher) return fetcherToConnectionAdapter(fetcher);
|
|
13
|
+
throw new Error("ChatClient: either `connection` or `fetcher` is required.");
|
|
14
|
+
}
|
|
4
15
|
class ChatClient {
|
|
5
16
|
processor;
|
|
6
17
|
connection;
|
|
@@ -19,8 +30,15 @@ class ChatClient {
|
|
|
19
30
|
status = "ready";
|
|
20
31
|
connectionStatus = "disconnected";
|
|
21
32
|
abortController = null;
|
|
22
|
-
events;
|
|
23
33
|
clientToolsRef;
|
|
34
|
+
devtoolsBridge;
|
|
35
|
+
/**
|
|
36
|
+
* Alias for `this.events`. The bridge installs an
|
|
37
|
+
* emitter that auto-attaches run/thread context and auto-emits a
|
|
38
|
+
* snapshot after every event, so chat-client only ever calls
|
|
39
|
+
* `this.events.X(...)` exactly like it did before devtools landed.
|
|
40
|
+
*/
|
|
41
|
+
events;
|
|
24
42
|
currentStreamId = null;
|
|
25
43
|
currentMessageId = null;
|
|
26
44
|
postStreamActions = [];
|
|
@@ -38,20 +56,22 @@ class ChatClient {
|
|
|
38
56
|
draining = false;
|
|
39
57
|
sessionGenerating = false;
|
|
40
58
|
activeRunIds = /* @__PURE__ */ new Set();
|
|
59
|
+
devtoolsMounted = false;
|
|
41
60
|
callbacksRef;
|
|
42
61
|
constructor(options) {
|
|
43
62
|
this.uniqueId = options.id || this.generateUniqueId("chat");
|
|
44
63
|
this.threadId = options.threadId || this.generateUniqueId("thread");
|
|
45
64
|
this.bodyOption = options.body || {};
|
|
46
65
|
this.forwardedPropsOption = options.forwardedProps || {};
|
|
47
|
-
this.connection = normalizeConnectionAdapter(options
|
|
48
|
-
this.events = new DefaultChatClientEventEmitter(this.uniqueId);
|
|
66
|
+
this.connection = normalizeConnectionAdapter(resolveTransport(options));
|
|
49
67
|
this.clientToolsRef = { current: /* @__PURE__ */ new Map() };
|
|
50
68
|
if (options.tools) {
|
|
51
69
|
for (const tool of options.tools) {
|
|
52
70
|
this.clientToolsRef.current.set(tool.name, tool);
|
|
53
71
|
}
|
|
54
72
|
}
|
|
73
|
+
this.devtoolsBridge = (options.devtoolsBridgeFactory ?? createNoOpChatDevtoolsBridge)(this.buildDevtoolsBridgeOptions(options.devtools));
|
|
74
|
+
this.events = this.devtoolsBridge.events;
|
|
55
75
|
this.callbacksRef = {
|
|
56
76
|
current: {
|
|
57
77
|
onResponse: options.onResponse || (() => {
|
|
@@ -123,10 +143,30 @@ class ChatClient {
|
|
|
123
143
|
this.events.thinkingUpdated(
|
|
124
144
|
this.currentStreamId,
|
|
125
145
|
messageId,
|
|
126
|
-
content
|
|
146
|
+
content,
|
|
147
|
+
void 0
|
|
127
148
|
);
|
|
128
149
|
}
|
|
129
150
|
},
|
|
151
|
+
onStructuredOutputChange: (args) => {
|
|
152
|
+
const streamId = this.devtoolsBridge.resolveStreamId();
|
|
153
|
+
const eventName = args.phase === "start" ? "structured-output:started" : args.phase === "complete" ? "structured-output:completed" : args.phase === "error" ? "structured-output:errored" : "structured-output:updated";
|
|
154
|
+
this.currentMessageId = args.messageId;
|
|
155
|
+
this.events.structuredOutputChanged(
|
|
156
|
+
eventName,
|
|
157
|
+
streamId,
|
|
158
|
+
args.messageId,
|
|
159
|
+
{
|
|
160
|
+
status: args.status,
|
|
161
|
+
raw: args.raw,
|
|
162
|
+
...args.partial !== void 0 ? { partial: args.partial } : {},
|
|
163
|
+
...args.data !== void 0 ? { data: args.data } : {},
|
|
164
|
+
...args.reasoning !== void 0 ? { reasoning: args.reasoning } : {},
|
|
165
|
+
...args.errorMessage !== void 0 ? { errorMessage: args.errorMessage } : {},
|
|
166
|
+
...args.delta !== void 0 ? { delta: args.delta } : {}
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
},
|
|
130
170
|
onToolCallStateChange: (messageId, toolCallId, state, args) => {
|
|
131
171
|
const messages = this.processor.getMessages();
|
|
132
172
|
const message = messages.find((m) => m.id === messageId);
|
|
@@ -149,23 +189,30 @@ class ChatClient {
|
|
|
149
189
|
const clientTool = this.clientToolsRef.current.get(args.toolName);
|
|
150
190
|
const executeFunc = clientTool?.execute;
|
|
151
191
|
if (executeFunc) {
|
|
192
|
+
const runEventContext = this.devtoolsBridge.getCurrentRunEventContext();
|
|
152
193
|
const executionPromise = (async () => {
|
|
153
194
|
try {
|
|
154
195
|
const output = await executeFunc(args.input);
|
|
155
|
-
await this.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
196
|
+
await this.addToolResultInternal(
|
|
197
|
+
{
|
|
198
|
+
toolCallId: args.toolCallId,
|
|
199
|
+
tool: args.toolName,
|
|
200
|
+
output,
|
|
201
|
+
state: "output-available"
|
|
202
|
+
},
|
|
203
|
+
runEventContext
|
|
204
|
+
);
|
|
161
205
|
} catch (error) {
|
|
162
|
-
await this.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
206
|
+
await this.addToolResultInternal(
|
|
207
|
+
{
|
|
208
|
+
toolCallId: args.toolCallId,
|
|
209
|
+
tool: args.toolName,
|
|
210
|
+
output: null,
|
|
211
|
+
state: "output-error",
|
|
212
|
+
errorText: error.message
|
|
213
|
+
},
|
|
214
|
+
runEventContext
|
|
215
|
+
);
|
|
169
216
|
} finally {
|
|
170
217
|
this.pendingToolExecutions.delete(args.toolCallId);
|
|
171
218
|
}
|
|
@@ -174,23 +221,29 @@ class ChatClient {
|
|
|
174
221
|
}
|
|
175
222
|
},
|
|
176
223
|
onApprovalRequest: (args) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
224
|
+
const streamId = this.devtoolsBridge.resolveStreamId();
|
|
225
|
+
const messageIdForApproval = this.findMessageIdForToolCall(args.toolCallId) ?? this.currentMessageId ?? "";
|
|
226
|
+
this.events.approvalRequested(
|
|
227
|
+
streamId,
|
|
228
|
+
messageIdForApproval,
|
|
229
|
+
args.toolCallId,
|
|
230
|
+
args.toolName,
|
|
231
|
+
args.input,
|
|
232
|
+
args.approvalId
|
|
233
|
+
);
|
|
187
234
|
},
|
|
188
235
|
onCustomEvent: (eventType, data, context) => {
|
|
189
236
|
this.callbacksRef.current.onCustomEvent(eventType, data, context);
|
|
190
237
|
}
|
|
191
238
|
}
|
|
192
239
|
});
|
|
193
|
-
|
|
240
|
+
}
|
|
241
|
+
mountDevtools() {
|
|
242
|
+
if (this.devtoolsMounted) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this.devtoolsMounted = true;
|
|
246
|
+
this.devtoolsBridge.mountWithTools(this.processor.getMessages().length);
|
|
194
247
|
}
|
|
195
248
|
generateUniqueId(prefix) {
|
|
196
249
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
@@ -203,19 +256,23 @@ class ChatClient {
|
|
|
203
256
|
setStatus(status) {
|
|
204
257
|
this.status = status;
|
|
205
258
|
this.callbacksRef.current.onStatusChange(status);
|
|
259
|
+
this.devtoolsBridge.emitSnapshot();
|
|
206
260
|
}
|
|
207
261
|
setIsSubscribed(isSubscribed) {
|
|
208
262
|
this.isSubscribed = isSubscribed;
|
|
209
263
|
this.callbacksRef.current.onSubscriptionChange(isSubscribed);
|
|
264
|
+
this.devtoolsBridge.emitSnapshot();
|
|
210
265
|
}
|
|
211
266
|
setConnectionStatus(status) {
|
|
212
267
|
this.connectionStatus = status;
|
|
213
268
|
this.callbacksRef.current.onConnectionStatusChange(status);
|
|
269
|
+
this.devtoolsBridge.emitSnapshot();
|
|
214
270
|
}
|
|
215
271
|
setSessionGenerating(isGenerating) {
|
|
216
272
|
if (this.sessionGenerating === isGenerating) return;
|
|
217
273
|
this.sessionGenerating = isGenerating;
|
|
218
274
|
this.callbacksRef.current.onSessionGeneratingChange(isGenerating);
|
|
275
|
+
this.devtoolsBridge.emitSnapshot();
|
|
219
276
|
}
|
|
220
277
|
resetSessionGenerating() {
|
|
221
278
|
this.activeRunIds.clear();
|
|
@@ -226,6 +283,51 @@ class ChatClient {
|
|
|
226
283
|
this.callbacksRef.current.onErrorChange(error);
|
|
227
284
|
this.events.errorChanged(error?.message || null);
|
|
228
285
|
}
|
|
286
|
+
buildDevtoolsBridgeOptions(devtools) {
|
|
287
|
+
return {
|
|
288
|
+
hookId: this.uniqueId,
|
|
289
|
+
clientId: this.uniqueId,
|
|
290
|
+
threadId: this.threadId,
|
|
291
|
+
metadata: {
|
|
292
|
+
hookName: devtools?.hookName ?? "useChat",
|
|
293
|
+
outputKind: devtools?.outputKind ?? "chat",
|
|
294
|
+
...devtools?.framework ? { framework: devtools.framework } : {},
|
|
295
|
+
...devtools?.name ? { name: devtools.name } : {}
|
|
296
|
+
},
|
|
297
|
+
getSnapshot: () => this.getDevtoolsSnapshot(),
|
|
298
|
+
getTools: () => this.clientToolsRef.current.values(),
|
|
299
|
+
getMessages: () => this.processor.getMessages(),
|
|
300
|
+
setMessages: (messages) => {
|
|
301
|
+
this.processor.setMessages(messages);
|
|
302
|
+
},
|
|
303
|
+
addToolResult: (toolCallId, output, errorText) => {
|
|
304
|
+
this.processor.addToolResult(toolCallId, output, errorText);
|
|
305
|
+
},
|
|
306
|
+
generateId: (prefix) => this.generateUniqueId(prefix)
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
getDevtoolsSnapshot() {
|
|
310
|
+
return {
|
|
311
|
+
messages: this.processor.getMessages(),
|
|
312
|
+
status: this.status,
|
|
313
|
+
isLoading: this.isLoading,
|
|
314
|
+
isSubscribed: this.isSubscribed,
|
|
315
|
+
connectionStatus: this.connectionStatus,
|
|
316
|
+
sessionGenerating: this.sessionGenerating,
|
|
317
|
+
activeRunIds: Array.from(this.activeRunIds),
|
|
318
|
+
...this.error ? { error: this.error.message } : {}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
findMessageIdForToolCall(toolCallId) {
|
|
322
|
+
const messages = this.processor.getMessages();
|
|
323
|
+
for (const message of messages) {
|
|
324
|
+
const match = message.parts.find(
|
|
325
|
+
(part) => part.type === "tool-call" && part.id === toolCallId
|
|
326
|
+
);
|
|
327
|
+
if (match) return message.id;
|
|
328
|
+
}
|
|
329
|
+
return void 0;
|
|
330
|
+
}
|
|
229
331
|
abortSubscriptionLoop() {
|
|
230
332
|
this.subscriptionAbortController?.abort();
|
|
231
333
|
this.subscriptionAbortController = null;
|
|
@@ -295,13 +397,14 @@ class ChatClient {
|
|
|
295
397
|
this.setConnectionStatus("connected");
|
|
296
398
|
}
|
|
297
399
|
this.callbacksRef.current.onChunk(chunk);
|
|
298
|
-
this.processor.processChunk(chunk);
|
|
299
400
|
if (chunk.type === "RUN_STARTED") {
|
|
300
401
|
this.activeRunIds.add(chunk.runId);
|
|
301
402
|
this.setSessionGenerating(true);
|
|
302
403
|
}
|
|
404
|
+
this.devtoolsBridge.observeChunk(chunk);
|
|
405
|
+
this.processor.processChunk(chunk);
|
|
303
406
|
if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") {
|
|
304
|
-
const runId = chunk.
|
|
407
|
+
const runId = "runId" in chunk && typeof chunk.runId === "string" ? chunk.runId : void 0;
|
|
305
408
|
if (runId) {
|
|
306
409
|
this.activeRunIds.delete(runId);
|
|
307
410
|
} else if (chunk.type === "RUN_ERROR") {
|
|
@@ -375,6 +478,7 @@ class ChatClient {
|
|
|
375
478
|
* ```
|
|
376
479
|
*/
|
|
377
480
|
async sendMessage(content, body) {
|
|
481
|
+
this.mountDevtools();
|
|
378
482
|
const emptyMessage = typeof content === "string" && !content.trim();
|
|
379
483
|
if (emptyMessage || this.isLoading) {
|
|
380
484
|
return;
|
|
@@ -402,6 +506,7 @@ class ChatClient {
|
|
|
402
506
|
* Append a message and stream the response
|
|
403
507
|
*/
|
|
404
508
|
async append(message) {
|
|
509
|
+
this.mountDevtools();
|
|
405
510
|
const normalizedMessage = normalizeToUIMessage(message, generateMessageId);
|
|
406
511
|
if (normalizedMessage.role === "system") {
|
|
407
512
|
return;
|
|
@@ -410,6 +515,7 @@ class ChatClient {
|
|
|
410
515
|
this.events.messageAppended(uiMessage);
|
|
411
516
|
const messages = this.processor.getMessages();
|
|
412
517
|
this.processor.setMessages([...messages, uiMessage]);
|
|
518
|
+
this.devtoolsBridge.emitSnapshot();
|
|
413
519
|
if (this.isLoading) {
|
|
414
520
|
this.queuePostStreamAction(async () => {
|
|
415
521
|
await this.streamResponse();
|
|
@@ -435,6 +541,8 @@ class ChatClient {
|
|
|
435
541
|
const signal = this.abortController.signal;
|
|
436
542
|
this.pendingToolExecutions.clear();
|
|
437
543
|
let streamCompletedSuccessfully = false;
|
|
544
|
+
let activeDevtoolsRunId = null;
|
|
545
|
+
let runTerminalEventEmitted = false;
|
|
438
546
|
try {
|
|
439
547
|
const messages = this.processor.getMessages();
|
|
440
548
|
await this.callbacksRef.current.onResponse();
|
|
@@ -448,6 +556,7 @@ class ChatClient {
|
|
|
448
556
|
};
|
|
449
557
|
this.pendingMessageBody = void 0;
|
|
450
558
|
this.currentStreamId = this.generateUniqueId("stream");
|
|
559
|
+
this.devtoolsBridge.setCurrentStreamId(this.currentStreamId);
|
|
451
560
|
this.currentMessageId = null;
|
|
452
561
|
this.processor.prepareAssistantMessage();
|
|
453
562
|
this.ensureSubscription();
|
|
@@ -464,12 +573,34 @@ class ChatClient {
|
|
|
464
573
|
),
|
|
465
574
|
forwardedProps: { ...mergedBody }
|
|
466
575
|
};
|
|
576
|
+
this.devtoolsBridge.beginRun(runContext.runId, this.threadId);
|
|
577
|
+
activeDevtoolsRunId = runContext.runId;
|
|
578
|
+
this.devtoolsBridge.emitRunLifecycle(
|
|
579
|
+
"run:created",
|
|
580
|
+
runContext.runId,
|
|
581
|
+
"created"
|
|
582
|
+
);
|
|
583
|
+
this.devtoolsBridge.emitRunLifecycle(
|
|
584
|
+
"run:started",
|
|
585
|
+
runContext.runId,
|
|
586
|
+
"started"
|
|
587
|
+
);
|
|
588
|
+
this.devtoolsBridge.emitSnapshot();
|
|
467
589
|
await this.connection.send(messages, mergedBody, signal, runContext);
|
|
468
590
|
await processingComplete;
|
|
469
591
|
if (generation !== this.streamGeneration) {
|
|
470
592
|
return false;
|
|
471
593
|
}
|
|
472
594
|
if (this.status === "error") {
|
|
595
|
+
if (activeDevtoolsRunId) {
|
|
596
|
+
this.devtoolsBridge.emitRunLifecycle(
|
|
597
|
+
"run:errored",
|
|
598
|
+
activeDevtoolsRunId,
|
|
599
|
+
"errored",
|
|
600
|
+
this.error ? { error: this.error.message } : {}
|
|
601
|
+
);
|
|
602
|
+
runTerminalEventEmitted = true;
|
|
603
|
+
}
|
|
473
604
|
return false;
|
|
474
605
|
}
|
|
475
606
|
if (this.pendingToolExecutions.size > 0) {
|
|
@@ -480,19 +611,52 @@ class ChatClient {
|
|
|
480
611
|
} catch (err) {
|
|
481
612
|
if (err instanceof Error) {
|
|
482
613
|
if (err.name === "AbortError") {
|
|
614
|
+
if (activeDevtoolsRunId) {
|
|
615
|
+
this.devtoolsBridge.emitRunLifecycle(
|
|
616
|
+
"run:cancelled",
|
|
617
|
+
activeDevtoolsRunId,
|
|
618
|
+
"cancelled"
|
|
619
|
+
);
|
|
620
|
+
runTerminalEventEmitted = true;
|
|
621
|
+
}
|
|
483
622
|
return false;
|
|
484
623
|
}
|
|
485
624
|
if (generation === this.streamGeneration) {
|
|
486
625
|
this.reportStreamError(err);
|
|
626
|
+
if (activeDevtoolsRunId) {
|
|
627
|
+
this.devtoolsBridge.emitRunLifecycle(
|
|
628
|
+
"run:errored",
|
|
629
|
+
activeDevtoolsRunId,
|
|
630
|
+
"errored",
|
|
631
|
+
{ error: err.message }
|
|
632
|
+
);
|
|
633
|
+
runTerminalEventEmitted = true;
|
|
634
|
+
}
|
|
487
635
|
}
|
|
488
636
|
}
|
|
489
637
|
} finally {
|
|
490
638
|
if (generation === this.streamGeneration) {
|
|
491
639
|
this.currentStreamId = null;
|
|
640
|
+
this.devtoolsBridge.setCurrentStreamId(null);
|
|
492
641
|
this.currentMessageId = null;
|
|
493
642
|
this.abortController = null;
|
|
494
643
|
this.setIsLoading(false);
|
|
495
644
|
this.pendingMessageBody = void 0;
|
|
645
|
+
if (activeDevtoolsRunId && !runTerminalEventEmitted) {
|
|
646
|
+
if (streamCompletedSuccessfully) {
|
|
647
|
+
this.devtoolsBridge.emitRunLifecycle(
|
|
648
|
+
"run:completed",
|
|
649
|
+
activeDevtoolsRunId,
|
|
650
|
+
"completed"
|
|
651
|
+
);
|
|
652
|
+
} else if (signal.aborted) {
|
|
653
|
+
this.devtoolsBridge.emitRunLifecycle(
|
|
654
|
+
"run:cancelled",
|
|
655
|
+
activeDevtoolsRunId,
|
|
656
|
+
"cancelled"
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
496
660
|
await this.drainPostStreamActions();
|
|
497
661
|
if (streamCompletedSuccessfully) {
|
|
498
662
|
const messages = this.processor.getMessages();
|
|
@@ -554,6 +718,7 @@ class ChatClient {
|
|
|
554
718
|
}
|
|
555
719
|
this.events.reloaded(lastUserMessageIndex);
|
|
556
720
|
this.processor.removeMessagesAfter(lastUserMessageIndex);
|
|
721
|
+
this.devtoolsBridge.emitSnapshot();
|
|
557
722
|
await this.streamResponse();
|
|
558
723
|
}
|
|
559
724
|
/**
|
|
@@ -575,11 +740,15 @@ class ChatClient {
|
|
|
575
740
|
* Add the result of a client-side tool execution
|
|
576
741
|
*/
|
|
577
742
|
async addToolResult(result) {
|
|
743
|
+
await this.addToolResultInternal(result);
|
|
744
|
+
}
|
|
745
|
+
async addToolResultInternal(result, context) {
|
|
578
746
|
this.events.toolResultAdded(
|
|
579
747
|
result.toolCallId,
|
|
580
748
|
result.tool,
|
|
581
749
|
result.output,
|
|
582
|
-
result.state || "output-available"
|
|
750
|
+
result.state || "output-available",
|
|
751
|
+
context
|
|
583
752
|
);
|
|
584
753
|
this.processor.addToolResult(
|
|
585
754
|
result.toolCallId,
|
|
@@ -615,6 +784,7 @@ class ChatClient {
|
|
|
615
784
|
);
|
|
616
785
|
}
|
|
617
786
|
this.processor.addToolApprovalResponse(response.id, response.approved);
|
|
787
|
+
this.devtoolsBridge.emitSnapshot();
|
|
618
788
|
if (this.isLoading) {
|
|
619
789
|
this.queuePostStreamAction(() => this.checkForContinuation());
|
|
620
790
|
return;
|
|
@@ -672,9 +842,13 @@ class ChatClient {
|
|
|
672
842
|
*/
|
|
673
843
|
shouldAutoSend() {
|
|
674
844
|
const messages = this.processor.getMessages();
|
|
675
|
-
const lastAssistant = messages.findLast(
|
|
845
|
+
const lastAssistant = messages.findLast(
|
|
846
|
+
(m) => m.role === "assistant"
|
|
847
|
+
);
|
|
676
848
|
if (!lastAssistant) return false;
|
|
677
|
-
const hasToolCalls = lastAssistant.parts.some(
|
|
849
|
+
const hasToolCalls = lastAssistant.parts.some(
|
|
850
|
+
(p) => p.type === "tool-call"
|
|
851
|
+
);
|
|
678
852
|
if (!hasToolCalls) return false;
|
|
679
853
|
return this.processor.areAllToolsComplete();
|
|
680
854
|
}
|
|
@@ -728,12 +902,13 @@ class ChatClient {
|
|
|
728
902
|
*/
|
|
729
903
|
setMessagesManually(messages) {
|
|
730
904
|
this.processor.setMessages(messages);
|
|
905
|
+
this.devtoolsBridge.emitSnapshot();
|
|
731
906
|
}
|
|
732
907
|
/**
|
|
733
908
|
* Update options refs (for use in React hooks to avoid recreating client)
|
|
734
909
|
*/
|
|
735
910
|
updateOptions(options) {
|
|
736
|
-
if (options.connection !== void 0) {
|
|
911
|
+
if (options.connection !== void 0 || options.fetcher !== void 0) {
|
|
737
912
|
const wasSubscribed = this.isSubscribed;
|
|
738
913
|
if (this.isLoading) {
|
|
739
914
|
this.cancelInFlightStream({
|
|
@@ -746,7 +921,12 @@ class ChatClient {
|
|
|
746
921
|
this.resetSessionGenerating();
|
|
747
922
|
this.setIsSubscribed(false);
|
|
748
923
|
this.setConnectionStatus("disconnected");
|
|
749
|
-
this.connection = normalizeConnectionAdapter(
|
|
924
|
+
this.connection = normalizeConnectionAdapter(
|
|
925
|
+
resolveTransport({
|
|
926
|
+
connection: options.connection,
|
|
927
|
+
fetcher: options.fetcher
|
|
928
|
+
})
|
|
929
|
+
);
|
|
750
930
|
if (wasSubscribed) {
|
|
751
931
|
this.subscribe();
|
|
752
932
|
}
|
|
@@ -762,6 +942,7 @@ class ChatClient {
|
|
|
762
942
|
for (const tool of options.tools) {
|
|
763
943
|
this.clientToolsRef.current.set(tool.name, tool);
|
|
764
944
|
}
|
|
945
|
+
this.devtoolsBridge.notifyToolsChanged();
|
|
765
946
|
}
|
|
766
947
|
if (options.onResponse !== void 0) {
|
|
767
948
|
this.callbacksRef.current.onResponse = options.onResponse;
|
|
@@ -788,6 +969,11 @@ class ChatClient {
|
|
|
788
969
|
this.callbacksRef.current.onCustomEvent = options.onCustomEvent;
|
|
789
970
|
}
|
|
790
971
|
}
|
|
972
|
+
dispose() {
|
|
973
|
+
this.unsubscribe();
|
|
974
|
+
this.devtoolsBridge.dispose();
|
|
975
|
+
this.devtoolsMounted = false;
|
|
976
|
+
}
|
|
791
977
|
}
|
|
792
978
|
export {
|
|
793
979
|
ChatClient
|