@tanstack/ai 0.13.0 → 0.15.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.
Files changed (81) hide show
  1. package/dist/esm/activities/chat/index.js +61 -9
  2. package/dist/esm/activities/chat/index.js.map +1 -1
  3. package/dist/esm/activities/chat/messages.js +22 -1
  4. package/dist/esm/activities/chat/messages.js.map +1 -1
  5. package/dist/esm/activities/chat/stream/message-updaters.d.ts +3 -2
  6. package/dist/esm/activities/chat/stream/message-updaters.js +7 -3
  7. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
  8. package/dist/esm/activities/chat/stream/processor.d.ts +21 -5
  9. package/dist/esm/activities/chat/stream/processor.js +107 -15
  10. package/dist/esm/activities/chat/stream/processor.js.map +1 -1
  11. package/dist/esm/activities/chat/stream/types.d.ts +4 -1
  12. package/dist/esm/activities/chat/tools/tool-calls.js +2 -1
  13. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  14. package/dist/esm/activities/error-payload.d.ts +12 -0
  15. package/dist/esm/activities/error-payload.js +25 -0
  16. package/dist/esm/activities/error-payload.js.map +1 -0
  17. package/dist/esm/activities/generateAudio/adapter.d.ts +62 -0
  18. package/dist/esm/activities/generateAudio/adapter.js +14 -0
  19. package/dist/esm/activities/generateAudio/adapter.js.map +1 -0
  20. package/dist/esm/activities/generateAudio/index.d.ts +74 -0
  21. package/dist/esm/activities/generateAudio/index.js +80 -0
  22. package/dist/esm/activities/generateAudio/index.js.map +1 -0
  23. package/dist/esm/activities/generateImage/adapter.d.ts +1 -1
  24. package/dist/esm/activities/generateImage/adapter.js +1 -1
  25. package/dist/esm/activities/generateImage/adapter.js.map +1 -1
  26. package/dist/esm/activities/generateSpeech/adapter.d.ts +1 -1
  27. package/dist/esm/activities/generateSpeech/adapter.js +1 -1
  28. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
  29. package/dist/esm/activities/generateSpeech/index.d.ts +3 -3
  30. package/dist/esm/activities/generateSpeech/index.js +11 -0
  31. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  32. package/dist/esm/activities/generateTranscription/adapter.d.ts +1 -1
  33. package/dist/esm/activities/generateTranscription/adapter.js +1 -1
  34. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
  35. package/dist/esm/activities/generateTranscription/index.d.ts +3 -3
  36. package/dist/esm/activities/generateTranscription/index.js +11 -0
  37. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  38. package/dist/esm/activities/generateVideo/index.js +7 -7
  39. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  40. package/dist/esm/activities/index.d.ts +5 -2
  41. package/dist/esm/activities/index.js +11 -6
  42. package/dist/esm/activities/index.js.map +1 -1
  43. package/dist/esm/activities/stream-generation-result.js +5 -6
  44. package/dist/esm/activities/stream-generation-result.js.map +1 -1
  45. package/dist/esm/adapter-internals.d.ts +1 -0
  46. package/dist/esm/adapter-internals.js +3 -1
  47. package/dist/esm/adapter-internals.js.map +1 -1
  48. package/dist/esm/index.d.ts +3 -2
  49. package/dist/esm/index.js +3 -0
  50. package/dist/esm/index.js.map +1 -1
  51. package/dist/esm/middlewares/otel.d.ts +75 -0
  52. package/dist/esm/middlewares/otel.js +624 -0
  53. package/dist/esm/middlewares/otel.js.map +1 -0
  54. package/dist/esm/stream-to-response.js +3 -8
  55. package/dist/esm/stream-to-response.js.map +1 -1
  56. package/dist/esm/types.d.ts +71 -6
  57. package/package.json +15 -2
  58. package/skills/ai-core/media-generation/SKILL.md +154 -10
  59. package/src/activities/chat/index.ts +74 -11
  60. package/src/activities/chat/messages.ts +23 -1
  61. package/src/activities/chat/stream/message-updaters.ts +10 -3
  62. package/src/activities/chat/stream/processor.ts +140 -17
  63. package/src/activities/chat/stream/types.ts +4 -1
  64. package/src/activities/chat/tools/tool-calls.ts +3 -1
  65. package/src/activities/error-payload.ts +35 -0
  66. package/src/activities/generateAudio/adapter.ts +89 -0
  67. package/src/activities/generateAudio/index.ts +224 -0
  68. package/src/activities/generateImage/adapter.ts +1 -1
  69. package/src/activities/generateSpeech/adapter.ts +1 -1
  70. package/src/activities/generateSpeech/index.ts +17 -7
  71. package/src/activities/generateTranscription/adapter.ts +1 -1
  72. package/src/activities/generateTranscription/index.ts +27 -4
  73. package/src/activities/generateVideo/index.ts +8 -8
  74. package/src/activities/index.ts +22 -0
  75. package/src/activities/stream-generation-result.ts +6 -7
  76. package/src/adapter-internals.ts +1 -0
  77. package/src/index.ts +4 -0
  78. package/src/middlewares/index.ts +5 -0
  79. package/src/middlewares/otel.ts +861 -0
  80. package/src/stream-to-response.ts +5 -10
  81. package/src/types.ts +79 -5
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Audio Generation Activity
3
+ *
4
+ * Generates audio (music, sound effects, etc.) from text prompts.
5
+ * This is a self-contained module with implementation, types, and JSDoc.
6
+ */
7
+
8
+ import { aiEventClient } from '@tanstack/ai-event-client'
9
+ import { streamGenerationResult } from '../stream-generation-result.js'
10
+ import { resolveDebugOption } from '../../logger/resolve'
11
+ import type { InternalLogger } from '../../logger/internal-logger'
12
+ import type { DebugOption } from '../../logger/types'
13
+ import type { AudioAdapter } from './adapter'
14
+ import type { AudioGenerationResult, StreamChunk } from '../../types'
15
+
16
+ // ===========================
17
+ // Activity Kind
18
+ // ===========================
19
+
20
+ /** The adapter kind this activity handles */
21
+ export const kind = 'audio' as const
22
+
23
+ // ===========================
24
+ // Type Extraction Helpers
25
+ // ===========================
26
+
27
+ /**
28
+ * Extract provider options from an AudioAdapter via ~types.
29
+ */
30
+ export type AudioProviderOptions<TAdapter> =
31
+ TAdapter extends AudioAdapter<any, any>
32
+ ? TAdapter['~types']['providerOptions']
33
+ : object
34
+
35
+ // ===========================
36
+ // Activity Options Type
37
+ // ===========================
38
+
39
+ /**
40
+ * Options for the audio generation activity.
41
+ * The model is extracted from the adapter's model property.
42
+ *
43
+ * @template TAdapter - The audio adapter type
44
+ * @template TStream - Whether to stream the output
45
+ */
46
+ export interface AudioActivityOptions<
47
+ TAdapter extends AudioAdapter<string, AudioProviderOptions<TAdapter>>,
48
+ TStream extends boolean = false,
49
+ > {
50
+ /** The audio adapter to use (must be created with a model) */
51
+ adapter: TAdapter & { kind: typeof kind }
52
+ /** Text description of the desired audio */
53
+ prompt: string
54
+ /** Desired duration in seconds */
55
+ duration?: number
56
+ /** Provider-specific options for audio generation */
57
+ modelOptions?: AudioProviderOptions<TAdapter>
58
+ /**
59
+ * Whether to stream the generation result.
60
+ * When true, returns an AsyncIterable<StreamChunk> for streaming transport.
61
+ * When false or not provided, returns a Promise<AudioGenerationResult>.
62
+ *
63
+ * @default false
64
+ */
65
+ stream?: TStream
66
+ /**
67
+ * Enable debug logging. Pass `true` to enable all categories, `false` to
68
+ * silence everything including errors, or a `DebugConfig` object for granular
69
+ * control and/or a custom `Logger`.
70
+ */
71
+ debug?: DebugOption
72
+ }
73
+
74
+ // ===========================
75
+ // Activity Result Type
76
+ // ===========================
77
+
78
+ /**
79
+ * Result type for the audio generation activity.
80
+ * - If stream is true: AsyncIterable<StreamChunk>
81
+ * - Otherwise: Promise<AudioGenerationResult>
82
+ */
83
+ export type AudioActivityResult<TStream extends boolean = false> =
84
+ TStream extends true
85
+ ? AsyncIterable<StreamChunk>
86
+ : Promise<AudioGenerationResult>
87
+
88
+ function createId(prefix: string): string {
89
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
90
+ }
91
+
92
+ // ===========================
93
+ // Activity Implementation
94
+ // ===========================
95
+
96
+ /**
97
+ * Audio generation activity - generates audio from text prompts.
98
+ *
99
+ * Uses AI models to create music, sound effects, and other audio content.
100
+ *
101
+ * @example Generate music from a prompt
102
+ * ```ts
103
+ * import { generateAudio } from '@tanstack/ai'
104
+ * import { falAudio } from '@tanstack/ai-fal'
105
+ *
106
+ * const result = await generateAudio({
107
+ * adapter: falAudio('fal-ai/diffrhythm'),
108
+ * prompt: 'An upbeat electronic track with synths',
109
+ * duration: 10
110
+ * })
111
+ *
112
+ * console.log(result.audio.url) // URL to generated audio
113
+ * ```
114
+ */
115
+ export function generateAudio<
116
+ TAdapter extends AudioAdapter<string, AudioProviderOptions<TAdapter>>,
117
+ TStream extends boolean = false,
118
+ >(
119
+ options: AudioActivityOptions<TAdapter, TStream>,
120
+ ): AudioActivityResult<TStream> {
121
+ if (options.stream) {
122
+ return streamGenerationResult(() =>
123
+ runGenerateAudio(options),
124
+ ) as AudioActivityResult<TStream>
125
+ }
126
+ return runGenerateAudio(options) as AudioActivityResult<TStream>
127
+ }
128
+
129
+ /**
130
+ * Run the core audio generation logic (non-streaming).
131
+ */
132
+ async function runGenerateAudio<
133
+ TAdapter extends AudioAdapter<string, AudioProviderOptions<TAdapter>>,
134
+ >(
135
+ options: AudioActivityOptions<TAdapter, boolean>,
136
+ ): Promise<AudioGenerationResult> {
137
+ const { adapter, stream: _stream, debug: _debug, ...rest } = options
138
+ const model = adapter.model
139
+ const requestId = createId('audio')
140
+ const startTime = Date.now()
141
+ const logger: InternalLogger = resolveDebugOption(options.debug)
142
+ const providerName =
143
+ (adapter as { name?: string; provider?: string }).provider ??
144
+ (adapter as { name?: string }).name ??
145
+ 'unknown'
146
+
147
+ aiEventClient.emit('audio:request:started', {
148
+ requestId,
149
+ provider: adapter.name,
150
+ model,
151
+ prompt: rest.prompt,
152
+ duration: rest.duration,
153
+ modelOptions: rest.modelOptions as Record<string, unknown> | undefined,
154
+ timestamp: startTime,
155
+ })
156
+
157
+ logger.request(`activity=generateAudio provider=${providerName}`, {
158
+ provider: providerName,
159
+ model,
160
+ })
161
+
162
+ try {
163
+ const result = await adapter.generateAudio({ ...rest, model, logger })
164
+ const elapsedMs = Date.now() - startTime
165
+
166
+ aiEventClient.emit('audio:request:completed', {
167
+ requestId,
168
+ provider: adapter.name,
169
+ model,
170
+ audio: result.audio,
171
+ duration: elapsedMs,
172
+ modelOptions: rest.modelOptions as Record<string, unknown> | undefined,
173
+ timestamp: Date.now(),
174
+ })
175
+
176
+ logger.output(`activity=generateAudio provider=${providerName}`, {
177
+ contentType: result.audio.contentType,
178
+ audioDuration: result.audio.duration,
179
+ })
180
+
181
+ return result
182
+ } catch (error) {
183
+ const elapsedMs = Date.now() - startTime
184
+ const err = error as Error
185
+ aiEventClient.emit('audio:request:error', {
186
+ requestId,
187
+ provider: adapter.name,
188
+ model,
189
+ error: { message: err.message, name: err.name },
190
+ duration: elapsedMs,
191
+ modelOptions: rest.modelOptions as Record<string, unknown> | undefined,
192
+ timestamp: Date.now(),
193
+ })
194
+ logger.errors('generateAudio activity failed', {
195
+ error,
196
+ source: 'generateAudio',
197
+ })
198
+ throw error
199
+ }
200
+ }
201
+
202
+ // ===========================
203
+ // Options Factory
204
+ // ===========================
205
+
206
+ /**
207
+ * Create typed options for the generateAudio() function without executing.
208
+ */
209
+ export function createAudioOptions<
210
+ TAdapter extends AudioAdapter<string, AudioProviderOptions<TAdapter>>,
211
+ TStream extends boolean = false,
212
+ >(
213
+ options: AudioActivityOptions<TAdapter, TStream>,
214
+ ): AudioActivityOptions<TAdapter, TStream> {
215
+ return options
216
+ }
217
+
218
+ // Re-export adapter types
219
+ export type {
220
+ AudioAdapter,
221
+ AudioAdapterConfig,
222
+ AnyAudioAdapter,
223
+ } from './adapter'
224
+ export { BaseAudioAdapter } from './adapter'
@@ -96,7 +96,7 @@ export abstract class BaseImageAdapter<
96
96
 
