@tanstack/ai 0.2.2 → 0.3.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/README.md +1 -0
- package/dist/esm/activities/chat/index.js +206 -172
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/stream/index.d.ts +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +22 -43
- package/dist/esm/activities/chat/stream/processor.js +89 -156
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +22 -21
- package/dist/esm/activities/chat/tools/tool-calls.js +40 -27
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/generateImage/index.js +41 -1
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.js +33 -1
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.js +29 -1
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.js +50 -0
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/summarize/index.js +9 -8
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/event-client.d.ts +378 -254
- package/dist/esm/event-client.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/stream-to-response.d.ts +1 -1
- package/dist/esm/stream-to-response.js +5 -3
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/types.d.ts +162 -46
- package/package.json +1 -1
- package/src/activities/chat/index.ts +267 -191
- package/src/activities/chat/stream/index.ts +1 -5
- package/src/activities/chat/stream/processor.ts +128 -269
- package/src/activities/chat/tools/tool-calls.ts +55 -49
- package/src/activities/generateImage/index.ts +47 -2
- package/src/activities/generateSpeech/index.ts +37 -1
- package/src/activities/generateTranscription/index.ts +33 -1
- package/src/activities/generateVideo/index.ts +54 -0
- package/src/activities/summarize/index.ts +10 -9
- package/src/event-client.ts +460 -256
- package/src/index.ts +2 -3
- package/src/stream-to-response.ts +8 -6
- package/src/types.ts +209 -65
package/README.md
CHANGED
|
@@ -45,6 +45,7 @@ A powerful, type-safe AI SDK for building AI-powered applications.
|
|
|
45
45
|
- Headless chat state management with adapters (SSE, HTTP stream, custom)
|
|
46
46
|
- Isomorphic type-safe tools with server/client execution
|
|
47
47
|
- **Enhanced integration with TanStack Start** - Share implementations between AI tools and server functions
|
|
48
|
+
- **Observability events** - Structured, typed events for text, tools, image, speech, transcription, and video ([docs](./docs/guides/observability.md))
|
|
48
49
|
|
|
49
50
|
### <a href="https://tanstack.com/ai">Read the docs →</b></a>
|
|
50
51
|
|
|
@@ -15,7 +15,7 @@ class TextEngine {
|
|
|
15
15
|
this.totalChunkCount = 0;
|
|
16
16
|
this.currentMessageId = null;
|
|
17
17
|
this.accumulatedContent = "";
|
|
18
|
-
this.
|
|
18
|
+
this.finishedEvent = null;
|
|
19
19
|
this.shouldEmitStreamEnd = true;
|
|
20
20
|
this.earlyTermination = false;
|
|
21
21
|
this.toolPhase = "continue";
|
|
@@ -66,61 +66,56 @@ class TextEngine {
|
|
|
66
66
|
}
|
|
67
67
|
beforeRun() {
|
|
68
68
|
this.streamStartTime = Date.now();
|
|
69
|
-
const {
|
|
70
|
-
model,
|
|
71
|
-
tools,
|
|
72
|
-
temperature,
|
|
73
|
-
topP,
|
|
74
|
-
maxTokens,
|
|
75
|
-
metadata,
|
|
76
|
-
modelOptions,
|
|
77
|
-
conversationId
|
|
78
|
-
} = this.params;
|
|
69
|
+
const { tools, temperature, topP, maxTokens, metadata } = this.params;
|
|
79
70
|
const options = {};
|
|
80
71
|
if (temperature !== void 0) options.temperature = temperature;
|
|
81
72
|
if (topP !== void 0) options.topP = topP;
|
|
82
73
|
if (maxTokens !== void 0) options.maxTokens = maxTokens;
|
|
83
74
|
if (metadata !== void 0) options.metadata = metadata;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
provider: this.adapter.name,
|
|
89
|
-
messageCount: this.initialMessageCount,
|
|
90
|
-
hasTools: !!tools && tools.length > 0,
|
|
91
|
-
streaming: true,
|
|
92
|
-
timestamp: Date.now(),
|
|
93
|
-
clientId: conversationId,
|
|
94
|
-
toolNames: tools?.map((t) => t.name),
|
|
95
|
-
options: Object.keys(options).length > 0 ? options : void 0,
|
|
96
|
-
modelOptions
|
|
97
|
-
});
|
|
98
|
-
aiEventClient.emit("stream:started", {
|
|
99
|
-
streamId: this.streamId,
|
|
100
|
-
model,
|
|
101
|
-
provider: this.adapter.name,
|
|
75
|
+
this.eventOptions = Object.keys(options).length > 0 ? options : void 0;
|
|
76
|
+
this.eventToolNames = tools?.map((t) => t.name);
|
|
77
|
+
aiEventClient.emit("text:request:started", {
|
|
78
|
+
...this.buildTextEventContext(),
|
|
102
79
|
timestamp: Date.now()
|
|
103
80
|
});
|
|
81
|
+
const messagesToEmit = this.params.conversationId ? this.messages.slice(-1).filter((m) => m.role === "user") : this.messages;
|
|
82
|
+
messagesToEmit.forEach((message, index) => {
|
|
83
|
+
const messageIndex = this.params.conversationId ? this.messages.length - 1 : index;
|
|
84
|
+
const messageId = this.createId("msg");
|
|
85
|
+
const baseContext = this.buildTextEventContext();
|
|
86
|
+
const content = this.getContentString(message.content);
|
|
87
|
+
aiEventClient.emit("text:message:created", {
|
|
88
|
+
...baseContext,
|
|
89
|
+
messageId,
|
|
90
|
+
role: message.role,
|
|
91
|
+
content,
|
|
92
|
+
toolCalls: message.toolCalls,
|
|
93
|
+
messageIndex,
|
|
94
|
+
timestamp: Date.now()
|
|
95
|
+
});
|
|
96
|
+
if (message.role === "user") {
|
|
97
|
+
aiEventClient.emit("text:message:user", {
|
|
98
|
+
...baseContext,
|
|
99
|
+
messageId,
|
|
100
|
+
role: "user",
|
|
101
|
+
content,
|
|
102
|
+
messageIndex,
|
|
103
|
+
timestamp: Date.now()
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
104
107
|
}
|
|
105
108
|
afterRun() {
|
|
106
109
|
if (!this.shouldEmitStreamEnd) {
|
|
107
110
|
return;
|
|
108
111
|
}
|
|
109
112
|
const now = Date.now();
|
|
110
|
-
aiEventClient.emit("text:completed", {
|
|
111
|
-
|
|
112
|
-
streamId: this.streamId,
|
|
113
|
-
model: this.params.model,
|
|
113
|
+
aiEventClient.emit("text:request:completed", {
|
|
114
|
+
...this.buildTextEventContext(),
|
|
114
115
|
content: this.accumulatedContent,
|
|
115
116
|
messageId: this.currentMessageId || void 0,
|
|
116
117
|
finishReason: this.lastFinishReason || void 0,
|
|
117
|
-
usage: this.
|
|
118
|
-
timestamp: now
|
|
119
|
-
});
|
|
120
|
-
aiEventClient.emit("stream:ended", {
|
|
121
|
-
requestId: this.requestId,
|
|
122
|
-
streamId: this.streamId,
|
|
123
|
-
totalChunks: this.totalChunkCount,
|
|
118
|
+
usage: this.finishedEvent?.usage,
|
|
124
119
|
duration: now - this.streamStartTime,
|
|
125
120
|
timestamp: now
|
|
126
121
|
});
|
|
@@ -141,7 +136,15 @@ class TextEngine {
|
|
|
141
136
|
beginIteration() {
|
|
142
137
|
this.currentMessageId = this.createId("msg");
|
|
143
138
|
this.accumulatedContent = "";
|
|
144
|
-
this.
|
|
139
|
+
this.finishedEvent = null;
|
|
140
|
+
const baseContext = this.buildTextEventContext();
|
|
141
|
+
aiEventClient.emit("text:message:created", {
|
|
142
|
+
...baseContext,
|
|
143
|
+
messageId: this.currentMessageId,
|
|
144
|
+
role: "assistant",
|
|
145
|
+
content: "",
|
|
146
|
+
timestamp: Date.now()
|
|
147
|
+
});
|
|
145
148
|
}
|
|
146
149
|
async *streamModelResponse() {
|
|
147
150
|
const { temperature, topP, maxTokens, metadata, modelOptions } = this.params;
|
|
@@ -176,102 +179,107 @@ class TextEngine {
|
|
|
176
179
|
}
|
|
177
180
|
handleStreamChunk(chunk) {
|
|
178
181
|
switch (chunk.type) {
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
// AG-UI Events
|
|
183
|
+
case "TEXT_MESSAGE_CONTENT":
|
|
184
|
+
this.handleTextMessageContentEvent(chunk);
|
|
185
|
+
break;
|
|
186
|
+
case "TOOL_CALL_START":
|
|
187
|
+
this.handleToolCallStartEvent(chunk);
|
|
181
188
|
break;
|
|
182
|
-
case "
|
|
183
|
-
this.
|
|
189
|
+
case "TOOL_CALL_ARGS":
|
|
190
|
+
this.handleToolCallArgsEvent(chunk);
|
|
184
191
|
break;
|
|
185
|
-
case "
|
|
186
|
-
this.
|
|
192
|
+
case "TOOL_CALL_END":
|
|
193
|
+
this.handleToolCallEndEvent(chunk);
|
|
187
194
|
break;
|
|
188
|
-
case "
|
|
189
|
-
this.
|
|
195
|
+
case "RUN_FINISHED":
|
|
196
|
+
this.handleRunFinishedEvent(chunk);
|
|
190
197
|
break;
|
|
191
|
-
case "
|
|
192
|
-
this.
|
|
198
|
+
case "RUN_ERROR":
|
|
199
|
+
this.handleRunErrorEvent(chunk);
|
|
193
200
|
break;
|
|
194
|
-
case "
|
|
195
|
-
this.
|
|
201
|
+
case "STEP_FINISHED":
|
|
202
|
+
this.handleStepFinishedEvent(chunk);
|
|
196
203
|
break;
|
|
197
204
|
}
|
|
198
205
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
206
|
+
// ===========================
|
|
207
|
+
// AG-UI Event Handlers
|
|
208
|
+
// ===========================
|
|
209
|
+
handleTextMessageContentEvent(chunk) {
|
|
210
|
+
if (chunk.content) {
|
|
211
|
+
this.accumulatedContent = chunk.content;
|
|
212
|
+
} else {
|
|
213
|
+
this.accumulatedContent += chunk.delta;
|
|
214
|
+
}
|
|
215
|
+
aiEventClient.emit("text:chunk:content", {
|
|
216
|
+
...this.buildTextEventContext(),
|
|
203
217
|
messageId: this.currentMessageId || void 0,
|
|
204
|
-
content:
|
|
218
|
+
content: this.accumulatedContent,
|
|
205
219
|
delta: chunk.delta,
|
|
206
220
|
timestamp: Date.now()
|
|
207
221
|
});
|
|
208
222
|
}
|
|
209
|
-
|
|
210
|
-
this.toolCallManager.
|
|
211
|
-
aiEventClient.emit("
|
|
212
|
-
|
|
223
|
+
handleToolCallStartEvent(chunk) {
|
|
224
|
+
this.toolCallManager.addToolCallStartEvent(chunk);
|
|
225
|
+
aiEventClient.emit("text:chunk:tool-call", {
|
|
226
|
+
...this.buildTextEventContext(),
|
|
213
227
|
messageId: this.currentMessageId || void 0,
|
|
214
|
-
toolCallId: chunk.
|
|
215
|
-
toolName: chunk.
|
|
216
|
-
index: chunk.index,
|
|
217
|
-
arguments:
|
|
228
|
+
toolCallId: chunk.toolCallId,
|
|
229
|
+
toolName: chunk.toolName,
|
|
230
|
+
index: chunk.index ?? 0,
|
|
231
|
+
arguments: "",
|
|
218
232
|
timestamp: Date.now()
|
|
219
233
|
});
|
|
220
234
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
235
|
+
handleToolCallArgsEvent(chunk) {
|
|
236
|
+
this.toolCallManager.addToolCallArgsEvent(chunk);
|
|
237
|
+
aiEventClient.emit("text:chunk:tool-call", {
|
|
238
|
+
...this.buildTextEventContext(),
|
|
224
239
|
messageId: this.currentMessageId || void 0,
|
|
225
240
|
toolCallId: chunk.toolCallId,
|
|
226
|
-
|
|
241
|
+
toolName: "",
|
|
242
|
+
index: 0,
|
|
243
|
+
arguments: chunk.delta,
|
|
227
244
|
timestamp: Date.now()
|
|
228
245
|
});
|
|
229
246
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
streamId: this.streamId,
|
|
244
|
-
messageId: this.currentMessageId || void 0,
|
|
245
|
-
model: this.params.model,
|
|
246
|
-
usage: chunk.usage,
|
|
247
|
-
timestamp: Date.now()
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
this.doneChunk = chunk;
|
|
253
|
-
this.lastFinishReason = chunk.finishReason;
|
|
254
|
-
aiEventClient.emit("stream:chunk:done", {
|
|
255
|
-
streamId: this.streamId,
|
|
247
|
+
handleToolCallEndEvent(chunk) {
|
|
248
|
+
this.toolCallManager.completeToolCall(chunk);
|
|
249
|
+
aiEventClient.emit("text:chunk:tool-result", {
|
|
250
|
+
...this.buildTextEventContext(),
|
|
251
|
+
messageId: this.currentMessageId || void 0,
|
|
252
|
+
toolCallId: chunk.toolCallId,
|
|
253
|
+
result: chunk.result || "",
|
|
254
|
+
timestamp: Date.now()
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
handleRunFinishedEvent(chunk) {
|
|
258
|
+
aiEventClient.emit("text:chunk:done", {
|
|
259
|
+
...this.buildTextEventContext(),
|
|
256
260
|
messageId: this.currentMessageId || void 0,
|
|
257
261
|
finishReason: chunk.finishReason,
|
|
258
262
|
usage: chunk.usage,
|
|
259
263
|
timestamp: Date.now()
|
|
260
264
|
});
|
|
261
265
|
if (chunk.usage) {
|
|
262
|
-
aiEventClient.emit("usage
|
|
263
|
-
|
|
264
|
-
streamId: this.streamId,
|
|
266
|
+
aiEventClient.emit("text:usage", {
|
|
267
|
+
...this.buildTextEventContext(),
|
|
265
268
|
messageId: this.currentMessageId || void 0,
|
|
266
|
-
model: this.params.model,
|
|
267
269
|
usage: chunk.usage,
|
|
268
270
|
timestamp: Date.now()
|
|
269
271
|
});
|
|
270
272
|
}
|
|
273
|
+
if (this.finishedEvent?.finishReason === "tool_calls" && chunk.finishReason === "stop") {
|
|
274
|
+
this.lastFinishReason = chunk.finishReason;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.finishedEvent = chunk;
|
|
278
|
+
this.lastFinishReason = chunk.finishReason;
|
|
271
279
|
}
|
|
272
|
-
|
|
273
|
-
aiEventClient.emit("
|
|
274
|
-
|
|
280
|
+
handleRunErrorEvent(chunk) {
|
|
281
|
+
aiEventClient.emit("text:chunk:error", {
|
|
282
|
+
...this.buildTextEventContext(),
|
|
275
283
|
messageId: this.currentMessageId || void 0,
|
|
276
284
|
error: chunk.error.message,
|
|
277
285
|
timestamp: Date.now()
|
|
@@ -279,29 +287,23 @@ class TextEngine {
|
|
|
279
287
|
this.earlyTermination = true;
|
|
280
288
|
this.shouldEmitStreamEnd = false;
|
|
281
289
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
+
handleStepFinishedEvent(chunk) {
|
|
291
|
+
if (chunk.content || chunk.delta) {
|
|
292
|
+
aiEventClient.emit("text:chunk:thinking", {
|
|
293
|
+
...this.buildTextEventContext(),
|
|
294
|
+
messageId: this.currentMessageId || void 0,
|
|
295
|
+
content: chunk.content || "",
|
|
296
|
+
delta: chunk.delta,
|
|
297
|
+
timestamp: Date.now()
|
|
298
|
+
});
|
|
299
|
+
}
|
|
290
300
|
}
|
|
291
301
|
async *checkForPendingToolCalls() {
|
|
292
302
|
const pendingToolCalls = this.getPendingToolCallsFromMessages();
|
|
293
303
|
if (pendingToolCalls.length === 0) {
|
|
294
304
|
return "continue";
|
|
295
305
|
}
|
|
296
|
-
const
|
|
297
|
-
aiEventClient.emit("text:iteration", {
|
|
298
|
-
requestId: this.requestId,
|
|
299
|
-
streamId: this.streamId,
|
|
300
|
-
iterationNumber: this.iterationCount + 1,
|
|
301
|
-
messageCount: this.messages.length,
|
|
302
|
-
toolCallCount: pendingToolCalls.length,
|
|
303
|
-
timestamp: Date.now()
|
|
304
|
-
});
|
|
306
|
+
const finishEvent = this.createSyntheticFinishedEvent();
|
|
305
307
|
const { approvals, clientToolResults } = this.collectClientState();
|
|
306
308
|
const executionResult = await executeToolCalls(
|
|
307
309
|
pendingToolCalls,
|
|
@@ -312,13 +314,13 @@ class TextEngine {
|
|
|
312
314
|
if (executionResult.needsApproval.length > 0 || executionResult.needsClientExecution.length > 0) {
|
|
313
315
|
for (const chunk of this.emitApprovalRequests(
|
|
314
316
|
executionResult.needsApproval,
|
|
315
|
-
|
|
317
|
+
finishEvent
|
|
316
318
|
)) {
|
|
317
319
|
yield chunk;
|
|
318
320
|
}
|
|
319
321
|
for (const chunk of this.emitClientToolInputs(
|
|
320
322
|
executionResult.needsClientExecution,
|
|
321
|
-
|
|
323
|
+
finishEvent
|
|
322
324
|
)) {
|
|
323
325
|
yield chunk;
|
|
324
326
|
}
|
|
@@ -327,7 +329,7 @@ class TextEngine {
|
|
|
327
329
|
}
|
|
328
330
|
const toolResultChunks = this.emitToolResults(
|
|
329
331
|
executionResult.results,
|
|
330
|
-
|
|
332
|
+
finishEvent
|
|
331
333
|
);
|
|
332
334
|
for (const chunk of toolResultChunks) {
|
|
333
335
|
yield chunk;
|
|
@@ -340,19 +342,11 @@ class TextEngine {
|
|
|
340
342
|
return;
|
|
341
343
|
}
|
|
342
344
|
const toolCalls = this.toolCallManager.getToolCalls();
|
|
343
|
-
const
|
|
344
|
-
if (!
|
|
345
|
+
const finishEvent = this.finishedEvent;
|
|
346
|
+
if (!finishEvent || toolCalls.length === 0) {
|
|
345
347
|
this.setToolPhase("stop");
|
|
346
348
|
return;
|
|
347
349
|
}
|
|
348
|
-
aiEventClient.emit("text:iteration", {
|
|
349
|
-
requestId: this.requestId,
|
|
350
|
-
streamId: this.streamId,
|
|
351
|
-
iterationNumber: this.iterationCount + 1,
|
|
352
|
-
messageCount: this.messages.length,
|
|
353
|
-
toolCallCount: toolCalls.length,
|
|
354
|
-
timestamp: Date.now()
|
|
355
|
-
});
|
|
356
350
|
this.addAssistantToolCallMessage(toolCalls);
|
|
357
351
|
const { approvals, clientToolResults } = this.collectClientState();
|
|
358
352
|
const executionResult = await executeToolCalls(
|
|
@@ -364,13 +358,13 @@ class TextEngine {
|
|
|
364
358
|
if (executionResult.needsApproval.length > 0 || executionResult.needsClientExecution.length > 0) {
|
|
365
359
|
for (const chunk of this.emitApprovalRequests(
|
|
366
360
|
executionResult.needsApproval,
|
|
367
|
-
|
|
361
|
+
finishEvent
|
|
368
362
|
)) {
|
|
369
363
|
yield chunk;
|
|
370
364
|
}
|
|
371
365
|
for (const chunk of this.emitClientToolInputs(
|
|
372
366
|
executionResult.needsClientExecution,
|
|
373
|
-
|
|
367
|
+
finishEvent
|
|
374
368
|
)) {
|
|
375
369
|
yield chunk;
|
|
376
370
|
}
|
|
@@ -379,7 +373,7 @@ class TextEngine {
|
|
|
379
373
|
}
|
|
380
374
|
const toolResultChunks = this.emitToolResults(
|
|
381
375
|
executionResult.results,
|
|
382
|
-
|
|
376
|
+
finishEvent
|
|
383
377
|
);
|
|
384
378
|
for (const chunk of toolResultChunks) {
|
|
385
379
|
yield chunk;
|
|
@@ -388,9 +382,10 @@ class TextEngine {
|
|
|
388
382
|
this.setToolPhase("continue");
|
|
389
383
|
}
|
|
390
384
|
shouldExecuteToolPhase() {
|
|
391
|
-
return this.
|
|
385
|
+
return this.finishedEvent?.finishReason === "tool_calls" && this.tools.length > 0 && this.toolCallManager.hasToolCalls();
|
|
392
386
|
}
|
|
393
387
|
addAssistantToolCallMessage(toolCalls) {
|
|
388
|
+
const messageId = this.currentMessageId ?? this.createId("msg");
|
|
394
389
|
this.messages = [
|
|
395
390
|
...this.messages,
|
|
396
391
|
{
|
|
@@ -399,6 +394,14 @@ class TextEngine {
|
|
|
399
394
|
toolCalls
|
|
400
395
|
}
|
|
401
396
|
];
|
|
397
|
+
aiEventClient.emit("text:message:created", {
|
|
398
|
+
...this.buildTextEventContext(),
|
|
399
|
+
messageId,
|
|
400
|
+
role: "assistant",
|
|
401
|
+
content: this.accumulatedContent || "",
|
|
402
|
+
toolCalls,
|
|
403
|
+
timestamp: Date.now()
|
|
404
|
+
});
|
|
402
405
|
}
|
|
403
406
|
collectClientState() {
|
|
404
407
|
const approvals = /* @__PURE__ */ new Map();
|
|
@@ -418,11 +421,11 @@ class TextEngine {
|
|
|
418
421
|
}
|
|
419
422
|
return { approvals, clientToolResults };
|
|
420
423
|
}
|
|
421
|
-
emitApprovalRequests(approvals,
|
|
424
|
+
emitApprovalRequests(approvals, finishEvent) {
|
|
422
425
|
const chunks = [];
|
|
423
426
|
for (const approval of approvals) {
|
|
424
|
-
aiEventClient.emit("
|
|
425
|
-
|
|
427
|
+
aiEventClient.emit("tools:approval:requested", {
|
|
428
|
+
...this.buildTextEventContext(),
|
|
426
429
|
messageId: this.currentMessageId || void 0,
|
|
427
430
|
toolCallId: approval.toolCallId,
|
|
428
431
|
toolName: approval.toolName,
|
|
@@ -431,26 +434,28 @@ class TextEngine {
|
|
|
431
434
|
timestamp: Date.now()
|
|
432
435
|
});
|
|
433
436
|
chunks.push({
|
|
434
|
-
type: "
|
|
435
|
-
id: doneChunk.id,
|
|
436
|
-
model: doneChunk.model,
|
|
437
|
+
type: "CUSTOM",
|
|
437
438
|
timestamp: Date.now(),
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
439
|
+
model: finishEvent.model,
|
|
440
|
+
name: "approval-requested",
|
|
441
|
+
data: {
|
|
442
|
+
toolCallId: approval.toolCallId,
|
|
443
|
+
toolName: approval.toolName,
|
|
444
|
+
input: approval.input,
|
|
445
|
+
approval: {
|
|
446
|
+
id: approval.approvalId,
|
|
447
|
+
needsApproval: true
|
|
448
|
+
}
|
|
444
449
|
}
|
|
445
450
|
});
|
|
446
451
|
}
|
|
447
452
|
return chunks;
|
|
448
453
|
}
|
|
449
|
-
emitClientToolInputs(clientRequests,
|
|
454
|
+
emitClientToolInputs(clientRequests, finishEvent) {
|
|
450
455
|
const chunks = [];
|
|
451
456
|
for (const clientTool of clientRequests) {
|
|
452
|
-
aiEventClient.emit("
|
|
453
|
-
|
|
457
|
+
aiEventClient.emit("tools:input:available", {
|
|
458
|
+
...this.buildTextEventContext(),
|
|
454
459
|
messageId: this.currentMessageId || void 0,
|
|
455
460
|
toolCallId: clientTool.toolCallId,
|
|
456
461
|
toolName: clientTool.toolName,
|
|
@@ -458,23 +463,24 @@ class TextEngine {
|
|
|
458
463
|
timestamp: Date.now()
|
|
459
464
|
});
|
|
460
465
|
chunks.push({
|
|
461
|
-
type: "
|
|
462
|
-
id: doneChunk.id,
|
|
463
|
-
model: doneChunk.model,
|
|
466
|
+
type: "CUSTOM",
|
|
464
467
|
timestamp: Date.now(),
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
+
model: finishEvent.model,
|
|
469
|
+
name: "tool-input-available",
|
|
470
|
+
data: {
|
|
471
|
+
toolCallId: clientTool.toolCallId,
|
|
472
|
+
toolName: clientTool.toolName,
|
|
473
|
+
input: clientTool.input
|
|
474
|
+
}
|
|
468
475
|
});
|
|
469
476
|
}
|
|
470
477
|
return chunks;
|
|
471
478
|
}
|
|
472
|
-
emitToolResults(results,
|
|
479
|
+
emitToolResults(results, finishEvent) {
|
|
473
480
|
const chunks = [];
|
|
474
481
|
for (const result of results) {
|
|
475
|
-
aiEventClient.emit("
|
|
476
|
-
|
|
477
|
-
streamId: this.streamId,
|
|
482
|
+
aiEventClient.emit("tools:call:completed", {
|
|
483
|
+
...this.buildTextEventContext(),
|
|
478
484
|
messageId: this.currentMessageId || void 0,
|
|
479
485
|
toolCallId: result.toolCallId,
|
|
480
486
|
toolName: result.toolName,
|
|
@@ -483,15 +489,14 @@ class TextEngine {
|
|
|
483
489
|
timestamp: Date.now()
|
|
484
490
|
});
|
|
485
491
|
const content = JSON.stringify(result.result);
|
|
486
|
-
|
|
487
|
-
type: "
|
|
488
|
-
id: doneChunk.id,
|
|
489
|
-
model: doneChunk.model,
|
|
492
|
+
chunks.push({
|
|
493
|
+
type: "TOOL_CALL_END",
|
|
490
494
|
timestamp: Date.now(),
|
|
495
|
+
model: finishEvent.model,
|
|
491
496
|
toolCallId: result.toolCallId,
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
497
|
+
toolName: result.toolName,
|
|
498
|
+
result: content
|
|
499
|
+
});
|
|
495
500
|
this.messages = [
|
|
496
501
|
...this.messages,
|
|
497
502
|
{
|
|
@@ -500,6 +505,13 @@ class TextEngine {
|
|
|
500
505
|
toolCallId: result.toolCallId
|
|
501
506
|
}
|
|
502
507
|
];
|
|
508
|
+
aiEventClient.emit("text:message:created", {
|
|
509
|
+
...this.buildTextEventContext(),
|
|
510
|
+
messageId: this.createId("msg"),
|
|
511
|
+
role: "tool",
|
|
512
|
+
content,
|
|
513
|
+
timestamp: Date.now()
|
|
514
|
+
});
|
|
503
515
|
}
|
|
504
516
|
return chunks;
|
|
505
517
|
}
|
|
@@ -520,10 +532,10 @@ class TextEngine {
|
|
|
520
532
|
}
|
|
521
533
|
return pending;
|
|
522
534
|
}
|
|
523
|
-
|
|
535
|
+
createSyntheticFinishedEvent() {
|
|
524
536
|
return {
|
|
525
|
-
type: "
|
|
526
|
-
|
|
537
|
+
type: "RUN_FINISHED",
|
|
538
|
+
runId: this.createId("pending"),
|
|
527
539
|
model: this.params.model,
|
|
528
540
|
timestamp: Date.now(),
|
|
529
541
|
finishReason: "tool_calls"
|
|
@@ -542,6 +554,28 @@ class TextEngine {
|
|
|
542
554
|
isAborted() {
|
|
543
555
|
return !!this.effectiveSignal?.aborted;
|
|
544
556
|
}
|
|
557
|
+
buildTextEventContext() {
|
|
558
|
+
return {
|
|
559
|
+
requestId: this.requestId,
|
|
560
|
+
streamId: this.streamId,
|
|
561
|
+
provider: this.adapter.name,
|
|
562
|
+
model: this.params.model,
|
|
563
|
+
clientId: this.params.conversationId,
|
|
564
|
+
source: "server",
|
|
565
|
+
systemPrompts: this.systemPrompts.length > 0 ? this.systemPrompts : void 0,
|
|
566
|
+
toolNames: this.eventToolNames,
|
|
567
|
+
options: this.eventOptions,
|
|
568
|
+
modelOptions: this.params.modelOptions,
|
|
569
|
+
messageCount: this.initialMessageCount,
|
|
570
|
+
hasTools: this.tools.length > 0,
|
|
571
|
+
streaming: true
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
getContentString(content) {
|
|
575
|
+
if (typeof content === "string") return content;
|
|
576
|
+
const text = content?.map((part) => part.type === "text" ? part.content : "").join("") || "";
|
|
577
|
+
return text;
|
|
578
|
+
}
|
|
545
579
|
setToolPhase(phase) {
|
|
546
580
|
this.toolPhase = phase;
|
|
547
581
|
if (phase === "wait") {
|