@tanstack/ai 0.10.3 → 0.11.1

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.
Files changed (39) hide show
  1. package/dist/esm/activities/chat/index.d.ts +4 -0
  2. package/dist/esm/activities/chat/index.js +50 -16
  3. package/dist/esm/activities/chat/index.js.map +1 -1
  4. package/dist/esm/activities/chat/middleware/compose.js +5 -4
  5. package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
  6. package/dist/esm/activities/chat/stream/processor.d.ts +16 -1
  7. package/dist/esm/activities/chat/stream/processor.js +120 -21
  8. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  9. package/dist/esm/activities/chat/stream/types.d.ts +1 -0
  10. package/dist/esm/activities/chat/tools/schema-converter.js +4 -2
  11. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
  12. package/dist/esm/activities/chat/tools/tool-calls.js +3 -1
  13. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  14. package/dist/esm/activities/generateVideo/index.js +6 -0
  15. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  16. package/dist/esm/activities/stream-generation-result.d.ts +3 -2
  17. package/dist/esm/activities/stream-generation-result.js +12 -4
  18. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  19. package/dist/esm/index.js +2 -0
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/stream-to-response.d.ts +2 -2
  22. package/dist/esm/stream-to-response.js +0 -1
  23. package/dist/esm/stream-to-response.js.map +1 -1
  24. package/dist/esm/strip-to-spec-middleware.d.ts +18 -0
  25. package/dist/esm/strip-to-spec-middleware.js +20 -0
  26. package/dist/esm/strip-to-spec-middleware.js.map +1 -0
  27. package/dist/esm/types.d.ts +229 -95
  28. package/package.json +3 -2
  29. package/src/activities/chat/index.ts +90 -25
  30. package/src/activities/chat/middleware/compose.ts +6 -4
  31. package/src/activities/chat/stream/processor.ts +152 -25
  32. package/src/activities/chat/stream/types.ts +1 -0
  33. package/src/activities/chat/tools/schema-converter.ts +8 -2
  34. package/src/activities/chat/tools/tool-calls.ts +4 -2
  35. package/src/activities/generateVideo/index.ts +13 -6
  36. package/src/activities/stream-generation-result.ts +19 -11
  37. package/src/stream-to-response.ts +2 -4
  38. package/src/strip-to-spec-middleware.ts +37 -0
  39. package/src/types.ts +276 -109
@@ -93,11 +93,12 @@ export class ToolCallManager {
93
93
  */
94
94
  addToolCallStartEvent(event: ToolCallStartEvent): void {
95
95
  const index = event.index ?? this.toolCallsMap.size
96
+ const name = event.toolCallName
96
97
  this.toolCallsMap.set(index, {
97
98
  id: event.toolCallId,
98
99
  type: 'function',
99
100
  function: {
100
- name: event.toolName,
101
+ name,
101
102
  arguments: '',
102
103
  },
103
104
  ...(event.providerMetadata && {
@@ -237,11 +238,12 @@ export class ToolCallManager {
237
238
  yield {
238
239
  type: 'TOOL_CALL_END',
239
240
  toolCallId: toolCall.id,
241
+ toolCallName: toolCall.function.name,
240
242
  toolName: toolCall.function.name,
241
243
  model: finishEvent.model,
242
244
  timestamp: Date.now(),
243
245
  result: toolResultContent,
244
- }
246
+ } as ToolCallEndEvent
245
247
 
246
248
  // Add tool result message
247
249
  toolResults.push({
@@ -268,11 +268,14 @@ async function* runStreamingVideoGeneration<
268
268
  const pollingInterval = options.pollingInterval ?? 2000
269
269
  const maxDuration = options.maxDuration ?? 600_000
270
270
 
271
+ const threadId = createId('thread')
272
+
271
273
  yield {
272
274
  type: 'RUN_STARTED',
273
275
  runId,
276
+ threadId,
274
277
  timestamp: Date.now(),
275
- }
278
+ } as StreamChunk
276
279
 
277
280
  try {
278
281
  // Create the video generation job
@@ -289,7 +292,7 @@ async function* runStreamingVideoGeneration<
289
292
  name: 'video:job:created',
290
293
  value: { jobId: jobResult.jobId },
291
294
  timestamp: Date.now(),
292
- }
295
+ } as StreamChunk
293
296
 
294
297
  // Poll for completion
295
298
  const startTime = Date.now()
@@ -308,7 +311,7 @@ async function* runStreamingVideoGeneration<
308
311
  error: statusResult.error,
309
312
  },
310
313
  timestamp: Date.now(),
311
- }
314
+ } as StreamChunk
312
315
 
313
316
  if (statusResult.status === 'completed') {
314
317
  const urlResult = await adapter.getVideoUrl(jobResult.jobId)
@@ -323,14 +326,15 @@ async function* runStreamingVideoGeneration<
323
326
  expiresAt: urlResult.expiresAt,
324
327
  },
325
328
  timestamp: Date.now(),
326
- }
329
+ } as StreamChunk
327
330
 
328
331
  yield {
329
332
  type: 'RUN_FINISHED',
330
333
  runId,
334
+ threadId,
331
335
  finishReason: 'stop',
332
336
  timestamp: Date.now(),
333
- }
337
+ } as StreamChunk
334
338
  return
