@tanstack/ai 0.11.1 → 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/activities/chat/adapter.d.ts +12 -4
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +23 -3
- 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/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/index.d.ts +7 -0
- package/dist/esm/activities/generateSpeech/index.js +21 -3
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +7 -0
- package/dist/esm/activities/generateTranscription/index.js +32 -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 +49 -7
- package/dist/esm/activities/generateVideo/index.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 +3 -0
- package/dist/esm/adapter-internals.js +7 -0
- package/dist/esm/adapter-internals.js.map +1 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +2 -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/tools/provider-tool.d.ts +21 -0
- package/dist/esm/types.d.ts +34 -0
- package/package.json +6 -2
- package/skills/ai-core/SKILL.md +5 -3
- package/skills/ai-core/debug-logging/SKILL.md +263 -0
- package/src/activities/chat/adapter.ts +14 -3
- package/src/activities/chat/index.ts +119 -24
- package/src/activities/chat/middleware/compose.ts +84 -1
- package/src/activities/generateImage/index.ts +29 -3
- package/src/activities/generateSpeech/index.ts +35 -3
- package/src/activities/generateTranscription/index.ts +45 -13
- package/src/activities/generateVideo/index.ts +65 -6
- package/src/activities/summarize/index.ts +66 -20
- package/src/adapter-internals.ts +7 -0
- package/src/index.ts +12 -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/tools/provider-tool.ts +25 -0
- package/src/types.ts +36 -0
|
@@ -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 { TranscriptionAdapter } from './adapter'
|
|
11
14
|
import type { StreamChunk, TranscriptionResult } from '../../types'
|
|
12
15
|
|
|
@@ -64,6 +67,12 @@ export interface TranscriptionActivityOptions<
|
|
|
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
|
// ===========================
|
|
@@ -156,10 +165,15 @@ async function runGenerateTranscription<
|
|
|
156
165
|
>(
|
|
157
166
|
options: TranscriptionActivityOptions<TAdapter, boolean>,
|
|
158
167
|
): Promise<TranscriptionResult> {
|
|
159
|
-
const { adapter, stream: _stream, ...rest } = options
|
|
168
|
+
const { adapter, stream: _stream, debug: _debug, ...rest } = options
|
|
160
169
|
const model = adapter.model
|
|
161
170
|
const requestId = createId('transcription')
|
|
162
171
|
const startTime = Date.now()
|
|
172
|
+
const logger: InternalLogger = resolveDebugOption(options.debug)
|
|
173
|
+
const providerName =
|
|
174
|
+
(adapter as { name?: string; provider?: string }).provider ??
|
|
175
|
+
(adapter as { name?: string }).name ??
|
|
176
|
+
'unknown'
|
|
163
177
|
|
|
164
178
|
aiEventClient.emit('transcription:request:started', {
|
|
165
179
|
requestId,
|
|
@@ -172,21 +186,39 @@ async function runGenerateTranscription<
|
|
|
172
186
|
timestamp: startTime,
|
|
173
187
|
})
|
|
174
188
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
aiEventClient.emit('transcription:request:completed', {
|
|
179
|
-
requestId,
|
|
180
|
-
provider: adapter.name,
|
|
189
|
+
logger.request(`activity=generateTranscription provider=${providerName}`, {
|
|
190
|
+
provider: providerName,
|
|
181
191
|
model,
|
|
182
|
-
text: result.text,
|
|
183
|
-
language: result.language,
|
|
184
|
-
duration,
|
|
185
|
-
modelOptions: rest.modelOptions as Record<string, unknown> | undefined,
|
|
186
|
-
timestamp: Date.now(),
|
|
187
192
|
})
|
|
188
193
|
|
|
189
|
-
|
|
194
|
+
try {
|
|
195
|
+
const result = await adapter.transcribe({ ...rest, model, logger })
|
|
196
|
+
const duration = Date.now() - startTime
|
|
197
|
+
|
|
198
|
+
aiEventClient.emit('transcription:request:completed', {
|
|
199
|
+
requestId,
|
|
200
|
+
provider: adapter.name,
|
|
201
|
+
model,
|
|
202
|
+
text: result.text,
|
|
203
|
+
language: result.language,
|
|
204
|
+
duration,
|
|
205
|
+
modelOptions: rest.modelOptions as Record<string, unknown> | undefined,
|
|
206
|
+
timestamp: Date.now(),
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
logger.output(
|
|
210
|
+
`activity=generateTranscription length=${result.text.length}`,
|
|
211
|
+
{ hasText: !!result.text },
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return result
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logger.errors('generateTranscription activity failed', {
|
|
217
|
+
error,
|
|
218
|
+
source: 'generateTranscription',
|
|
219
|
+
})
|
|
220
|
+
throw error
|
|
221
|
+
}
|
|
190
222
|
}
|
|
191
223
|
|
|
192
224
|
// ===========================
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { aiEventClient } from '@tanstack/ai-event-client'
|
|
11
|
+
import { resolveDebugOption } from '../../logger/resolve'
|
|
12
|
+
import type { InternalLogger } from '../../logger/internal-logger'
|
|
13
|
+
import type { DebugOption } from '../../logger/types'
|
|
11
14
|
import type { VideoAdapter } from './adapter'
|
|
12
15
|
import type {
|
|
13
16
|
StreamChunk,
|
|
@@ -100,6 +103,12 @@ export type VideoCreateOptions<
|
|
|
100
103
|
maxDuration?: number
|
|
101
104
|
/** Custom run ID (stream mode only) */
|
|
102
105
|
runId?: string
|
|
106
|
+
/**
|
|
107
|
+
* Enable debug logging. Pass `true` to enable all categories, `false` to
|
|
108
|
+
* silence everything including errors, or a `DebugConfig` object for granular
|
|
109
|
+
* control and/or a custom `Logger`.
|
|
110
|
+
*/
|
|
111
|
+
debug?: DebugOption
|
|
103
112
|
} & ({} extends VideoProviderOptions<TAdapter>
|
|
104
113
|
? {
|
|
105
114
|
/** Provider-specific options for video generation */ modelOptions?: VideoProviderOptions<TAdapter>
|
|
@@ -241,14 +250,38 @@ async function runCreateVideoJob<
|
|
|
241
250
|
>(options: VideoCreateOptions<TAdapter, boolean>): Promise<VideoJobResult> {
|
|
242
251
|
const { adapter, prompt, size, duration, modelOptions } = options
|
|
243
252
|
const model = adapter.model
|
|
244
|
-
|
|
245
|
-
|
|
253
|
+
const logger: InternalLogger = resolveDebugOption(options.debug)
|
|
254
|
+
const providerName =
|
|
255
|
+
(adapter as { name?: string; provider?: string }).provider ??
|
|
256
|
+
(adapter as { name?: string }).name ??
|
|
257
|
+
'unknown'
|
|
258
|
+
|
|
259
|
+
logger.request(`activity=generateVideo provider=${providerName}`, {
|
|
260
|
+
provider: providerName,
|
|
246
261
|
model,
|
|
247
|
-
prompt,
|
|
248
|
-
size,
|
|
249
|
-
duration,
|
|
250
|
-
modelOptions,
|
|
251
262
|
})
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
const result = await adapter.createVideoJob({
|
|
266
|
+
model,
|
|
267
|
+
prompt,
|
|
268
|
+
size,
|
|
269
|
+
duration,
|
|
270
|
+
modelOptions,
|
|
271
|
+
logger,
|
|
272
|
+
})
|
|
273
|
+
logger.output(`activity=generateVideo jobId=${result.jobId}`, {
|
|
274
|
+
jobId: result.jobId,
|
|
275
|
+
model: result.model,
|
|
276
|
+
})
|
|
277
|
+
return result
|
|
278
|
+
} catch (error) {
|
|
279
|
+
logger.errors('generateVideo activity failed', {
|
|
280
|
+
error,
|
|
281
|
+
source: 'generateVideo',
|
|
282
|
+
})
|
|
283
|
+
throw error
|
|
284
|
+
}
|
|
252
285
|
}
|
|
253
286
|
|
|
254
287
|
function sleep(ms: number): Promise<void> {
|
|
@@ -267,6 +300,11 @@ async function* runStreamingVideoGeneration<
|
|
|
267
300
|
const runId = options.runId ?? createId('run')
|
|
268
301
|
const pollingInterval = options.pollingInterval ?? 2000
|
|
269
302
|
const maxDuration = options.maxDuration ?? 600_000
|
|
303
|
+
const logger: InternalLogger = resolveDebugOption(options.debug)
|
|
304
|
+
const providerName =
|
|
305
|
+
(adapter as { name?: string; provider?: string }).provider ??
|
|
306
|
+
(adapter as { name?: string }).name ??
|
|
307
|
+
'unknown'
|
|
270
308
|
|
|
271
309
|
const threadId = createId('thread')
|
|
272
310
|
|
|
@@ -277,6 +315,14 @@ async function* runStreamingVideoGeneration<
|
|
|
277
315
|
timestamp: Date.now(),
|
|
278
316
|
} as StreamChunk
|
|
279
317
|
|
|
318
|
+
logger.request(
|
|
319
|
+
`activity=generateVideo provider=${providerName} stream=true`,
|
|
320
|
+
{
|
|
321
|
+
provider: providerName,
|
|
322
|
+
model,
|
|
323
|
+
},
|
|
324
|
+
)
|
|
325
|
+
|
|
280
326
|
try {
|
|
281
327
|
// Create the video generation job
|
|
282
328
|
const jobResult = await adapter.createVideoJob({
|
|
@@ -285,6 +331,7 @@ async function* runStreamingVideoGeneration<
|
|
|
285
331
|
size,
|
|
286
332
|
duration,
|
|
287
333
|
modelOptions,
|
|
334
|
+
logger,
|
|
288
335
|
})
|
|
289
336
|
|
|
290
337
|
yield {
|
|
@@ -316,6 +363,14 @@ async function* runStreamingVideoGeneration<
|
|
|
316
363
|
if (statusResult.status === 'completed') {
|
|
317
364
|
const urlResult = await adapter.getVideoUrl(jobResult.jobId)
|
|
318
365
|
|
|
366
|
+
logger.output(
|
|
367
|
+
`activity=generateVideo jobId=${jobResult.jobId} status=completed`,
|
|
368
|
+
{
|
|
369
|
+
jobId: jobResult.jobId,
|
|
370
|
+
url: urlResult.url,
|
|
371
|
+
},
|
|
372
|
+
)
|
|
373
|
+
|
|
319
374
|
yield {
|
|
320
375
|
type: 'CUSTOM',
|
|
321
376
|
name: 'generation:result',
|
|
@@ -345,6 +400,10 @@ async function* runStreamingVideoGeneration<
|
|
|
345
400
|
|
|
346
401
|
throw new Error('Video generation timed out')
|
|
347
402
|
} catch (error: any) {
|
|
403
|
+
logger.errors('generateVideo activity failed', {
|
|
404
|
+
error,
|
|
405
|
+
source: 'generateVideo',
|
|
406
|
+
})
|
|
348
407
|
yield {
|
|
349
408
|
type: 'RUN_ERROR',
|
|
350
409
|
runId,
|
|
@@ -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 { SummarizeAdapter } from './adapter'
|
|
11
14
|
import type {
|
|
12
15
|
StreamChunk,
|
|
@@ -66,6 +69,12 @@ export interface SummarizeActivityOptions<
|
|
|
66
69
|
* @default false
|
|
67
70
|
*/
|
|
68
71
|
stream?: TStream
|
|
72
|
+
/**
|
|
73
|
+
* Enable debug logging. Pass `true` to enable all categories, `false` to
|
|
74
|
+
* silence everything including errors, or a `DebugConfig` object for granular
|
|
75
|
+
* control and/or a custom `Logger`.
|
|
76
|
+
*/
|
|
77
|
+
debug?: DebugOption
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
// ===========================
|
|
@@ -180,6 +189,7 @@ async function runSummarize(
|
|
|
180
189
|
const requestId = createId('summarize')
|
|
181
190
|
const inputLength = text.length
|
|
182
191
|
const startTime = Date.now()
|
|
192
|
+
const logger: InternalLogger = resolveDebugOption(options.debug)
|
|
183
193
|
|
|
184
194
|
aiEventClient.emit('summarize:request:started', {
|
|
185
195
|
requestId,
|
|
@@ -189,30 +199,50 @@ async function runSummarize(
|
|
|
189
199
|
timestamp: startTime,
|
|
190
200
|
})
|
|
191
201
|
|
|
202
|
+
logger.request(`activity=summarize provider=${adapter.name}`, {
|
|
203
|
+
provider: adapter.name,
|
|
204
|
+
model,
|
|
205
|
+
inputLength,
|
|
206
|
+
})
|
|
207
|
+
|
|
192
208
|
const summarizeOptions: SummarizationOptions = {
|
|
193
209
|
model,
|
|
194
210
|
text,
|
|
195
211
|
maxLength,
|
|
196
212
|
style,
|
|
197
213
|
focus,
|
|
214
|
+
logger,
|
|
198
215
|
}
|
|
199
216
|
|
|
200
|
-
|
|
217
|
+
try {
|
|
218
|
+
const result = await adapter.summarize(summarizeOptions)
|
|
201
219
|
|
|
202
|
-
|
|
203
|
-
|
|
220
|
+
const duration = Date.now() - startTime
|
|
221
|
+
const outputLength = result.summary.length
|
|
204
222
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
223
|
+
aiEventClient.emit('summarize:request:completed', {
|
|
224
|
+
requestId,
|
|
225
|
+
provider: adapter.name,
|
|
226
|
+
model,
|
|
227
|
+
inputLength,
|
|
228
|
+
outputLength,
|
|
229
|
+
duration,
|
|
230
|
+
timestamp: Date.now(),
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
logger.output(`activity=summarize length=${outputLength}`, {
|
|
234
|
+
hasSummary: !!result.summary,
|
|
235
|
+
outputLength,
|
|
236
|
+
})
|
|
214
237
|
|
|
215
|
-
|
|
238
|
+
return result
|
|
239
|
+
} catch (error) {
|
|
240
|
+
logger.errors('summarize activity failed', {
|
|
241
|
+
error,
|
|
242
|
+
source: 'summarize',
|
|
243
|
+
})
|
|
244
|
+
throw error
|
|
245
|
+
}
|
|
216
246
|
}
|
|
217
247
|
|
|
218
248
|
/**
|
|
@@ -225,6 +255,13 @@ async function* runStreamingSummarize(
|
|
|
225
255
|
): AsyncIterable<StreamChunk> {
|
|
226
256
|
const { adapter, text, maxLength, style, focus } = options
|
|
227
257
|
const model = adapter.model
|
|
258
|
+
const logger: InternalLogger = resolveDebugOption(options.debug)
|
|
259
|
+
|
|
260
|
+
logger.request(`activity=summarize provider=${adapter.name}`, {
|
|
261
|
+
provider: adapter.name,
|
|
262
|
+
model,
|
|
263
|
+
stream: true,
|
|
264
|
+
})
|
|
228
265
|
|
|
229
266
|
const summarizeOptions: SummarizationOptions = {
|
|
230
267
|
model,
|
|
@@ -232,16 +269,25 @@ async function* runStreamingSummarize(
|
|
|
232
269
|
maxLength,
|
|
233
270
|
style,
|
|
234
271
|
focus,
|
|
272
|
+
logger,
|
|
235
273
|
}
|
|
236
274
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
275
|
+
try {
|
|
276
|
+
// Use real streaming if the adapter supports it
|
|
277
|
+
if (adapter.summarizeStream) {
|
|
278
|
+
yield* adapter.summarizeStream(summarizeOptions)
|
|
279
|
+
return
|
|
280
|
+
}
|
|
242
281
|
|
|
243
|
-
|
|
244
|
-
|
|
282
|
+
// Fall back to non-streaming — wrap result with streamGenerationResult
|
|
283
|
+
yield* streamGenerationResult(() => adapter.summarize(summarizeOptions))
|
|
284
|
+
} catch (error) {
|
|
285
|
+
logger.errors('summarize activity failed', {
|
|
286
|
+
error,
|
|
287
|
+
source: 'summarize',
|
|
288
|
+
})
|
|
289
|
+
throw error
|
|
290
|
+
}
|
|
245
291
|
}
|
|
246
292
|
|
|
247
293
|
// ===========================
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// NOTE: This module is exposed ONLY via the `@tanstack/ai/adapter-internals`
|
|
2
|
+
// subpath export. It gives provider adapter packages access to the internal
|
|
3
|
+
// logger plumbing without leaking those symbols to end users.
|
|
4
|
+
|
|
5
|
+
export type { ResolvedCategories } from './logger/internal-logger'
|
|
6
|
+
export { InternalLogger } from './logger/internal-logger'
|
|
7
|
+
export { resolveDebugOption } from './logger/resolve'
|
package/src/index.ts
CHANGED
|
@@ -63,6 +63,9 @@ export {
|
|
|
63
63
|
// Tool call management
|
|
64
64
|
export { ToolCallManager } from './activities/chat/tools/tool-calls'
|
|
65
65
|
|
|
66
|
+
// Provider tool type
|
|
67
|
+
export type { ProviderTool } from './tools/provider-tool'
|
|
68
|
+
|
|
66
69
|
// Agent loop strategies
|
|
67
70
|
export {
|
|
68
71
|
maxIterations,
|
|
@@ -164,3 +167,12 @@ export type {
|
|
|
164
167
|
// Adapter extension utilities
|
|
165
168
|
export { createModel, extendAdapter } from './extend-adapter'
|
|
166
169
|
export type { ExtendedModelDef } from './extend-adapter'
|
|
170
|
+
|
|
171
|
+
// Logger
|
|
172
|
+
export type {
|
|
173
|
+
Logger,
|
|
174
|
+
DebugCategories,
|
|
175
|
+
DebugConfig,
|
|
176
|
+
DebugOption,
|
|
177
|
+
} from './logger/types'
|
|
178
|
+
export { ConsoleLogger } from './logger/console-logger'
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Logger } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default `Logger` implementation that routes each level to the matching
|
|
5
|
+
* `console` method:
|
|
6
|
+
*
|
|
7
|
+
* - `debug` → `console.debug`
|
|
8
|
+
* - `info` → `console.info`
|
|
9
|
+
* - `warn` → `console.warn`
|
|
10
|
+
* - `error` → `console.error`
|
|
11
|
+
*
|
|
12
|
+
* When a `meta` object is supplied, the message is logged first and the meta
|
|
13
|
+
* object is then printed via `console.dir(meta, { depth: null, colors: true })`
|
|
14
|
+
* so deeply nested structures (e.g. provider chunk payloads with `usage`,
|
|
15
|
+
* `output`, `reasoning`, `tools`) render in full instead of truncating to
|
|
16
|
+
* `[Object]` / `[Array]`. On Node this produces a depth-unlimited inspect
|
|
17
|
+
* dump; browsers present the object as an interactive tree (extra options
|
|
18
|
+
* are ignored).
|
|
19
|
+
*
|
|
20
|
+
* This is the logger used when `debug` is enabled on any activity and no
|
|
21
|
+
* custom `logger` is supplied via `debug: { logger }`.
|
|
22
|
+
*/
|
|
23
|
+
const DIR_OPTIONS = { depth: null, colors: true } as const
|
|
24
|
+
|
|
25
|
+
export class ConsoleLogger implements Logger {
|
|
26
|
+
/** Log a debug-level message; forwards to `console.debug`. */
|
|
27
|
+
debug(message: string, meta?: Record<string, unknown>): void {
|
|
28
|
+
console.debug(message)
|
|
29
|
+
if (meta !== undefined) console.dir(meta, DIR_OPTIONS)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Log an info-level message; forwards to `console.info`. */
|
|
33
|
+
info(message: string, meta?: Record<string, unknown>): void {
|
|
34
|
+
console.info(message)
|
|
35
|
+
if (meta !== undefined) console.dir(meta, DIR_OPTIONS)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Log a warning-level message; forwards to `console.warn`. */
|
|
39
|
+
warn(message: string, meta?: Record<string, unknown>): void {
|
|
40
|
+
console.warn(message)
|
|
41
|
+
if (meta !== undefined) console.dir(meta, DIR_OPTIONS)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Log an error-level message; forwards to `console.error`. */
|
|
45
|
+
error(message: string, meta?: Record<string, unknown>): void {
|
|
46
|
+
console.error(message)
|
|
47
|
+
if (meta !== undefined) console.dir(meta, DIR_OPTIONS)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { DebugCategories, Logger } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fully-resolved categories map. Every flag is a definite boolean (never
|
|
5
|
+
* undefined), produced by `resolveDebugOption` from a `DebugOption`.
|
|
6
|
+
*/
|
|
7
|
+
export type ResolvedCategories = Required<DebugCategories>
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Package-internal logger wrapper used by every activity and adapter in
|
|
11
|
+
* `@tanstack/ai`. Wraps a user-supplied (or default `ConsoleLogger`) `Logger`
|
|
12
|
+
* plus a fully-resolved per-category map. Each category has a dedicated
|
|
13
|
+
* method that no-ops when its flag is `false`, or prepends a
|
|
14
|
+
* `[tanstack-ai:<category>] ` prefix and calls the underlying logger's
|
|
15
|
+
* `error` (for the `errors` category) or `debug` (for everything else).
|
|
16
|
+
*
|
|
17
|
+
* Not exported from the package root. Adapter packages consume it via the
|
|
18
|
+
* `@tanstack/ai/adapter-internals` subpath export.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Emoji marker per category — bracketing the `[tanstack-ai:<cat>]` tag on
|
|
22
|
+
* both sides makes it trivial to visually pick out a category when scanning
|
|
23
|
+
* dense streaming logs.
|
|
24
|
+
*/
|
|
25
|
+
const CATEGORY_EMOJI: Record<keyof ResolvedCategories, string> = {
|
|
26
|
+
request: '📤',
|
|
27
|
+
provider: '📥',
|
|
28
|
+
output: '📨',
|
|
29
|
+
middleware: '🧩',
|
|
30
|
+
tools: '🔧',
|
|
31
|
+
agentLoop: '🔁',
|
|
32
|
+
config: '⚙️',
|
|
33
|
+
errors: '❌',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class InternalLogger {
|
|
37
|
+
constructor(
|
|
38
|
+
private readonly logger: Logger,
|
|
39
|
+
private readonly categories: ResolvedCategories,
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
/** Whether a category is enabled. Cheap, safe to call on hot paths. */
|
|
43
|
+
isEnabled(category: keyof ResolvedCategories): boolean {
|
|
44
|
+
return this.categories[category]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private emit(
|
|
48
|
+
level: 'debug' | 'error',
|
|
49
|
+
category: keyof ResolvedCategories,
|
|
50
|
+
message: string,
|
|
51
|
+
meta?: Record<string, unknown>,
|
|
52
|
+
): void {
|
|
53
|
+
if (!this.categories[category]) return
|
|
54
|
+
const emoji = CATEGORY_EMOJI[category]
|
|
55
|
+
const prefixed = `${emoji} [tanstack-ai:${category}] ${emoji} ${message}`
|
|
56
|
+
try {
|
|
57
|
+
if (level === 'error') this.logger.error(prefixed, meta)
|
|
58
|
+
else this.logger.debug(prefixed, meta)
|
|
59
|
+
} catch {
|
|
60
|
+
// User-supplied logger threw; swallow so we never mask the original
|
|
61
|
+
// error that triggered this log call.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Log a raw chunk/frame received from a provider SDK. */
|
|
66
|
+
provider(message: string, meta?: Record<string, unknown>): void {
|
|
67
|
+
this.emit('debug', 'provider', message, meta)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Log a chunk/result yielded to the consumer after middleware. */
|
|
71
|
+
output(message: string, meta?: Record<string, unknown>): void {
|
|
72
|
+
this.emit('debug', 'output', message, meta)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Log inputs/outputs around a middleware hook invocation. Chat-only. */
|
|
76
|
+
middleware(message: string, meta?: Record<string, unknown>): void {
|
|
77
|
+
this.emit('debug', 'middleware', message, meta)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Log before/after a tool-call execution. Chat-only. */
|
|
81
|
+
tools(message: string, meta?: Record<string, unknown>): void {
|
|
82
|
+
this.emit('debug', 'tools', message, meta)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Log an agent-loop iteration marker or phase transition. Chat-only. */
|
|
86
|
+
agentLoop(message: string, meta?: Record<string, unknown>): void {
|
|
87
|
+
this.emit('debug', 'agentLoop', message, meta)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Log a config transform returned by a middleware `onConfig` hook. Chat-only. */
|
|
91
|
+
config(message: string, meta?: Record<string, unknown>): void {
|
|
92
|
+
this.emit('debug', 'config', message, meta)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Log a caught error. Defaults to on even when `debug` is unspecified.
|
|
97
|
+
* Uses the underlying logger's `error` level.
|
|
98
|
+
*/
|
|
99
|
+
errors(message: string, meta?: Record<string, unknown>): void {
|
|
100
|
+
this.emit('error', 'errors', message, meta)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Log outgoing request metadata before an adapter SDK call. */
|
|
104
|
+
request(message: string, meta?: Record<string, unknown>): void {
|
|
105
|
+
this.emit('debug', 'request', message, meta)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ConsoleLogger } from './console-logger'
|
|
2
|
+
import { InternalLogger } from './internal-logger'
|
|
3
|
+
import type { ResolvedCategories } from './internal-logger'
|
|
4
|
+
import type { DebugCategories, DebugConfig, DebugOption, Logger } from './types'
|
|
5
|
+
|
|
6
|
+
const ALL_OFF: ResolvedCategories = {
|
|
7
|
+
provider: false,
|
|
8
|
+
output: false,
|
|
9
|
+
middleware: false,
|
|
10
|
+
tools: false,
|
|
11
|
+
agentLoop: false,
|
|
12
|
+
config: false,
|
|
13
|
+
errors: false,
|
|
14
|
+
request: false,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ALL_ON: ResolvedCategories = {
|
|
18
|
+
provider: true,
|
|
19
|
+
output: true,
|
|
20
|
+
middleware: true,
|
|
21
|
+
tools: true,
|
|
22
|
+
agentLoop: true,
|
|
23
|
+
config: true,
|
|
24
|
+
errors: true,
|
|
25
|
+
request: true,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const errorsOnlyCategories = (): ResolvedCategories => ({
|
|
29
|
+
...ALL_OFF,
|
|
30
|
+
errors: true,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const resolveCategoriesFromPartial = (
|
|
34
|
+
partial: DebugCategories,
|
|
35
|
+
): ResolvedCategories => ({
|
|
36
|
+
provider: partial.provider ?? true,
|
|
37
|
+
output: partial.output ?? true,
|
|
38
|
+
middleware: partial.middleware ?? true,
|
|
39
|
+
tools: partial.tools ?? true,
|
|
40
|
+
agentLoop: partial.agentLoop ?? true,
|
|
41
|
+
config: partial.config ?? true,
|
|
42
|
+
errors: partial.errors ?? true,
|
|
43
|
+
request: partial.request ?? true,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Normalize a `DebugOption` into an `InternalLogger` ready to be threaded
|
|
48
|
+
* through the library's activities and adapters. See the `DebugOption`
|
|
49
|
+
* resolution table in the spec for the complete rules.
|
|
50
|
+
*
|
|
51
|
+
* - `undefined`: only the `errors` category is enabled; default `ConsoleLogger`.
|
|
52
|
+
* - `true`: all categories enabled; default `ConsoleLogger`.
|
|
53
|
+
* - `false`: all categories disabled (including `errors`); default `ConsoleLogger`.
|
|
54
|
+
* - `DebugConfig`: each unspecified category defaults to `true`; an optional
|
|
55
|
+
* `logger` replaces the default `ConsoleLogger`.
|
|
56
|
+
*/
|
|
57
|
+
export function resolveDebugOption(
|
|
58
|
+
debug: DebugOption | undefined,
|
|
59
|
+
): InternalLogger {
|
|
60
|
+
if (debug === undefined) {
|
|
61
|
+
return new InternalLogger(new ConsoleLogger(), errorsOnlyCategories())
|
|
62
|
+
}
|
|
63
|
+
if (debug === true) {
|
|
64
|
+
return new InternalLogger(new ConsoleLogger(), ALL_ON)
|
|
65
|
+
}
|
|
66
|
+
if (debug === false) {
|
|
67
|
+
return new InternalLogger(new ConsoleLogger(), ALL_OFF)
|
|
68
|
+
}
|
|
69
|
+
const { logger, ...cats }: DebugConfig = debug
|
|
70
|
+
const userLogger: Logger = logger ?? new ConsoleLogger()
|
|
71
|
+
return new InternalLogger(userLogger, resolveCategoriesFromPartial(cats))
|
|
72
|
+
}
|