@tanstack/ai 0.12.0 → 0.14.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/activities/chat/adapter.d.ts +6 -1
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +8 -0
- package/dist/esm/activities/chat/index.js +78 -17
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +3 -1
- package/dist/esm/activities/chat/middleware/compose.js +79 -1
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/error-payload.d.ts +12 -0
- package/dist/esm/activities/error-payload.js +25 -0
- package/dist/esm/activities/error-payload.js.map +1 -0
- package/dist/esm/activities/generateAudio/adapter.d.ts +62 -0
- package/dist/esm/activities/generateAudio/adapter.js +14 -0
- package/dist/esm/activities/generateAudio/adapter.js.map +1 -0
- package/dist/esm/activities/generateAudio/index.d.ts +74 -0
- package/dist/esm/activities/generateAudio/index.js +80 -0
- package/dist/esm/activities/generateAudio/index.js.map +1 -0
- package/dist/esm/activities/generateImage/adapter.d.ts +1 -1
- package/dist/esm/activities/generateImage/adapter.js +1 -1
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.d.ts +7 -0
- package/dist/esm/activities/generateImage/index.js +19 -3
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/adapter.d.ts +1 -1
- package/dist/esm/activities/generateSpeech/adapter.js +1 -1
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +10 -3
- package/dist/esm/activities/generateSpeech/index.js +32 -3
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/adapter.d.ts +1 -1
- package/dist/esm/activities/generateTranscription/adapter.js +1 -1
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +10 -3
- package/dist/esm/activities/generateTranscription/index.js +43 -13
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.d.ts +7 -0
- package/dist/esm/activities/generateVideo/index.js +55 -13
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/index.d.ts +5 -2
- package/dist/esm/activities/index.js +11 -6
- package/dist/esm/activities/index.js.map +1 -1
- package/dist/esm/activities/stream-generation-result.js +5 -6
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/index.d.ts +7 -0
- package/dist/esm/activities/summarize/index.js +54 -19
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +4 -0
- package/dist/esm/adapter-internals.js +9 -0
- package/dist/esm/adapter-internals.js.map +1 -0
- package/dist/esm/index.d.ts +5 -2
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger/console-logger.d.ts +11 -0
- package/dist/esm/logger/console-logger.js +27 -0
- package/dist/esm/logger/console-logger.js.map +1 -0
- package/dist/esm/logger/internal-logger.d.ts +33 -0
- package/dist/esm/logger/internal-logger.js +69 -0
- package/dist/esm/logger/internal-logger.js.map +1 -0
- package/dist/esm/logger/resolve.d.ts +14 -0
- package/dist/esm/logger/resolve.js +54 -0
- package/dist/esm/logger/resolve.js.map +1 -0
- package/dist/esm/logger/types.d.ts +75 -0
- package/dist/esm/stream-to-response.js +3 -8
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/types.d.ts +97 -6
- package/package.json +6 -2
- package/skills/ai-core/SKILL.md +5 -3
- package/skills/ai-core/debug-logging/SKILL.md +263 -0
- package/skills/ai-core/media-generation/SKILL.md +154 -10
- package/src/activities/chat/adapter.ts +6 -1
- package/src/activities/chat/index.ts +104 -22
- package/src/activities/chat/middleware/compose.ts +84 -1
- package/src/activities/error-payload.ts +35 -0
- package/src/activities/generateAudio/adapter.ts +89 -0
- package/src/activities/generateAudio/index.ts +224 -0
- package/src/activities/generateImage/adapter.ts +1 -1
- package/src/activities/generateImage/index.ts +29 -3
- package/src/activities/generateSpeech/adapter.ts +1 -1
- package/src/activities/generateSpeech/index.ts +51 -9
- package/src/activities/generateTranscription/adapter.ts +1 -1
- package/src/activities/generateTranscription/index.ts +72 -17
- package/src/activities/generateVideo/index.ts +72 -13
- package/src/activities/index.ts +22 -0
- package/src/activities/stream-generation-result.ts +6 -7
- package/src/activities/summarize/index.ts +66 -20
- package/src/adapter-internals.ts +8 -0
- package/src/index.ts +13 -0
- package/src/logger/console-logger.ts +49 -0
- package/src/logger/internal-logger.ts +107 -0
- package/src/logger/resolve.ts +72 -0
- package/src/logger/types.ts +78 -0
- package/src/stream-to-response.ts +5 -10
- package/src/types.ts +110 -5
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared error-narrowing helper for activities that convert thrown values
|
|
3
|
+
* into structured `RUN_ERROR` events.
|
|
4
|
+
*
|
|
5
|
+
* Accepts Error instances, objects with string-ish `message`/`code`, or bare
|
|
6
|
+
* strings; always returns a shape safe to serialize. Never leaks the full
|
|
7
|
+
* error object (which may carry request/response state from an SDK).
|
|
8
|
+
*/
|
|
9
|
+
export function toRunErrorPayload(
|
|
10
|
+
error: unknown,
|
|
11
|
+
fallbackMessage = 'Unknown error occurred',
|
|
12
|
+
): { message: string; code: string | undefined } {
|
|
13
|
+
if (error instanceof Error) {
|
|
14
|
+
const codeField = (error as Error & { code?: unknown }).code
|
|
15
|
+
return {
|
|
16
|
+
message: error.message || fallbackMessage,
|
|
17
|
+
code: typeof codeField === 'string' ? codeField : undefined,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (typeof error === 'object' && error !== null) {
|
|
21
|
+
const messageField = (error as { message?: unknown }).message
|
|
22
|
+
const codeField = (error as { code?: unknown }).code
|
|
23
|
+
return {
|
|
24
|
+
message:
|
|
25
|
+
typeof messageField === 'string' && messageField.length > 0
|
|
26
|
+
? messageField
|
|
27
|
+
: fallbackMessage,
|
|
28
|
+
code: typeof codeField === 'string' ? codeField : undefined,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (typeof error === 'string' && error.length > 0) {
|
|
32
|
+
return { message: error, code: undefined }
|
|
33
|
+
}
|
|
34
|
+
return { message: fallbackMessage, code: undefined }
|
|
35
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { AudioGenerationOptions, AudioGenerationResult } from '../../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for audio generation adapter instances
|
|
5
|
+
*/
|
|
6
|
+
export interface AudioAdapterConfig {
|
|
7
|
+
apiKey?: string
|
|
8
|
+
baseUrl?: string
|
|
9
|
+
timeout?: number
|
|
10
|
+
maxRetries?: number
|
|
11
|
+
headers?: Record<string, string>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Audio generation adapter interface with pre-resolved generics.
|
|
16
|
+
*
|
|
17
|
+
* An adapter is created by a provider function: `provider('model')` → `adapter`
|
|
18
|
+
* All type resolution happens at the provider call site, not in this interface.
|
|
19
|
+
*
|
|
20
|
+
* Generic parameters:
|
|
21
|
+
* - TModel: The specific model name (e.g., 'fal-ai/diffrhythm')
|
|
22
|
+
* - TProviderOptions: Provider-specific options (already resolved)
|
|
23
|
+
*/
|
|
24
|
+
export interface AudioAdapter<
|
|
25
|
+
TModel extends string = string,
|
|
26
|
+
TProviderOptions extends object = Record<string, unknown>,
|
|
27
|
+
> {
|
|
28
|
+
/** Discriminator for adapter kind - used to determine API shape */
|
|
29
|
+
readonly kind: 'audio'
|
|
30
|
+
/** Adapter name identifier */
|
|
31
|
+
readonly name: string
|
|
32
|
+
/** The model this adapter is configured for */
|
|
33
|
+
readonly model: TModel
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @internal Type-only properties for inference. Not assigned at runtime.
|
|
37
|
+
*/
|
|
38
|
+
'~types': {
|
|
39
|
+
providerOptions: TProviderOptions
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate audio from a text prompt
|
|
44
|
+
*/
|
|
45
|
+
generateAudio: (
|
|
46
|
+
options: AudioGenerationOptions<TProviderOptions>,
|
|
47
|
+
) => Promise<AudioGenerationResult>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* An AudioAdapter with any/unknown type parameters.
|
|
52
|
+
* Useful as a constraint in generic functions and interfaces.
|
|
53
|
+
*/
|
|
54
|
+
export type AnyAudioAdapter = AudioAdapter<any, any>
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Abstract base class for audio generation adapters.
|
|
58
|
+
* Extend this class to implement an audio adapter for a specific provider.
|
|
59
|
+
*
|
|
60
|
+
* Generic parameters match AudioAdapter - all pre-resolved by the provider function.
|
|
61
|
+
*/
|
|
62
|
+
export abstract class BaseAudioAdapter<
|
|
63
|
+
TModel extends string = string,
|
|
64
|
+
TProviderOptions extends object = Record<string, unknown>,
|
|
65
|
+
> implements AudioAdapter<TModel, TProviderOptions> {
|
|
66
|
+
readonly kind = 'audio' as const
|
|
67
|
+
abstract readonly name: string
|
|
68
|
+
readonly model: TModel
|
|
69
|
+
|
|
70
|
+
// Type-only property - never assigned at runtime
|
|
71
|
+
declare '~types': {
|
|
72
|
+
providerOptions: TProviderOptions
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
protected config: AudioAdapterConfig
|
|
76
|
+
|
|
77
|
+
constructor(model: TModel, config: AudioAdapterConfig = {}) {
|
|
78
|
+
this.config = config
|
|
79
|
+
this.model = model
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
abstract generateAudio(
|
|
83
|
+
options: AudioGenerationOptions<TProviderOptions>,
|
|
84
|
+
): Promise<AudioGenerationResult>
|
|
85
|
+
|
|
86
|
+
protected generateId(): string {
|
|
87
|
+
return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -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 = {}
|
|
99
|
+
constructor(model: TModel, config: ImageAdapterConfig = {}) {
|
|
100
100
|
this.config = config
|
|
101
101
|
this.model = model
|
|
102
102
|
}
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
import { aiEventClient } from '@tanstack/ai-event-client'
|
|
9
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'
|
|
10
13
|
import type { ImageAdapter } from './adapter'
|
|
11
14
|
import type { ImageGenerationResult, StreamChunk } from '../../types'
|
|
12
15
|
|
|
@@ -83,6 +86,12 @@ export type ImageActivityOptions<
|
|
|
83
86
|
* @default false
|
|
84
87
|
*/
|
|
85
88
|
stream?: TStream
|
|
89
|
+
/**
|
|
90
|
+
* Enable debug logging. Pass `true` to enable all categories, `false` to
|
|
91
|
+
* silence everything including errors, or a `DebugConfig` object for granular
|
|
92
|
+
* control and/or a custom `Logger`.
|
|
93
|
+
*/
|
|
94
|
+
debug?: DebugOption
|
|
86
95
|
} & ({} extends ImageProviderOptionsForModel<TAdapter, TAdapter['model']>
|
|
87
96
|
? {
|
|
88
97
|
/** Provider-specific options for image generation */ modelOptions?: ImageProviderOptionsForModel<
|
|
@@ -188,10 +197,11 @@ async function runGenerateImage<
|
|
|
188
197
|
>(
|
|
189
198
|
options: ImageActivityOptions<TAdapter, boolean>,
|
|
190
199
|
): Promise<ImageGenerationResult> {
|
|
191
|
-
const { adapter, stream: _stream, ...rest } = options
|
|
200
|
+
const { adapter, stream: _stream, debug: _debug, ...rest } = options
|
|
192
201
|
const model = adapter.model
|
|
193
202
|
const requestId = createId('image')
|
|
194
203
|
const startTime = Date.now()
|
|
204
|
+
const logger: InternalLogger = resolveDebugOption(options.debug)
|
|
195
205
|
|
|
196
206
|
aiEventClient.emit('image:request:started', {
|
|
197
207
|
requestId,
|
|
@@ -204,7 +214,13 @@ async function runGenerateImage<
|
|
|
204
214
|
timestamp: startTime,
|
|
205
215
|
})
|
|
206
216
|
|
|
207
|
-
|
|
217
|
+
logger.request(`activity=generateImage provider=${adapter.name}`, {
|
|
218
|
+
provider: adapter.name,
|
|
219
|
+
model,
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const result = await adapter.generateImages({ ...rest, model, logger })
|
|
208
224
|
const duration = Date.now() - startTime
|
|
209
225
|
|
|
210
226
|
aiEventClient.emit('image:request:completed', {
|
|
@@ -230,8 +246,18 @@ async function runGenerateImage<
|
|
|
230
246
|
})
|
|
231
247
|
}
|
|
232
248
|
|
|
249
|
+
logger.output(`activity=generateImage count=${result.images.length}`, {
|
|
250
|
+
count: result.images.length,
|
|
251
|
+
})
|
|
252
|
+
|
|
233
253
|
return result
|
|
234
|
-
})
|
|
254
|
+
} catch (error) {
|
|
255
|
+
logger.errors('generateImage activity failed', {
|
|
256
|
+
error,
|
|
257
|
+
source: 'generateImage',
|
|
258
|
+
})
|
|
259
|
+
throw error
|
|
260
|
+
}
|
|
235
261
|
}
|
|
236
262
|
|
|
237
263
|
// ===========================
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
import { aiEventClient } from '@tanstack/ai-event-client'
|
|
9
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'
|
|
10
13
|
import type { TTSAdapter } from './adapter'
|
|
11
14
|
import type { StreamChunk, TTSResult } from '../../types'
|
|
12
15
|
|
|
@@ -41,7 +44,7 @@ export type TTSProviderOptions<TAdapter> =
|
|
|
41
44
|
* @template TStream - Whether to stream the output
|
|
42
45
|
*/
|
|
43
46
|
export interface TTSActivityOptions<
|
|
44
|
-
TAdapter extends TTSAdapter<string,
|
|
47
|
+
TAdapter extends TTSAdapter<string, TTSProviderOptions<TAdapter>>,
|
|
45
48
|
TStream extends boolean = false,
|
|
46
49
|
> {
|
|
47
50
|
/** The TTS adapter to use (must be created with a model) */
|
|
@@ -64,6 +67,12 @@ export interface TTSActivityOptions<
|
|
|
64
67
|
* @default false
|
|
65
68
|
*/
|
|
66
69
|
stream?: TStream
|
|
70
|
+
/**
|
|
71
|
+
* Enable debug logging. Pass `true` to enable all categories, `false` to
|
|
72
|
+
* silence everything including errors, or a `DebugConfig` object for granular
|
|
73
|
+
* control and/or a custom `Logger`.
|
|
74
|
+
*/
|
|
75
|
+
debug?: DebugOption
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
// ===========================
|
|
@@ -117,7 +126,7 @@ function createId(prefix: string): string {
|
|
|
117
126
|
* ```
|
|
118
127
|
*/
|
|
119
128
|
export function generateSpeech<
|
|
120
|
-
TAdapter extends TTSAdapter<string,
|
|
129
|
+
TAdapter extends TTSAdapter<string, TTSProviderOptions<TAdapter>>,
|
|
121
130
|
TStream extends boolean = false,
|
|
122
131
|
>(options: TTSActivityOptions<TAdapter, TStream>): TTSActivityResult<TStream> {
|
|
123
132
|
if (options.stream) {
|
|
@@ -131,13 +140,18 @@ export function generateSpeech<
|
|
|
131
140
|
/**
|
|
132
141
|
* Run the core TTS generation logic (non-streaming).
|
|
133
142
|
*/
|
|
134
|
-
async function runGenerateSpeech<
|
|
135
|
-
|
|
136
|
-
): Promise<TTSResult> {
|
|
137
|
-
const { adapter, stream: _stream, ...rest } = options
|
|
143
|
+
async function runGenerateSpeech<
|
|
144
|
+
TAdapter extends TTSAdapter<string, TTSProviderOptions<TAdapter>>,
|
|
145
|
+
>(options: TTSActivityOptions<TAdapter, boolean>): Promise<TTSResult> {
|
|
146
|
+
const { adapter, stream: _stream, debug: _debug, ...rest } = options
|
|
138
147
|
const model = adapter.model
|
|
139
148
|
const requestId = createId('speech')
|
|
140
149
|
const startTime = Date.now()
|
|
150
|
+
const logger: InternalLogger = resolveDebugOption(options.debug)
|
|
151
|
+
const providerName =
|
|
152
|
+
(adapter as { name?: string; provider?: string }).provider ??
|
|
153
|
+
(adapter as { name?: string }).name ??
|
|
154
|
+
'unknown'
|
|
141
155
|
|
|
142
156
|
aiEventClient.emit('speech:request:started', {
|
|
143
157
|
requestId,
|
|
@@ -151,7 +165,13 @@ async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
|
|
|
151
165
|
timestamp: startTime,
|
|
152
166
|
})
|
|
153
167
|
|
|
154
|
-
|
|
168
|
+
logger.request(`activity=generateSpeech provider=${providerName}`, {
|
|
169
|
+
provider: providerName,
|
|
170
|
+
model,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const result = await adapter.generateSpeech({ ...rest, model, logger })
|
|
155
175
|
const duration = Date.now() - startTime
|
|
156
176
|
|
|
157
177
|
aiEventClient.emit('speech:request:completed', {
|
|
@@ -167,8 +187,30 @@ async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
|
|
|
167
187
|
timestamp: Date.now(),
|
|
168
188
|
})
|
|
169
189
|
|
|
190
|
+
logger.output(`activity=generateSpeech bytes=${result.audio.length}`, {
|
|
191
|
+
bytes: result.audio.length,
|
|
192
|
+
contentType: result.contentType,
|
|
193
|
+
})
|
|
194
|
+
|
|
170
195
|
return result
|
|
171
|
-
})
|
|
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
|
+
})
|
|
208
|
+
logger.errors('generateSpeech activity failed', {
|
|
209
|
+
error,
|
|
210
|
+
source: 'generateSpeech',
|
|
211
|
+
})
|
|
212
|
+
throw error
|
|
213
|
+
}
|
|
172
214
|
}
|
|
173
215
|
|
|
174
216
|
// ===========================
|
|
@@ -179,7 +221,7 @@ async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
|
|
|
179
221
|
* Create typed options for the generateSpeech() function without executing.
|
|
180
222
|
*/
|
|
181
223
|
export function createSpeechOptions<
|
|
182
|
-
TAdapter extends TTSAdapter<string,
|
|
224
|
+
TAdapter extends TTSAdapter<string, TTSProviderOptions<TAdapter>>,
|
|
183
225
|
TStream extends boolean = false,
|
|
184
226
|
>(
|
|
185
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 = {}
|
|
77
|
+
constructor(model: TModel, config: TranscriptionAdapterConfig = {}) {
|
|
78
78
|
this.config = config
|
|
79
79
|
this.model = model
|
|
80
80
|
}
|