335
339
  }
336
340
 
@@ -344,12 +348,15 @@ async function* runStreamingVideoGeneration<
344
348
  yield {
345
349
  type: 'RUN_ERROR',
346
350
  runId,
351
+ threadId,
352
+ message: error.message || 'Video generation failed',
353
+ code: error.code,
347
354
  error: {
348
355
  message: error.message || 'Video generation failed',
349
356
  code: error.code,
350
357
  },
351
358
  timestamp: Date.now(),
352
- }
359
+ } as StreamChunk
353
360
  }
354
361
  }
355
362
 
@@ -4,6 +4,7 @@
4
4
  * implementations to support `stream: true`.
5
5
  */
6
6
 
7
+ import { EventType } from '@ag-ui/core'
7
8
  import type { StreamChunk } from '../types'
8
9
 
9
10
  function createId(prefix: string): string {
@@ -17,46 +18,53 @@ function createId(prefix: string): string {
17
18
  * to be sent over the same streaming transport as chat.
18
19
  *
19
20
  * @param generator - An async function that performs the generation and returns the result
20
- * @param options - Optional configuration (runId)
21
- * @returns An AsyncIterable of StreamChunks with RUN_STARTED, CUSTOM(generation:result), and RUN_FINISHED events
21
+ * @param options - Optional configuration (runId, threadId)
22
+ * @returns An AsyncIterable of StreamChunks with RUN_STARTED, CUSTOM(generation:result), and RUN_FINISHED events on success, or RUN_STARTED and RUN_ERROR on failure
22
23
  */
23
24
  export async function* streamGenerationResult<TResult>(
24
25
  generator: () => Promise<TResult>,
25
- options?: { runId?: string },
26
+ options?: { runId?: string; threadId?: string },
26
27
  ): AsyncIterable<StreamChunk> {
27
28
  const runId = options?.runId ?? createId('run')
29
+ const threadId = options?.threadId ?? createId('thread')
28
30
 
29
31
  yield {
30
- type: 'RUN_STARTED',
32
+ type: EventType.RUN_STARTED,
31
33
  runId,
34
+ threadId,
32
35
  timestamp: Date.now(),
33
- }
36
+ } as StreamChunk
34
37
 
35
38
  try {
36
39
  const result = await generator()
37
40
 
38
41
  yield {
39
- type: 'CUSTOM',
42
+ type: EventType.CUSTOM,
40
43
  name: 'generation:result',
41
44
  value: result as unknown,
42
45
  timestamp: Date.now(),
43
- }
46
+ } as StreamChunk
44
47
 
45
48
  yield {
46
- type: 'RUN_FINISHED',
49
+ type: EventType.RUN_FINISHED,
47
50
  runId,
51
+ threadId,
48
52
  finishReason: 'stop',
49
53
  timestamp: Date.now(),
50
- }
54
+ } as StreamChunk
51
55
  } catch (error: any) {
52
56
  yield {
53
- type: 'RUN_ERROR',
57
+ type: EventType.RUN_ERROR,
54
58
  runId,
59
+ threadId,
60
+ message: error.message || 'Generation failed',
61
+ code: error.code,
62
+ // Deprecated nested form for backward compatibility
55
63
  error: {
56
64
  message: error.message || 'Generation failed',
57
65
  code: error.code,
58
66
  },
59
67
  timestamp: Date.now(),
60
- }
68
+ } as StreamChunk
61
69
  }
