@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,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable logger interface consumed by every `@tanstack/ai` activity when `debug` is enabled. Supply a custom implementation via `debug: { logger }` on `chat()`, `summarize()`, `generateImage()`, etc. The four methods correspond to log levels: use `debug` for chunk-level diagnostic output, `info`/`warn` for notable events, `error` for caught exceptions.
|
|
3
|
+
*/
|
|
4
|
+
export interface Logger {
|
|
5
|
+
/**
|
|
6
|
+
* Called for chunk-level diagnostic output (raw provider chunks, per-chunk output, agent-loop iteration markers).
|
|
7
|
+
* @param meta Structured data forwarded to the underlying logger. Loggers like pino will preserve this as a structured record; console-based loggers pass it as the second argument to `console.<level>`.
|
|
8
|
+
*/
|
|
9
|
+
debug: (message: string, meta?: Record<string, unknown>) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Called for notable informational events (outgoing requests, tool invocations, middleware transitions).
|
|
12
|
+
* @param meta Structured data forwarded to the underlying logger. Loggers like pino will preserve this as a structured record; console-based loggers pass it as the second argument to `console.<level>`.
|
|
13
|
+
*/
|
|
14
|
+
info: (message: string, meta?: Record<string, unknown>) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Called for notable warnings that don't halt execution (deprecations, recoverable anomalies).
|
|
17
|
+
* @param meta Structured data forwarded to the underlying logger. Loggers like pino will preserve this as a structured record; console-based loggers pass it as the second argument to `console.<level>`.
|
|
18
|
+
*/
|
|
19
|
+
warn: (message: string, meta?: Record<string, unknown>) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Called for caught exceptions throughout the pipeline.
|
|
22
|
+
* @param meta Structured data forwarded to the underlying logger. Loggers like pino will preserve this as a structured record; console-based loggers pass it as the second argument to `console.<level>`.
|
|
23
|
+
*/
|
|
24
|
+
error: (message: string, meta?: Record<string, unknown>) => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Per-category toggles for debug logging. Each flag enables or disables one class of log message. Unspecified flags default to `true` when `DebugConfig` is partially specified; `undefined` on the `debug` option defaults all flags to `false` except `errors`.
|
|
28
|
+
*/
|
|
29
|
+
export interface DebugCategories {
|
|
30
|
+
/**
|
|
31
|
+
* Raw chunks/frames received from a provider SDK (OpenAI, Anthropic, Gemini, Ollama, Grok, Groq, OpenRouter, fal, ElevenLabs). Emitted inside every streaming adapter's chunk loop.
|
|
32
|
+
*/
|
|
33
|
+
provider?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Chunks/results yielded to the consumer after all middleware. For streaming activities this fires per chunk; for non-streaming activities it fires once per result.
|
|
36
|
+
*/
|
|
37
|
+
output?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Inputs and outputs around each middleware hook invocation. Chat-only.
|
|
40
|
+
*/
|
|
41
|
+
middleware?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Before/after tool-call execution in the chat agent loop. Chat-only.
|
|
44
|
+
*/
|
|
45
|
+
tools?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Iteration markers and phase transitions in the chat agent loop. Chat-only.
|
|
48
|
+
*/
|
|
49
|
+
agentLoop?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Config transforms returned by middleware `onConfig` hooks. Chat-only.
|
|
52
|
+
*/
|
|
53
|
+
config?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Caught errors throughout the pipeline. Unlike other categories, defaults to `true` even when `debug` is unspecified. Explicitly set `errors: false` or `debug: false` to silence.
|
|
56
|
+
*/
|
|
57
|
+
errors?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Outgoing call metadata (provider, model, message/tool counts) emitted before each adapter SDK call.
|
|
60
|
+
*/
|
|
61
|
+
request?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Granular debug configuration combining per-category toggles with an optional custom logger. Any unspecified category flag defaults to `true`.
|
|
65
|
+
*/
|
|
66
|
+
export interface DebugConfig extends DebugCategories {
|
|
67
|
+
/**
|
|
68
|
+
* Custom `Logger` implementation. When omitted, a default `ConsoleLogger` routes output to `console.debug`/`info`/`warn`/`error`.
|
|
69
|
+
*/
|
|
70
|
+
logger?: Logger;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The shape accepted by the `debug` option on every `@tanstack/ai` activity. Pass `true` to enable all categories with the default console logger; `false` to silence everything including errors; an object for granular control.
|
|
74
|
+
*/
|
|
75
|
+
export type DebugOption = boolean | DebugConfig;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toRunErrorPayload } from "./activities/error-payload.js";
|
|
1
2
|
async function streamToText(stream) {
|
|
2
3
|
let accumulatedContent = "";
|
|
3
4
|
for await (const chunk of stream) {
|
|
@@ -33,10 +34,7 @@ function toServerSentEventsStream(stream, abortController) {
|
|
|
33
34
|
`data: ${JSON.stringify({
|
|
34
35
|
type: "RUN_ERROR",
|
|
35
36
|
timestamp: Date.now(),
|
|
36
|
-
error:
|
|
37
|
-
message: error.message || "Unknown error occurred",
|
|
38
|
-
code: error.code
|
|
39
|
-
}
|
|
37
|
+
error: toRunErrorPayload(error)
|
|
40
38
|
})}
|
|
41
39
|
|
|
42
40
|
`
|
|
@@ -93,10 +91,7 @@ function toHttpStream(stream, abortController) {
|
|
|
93
91
|
`${JSON.stringify({
|
|
94
92
|
type: "RUN_ERROR",
|
|
95
93
|
timestamp: Date.now(),
|
|
96
|
-
error:
|
|
97
|
-
message: error.message || "Unknown error occurred",
|
|
98
|
-
code: error.code
|
|
99
|
-
}
|
|
94
|
+
error: toRunErrorPayload(error)
|
|
100
95
|
})}
|
|
101
96
|
`
|
|
102
97
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-to-response.js","sources":["../../src/stream-to-response.ts"],"sourcesContent":["import type { StreamChunk } from './types'\n\n/**\n * Collect all text content from a StreamChunk async iterable and return as a string.\n *\n * This function consumes the entire stream, accumulating content from TEXT_MESSAGE_CONTENT events,\n * and returns the final concatenated text.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @returns Promise<string> - The accumulated text content\n *\n * @example\n * ```typescript\n * const stream = chat({\n * adapter: openaiText(),\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }]\n * });\n * const text = await streamToText(stream);\n * console.log(text); // \"Hello! How can I help you today?\"\n * ```\n */\nexport async function streamToText(\n stream: AsyncIterable<StreamChunk>,\n): Promise<string> {\n let accumulatedContent = ''\n\n for await (const chunk of stream) {\n if (chunk.type === 'TEXT_MESSAGE_CONTENT' && chunk.delta) {\n accumulatedContent += chunk.delta\n }\n }\n\n return accumulatedContent\n}\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in Server-Sent Events format\n *\n * This creates a ReadableStream that emits chunks in SSE format:\n * - Each chunk is prefixed with \"data: \"\n * - Each chunk is followed by \"\\n\\n\"\n * - Stream ends when the underlying iterable is exhausted (RUN_FINISHED is the terminal event)\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in Server-Sent Events format\n */\nexport function toServerSentEventsStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as Server-Sent Events format\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n )\n }\n\n controller.close()\n } catch (error: any) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error event (AG-UI RUN_ERROR)\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({\n type: 'RUN_ERROR',\n timestamp: Date.now(),\n error: {\n message: error.message || 'Unknown error occurred',\n code: error.code,\n },\n })}\\n\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a Response in Server-Sent Events format\n *\n * This creates a Response that emits chunks in SSE format:\n * - Each chunk is prefixed with \"data: \"\n * - Each chunk is followed by \"\\n\\n\"\n * - Stream ends when the underlying iterable is exhausted (RUN_FINISHED is the terminal event)\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param init - Optional Response initialization options (including `abortController`)\n * @returns Response in Server-Sent Events format\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * return toServerSentEventsResponse(stream, { abortController });\n * ```\n */\nexport function toServerSentEventsResponse(\n stream: AsyncIterable<StreamChunk>,\n init?: ResponseInit & { abortController?: AbortController },\n): Response {\n const { headers, abortController, ...responseInit } = init ?? {}\n\n // Start with default SSE headers\n const mergedHeaders = new Headers({\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n // Override with user headers if provided, handling all HeadersInit forms:\n // Headers instance, string[][], or plain object\n if (headers) {\n const userHeaders = new Headers(headers)\n userHeaders.forEach((value, key) => {\n mergedHeaders.set(key, value)\n })\n }\n\n return new Response(toServerSentEventsStream(stream, abortController), {\n ...responseInit,\n headers: mergedHeaders,\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in HTTP stream format (newline-delimited JSON)\n *\n * This creates a ReadableStream that emits chunks as newline-delimited JSON:\n * - Each chunk is JSON.stringify'd and followed by \"\\n\"\n * - No SSE formatting (no \"data: \" prefix)\n *\n * This format is compatible with `fetchHttpStream` connection adapter.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in HTTP stream format (newline-delimited JSON)\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * const readableStream = toHttpStream(stream);\n * // Use with Response for HTTP streaming (not SSE)\n * return new Response(readableStream, {\n * headers: { 'Content-Type': 'application/x-ndjson' }\n * });\n * ```\n */\nexport function toHttpStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as newline-delimited JSON\n controller.enqueue(encoder.encode(`${JSON.stringify(chunk)}\\n`))\n }\n\n controller.close()\n } catch (error: any) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error event (AG-UI RUN_ERROR)\n controller.enqueue(\n encoder.encode(\n `${JSON.stringify({\n type: 'RUN_ERROR',\n timestamp: Date.now(),\n error: {\n message: error.message || 'Unknown error occurred',\n code: error.code,\n },\n })}\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a Response in HTTP stream format (newline-delimited JSON)\n *\n * This creates a Response that emits chunks in HTTP stream format:\n * - Each chunk is JSON.stringify'd and followed by \"\\n\"\n * - No SSE formatting (no \"data: \" prefix)\n *\n * This format is compatible with `fetchHttpStream` connection adapter.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param init - Optional Response initialization options (including `abortController`)\n * @returns Response in HTTP stream format (newline-delimited JSON)\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * return toHttpResponse(stream, { abortController });\n * ```\n */\nexport function toHttpResponse(\n stream: AsyncIterable<StreamChunk>,\n init?: ResponseInit & { abortController?: AbortController },\n): Response {\n return new Response(toHttpStream(stream, init?.abortController), {\n ...init,\n })\n}\n"],"names":[],"mappings":"AAsBA,eAAsB,aACpB,QACiB;AACjB,MAAI,qBAAqB;AAEzB,mBAAiB,SAAS,QAAQ;AAChC,QAAI,MAAM,SAAS,0BAA0B,MAAM,OAAO;AACxD,4BAAsB,MAAM;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AACT;AAcO,SAAS,yBACd,QACA,iBAC4B;AAC5B,QAAM,UAAU,IAAI,YAAA;AAEpB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,MAAM,YAAY;AACtB,UAAI;AACF,yBAAiB,SAAS,QAAQ;AAEhC,cAAI,iBAAiB,OAAO,SAAS;AACnC;AAAA,UACF;AAGA,qBAAW;AAAA,YACT,QAAQ,OAAO,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,CAAM;AAAA,UAAA;AAAA,QAEvD;AAEA,mBAAW,MAAA;AAAA,MACb,SAAS,OAAY;AAEnB,YAAI,iBAAiB,OAAO,SAAS;AACnC,qBAAW,MAAA;AACX;AAAA,QACF;AAGA,mBAAW;AAAA,UACT,QAAQ;AAAA,YACN,SAAS,KAAK,UAAU;AAAA,cACtB,MAAM;AAAA,cACN,WAAW,KAAK,IAAA;AAAA,cAChB,OAAO;AAAA,gBACL,SAAS,MAAM,WAAW;AAAA,gBAC1B,MAAM,MAAM;AAAA,cAAA;AAAA,YACd,CACD,CAAC;AAAA;AAAA;AAAA,UAAA;AAAA,QACJ;AAEF,mBAAW,MAAA;AAAA,MACb;AAAA,IACF;AAAA,IACA,SAAS;AAGP,UAAI,iBAAiB;AACnB,wBAAgB,MAAA;AAAA,MAClB;AAAA,IACF;AAAA,EAAA,CACD;AACH;AAoBO,SAAS,2BACd,QACA,MACU;AACV,QAAM,EAAE,SAAS,iBAAiB,GAAG,aAAA,IAAiB,QAAQ,CAAA;AAG9D,QAAM,gBAAgB,IAAI,QAAQ;AAAA,IAChC,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,YAAY;AAAA,EAAA,CACb;AAID,MAAI,SAAS;AACX,UAAM,cAAc,IAAI,QAAQ,OAAO;AACvC,gBAAY,QAAQ,CAAC,OAAO,QAAQ;AAClC,oBAAc,IAAI,KAAK,KAAK;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,SAAS,yBAAyB,QAAQ,eAAe,GAAG;AAAA,IACrE,GAAG;AAAA,IACH,SAAS;AAAA,EAAA,CACV;AACH;AAyBO,SAAS,aACd,QACA,iBAC4B;AAC5B,QAAM,UAAU,IAAI,YAAA;AAEpB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,MAAM,YAAY;AACtB,UAAI;AACF,yBAAiB,SAAS,QAAQ;AAEhC,cAAI,iBAAiB,OAAO,SAAS;AACnC;AAAA,UACF;AAGA,qBAAW,QAAQ,QAAQ,OAAO,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,CAAI,CAAC;AAAA,QACjE;AAEA,mBAAW,MAAA;AAAA,MACb,SAAS,OAAY;AAEnB,YAAI,iBAAiB,OAAO,SAAS;AACnC,qBAAW,MAAA;AACX;AAAA,QACF;AAGA,mBAAW;AAAA,UACT,QAAQ;AAAA,YACN,GAAG,KAAK,UAAU;AAAA,cAChB,MAAM;AAAA,cACN,WAAW,KAAK,IAAA;AAAA,cAChB,OAAO;AAAA,gBACL,SAAS,MAAM,WAAW;AAAA,gBAC1B,MAAM,MAAM;AAAA,cAAA;AAAA,YACd,CACD,CAAC;AAAA;AAAA,UAAA;AAAA,QACJ;AAEF,mBAAW,MAAA;AAAA,MACb;AAAA,IACF;AAAA,IACA,SAAS;AAGP,UAAI,iBAAiB;AACnB,wBAAgB,MAAA;AAAA,MAClB;AAAA,IACF;AAAA,EAAA,CACD;AACH;AAqBO,SAAS,eACd,QACA,MACU;AACV,SAAO,IAAI,SAAS,aAAa,QAAQ,MAAM,eAAe,GAAG;AAAA,IAC/D,GAAG;AAAA,EAAA,CACJ;AACH;"}
|
|
1
|
+
{"version":3,"file":"stream-to-response.js","sources":["../../src/stream-to-response.ts"],"sourcesContent":["import { toRunErrorPayload } from './activities/error-payload'\nimport type { StreamChunk } from './types'\n\n/**\n * Collect all text content from a StreamChunk async iterable and return as a string.\n *\n * This function consumes the entire stream, accumulating content from TEXT_MESSAGE_CONTENT events,\n * and returns the final concatenated text.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @returns Promise<string> - The accumulated text content\n *\n * @example\n * ```typescript\n * const stream = chat({\n * adapter: openaiText(),\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }]\n * });\n * const text = await streamToText(stream);\n * console.log(text); // \"Hello! How can I help you today?\"\n * ```\n */\nexport async function streamToText(\n stream: AsyncIterable<StreamChunk>,\n): Promise<string> {\n let accumulatedContent = ''\n\n for await (const chunk of stream) {\n if (chunk.type === 'TEXT_MESSAGE_CONTENT' && chunk.delta) {\n accumulatedContent += chunk.delta\n }\n }\n\n return accumulatedContent\n}\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in Server-Sent Events format\n *\n * This creates a ReadableStream that emits chunks in SSE format:\n * - Each chunk is prefixed with \"data: \"\n * - Each chunk is followed by \"\\n\\n\"\n * - Stream ends when the underlying iterable is exhausted (RUN_FINISHED is the terminal event)\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in Server-Sent Events format\n */\nexport function toServerSentEventsStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as Server-Sent Events format\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n )\n }\n\n controller.close()\n } catch (error: unknown) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error event (AG-UI RUN_ERROR)\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({\n type: 'RUN_ERROR',\n timestamp: Date.now(),\n error: toRunErrorPayload(error),\n })}\\n\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a Response in Server-Sent Events format\n *\n * This creates a Response that emits chunks in SSE format:\n * - Each chunk is prefixed with \"data: \"\n * - Each chunk is followed by \"\\n\\n\"\n * - Stream ends when the underlying iterable is exhausted (RUN_FINISHED is the terminal event)\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param init - Optional Response initialization options (including `abortController`)\n * @returns Response in Server-Sent Events format\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * return toServerSentEventsResponse(stream, { abortController });\n * ```\n */\nexport function toServerSentEventsResponse(\n stream: AsyncIterable<StreamChunk>,\n init?: ResponseInit & { abortController?: AbortController },\n): Response {\n const { headers, abortController, ...responseInit } = init ?? {}\n\n // Start with default SSE headers\n const mergedHeaders = new Headers({\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n // Override with user headers if provided, handling all HeadersInit forms:\n // Headers instance, string[][], or plain object\n if (headers) {\n const userHeaders = new Headers(headers)\n userHeaders.forEach((value, key) => {\n mergedHeaders.set(key, value)\n })\n }\n\n return new Response(toServerSentEventsStream(stream, abortController), {\n ...responseInit,\n headers: mergedHeaders,\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in HTTP stream format (newline-delimited JSON)\n *\n * This creates a ReadableStream that emits chunks as newline-delimited JSON:\n * - Each chunk is JSON.stringify'd and followed by \"\\n\"\n * - No SSE formatting (no \"data: \" prefix)\n *\n * This format is compatible with `fetchHttpStream` connection adapter.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in HTTP stream format (newline-delimited JSON)\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * const readableStream = toHttpStream(stream);\n * // Use with Response for HTTP streaming (not SSE)\n * return new Response(readableStream, {\n * headers: { 'Content-Type': 'application/x-ndjson' }\n * });\n * ```\n */\nexport function toHttpStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as newline-delimited JSON\n controller.enqueue(encoder.encode(`${JSON.stringify(chunk)}\\n`))\n }\n\n controller.close()\n } catch (error: unknown) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error event (AG-UI RUN_ERROR)\n controller.enqueue(\n encoder.encode(\n `${JSON.stringify({\n type: 'RUN_ERROR',\n timestamp: Date.now(),\n error: toRunErrorPayload(error),\n })}\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a Response in HTTP stream format (newline-delimited JSON)\n *\n * This creates a Response that emits chunks in HTTP stream format:\n * - Each chunk is JSON.stringify'd and followed by \"\\n\"\n * - No SSE formatting (no \"data: \" prefix)\n *\n * This format is compatible with `fetchHttpStream` connection adapter.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param init - Optional Response initialization options (including `abortController`)\n * @returns Response in HTTP stream format (newline-delimited JSON)\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * return toHttpResponse(stream, { abortController });\n * ```\n */\nexport function toHttpResponse(\n stream: AsyncIterable<StreamChunk>,\n init?: ResponseInit & { abortController?: AbortController },\n): Response {\n return new Response(toHttpStream(stream, init?.abortController), {\n ...init,\n })\n}\n"],"names":[],"mappings":";AAuBA,eAAsB,aACpB,QACiB;AACjB,MAAI,qBAAqB;AAEzB,mBAAiB,SAAS,QAAQ;AAChC,QAAI,MAAM,SAAS,0BAA0B,MAAM,OAAO;AACxD,4BAAsB,MAAM;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AACT;AAcO,SAAS,yBACd,QACA,iBAC4B;AAC5B,QAAM,UAAU,IAAI,YAAA;AAEpB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,MAAM,YAAY;AACtB,UAAI;AACF,yBAAiB,SAAS,QAAQ;AAEhC,cAAI,iBAAiB,OAAO,SAAS;AACnC;AAAA,UACF;AAGA,qBAAW;AAAA,YACT,QAAQ,OAAO,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,CAAM;AAAA,UAAA;AAAA,QAEvD;AAEA,mBAAW,MAAA;AAAA,MACb,SAAS,OAAgB;AAEvB,YAAI,iBAAiB,OAAO,SAAS;AACnC,qBAAW,MAAA;AACX;AAAA,QACF;AAGA,mBAAW;AAAA,UACT,QAAQ;AAAA,YACN,SAAS,KAAK,UAAU;AAAA,cACtB,MAAM;AAAA,cACN,WAAW,KAAK,IAAA;AAAA,cAChB,OAAO,kBAAkB,KAAK;AAAA,YAAA,CAC/B,CAAC;AAAA;AAAA;AAAA,UAAA;AAAA,QACJ;AAEF,mBAAW,MAAA;AAAA,MACb;AAAA,IACF;AAAA,IACA,SAAS;AAGP,UAAI,iBAAiB;AACnB,wBAAgB,MAAA;AAAA,MAClB;AAAA,IACF;AAAA,EAAA,CACD;AACH;AAoBO,SAAS,2BACd,QACA,MACU;AACV,QAAM,EAAE,SAAS,iBAAiB,GAAG,aAAA,IAAiB,QAAQ,CAAA;AAG9D,QAAM,gBAAgB,IAAI,QAAQ;AAAA,IAChC,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,YAAY;AAAA,EAAA,CACb;AAID,MAAI,SAAS;AACX,UAAM,cAAc,IAAI,QAAQ,OAAO;AACvC,gBAAY,QAAQ,CAAC,OAAO,QAAQ;AAClC,oBAAc,IAAI,KAAK,KAAK;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,SAAS,yBAAyB,QAAQ,eAAe,GAAG;AAAA,IACrE,GAAG;AAAA,IACH,SAAS;AAAA,EAAA,CACV;AACH;AAyBO,SAAS,aACd,QACA,iBAC4B;AAC5B,QAAM,UAAU,IAAI,YAAA;AAEpB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,MAAM,YAAY;AACtB,UAAI;AACF,yBAAiB,SAAS,QAAQ;AAEhC,cAAI,iBAAiB,OAAO,SAAS;AACnC;AAAA,UACF;AAGA,qBAAW,QAAQ,QAAQ,OAAO,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,CAAI,CAAC;AAAA,QACjE;AAEA,mBAAW,MAAA;AAAA,MACb,SAAS,OAAgB;AAEvB,YAAI,iBAAiB,OAAO,SAAS;AACnC,qBAAW,MAAA;AACX;AAAA,QACF;AAGA,mBAAW;AAAA,UACT,QAAQ;AAAA,YACN,GAAG,KAAK,UAAU;AAAA,cAChB,MAAM;AAAA,cACN,WAAW,KAAK,IAAA;AAAA,cAChB,OAAO,kBAAkB,KAAK;AAAA,YAAA,CAC/B,CAAC;AAAA;AAAA,UAAA;AAAA,QACJ;AAEF,mBAAW,MAAA;AAAA,MACb;AAAA,IACF;AAAA,IACA,SAAS;AAGP,UAAI,iBAAiB;AACnB,wBAAgB,MAAA;AAAA,MAClB;AAAA,IACF;AAAA,EAAA,CACD;AACH;AAqBO,SAAS,eACd,QACA,MACU;AACV,SAAO,IAAI,SAAS,aAAa,QAAQ,MAAM,eAAe,GAAG;AAAA,IAC/D,GAAG;AAAA,EAAA,CACJ;AACH;"}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StandardJSONSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
import { InternalLogger } from './logger/internal-logger.js';
|
|
2
3
|
import { BaseEvent as AGUIBaseEvent, CustomEvent as AGUICustomEvent, MessagesSnapshotEvent as AGUIMessagesSnapshotEvent, ReasoningEncryptedValueEvent as AGUIReasoningEncryptedValueEvent, ReasoningEndEvent as AGUIReasoningEndEvent, ReasoningMessageContentEvent as AGUIReasoningMessageContentEvent, ReasoningMessageEndEvent as AGUIReasoningMessageEndEvent, ReasoningMessageStartEvent as AGUIReasoningMessageStartEvent, ReasoningStartEvent as AGUIReasoningStartEvent, RunErrorEvent as AGUIRunErrorEvent, RunFinishedEvent as AGUIRunFinishedEvent, RunStartedEvent as AGUIRunStartedEvent, StateDeltaEvent as AGUIStateDeltaEvent, StateSnapshotEvent as AGUIStateSnapshotEvent, StepFinishedEvent as AGUIStepFinishedEvent, StepStartedEvent as AGUIStepStartedEvent, TextMessageContentEvent as AGUITextMessageContentEvent, TextMessageEndEvent as AGUITextMessageEndEvent, TextMessageStartEvent as AGUITextMessageStartEvent, ToolCallArgsEvent as AGUIToolCallArgsEvent, ToolCallEndEvent as AGUIToolCallEndEvent, ToolCallResultEvent as AGUIToolCallResultEvent, ToolCallStartEvent as AGUIToolCallStartEvent, EventType } from '@ag-ui/core';
|
|
3
4
|
/**
|
|
4
5
|
* Tool call states - track the lifecycle of a tool call
|
|
@@ -604,6 +605,12 @@ export interface TextOptions<TProviderOptionsSuperset extends Record<string, any
|
|
|
604
605
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/AbortController
|
|
605
606
|
*/
|
|
606
607
|
abortController?: AbortController;
|
|
608
|
+
/**
|
|
609
|
+
* Internal logger threaded from the chat entry point. Adapter implementations
|
|
610
|
+
* must call `logger.request()` before SDK calls, `logger.provider()` for each
|
|
611
|
+
* chunk received, and `logger.errors()` in catch blocks.
|
|
612
|
+
*/
|
|
613
|
+
logger: InternalLogger;
|
|
607
614
|
/**
|
|
608
615
|
* Thread ID for AG-UI protocol run correlation.
|
|
609
616
|
* When provided, this will be used in RunStartedEvent and RunFinishedEvent.
|
|
@@ -959,6 +966,11 @@ export interface SummarizationOptions {
|
|
|
959
966
|
maxLength?: number;
|
|
960
967
|
style?: 'bullet-points' | 'paragraph' | 'concise';
|
|
961
968
|
focus?: Array<string>;
|
|
969
|
+
/**
|
|
970
|
+
* Internal logger threaded from the summarize() entry point. Adapters must
|
|
971
|
+
* call logger.request() before the SDK call and logger.errors() in catch blocks.
|
|
972
|
+
*/
|
|
973
|
+
logger: InternalLogger;
|
|
962
974
|
}
|
|
963
975
|
export interface SummarizationResult {
|
|
964
976
|
id: string;
|
|
@@ -985,18 +997,34 @@ export interface ImageGenerationOptions<TProviderOptions extends object = object
|
|
|
985
997
|
size?: TSize;
|
|
986
998
|
/** Model-specific options for image generation */
|
|
987
999
|
modelOptions?: TProviderOptions;
|
|
1000
|
+
/**
|
|
1001
|
+
* Internal logger threaded from the generateImage() entry point. Adapters must
|
|
1002
|
+
* call logger.request() before the SDK call and logger.errors() in catch blocks.
|
|
1003
|
+
*/
|
|
1004
|
+
logger: InternalLogger;
|
|
988
1005
|
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Source of a generated media asset. Exactly one of `url` or `b64Json` is
|
|
1008
|
+
* present; the other is absent. Modeled as a mutually-exclusive union so the
|
|
1009
|
+
* type rejects `{}` and `{ url, b64Json }` together at compile time while
|
|
1010
|
+
* preserving the flat `.url` / `.b64Json` access patterns.
|
|
1011
|
+
*/
|
|
1012
|
+
export type GeneratedMediaSource = {
|
|
1013
|
+
/** URL to the generated asset (may be temporary) */
|
|
1014
|
+
url: string;
|
|
1015
|
+
b64Json?: never;
|
|
1016
|
+
} | {
|
|
1017
|
+
/** Base64-encoded asset data */
|
|
1018
|
+
b64Json: string;
|
|
1019
|
+
url?: never;
|
|
1020
|
+
};
|
|
989
1021
|
/**
|
|
990
1022
|
* A single generated image
|
|
991
1023
|
*/
|
|
992
|
-
export
|
|
993
|
-
/** Base64-encoded image data */
|
|
994
|
-
b64Json?: string;
|
|
995
|
-
/** URL to the generated image (may be temporary) */
|
|
996
|
-
url?: string;
|
|
1024
|
+
export type GeneratedImage = GeneratedMediaSource & {
|
|
997
1025
|
/** Revised prompt used by the model (if applicable) */
|
|
998
1026
|
revisedPrompt?: string;
|
|
999
|
-
}
|
|
1027
|
+
};
|
|
1000
1028
|
/**
|
|
1001
1029
|
* Result of image generation
|
|
1002
1030
|
*/
|
|
@@ -1014,6 +1042,52 @@ export interface ImageGenerationResult {
|
|
|
1014
1042
|
totalTokens?: number;
|
|
1015
1043
|
};
|
|
1016
1044
|
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Options for audio generation (music, sound effects, etc.).
|
|
1047
|
+
* These are the common options supported across providers.
|
|
1048
|
+
*/
|
|
1049
|
+
export interface AudioGenerationOptions<TProviderOptions extends object = object> {
|
|
1050
|
+
/** The model to use for audio generation */
|
|
1051
|
+
model: string;
|
|
1052
|
+
/** Text description of the desired audio */
|
|
1053
|
+
prompt: string;
|
|
1054
|
+
/** Desired duration in seconds */
|
|
1055
|
+
duration?: number;
|
|
1056
|
+
/** Model-specific options for audio generation */
|
|
1057
|
+
modelOptions?: TProviderOptions;
|
|
1058
|
+
/**
|
|
1059
|
+
* Internal logger threaded from the generateAudio() entry point. Adapters
|
|
1060
|
+
* must call logger.request() before the SDK call and logger.errors() in
|
|
1061
|
+
* catch blocks.
|
|
1062
|
+
*/
|
|
1063
|
+
logger: InternalLogger;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* A single generated audio output
|
|
1067
|
+
*/
|
|
1068
|
+
export type GeneratedAudio = GeneratedMediaSource & {
|
|
1069
|
+
/** Content type of the audio (e.g., 'audio/wav', 'audio/mp3') */
|
|
1070
|
+
contentType?: string;
|
|
1071
|
+
/** Duration of the generated audio in seconds */
|
|
1072
|
+
duration?: number;
|
|
1073
|
+
};
|
|
1074
|
+
/**
|
|
1075
|
+
* Result of audio generation
|
|
1076
|
+
*/
|
|
1077
|
+
export interface AudioGenerationResult {
|
|
1078
|
+
/** Unique identifier for the generation */
|
|
1079
|
+
id: string;
|
|
1080
|
+
/** Model used for generation */
|
|
1081
|
+
model: string;
|
|
1082
|
+
/** The generated audio */
|
|
1083
|
+
audio: GeneratedAudio;
|
|
1084
|
+
/** Token usage information (if available) */
|
|
1085
|
+
usage?: {
|
|
1086
|
+
inputTokens?: number;
|
|
1087
|
+
outputTokens?: number;
|
|
1088
|
+
totalTokens?: number;
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1017
1091
|
/**
|
|
1018
1092
|
* Options for video generation.
|
|
1019
1093
|
* These are the common options supported across providers.
|
|
@@ -1031,6 +1105,11 @@ export interface VideoGenerationOptions<TProviderOptions extends object = object
|
|
|
1031
1105
|
duration?: number;
|
|
1032
1106
|
/** Model-specific options for video generation */
|
|
1033
1107
|
modelOptions?: TProviderOptions;
|
|
1108
|
+
/**
|
|
1109
|
+
* Internal logger threaded from the generateVideo() entry point. Adapters must
|
|
1110
|
+
* call logger.request() before the SDK call and logger.errors() in catch blocks.
|
|
1111
|
+
*/
|
|
1112
|
+
logger: InternalLogger;
|
|
1034
1113
|
}
|
|
1035
1114
|
/**
|
|
1036
1115
|
* Result of creating a video generation job.
|
|
@@ -1088,6 +1167,12 @@ export interface TTSOptions<TProviderOptions extends object = object> {
|
|
|
1088
1167
|
speed?: number;
|
|
1089
1168
|
/** Model-specific options for TTS generation */
|
|
1090
1169
|
modelOptions?: TProviderOptions;
|
|
1170
|
+
/**
|
|
1171
|
+
* Internal logger threaded from the generateSpeech() entry point. Adapters
|
|
1172
|
+
* must call logger.request() before the SDK call and logger.errors() in
|
|
1173
|
+
* catch blocks.
|
|
1174
|
+
*/
|
|
1175
|
+
logger: InternalLogger;
|
|
1091
1176
|
}
|
|
1092
1177
|
/**
|
|
1093
1178
|
* Result of text-to-speech generation.
|
|
@@ -1123,6 +1208,12 @@ export interface TranscriptionOptions<TProviderOptions extends object = object>
|
|
|
1123
1208
|
responseFormat?: 'json' | 'text' | 'srt' | 'verbose_json' | 'vtt';
|
|
1124
1209
|
/** Model-specific options for transcription */
|
|
1125
1210
|
modelOptions?: TProviderOptions;
|
|
1211
|
+
/**
|
|
1212
|
+
* Internal logger threaded from the generateTranscription() entry point.
|
|
1213
|
+
* Adapters must call logger.request() before the SDK call and logger.errors()
|
|
1214
|
+
* in catch blocks.
|
|
1215
|
+
*/
|
|
1216
|
+
logger: InternalLogger;
|
|
1126
1217
|
}
|
|
1127
1218
|
/**
|
|
1128
1219
|
* A single segment of transcribed audio with timing information.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Core TanStack AI library - Open source AI SDK",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"./middlewares": {
|
|
25
25
|
"types": "./dist/esm/middlewares/index.d.ts",
|
|
26
26
|
"import": "./dist/esm/middlewares/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./adapter-internals": {
|
|
29
|
+
"types": "./dist/esm/adapter-internals.d.ts",
|
|
30
|
+
"import": "./dist/esm/adapter-internals.js"
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
33
|
"sideEffects": false,
|
|
@@ -47,7 +51,7 @@
|
|
|
47
51
|
"dependencies": {
|
|
48
52
|
"@ag-ui/core": "0.0.49",
|
|
49
53
|
"partial-json": "^0.1.7",
|
|
50
|
-
"@tanstack/ai-event-client": "0.2.
|
|
54
|
+
"@tanstack/ai-event-client": "0.2.8"
|
|
51
55
|
},
|
|
52
56
|
"devDependencies": {
|
|
53
57
|
"@standard-schema/spec": "^1.1.0",
|
package/skills/ai-core/SKILL.md
CHANGED
|
@@ -3,9 +3,9 @@ name: ai-core
|
|
|
3
3
|
description: >
|
|
4
4
|
Entry point for TanStack AI skills. Routes to chat-experience, tool-calling,
|
|
5
5
|
media-generation, structured-outputs, adapter-configuration, ag-ui-protocol,
|
|
6
|
-
middleware,
|
|
7
|
-
openaiText() not createOpenAI(), toServerSentEventsResponse()
|
|
8
|
-
middleware hooks not onEnd callbacks.
|
|
6
|
+
middleware, custom-backend-integration, and debug-logging. Use chat() not
|
|
7
|
+
streamText(), openaiText() not createOpenAI(), toServerSentEventsResponse()
|
|
8
|
+
not manual SSE, middleware hooks not onEnd callbacks.
|
|
9
9
|
type: core
|
|
10
10
|
library: tanstack-ai
|
|
11
11
|
library_version: '0.10.0'
|
|
@@ -31,6 +31,7 @@ Always import from the framework package on the client — never from
|
|
|
31
31
|
| Implement AG-UI streaming protocol server-side | ai-core/ag-ui-protocol/SKILL.md |
|
|
32
32
|
| Add analytics, logging, or lifecycle hooks | ai-core/middleware/SKILL.md |
|
|
33
33
|
| Connect to a non-TanStack-AI backend | ai-core/custom-backend-integration/SKILL.md |
|
|
34
|
+
| Turn on/off debug logging, pipe into pino/winston | ai-core/debug-logging/SKILL.md |
|
|
34
35
|
| Set up Code Mode (LLM code execution) | See `@tanstack/ai-code-mode` package skills |
|
|
35
36
|
|
|
36
37
|
## Quick Decision Tree
|
|
@@ -43,6 +44,7 @@ Always import from the framework package on the client — never from
|
|
|
43
44
|
- Building a server-only AG-UI backend? → ai-core/ag-ui-protocol
|
|
44
45
|
- Adding analytics or post-stream events? → ai-core/middleware
|
|
45
46
|
- Connecting to a custom backend? → ai-core/custom-backend-integration
|
|
47
|
+
- Turning on debug logging to trace chunks/tools/middleware? → ai-core/debug-logging
|
|
46
48
|
- Debugging mistakes? → Check Common Mistakes in the relevant sub-skill
|
|
47
49
|
|
|
48
50
|
## Critical Rules
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ai-core/debug-logging
|
|
3
|
+
description: >
|
|
4
|
+
Pluggable, category-toggleable debug logging for TanStack AI activities.
|
|
5
|
+
Toggle with `debug: true | false | DebugConfig` on chat(), summarize(),
|
|
6
|
+
generateImage(), generateSpeech(), generateTranscription(), generateVideo().
|
|
7
|
+
Categories: request, provider, output, middleware, tools, agentLoop,
|
|
8
|
+
config, errors. Pipe into pino/winston/etc via `debug: { logger }`. Errors
|
|
9
|
+
log by default even when `debug` is omitted; silence with `debug: false`.
|
|
10
|
+
type: sub-skill
|
|
11
|
+
library: tanstack-ai
|
|
12
|
+
library_version: '0.10.0'
|
|
13
|
+
sources:
|
|
14
|
+
- 'TanStack/ai:docs/advanced/debug-logging.md'
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Debug Logging
|
|
18
|
+
|
|
19
|
+
> **Dependency note:** This skill builds on ai-core. Read it first for critical rules.
|
|
20
|
+
|
|
21
|
+
Use this skill when you need to turn debug logging on or off, narrow what's
|
|
22
|
+
printed, or pipe logs into a custom logger (pino, winston, etc.). The same
|
|
23
|
+
`debug` option works on every activity — `chat()`, `summarize()`,
|
|
24
|
+
`generateImage()`, `generateSpeech()`, `generateTranscription()`,
|
|
25
|
+
`generateVideo()`.
|
|
26
|
+
|
|
27
|
+
## Turn it on
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { chat } from '@tanstack/ai'
|
|
31
|
+
import { openaiText } from '@tanstack/ai-openai'
|
|
32
|
+
|
|
33
|
+
const stream = chat({
|
|
34
|
+
adapter: openaiText('gpt-5.2'),
|
|
35
|
+
messages,
|
|
36
|
+
debug: true, // all categories on, prints to console
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Each log line is prefixed with an emoji and `[tanstack-ai:<category>]`:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
📤 [tanstack-ai:request] 📤 activity=chat provider=openai model=gpt-5.2 messages=1 tools=0 stream=true
|
|
44
|
+
🔁 [tanstack-ai:agentLoop] 🔁 run started
|
|
45
|
+
📥 [tanstack-ai:provider] 📥 provider=openai type=response.output_text.delta
|
|
46
|
+
📨 [tanstack-ai:output] 📨 type=TEXT_MESSAGE_CONTENT
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Turn it off
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
chat({
|
|
53
|
+
adapter: openaiText('gpt-5.2'),
|
|
54
|
+
messages,
|
|
55
|
+
debug: false, // silence everything, including errors
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Omitting `debug` is **not** the same as `debug: false`. When omitted, the
|
|
60
|
+
`errors` category is still on (errors are cheap and important). Use
|
|
61
|
+
`debug: false` or `debug: { errors: false }` for true silence.
|
|
62
|
+
|
|
63
|
+
## `DebugOption` — the accepted shapes
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
type DebugOption = boolean | DebugConfig
|
|
67
|
+
|
|
68
|
+
interface DebugConfig {
|
|
69
|
+
// Per-category flags. Any flag omitted from a DebugConfig defaults to true.
|
|
70
|
+
request?: boolean
|
|
71
|
+
provider?: boolean
|
|
72
|
+
output?: boolean
|
|
73
|
+
middleware?: boolean
|
|
74
|
+
tools?: boolean
|
|
75
|
+
agentLoop?: boolean
|
|
76
|
+
config?: boolean
|
|
77
|
+
errors?: boolean
|
|
78
|
+
// Optional custom logger. Defaults to ConsoleLogger.
|
|
79
|
+
logger?: Logger
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Resolution rules for the `debug?: DebugOption` field on every activity:
|
|
84
|
+
|
|
85
|
+
| `debug` value | Effect |
|
|
86
|
+
| --------------------- | ---------------------------------------------------------------------------- |
|
|
87
|
+
| omitted (`undefined`) | Only `errors` is active; default `ConsoleLogger`. |
|
|
88
|
+
| `true` | All categories on; default `ConsoleLogger`. |
|
|
89
|
+
| `false` | All categories off (including `errors`); default `ConsoleLogger`. |
|
|
90
|
+
| `DebugConfig` object | Each unspecified flag defaults to `true`; `logger` replaces `ConsoleLogger`. |
|
|
91
|
+
|
|
92
|
+
## Narrow what's printed
|
|
93
|
+
|
|
94
|
+
Pass a `DebugConfig` object. Unspecified categories default to `true`, so it's
|
|
95
|
+
easiest to toggle by setting specific flags to `false`:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
chat({
|
|
99
|
+
adapter: openaiText('gpt-5.2'),
|
|
100
|
+
messages,
|
|
101
|
+
debug: { middleware: false }, // everything except middleware
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
To print only a specific set, set the rest to `false` explicitly:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
chat({
|
|
109
|
+
adapter: openaiText('gpt-5.2'),
|
|
110
|
+
messages,
|
|
111
|
+
debug: {
|
|
112
|
+
provider: true,
|
|
113
|
+
output: true,
|
|
114
|
+
middleware: false,
|
|
115
|
+
tools: false,
|
|
116
|
+
agentLoop: false,
|
|
117
|
+
config: false,
|
|
118
|
+
errors: true, // keep errors on — they're cheap and important
|
|
119
|
+
request: false,
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Pipe into your own logger
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import type { Logger } from '@tanstack/ai'
|
|
128
|
+
import pino from 'pino'
|
|
129
|
+
|
|
130
|
+
const pinoLogger = pino()
|
|
131
|
+
const logger: Logger = {
|
|
132
|
+
debug: (msg, meta) => pinoLogger.debug(meta, msg),
|
|
133
|
+
info: (msg, meta) => pinoLogger.info(meta, msg),
|
|
134
|
+
warn: (msg, meta) => pinoLogger.warn(meta, msg),
|
|
135
|
+
error: (msg, meta) => pinoLogger.error(meta, msg),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
chat({
|
|
139
|
+
adapter: openaiText('gpt-5.2'),
|
|
140
|
+
messages,
|
|
141
|
+
debug: { logger }, // all categories on, piped to pino
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The default console logger is exported as `ConsoleLogger` if you want to wrap
|
|
146
|
+
it:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { ConsoleLogger } from '@tanstack/ai'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Categories
|
|
153
|
+
|
|
154
|
+
| Category | Logs | Applies to |
|
|
155
|
+
| ------------ | -------------------------------------------------------------- | ------------------------------------- |
|
|
156
|
+
| `request` | Outgoing call to a provider (model, message count, tool count) | All activities |
|
|
157
|
+
| `provider` | Every raw chunk/frame received from a provider SDK | Streaming activities (chat, realtime) |
|
|
158
|
+
| `output` | Every chunk or result yielded to the caller | All activities |
|
|
159
|
+
| `middleware` | Inputs and outputs around every middleware hook | `chat()` only |
|
|
160
|
+
| `tools` | Before/after tool call execution | `chat()` only |
|
|
161
|
+
| `agentLoop` | Agent-loop iterations and phase transitions | `chat()` only |
|
|
162
|
+
| `config` | Config transforms returned by middleware `onConfig` hooks | `chat()` only |
|
|
163
|
+
| `errors` | Every caught error anywhere in the pipeline | All activities |
|
|
164
|
+
|
|
165
|
+
Chat-only categories simply never fire for non-chat activities — those
|
|
166
|
+
concepts don't exist in their pipelines.
|
|
167
|
+
|
|
168
|
+
## Non-chat activities
|
|
169
|
+
|
|
170
|
+
Same `debug` option everywhere:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
summarize({ adapter, text, debug: true })
|
|
174
|
+
generateImage({ adapter, prompt: 'a cat', debug: { logger } })
|
|
175
|
+
generateSpeech({ adapter, text, debug: { request: true } })
|
|
176
|
+
generateTranscription({ adapter, audio, debug: false })
|
|
177
|
+
generateVideo({ adapter, prompt: 'a wave', debug: { output: true } })
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Realtime session adapters in provider packages (e.g. `openaiRealtime`,
|
|
181
|
+
`elevenlabsRealtime`) accept the same `debug?: DebugOption` on their session
|
|
182
|
+
options. They emit `request`, `provider`, and `errors` lines; the chat-only
|
|
183
|
+
categories don't apply.
|
|
184
|
+
|
|
185
|
+
## Common Mistakes
|
|
186
|
+
|
|
187
|
+
### a. HIGH: Treating omitted `debug` as silent
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// WRONG — expecting this to be completely silent
|
|
191
|
+
chat({ adapter, messages })
|
|
192
|
+
// Errors still print via [tanstack-ai:errors] ... on failure.
|
|
193
|
+
|
|
194
|
+
// CORRECT — explicit silence
|
|
195
|
+
chat({ adapter, messages, debug: false })
|
|
196
|
+
chat({ adapter, messages, debug: { errors: false } })
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`debug` undefined means "only errors"; `debug: false` means "nothing at all".
|
|
200
|
+
|
|
201
|
+
Source: docs/advanced/debug-logging.md
|
|
202
|
+
|
|
203
|
+
### b. MEDIUM: Reaching for middleware when `debug` would do
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// WRONG — writing logging middleware to see chunks flow
|
|
207
|
+
const chunkLogger: ChatMiddleware = {
|
|
208
|
+
name: 'chunk-logger',
|
|
209
|
+
onChunk: (ctx, chunk) => {
|
|
210
|
+
console.log(chunk.type, chunk)
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
chat({ adapter, messages, middleware: [chunkLogger] })
|
|
214
|
+
|
|
215
|
+
// CORRECT — just turn on the relevant categories
|
|
216
|
+
chat({
|
|
217
|
+
adapter,
|
|
218
|
+
messages,
|
|
219
|
+
debug: { provider: true, output: true },
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
For observing the built-in pipeline, the `debug` option is strictly faster
|
|
224
|
+
than writing logging middleware. Reach for middleware when you need to
|
|
225
|
+
_transform_ chunks, not just see them.
|
|
226
|
+
|
|
227
|
+
Source: docs/advanced/debug-logging.md
|
|
228
|
+
|
|
229
|
+
### c. LOW: Logger implementation that can throw
|
|
230
|
+
|
|
231
|
+
A user-supplied `Logger` that throws will have its exception swallowed by the
|
|
232
|
+
SDK so it never masks the real error that triggered the log call. Still,
|
|
233
|
+
prefer implementations that don't throw — silenced exceptions are harder to
|
|
234
|
+
debug than loud ones.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// WRONG — a logger that can throw on serialization
|
|
238
|
+
const fragile: Logger = {
|
|
239
|
+
debug: (msg, meta) => console.debug(msg, JSON.stringify(meta)), // cyclic meta → throws
|
|
240
|
+
/* ... */
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// CORRECT — guard serialization in the logger itself
|
|
244
|
+
const safe: Logger = {
|
|
245
|
+
debug: (msg, meta) => {
|
|
246
|
+
try {
|
|
247
|
+
console.debug(msg, meta)
|
|
248
|
+
} catch {
|
|
249
|
+
console.debug(msg)
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
/* ... */
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Source: packages/typescript/ai/src/logger/internal-logger.ts
|
|
257
|
+
|
|
258
|
+
## Cross-References
|
|
259
|
+
|
|
260
|
+
- See also: **ai-core/middleware/SKILL.md** — if you need to transform
|
|
261
|
+
chunks/config, not just observe them.
|
|
262
|
+
- See also: **Observability** (`docs/advanced/observability.md`) — the
|
|
263
|
+
programmatic event client for a richer, structured feed beyond log lines.
|