97
97
  protected config: ImageAdapterConfig
98
98
 
99
- constructor(config: ImageAdapterConfig = {}, model: TModel) {
99
+ constructor(model: TModel, config: ImageAdapterConfig = {}) {
100
100
  this.config = config
101
101
  this.model = model
102
102
  }
@@ -72,7 +72,7 @@ export abstract class BaseTTSAdapter<
72
72
 
73
73
  protected config: TTSAdapterConfig
74
74
 
75
- constructor(config: TTSAdapterConfig = {}, model: TModel) {
75
+ constructor(model: TModel, config: TTSAdapterConfig = {}) {
76
76
  this.config = config
77
77
  this.model = model
78
78
  }
@@ -44,7 +44,7 @@ export type TTSProviderOptions<TAdapter> =
44
44
  * @template TStream - Whether to stream the output
45
45
  */
46
46
  export interface TTSActivityOptions<
47
- TAdapter extends TTSAdapter<string, object>,
47
+ TAdapter extends TTSAdapter<string, TTSProviderOptions<TAdapter>>,
48
48
  TStream extends boolean = false,
49
49
  > {
50
50
  /** The TTS adapter to use (must be created with a model) */
@@ -126,7 +126,7 @@ function createId(prefix: string): string {
126
126
  * ```
127
127
  */
128
128
  export function generateSpeech<
129
- TAdapter extends TTSAdapter<string, object>,
129
+ TAdapter extends TTSAdapter<string, TTSProviderOptions<TAdapter>>,
130
130
  TStream extends boolean = false,
131
131
  >(options: TTSActivityOptions<TAdapter, TStream>): TTSActivityResult<TStream> {
132
132
  if (options.stream) {
@@ -140,9 +140,9 @@ export function generateSpeech<
140
140
  /**
141
141
  * Run the core TTS generation logic (non-streaming).
142
142
  */
143
- async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
144
- options: TTSActivityOptions<TAdapter, boolean>,
145
- ): Promise<TTSResult> {
143
+ async function runGenerateSpeech<
144
+ TAdapter extends TTSAdapter<string, TTSProviderOptions<TAdapter>>,
145
+ >(options: TTSActivityOptions<TAdapter, boolean>): Promise<TTSResult> {
146
146
  const { adapter, stream: _stream, debug: _debug, ...rest } = options
147
147
  const model = adapter.model
148
148
  const requestId = createId('speech')
@@ -172,7 +172,6 @@ async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
172
172
 
173
173
  try {
174
174
  const result = await adapter.generateSpeech({ ...rest, model, logger })
175
-
176
175
  const duration = Date.now() - startTime
177
176
 
178
177
  aiEventClient.emit('speech:request:completed', {
@@ -195,6 +194,17 @@ async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
195
194
 
196
195
  return result
197
196
  } catch (error) {
197
+ const duration = Date.now() - startTime
198
+ const err = error as Error
199
+ aiEventClient.emit('speech:request:error', {
200
+ requestId,
201
+ provider: adapter.name,
202
+ model,
203
+ error: { message: err.message, name: err.name },
204
+ duration,
205
+ modelOptions: rest.modelOptions as Record<string, unknown> | undefined,
206
+ timestamp: Date.now(),
207
+ })
198
208
  logger.errors('generateSpeech activity failed', {
199
209
  error,
200
210
  source: 'generateSpeech',
@@ -211,7 +221,7 @@ async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
211
221
  * Create typed options for the generateSpeech() function without executing.
212
222
  */
213
223
  export function createSpeechOptions<
214
- TAdapter extends TTSAdapter<string, object>,
224
+ TAdapter extends TTSAdapter<string, TTSProviderOptions<TAdapter>>,
215
225
  TStream extends boolean = false,
216
226
  >(
217
227
  options: TTSActivityOptions<TAdapter, TStream>,
@@ -74,7 +74,7 @@ export abstract class BaseTranscriptionAdapter<
74
74
 
75
75
  protected config: TranscriptionAdapterConfig
76
76
 
77
- constructor(config: TranscriptionAdapterConfig = {}, model: TModel) {
77
+ constructor(model: TModel, config: TranscriptionAdapterConfig = {}) {
78
78
  this.config = config
79
79
  this.model = model
80
80
  }
@@ -44,7 +44,10 @@ export type TranscriptionProviderOptions<TAdapter> =
44
44
  * @template TStream - Whether to stream the output
45
45
  */
46
46
  export interface TranscriptionActivityOptions<
47
- TAdapter extends TranscriptionAdapter<string, object>,
47
+ TAdapter extends TranscriptionAdapter<
48
+ string,
49
+ TranscriptionProviderOptions<TAdapter>
50
+ >,
48
51
  TStream extends boolean = false,
49
52
  > {
50
53
  /** The transcription adapter to use (must be created with a model) */
@@ -141,7 +144,10 @@ function createId(prefix: string): string {
141
144
  * ```
142
145
  */
143
146
  export function generateTranscription<
144
- TAdapter extends TranscriptionAdapter<string, object>,
147
+ TAdapter extends TranscriptionAdapter<
148
+ string,
149
+ TranscriptionProviderOptions<TAdapter>
150
+ >,
145
151
  TStream extends boolean = false,
146
152
  >(
147
153
  options: TranscriptionActivityOptions<TAdapter, TStream>,
@@ -161,7 +167,10 @@ export function generateTranscription<
161
167
  * Run non-streaming transcription
162
168
  */
163
169
  async function runGenerateTranscription<
164
- TAdapter extends TranscriptionAdapter<string, object>,
170
+ TAdapter extends TranscriptionAdapter<
171
+ string,
172
+ TranscriptionProviderOptions<TAdapter>
173
+ >,
165
174
  >(
166
175
  options: TranscriptionActivityOptions<TAdapter, boolean>,
167
176
  ): Promise<TranscriptionResult> {
@@ -213,6 +222,17 @@ async function runGenerateTranscription<
213
222
 
214
223
  return result
215
224
  } catch (error) {
225
+ const duration = Date.now() - startTime
226
+ const err = error as Error
227
+ aiEventClient.emit('transcription:request:error', {
228
+ requestId,
229
+ provider: adapter.name,
230
+ model,
231
+ error: { message: err.message, name: err.name },
232
+ duration,
233
+ modelOptions: rest.modelOptions as Record<string, unknown> | undefined,
234
+ timestamp: Date.now(),
235
+ })
216
236
  logger.errors('generateTranscription activity failed', {
217
237
  error,
218
238
  source: 'generateTranscription',
@@ -229,7 +249,10 @@ async function runGenerateTranscription<
229
249
  * Create typed options for the generateTranscription() function without executing.
230
250
  */
231
251
  export function createTranscriptionOptions<
232
- TAdapter extends TranscriptionAdapter<string, object>,
252
+ TAdapter extends TranscriptionAdapter<
253
+ string,
254
+ TranscriptionProviderOptions<TAdapter>
255
+ >,
233
256
  TStream extends boolean = false,
234
257
  >(
235
258
  options: TranscriptionActivityOptions<TAdapter, TStream>,
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { aiEventClient } from '@tanstack/ai-event-client'
11
+ import { toRunErrorPayload } from '../error-payload'
11
12
  import { resolveDebugOption } from '../../logger/resolve'
12
13
  import type { InternalLogger } from '../../logger/internal-logger'
13
14
  import type { DebugOption } from '../../logger/types'
@@ -399,21 +400,20 @@ async function* runStreamingVideoGeneration<
399
400
  }
400
401
 
401
402
  throw new Error('Video generation timed out')
402
- } catch (error: any) {
403
+ } catch (error: unknown) {
404
+ const payload = toRunErrorPayload(error, 'Video generation failed')
403
405
  logger.errors('generateVideo activity failed', {
404
- error,
406
+ message: payload.message,
407
+ code: payload.code,
405
408
  source: 'generateVideo',
406
409
  })
407
410
  yield {
408
411
  type: 'RUN_ERROR',
409
412
  runId,
410
413
  threadId,
411
- message: error.message || 'Video generation failed',
412
- code: error.code,
413
- error: {
414
- message: error.message || 'Video generation failed',
415
- code: error.code,
416
- },
414
+ message: payload.message,
415
+ code: payload.code,
416
+ error: payload,
417
417
  timestamp: Date.now(),
418
418
  } as StreamChunk
419
419
  }
@@ -17,6 +17,7 @@
17
17
  import type { AnyTextAdapter } from './chat/adapter'
18
18
  import type { AnySummarizeAdapter } from './summarize/adapter'
19
19
  import type { AnyImageAdapter } from './generateImage/adapter'
20
+ import type { AnyAudioAdapter } from './generateAudio/adapter'
20
21
  import type { AnyVideoAdapter } from './generateVideo/adapter'
21
22
  import type { AnyTTSAdapter } from './generateSpeech/adapter'
22
23
  import type { AnyTranscriptionAdapter } from './generateTranscription/adapter'
@@ -80,6 +81,25 @@ export {
80
81
  type AnyImageAdapter,
81
82
  } from './generateImage/adapter'
82
83
 
84
+ // ===========================
85
+ // Audio Activity
86
+ // ===========================
87
+
88
+ export {
89
+ kind as audioKind,
90
+ generateAudio,
91
+ type AudioActivityOptions,
92
+ type AudioActivityResult,
93
+ type AudioProviderOptions,
94
+ } from './generateAudio/index'
95
+
96
+ export {
97
+ BaseAudioAdapter,
98
+ type AudioAdapter,
99
+ type AudioAdapterConfig,
100
+ type AnyAudioAdapter,
101
+ } from './generateAudio/adapter'
102
+
83
103
  // ===========================
84
104
  // Video Activity (Experimental)
85
105
  // ===========================
@@ -150,6 +170,7 @@ export type AIAdapter =
150
170
  | AnyTextAdapter
151
171
  | AnySummarizeAdapter
152
172
  | AnyImageAdapter
173
+ | AnyAudioAdapter
153
174
  | AnyVideoAdapter
154
175
  | AnyTTSAdapter
155
176
  | AnyTranscriptionAdapter
@@ -159,6 +180,7 @@ export type AdapterKind =
159
180
  | 'text'
160
181
  | 'summarize'
161
182
  | 'image'
183
+ | 'audio'
162
184
  | 'video'
163
185
  | 'tts'
164
186
  | 'transcription'
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { EventType } from '@ag-ui/core'
8
+ import { toRunErrorPayload } from './error-payload'
8
9
  import type { StreamChunk } from '../types'
9
10
 
10
11
  function createId(prefix: string): string {
@@ -52,18 +53,16 @@ export async function* streamGenerationResult<TResult>(
52
53
  finishReason: 'stop',
53
54
  timestamp: Date.now(),
54
55
  } as StreamChunk
55
- } catch (error: any) {
56
+ } catch (error: unknown) {
57
+ const payload = toRunErrorPayload(error, 'Generation failed')
56
58
  yield {
57
59
  type: EventType.RUN_ERROR,
58
60
  runId,
59
61
  threadId,
60
- message: error.message || 'Generation failed',
61
- code: error.code,
62
+ message: payload.message,
63
+ code: payload.code,
62
64
  // Deprecated nested form for backward compatibility
63
- error: {
64
- message: error.message || 'Generation failed',
65
- code: error.code,
66
- },
65
+ error: payload,
67
66
  timestamp: Date.now(),
68
67
  } as StreamChunk
69
68
  }
@@ -5,3 +5,4 @@
5
5
  export type { ResolvedCategories } from './logger/internal-logger'
6
6
  export { InternalLogger } from './logger/internal-logger'
7
7
  export { resolveDebugOption } from './logger/resolve'
8
+ export { toRunErrorPayload } from './activities/error-payload'
package/src/index.ts CHANGED
@@ -3,6 +3,7 @@ export {
3
3
  chat,
4
4
  summarize,
5
5
  generateImage,
6
+ generateAudio,
6
7
  generateVideo,
7
8
  getVideoJobStatus,
8
9
  generateSpeech,
@@ -13,6 +14,7 @@ export {
13
14
  export { createChatOptions } from './activities/chat/index'
14
15
  export { createSummarizeOptions } from './activities/summarize/index'
15
16
  export { createImageOptions } from './activities/generateImage/index'
17
+ export { createAudioOptions } from './activities/generateAudio/index'
16
18
  export { createVideoOptions } from './activities/generateVideo/index'
17
19
  export { createSpeechOptions } from './activities/generateSpeech/index'
18
20
  export { createTranscriptionOptions } from './activities/generateTranscription/index'
@@ -26,6 +28,8 @@ export type {
26
28
  AnyTextAdapter,
27
29
  AnySummarizeAdapter,
28
30
  SummarizeAdapter,
31
+ AnyAudioAdapter,
32
+ AudioAdapter,
29
33
  AnyTTSAdapter,
30
34
  TTSAdapter,
31
35
  AnyTranscriptionAdapter,
@@ -11,3 +11,8 @@ export {
11
11
  type ContentGuardRule,
12
12
  type ContentFilteredInfo,
13
13
  } from './content-guard'
14
+
15
+ // otelMiddleware is exported from the dedicated subpath
16
+ // `@tanstack/ai/middlewares/otel` so that importing the main middlewares barrel
17
+ // does not eagerly require `@opentelemetry/api` (which is an optional peer
18
+ // dependency).