@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
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { devtoolsMiddleware } from '@tanstack/ai-event-client'
|
|
9
9
|
import { stripToSpecMiddleware } from '../../strip-to-spec-middleware'
|
|
10
10
|
import { streamToText } from '../../stream-to-response.js'
|
|
11
|
+
import { resolveDebugOption } from '../../logger/resolve'
|
|
11
12
|
import { LazyToolManager } from './tools/lazy-tool-manager'
|
|
12
13
|
import {
|
|
13
14
|
MiddlewareAbortError,
|
|
@@ -51,6 +52,9 @@ import type {
|
|
|
51
52
|
ChatMiddlewareContext,
|
|
52
53
|
ChatMiddlewarePhase,
|
|
53
54
|
} from './middleware/types'
|
|
55
|
+
import type { InternalLogger } from '../../logger/internal-logger'
|
|
56
|
+
import type { DebugOption } from '../../logger/types'
|
|
57
|
+
import type { ProviderTool } from '../../tools/provider-tool'
|
|
54
58
|
|
|
55
59
|
// ===========================
|
|
56
60
|
// Activity Kind
|
|
@@ -87,8 +91,20 @@ export interface TextActivityOptions<
|
|
|
87
91
|
>
|
|
88
92
|
/** System prompts to prepend to the conversation */
|
|
89
93
|
systemPrompts?: TextOptions['systemPrompts']
|
|
90
|
-
/**
|
|
91
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Tools for function calling (auto-executed when called).
|
|
96
|
+
*
|
|
97
|
+
* Accepts two shapes:
|
|
98
|
+
* - User-defined tools via `toolDefinition()` — plain `Tool`, always assignable.
|
|
99
|
+
* - Provider tools from `@tanstack/ai-<provider>/tools` (e.g. `webSearchTool`)
|
|
100
|
+
* — branded and type-checked against the selected model's
|
|
101
|
+
* `supports.tools` list. Passing an unsupported tool produces a
|
|
102
|
+
* compile-time error on the array element.
|
|
103
|
+
*/
|
|
104
|
+
tools?: Array<
|
|
105
|
+
| (Tool & { readonly '~toolKind'?: never })
|
|
106
|
+
| ProviderTool<string, TAdapter['~types']['toolCapabilities'][number]>
|
|
107
|
+
>
|
|
92
108
|
/** Controls the randomness of the output. Higher values make output more random. Range: [0.0, 2.0] */
|
|
93
109
|
temperature?: TextOptions['temperature']
|
|
94
110
|
/** Nucleus sampling parameter. The model considers tokens with topP probability mass. */
|
|
@@ -168,6 +184,13 @@ export interface TextActivityOptions<
|
|
|
168
184
|
* Can be used to pass request-scoped data (e.g., user ID, request context).
|
|
169
185
|
*/
|
|
170
186
|
context?: unknown
|
|
187
|
+
/**
|
|
188
|
+
* Enable debug logging. Pass `true` to enable all categories with the default
|
|
189
|
+
* console logger, `false` to silence everything, or a `DebugConfig` object for
|
|
190
|
+
* granular control and/or a custom `Logger`. Defaults to `undefined`, which
|
|
191
|
+
* means only the `errors` category is active.
|
|
192
|
+
*/
|
|
193
|
+
debug?: DebugOption
|
|
171
194
|
}
|
|
172
195
|
|
|
173
196
|
// ===========================
|
|
@@ -280,7 +303,13 @@ class TextEngine<
|
|
|
280
303
|
private middlewareAbortController?: AbortController
|
|
281
304
|
private terminalHookCalled = false
|
|
282
305
|
|
|
283
|
-
|
|
306
|
+
private readonly logger: InternalLogger
|
|
307
|
+
|
|
308
|
+
constructor(
|
|
309
|
+
config: TextEngineConfig<TAdapter, TParams>,
|
|
310
|
+
logger: InternalLogger,
|
|
311
|
+
) {
|
|
312
|
+
this.logger = logger
|
|
284
313
|
this.adapter = config.adapter
|
|
285
314
|
this.params = config.params
|
|
286
315
|
this.systemPrompts = config.params.systemPrompts || []
|
|
@@ -328,7 +357,7 @@ class TextEngine<
|
|
|
328
357
|
...(config.middleware || []),
|
|
329
358
|
stripToSpecMiddleware(),
|
|
330
359
|
]
|
|
331
|
-
this.middlewareRunner = new MiddlewareRunner(allMiddleware)
|
|
360
|
+
this.middlewareRunner = new MiddlewareRunner(allMiddleware, logger)
|
|
332
361
|
this.middlewareAbortController = new AbortController()
|
|
333
362
|
this.middlewareCtx = {
|
|
334
363
|
requestId: this.requestId,
|
|
@@ -380,6 +409,9 @@ class TextEngine<
|
|
|
380
409
|
|
|
381
410
|
async *run(): AsyncGenerator<StreamChunk> {
|
|
382
411
|
this.beforeRun()
|
|
412
|
+
this.logger.agentLoop('run started', {
|
|
413
|
+
conversationId: this.middlewareCtx.conversationId,
|
|
414
|
+
})
|
|
383
415
|
|
|
384
416
|
try {
|
|
385
417
|
// Run initial onConfig (phase = init)
|
|
@@ -404,6 +436,10 @@ class TextEngine<
|
|
|
404
436
|
return
|
|
405
437
|
}
|
|
406
438
|
|
|
439
|
+
this.logger.agentLoop(`iteration=${this.middlewareCtx.iteration}`, {
|
|
440
|
+
iteration: this.middlewareCtx.iteration,
|
|
441
|
+
})
|
|
442
|
+
|
|
407
443
|
await this.beginCycle()
|
|
408
444
|
|
|
409
445
|
if (this.cyclePhase === 'processText') {
|
|
@@ -425,6 +461,10 @@ class TextEngine<
|
|
|
425
461
|
this.endCycle()
|
|
426
462
|
} while (this.shouldContinue())
|
|
427
463
|
|
|
464
|
+
this.logger.agentLoop('run finished', {
|
|
465
|
+
finishReason: this.lastFinishReason,
|
|
466
|
+
})
|
|
467
|
+
|
|
428
468
|
// Call terminal onFinish hook (skip when waiting for client — stream is paused, not finished)
|
|
429
469
|
if (!this.terminalHookCalled && this.toolPhase !== 'wait') {
|
|
430
470
|
this.terminalHookCalled = true
|
|
@@ -447,6 +487,10 @@ class TextEngine<
|
|
|
447
487
|
})
|
|
448
488
|
} else {
|
|
449
489
|
// Genuine error — call onError
|
|
490
|
+
this.logger.errors('chat run failed', {
|
|
491
|
+
error,
|
|
492
|
+
conversationId: this.middlewareCtx.conversationId,
|
|
493
|
+
})
|
|
450
494
|
await this.middlewareRunner.runOnError(this.middlewareCtx, {
|
|
451
495
|
error,
|
|
452
496
|
duration: Date.now() - this.streamStartTime,
|
|
@@ -542,6 +586,18 @@ class TextEngine<
|
|
|
542
586
|
|
|
543
587
|
this.middlewareCtx.phase = 'modelStream'
|
|
544
588
|
|
|
589
|
+
const providerName =
|
|
590
|
+
(this.adapter as { provider?: string }).provider ?? this.adapter.name
|
|
591
|
+
this.logger.request(
|
|
592
|
+
`activity=chat provider=${providerName} model=${this.params.model} messages=${this.messages.length} tools=${this.tools.length} stream=true`,
|
|
593
|
+
{
|
|
594
|
+
provider: providerName,
|
|
595
|
+
model: this.params.model,
|
|
596
|
+
messageCount: this.messages.length,
|
|
597
|
+
toolCount: this.tools.length,
|
|
598
|
+
},
|
|
599
|
+
)
|
|
600
|
+
|
|
545
601
|
for await (const chunk of this.adapter.chatStream({
|
|
546
602
|
model: this.params.model,
|
|
547
603
|
messages: this.messages,
|
|
@@ -553,6 +609,7 @@ class TextEngine<
|
|
|
553
609
|
request: this.effectiveRequest,
|
|
554
610
|
modelOptions,
|
|
555
611
|
systemPrompts: this.systemPrompts,
|
|
612
|
+
logger: this.logger,
|
|
556
613
|
threadId: this.threadId,
|
|
557
614
|
runId: this.runIdOverride,
|
|
558
615
|
})) {
|
|
@@ -572,6 +629,7 @@ class TextEngine<
|
|
|
572
629
|
chunk,
|
|
573
630
|
)
|
|
574
631
|
for (const outputChunk of outputChunks) {
|
|
632
|
+
this.logger.output(`type=${outputChunk.type}`, { chunk: outputChunk })
|
|
575
633
|
yield outputChunk
|
|
576
634
|
this.middlewareCtx.chunkIndex++
|
|
577
635
|
}
|
|
@@ -728,6 +786,10 @@ class TextEngine<
|
|
|
728
786
|
(eventName, data) => this.createCustomEventChunk(eventName, data),
|
|
729
787
|
{
|
|
730
788
|
onBeforeToolCall: async (toolCall, tool, args) => {
|
|
789
|
+
this.logger.tools(`phase=before name=${toolCall.function.name}`, {
|
|
790
|
+
name: toolCall.function.name,
|
|
791
|
+
args,
|
|
792
|
+
})
|
|
731
793
|
const hookCtx = {
|
|
732
794
|
toolCall,
|
|
733
795
|
tool,
|
|
@@ -741,6 +803,10 @@ class TextEngine<
|
|
|
741
803
|
)
|
|
742
804
|
},
|
|
743
805
|
onAfterToolCall: async (info) => {
|
|
806
|
+
this.logger.tools(`phase=after name=${info.toolName}`, {
|
|
807
|
+
name: info.toolName,
|
|
808
|
+
result: info.result,
|
|
809
|
+
})
|
|
744
810
|
await this.middlewareRunner.runOnAfterToolCall(
|
|
745
811
|
this.middlewareCtx,
|
|
746
812
|
info,
|
|
@@ -881,6 +947,10 @@ class TextEngine<
|
|
|
881
947
|
(eventName, data) => this.createCustomEventChunk(eventName, data),
|
|
882
948
|
{
|
|
883
949
|
onBeforeToolCall: async (toolCall, tool, args) => {
|
|
950
|
+
this.logger.tools(`phase=before name=${toolCall.function.name}`, {
|
|
951
|
+
name: toolCall.function.name,
|
|
952
|
+
args,
|
|
953
|
+
})
|
|
884
954
|
const hookCtx = {
|
|
885
955
|
toolCall,
|
|
886
956
|
tool,
|
|
@@ -894,6 +964,10 @@ class TextEngine<
|
|
|
894
964
|
)
|
|
895
965
|
},
|
|
896
966
|
onAfterToolCall: async (info) => {
|
|
967
|
+
this.logger.tools(`phase=after name=${info.toolName}`, {
|
|
968
|
+
name: info.toolName,
|
|
969
|
+
result: info.result,
|
|
970
|
+
})
|
|
897
971
|
await this.middlewareRunner.runOnAfterToolCall(
|
|
898
972
|
this.middlewareCtx,
|
|
899
973
|
info,
|
|
@@ -1478,18 +1552,22 @@ export function chat<
|
|
|
1478
1552
|
async function* runStreamingText(
|
|
1479
1553
|
options: TextActivityOptions<AnyTextAdapter, undefined, true>,
|
|
1480
1554
|
): AsyncIterable<StreamChunk> {
|
|
1481
|
-
const { adapter, middleware, context, ...textOptions } = options
|
|
1555
|
+
const { adapter, middleware, context, debug, ...textOptions } = options
|
|
1482
1556
|
const model = adapter.model
|
|
1557
|
+
const logger = resolveDebugOption(debug)
|
|
1483
1558
|
|
|
1484
|
-
const engine = new TextEngine(
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1559
|
+
const engine = new TextEngine(
|
|
1560
|
+
{
|
|
1561
|
+
adapter,
|
|
1562
|
+
params: { ...textOptions, model, logger } as TextOptions<
|
|
1563
|
+
Record<string, any>,
|
|
1564
|
+
Record<string, any>
|
|
1565
|
+
>,
|
|
1566
|
+
middleware,
|
|
1567
|
+
context,
|
|
1568
|
+
},
|
|
1569
|
+
logger,
|
|
1570
|
+
)
|
|
1493
1571
|
|
|
1494
1572
|
for await (const chunk of engine.run()) {
|
|
1495
1573
|
yield chunk
|
|
@@ -1520,23 +1598,28 @@ function runNonStreamingText(
|
|
|
1520
1598
|
async function runAgenticStructuredOutput<TSchema extends SchemaInput>(
|
|
1521
1599
|
options: TextActivityOptions<AnyTextAdapter, TSchema, boolean>,
|
|
1522
1600
|
): Promise<InferSchemaType<TSchema>> {
|
|
1523
|
-
const { adapter, outputSchema, middleware, context, ...textOptions } =
|
|
1601
|
+
const { adapter, outputSchema, middleware, context, debug, ...textOptions } =
|
|
1602
|
+
options
|
|
1524
1603
|
const model = adapter.model
|
|
1604
|
+
const logger = resolveDebugOption(debug)
|
|
1525
1605
|
|
|
1526
1606
|
if (!outputSchema) {
|
|
1527
1607
|
throw new Error('outputSchema is required for structured output')
|
|
1528
1608
|
}
|
|
1529
1609
|
|
|
1530
1610
|
// Create the engine and run the agentic loop
|
|
1531
|
-
const engine = new TextEngine(
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1611
|
+
const engine = new TextEngine(
|
|
1612
|
+
{
|
|
1613
|
+
adapter,
|
|
1614
|
+
params: { ...textOptions, model, logger } as TextOptions<
|
|
1615
|
+
Record<string, unknown>,
|
|
1616
|
+
Record<string, unknown>
|
|
1617
|
+
>,
|
|
1618
|
+
middleware,
|
|
1619
|
+
context,
|
|
1620
|
+
},
|
|
1621
|
+
logger,
|
|
1622
|
+
)
|
|
1540
1623
|
|
|
1541
1624
|
// Consume the stream to run the agentic loop
|
|
1542
1625
|
for await (const _chunk of engine.run()) {
|
|
@@ -1560,6 +1643,17 @@ async function runAgenticStructuredOutput<TSchema extends SchemaInput>(
|
|
|
1560
1643
|
throw new Error('Failed to convert output schema to JSON Schema')
|
|
1561
1644
|
}
|
|
1562
1645
|
|
|
1646
|
+
const providerName =
|
|
1647
|
+
(adapter as { provider?: string }).provider ?? adapter.name
|
|
1648
|
+
logger.request(
|
|
1649
|
+
`activity=chat-structured provider=${providerName} model=${model} messages=${finalMessages.length}`,
|
|
1650
|
+
{
|
|
1651
|
+
provider: providerName,
|
|
1652
|
+
model,
|
|
1653
|
+
messageCount: finalMessages.length,
|
|
1654
|
+
},
|
|
1655
|
+
)
|
|
1656
|
+
|
|
1563
1657
|
// Call the adapter's structured output method with the conversation context
|
|
1564
1658
|
// The adapter receives JSON Schema and can apply vendor-specific patches
|
|
1565
1659
|
const result = await adapter.structuredOutput({
|
|
@@ -1567,6 +1661,7 @@ async function runAgenticStructuredOutput<TSchema extends SchemaInput>(
|
|
|
1567
1661
|
...structuredTextOptions,
|
|
1568
1662
|
model,
|
|
1569
1663
|
messages: finalMessages,
|
|
1664
|
+
logger,
|
|
1570
1665
|
},
|
|
1571
1666
|
outputSchema: jsonSchema,
|
|
1572
1667
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { aiEventClient } from '@tanstack/ai-event-client'
|
|
2
2
|
import type { StreamChunk } from '../../../types'
|
|
3
|
+
import type { InternalLogger } from '../../../logger/internal-logger'
|
|
3
4
|
import type {
|
|
4
5
|
AbortInfo,
|
|
5
6
|
AfterToolCallInfo,
|
|
@@ -36,9 +37,14 @@ function instrumentCtx(ctx: ChatMiddlewareContext) {
|
|
|
36
37
|
*/
|
|
37
38
|
export class MiddlewareRunner {
|
|
38
39
|
private readonly middlewares: ReadonlyArray<ChatMiddleware>
|
|
40
|
+
private readonly logger: InternalLogger
|
|
39
41
|
|
|
40
|
-
constructor(
|
|
42
|
+
constructor(
|
|
43
|
+
middlewares: ReadonlyArray<ChatMiddleware>,
|
|
44
|
+
logger: InternalLogger,
|
|
45
|
+
) {
|
|
41
46
|
this.middlewares = middlewares
|
|
47
|
+
this.logger = logger
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
get hasMiddleware(): boolean {
|
|
@@ -63,6 +69,15 @@ export class MiddlewareRunner {
|
|
|
63
69
|
const hasTransform = result !== undefined && result !== null
|
|
64
70
|
if (hasTransform) {
|
|
65
71
|
current = { ...current, ...result }
|
|
72
|
+
if (!skip) {
|
|
73
|
+
this.logger.config(
|
|
74
|
+
`middleware=${mw.name ?? 'unnamed'} keys=${Object.keys(result as object).join(',')}`,
|
|
75
|
+
{
|
|
76
|
+
middleware: mw.name ?? 'unnamed',
|
|
77
|
+
changes: result,
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
}
|
|
66
81
|
}
|
|
67
82
|
if (!skip) {
|
|
68
83
|
const base = instrumentCtx(ctx)
|
|
@@ -98,6 +113,10 @@ export class MiddlewareRunner {
|
|
|
98
113
|
const start = Date.now()
|
|
99
114
|
await mw.onStart(ctx)
|
|
100
115
|
if (!skip) {
|
|
116
|
+
this.logger.middleware(
|
|
117
|
+
`hook=onStart middleware=${mw.name ?? 'unnamed'}`,
|
|
118
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onStart' },
|
|
119
|
+
)
|
|
101
120
|
aiEventClient.emit('middleware:hook:executed', {
|
|
102
121
|
...instrumentCtx(ctx),
|
|
103
122
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -134,10 +153,24 @@ export class MiddlewareRunner {
|
|
|
134
153
|
for (const c of chunks) {
|
|
135
154
|
// Cast: @ag-ui/core Zod passthrough types prevent direct `.type` access
|
|
136
155
|
const chunkType = (c as StreamChunk & { type: string }).type
|
|
156
|
+
if (!skip) {
|
|
157
|
+
this.logger.middleware(
|
|
158
|
+
`hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType}`,
|
|
159
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onChunk', in: c },
|
|
160
|
+
)
|
|
161
|
+
}
|
|
137
162
|
const result = await mw.onChunk(ctx, c)
|
|
138
163
|
if (result === null) {
|
|
139
164
|
// Drop this chunk
|
|
140
165
|
if (!skip) {
|
|
166
|
+
this.logger.middleware(
|
|
167
|
+
`hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=<dropped>`,
|
|
168
|
+
{
|
|
169
|
+
middleware: mw.name ?? 'unnamed',
|
|
170
|
+
hook: 'onChunk',
|
|
171
|
+
dropped: true,
|
|
172
|
+
},
|
|
173
|
+
)
|
|
141
174
|
aiEventClient.emit('middleware:chunk:transformed', {
|
|
142
175
|
...instrumentCtx(ctx),
|
|
143
176
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -154,6 +187,15 @@ export class MiddlewareRunner {
|
|
|
154
187
|
// Expand
|
|
155
188
|
nextChunks.push(...result)
|
|
156
189
|
if (!skip) {
|
|
190
|
+
this.logger.middleware(
|
|
191
|
+
`hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=[${result.map((r: StreamChunk) => (r as StreamChunk & { type: string }).type).join(',')}]`,
|
|
192
|
+
{
|
|
193
|
+
middleware: mw.name ?? 'unnamed',
|
|
194
|
+
hook: 'onChunk',
|
|
195
|
+
in: c,
|
|
196
|
+
out: result,
|
|
197
|
+
},
|
|
198
|
+
)
|
|
157
199
|
aiEventClient.emit('middleware:chunk:transformed', {
|
|
158
200
|
...instrumentCtx(ctx),
|
|
159
201
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -166,6 +208,15 @@ export class MiddlewareRunner {
|
|
|
166
208
|
// Replace
|
|
167
209
|
nextChunks.push(result)
|
|
168
210
|
if (!skip) {
|
|
211
|
+
this.logger.middleware(
|
|
212
|
+
`hook=onChunk middleware=${mw.name ?? 'unnamed'} in=${chunkType} out=${(result as StreamChunk & { type: string }).type}`,
|
|
213
|
+
{
|
|
214
|
+
middleware: mw.name ?? 'unnamed',
|
|
215
|
+
hook: 'onChunk',
|
|
216
|
+
in: c,
|
|
217
|
+
out: result,
|
|
218
|
+
},
|
|
219
|
+
)
|
|
169
220
|
aiEventClient.emit('middleware:chunk:transformed', {
|
|
170
221
|
...instrumentCtx(ctx),
|
|
171
222
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -197,6 +248,10 @@ export class MiddlewareRunner {
|
|
|
197
248
|
const decision = await mw.onBeforeToolCall(ctx, hookCtx)
|
|
198
249
|
const hasTransform = decision !== undefined && decision !== null
|
|
199
250
|
if (!skip) {
|
|
251
|
+
this.logger.middleware(
|
|
252
|
+
`hook=onBeforeToolCall middleware=${mw.name ?? 'unnamed'}`,
|
|
253
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onBeforeToolCall' },
|
|
254
|
+
)
|
|
200
255
|
aiEventClient.emit('middleware:hook:executed', {
|
|
201
256
|
...instrumentCtx(ctx),
|
|
202
257
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -227,6 +282,10 @@ export class MiddlewareRunner {
|
|
|
227
282
|
const start = Date.now()
|
|
228
283
|
await mw.onAfterToolCall(ctx, info)
|
|
229
284
|
if (!skip) {
|
|
285
|
+
this.logger.middleware(
|
|
286
|
+
`hook=onAfterToolCall middleware=${mw.name ?? 'unnamed'}`,
|
|
287
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onAfterToolCall' },
|
|
288
|
+
)
|
|
230
289
|
aiEventClient.emit('middleware:hook:executed', {
|
|
231
290
|
...instrumentCtx(ctx),
|
|
232
291
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -253,6 +312,10 @@ export class MiddlewareRunner {
|
|
|
253
312
|
const start = Date.now()
|
|
254
313
|
await mw.onUsage(ctx, usage)
|
|
255
314
|
if (!skip) {
|
|
315
|
+
this.logger.middleware(
|
|
316
|
+
`hook=onUsage middleware=${mw.name ?? 'unnamed'}`,
|
|
317
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onUsage' },
|
|
318
|
+
)
|
|
256
319
|
aiEventClient.emit('middleware:hook:executed', {
|
|
257
320
|
...instrumentCtx(ctx),
|
|
258
321
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -279,6 +342,10 @@ export class MiddlewareRunner {
|
|
|
279
342
|
const start = Date.now()
|
|
280
343
|
await mw.onFinish(ctx, info)
|
|
281
344
|
if (!skip) {
|
|
345
|
+
this.logger.middleware(
|
|
346
|
+
`hook=onFinish middleware=${mw.name ?? 'unnamed'}`,
|
|
347
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onFinish' },
|
|
348
|
+
)
|
|
282
349
|
aiEventClient.emit('middleware:hook:executed', {
|
|
283
350
|
...instrumentCtx(ctx),
|
|
284
351
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -302,6 +369,10 @@ export class MiddlewareRunner {
|
|
|
302
369
|
const start = Date.now()
|
|
303
370
|
await mw.onAbort(ctx, info)
|
|
304
371
|
if (!skip) {
|
|
372
|
+
this.logger.middleware(
|
|
373
|
+
`hook=onAbort middleware=${mw.name ?? 'unnamed'}`,
|
|
374
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onAbort' },
|
|
375
|
+
)
|
|
305
376
|
aiEventClient.emit('middleware:hook:executed', {
|
|
306
377
|
...instrumentCtx(ctx),
|
|
307
378
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -325,6 +396,10 @@ export class MiddlewareRunner {
|
|
|
325
396
|
const start = Date.now()
|
|
326
397
|
await mw.onError(ctx, info)
|
|
327
398
|
if (!skip) {
|
|
399
|
+
this.logger.middleware(
|
|
400
|
+
`hook=onError middleware=${mw.name ?? 'unnamed'}`,
|
|
401
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onError' },
|
|
402
|
+
)
|
|
328
403
|
aiEventClient.emit('middleware:hook:executed', {
|
|
329
404
|
...instrumentCtx(ctx),
|
|
330
405
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -352,6 +427,10 @@ export class MiddlewareRunner {
|
|
|
352
427
|
const start = Date.now()
|
|
353
428
|
await mw.onIteration(ctx, info)
|
|
354
429
|
if (!skip) {
|
|
430
|
+
this.logger.middleware(
|
|
431
|
+
`hook=onIteration middleware=${mw.name ?? 'unnamed'}`,
|
|
432
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onIteration' },
|
|
433
|
+
)
|
|
355
434
|
aiEventClient.emit('middleware:hook:executed', {
|
|
356
435
|
...instrumentCtx(ctx),
|
|
357
436
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -379,6 +458,10 @@ export class MiddlewareRunner {
|
|
|
379
458
|
const start = Date.now()
|
|
380
459
|
await mw.onToolPhaseComplete(ctx, info)
|
|
381
460
|
if (!skip) {
|
|
461
|
+
this.logger.middleware(
|
|
462
|
+
`hook=onToolPhaseComplete middleware=${mw.name ?? 'unnamed'}`,
|
|
463
|
+
{ middleware: mw.name ?? 'unnamed', hook: 'onToolPhaseComplete' },
|
|
464
|
+
)
|
|
382
465
|
aiEventClient.emit('middleware:hook:executed', {
|
|
383
466
|
...instrumentCtx(ctx),
|
|
384
467
|
middlewareName: mw.name || 'unnamed',
|
|
@@ -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
|
|
|
@@ -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
|
// ===========================
|
|
@@ -134,10 +143,15 @@ export function generateSpeech<
|
|
|
134
143
|
async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
|
|
135
144
|
options: TTSActivityOptions<TAdapter, boolean>,
|
|
136
145
|
): Promise<TTSResult> {
|
|
137
|
-
const { adapter, stream: _stream, ...rest } = options
|
|
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,14 @@ 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 })
|
|
175
|
+
|
|
155
176
|
const duration = Date.now() - startTime
|
|
156
177
|
|
|
157
178
|
aiEventClient.emit('speech:request:completed', {
|
|
@@ -167,8 +188,19 @@ async function runGenerateSpeech<TAdapter extends TTSAdapter<string, object>>(
|
|
|
167
188
|
timestamp: Date.now(),
|
|
168
189
|
})
|
|
169
190
|
|
|
191
|
+
logger.output(`activity=generateSpeech bytes=${result.audio.length}`, {
|
|
192
|
+
bytes: result.audio.length,
|
|
193
|
+
contentType: result.contentType,
|
|
194
|
+
})
|
|
195
|
+
|
|
170
196
|
return result
|
|
171
|
-
})
|
|
197
|
+
} catch (error) {
|
|
198
|
+
logger.errors('generateSpeech activity failed', {
|
|
199
|
+
error,
|
|
200
|
+
source: 'generateSpeech',
|
|
201
|
+
})
|
|
202
|
+
throw error
|
|
203
|
+
}
|
|
172
204
|
}
|
|
173
205
|
|
|
174
206
|
// ===========================
|