62
70
  }
@@ -40,7 +40,7 @@ export async function streamToText(
40
40
  * This creates a ReadableStream that emits chunks in SSE format:
41
41
  * - Each chunk is prefixed with "data: "
42
42
  * - Each chunk is followed by "\n\n"
43
- * - Stream ends with "data: [DONE]\n\n"
43
+ * - Stream ends when the underlying iterable is exhausted (RUN_FINISHED is the terminal event)
44
44
  *
45
45
  * @param stream - AsyncIterable of StreamChunks from chat()
46
46
  * @param abortController - Optional AbortController to abort when stream is cancelled
@@ -67,8 +67,6 @@ export function toServerSentEventsStream(
67
67
  )
68
68
  }
69
69
 
70
- // Send completion marker
71
- controller.enqueue(encoder.encode('data: [DONE]\n\n'))
72
70
  controller.close()
73
71
  } catch (error: any) {
74
72
  // Don't send error if aborted
@@ -109,7 +107,7 @@ export function toServerSentEventsStream(
109
107
  * This creates a Response that emits chunks in SSE format:
110
108
  * - Each chunk is prefixed with "data: "
111
109
  * - Each chunk is followed by "\n\n"
112
- * - Stream ends with "data: [DONE]\n\n"
110
+ * - Stream ends when the underlying iterable is exhausted (RUN_FINISHED is the terminal event)
113
111
  *
114
112
  * @param stream - AsyncIterable of StreamChunks from chat()
115
113
  * @param init - Optional Response initialization options (including `abortController`)
@@ -0,0 +1,37 @@
1
+ import type { ChatMiddleware } from './activities/chat/middleware/types'
2
+ import type { StreamChunk } from './types'
3
+
4
+ /**
5
+ * Strip only the deprecated nested `error` object from RUN_ERROR events.
6
+ * The flat `message`/`code` fields are the spec-compliant form.
7
+ *
8
+ * All other fields pass through unchanged. @ag-ui/core's BaseEventSchema
9
+ * uses `.passthrough()`, so extra fields (model, content, usage,
10
+ * finishReason, toolName, stepId, etc.) are allowed and won't break
11
+ * spec validation or verifyEvents.
12
+ */
13
+ export function stripToSpec(chunk: StreamChunk): StreamChunk {
14
+ // Only strip the deprecated nested error object from RUN_ERROR
15
+ if (
16
+ (chunk as StreamChunk & { type: string }).type === 'RUN_ERROR' &&
17
+ 'error' in chunk
18
+ ) {
19
+ const { error: _deprecated, ...rest } = chunk as Record<string, unknown>
20
+ return rest as StreamChunk
21
+ }
22
+ return chunk
23
+ }
24
+
25
+ /**
26
+ * Middleware that ensures events are AG-UI spec compliant.
27
+ * Currently only strips the deprecated nested `error` object from RUN_ERROR.
28
+ * All other fields pass through unchanged (passthrough allowed by spec).
29
+ */
30
+ export function stripToSpecMiddleware(): ChatMiddleware {
31
+ return {
32
+ name: 'strip-to-spec',
33
+ onChunk(_ctx, chunk) {
34
+ return stripToSpec(chunk)
35
+ },
36
+ }
37
+ }