@tanstack/ai 0.16.0 → 0.18.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 +14 -0
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +27 -8
- package/dist/esm/activities/chat/index.js +245 -14
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +26 -2
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.js +1 -1
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +12 -1
- package/dist/esm/activities/chat/tools/schema-converter.js +5 -0
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/error-payload.d.ts +0 -8
- package/dist/esm/activities/error-payload.js +20 -2
- package/dist/esm/activities/error-payload.js.map +1 -1
- package/dist/esm/activities/generateImage/adapter.d.ts +2 -2
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.d.ts +2 -2
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/index.d.ts +1 -0
- package/dist/esm/activities/index.js +2 -0
- package/dist/esm/activities/index.js.map +1 -1
- package/dist/esm/activities/stream-generation-result.js +0 -2
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/adapter.d.ts +4 -4
- package/dist/esm/activities/summarize/adapter.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +45 -0
- package/dist/esm/activities/summarize/chat-stream-summarize.js +148 -0
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -0
- package/dist/esm/activities/summarize/index.d.ts +1 -0
- package/dist/esm/activities/summarize/index.js +4 -2
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.d.ts +123 -11
- package/dist/esm/utilities/ag-ui-wire.d.ts +44 -0
- package/dist/esm/utilities/ag-ui-wire.js +96 -0
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -0
- package/dist/esm/utilities/chat-params.d.ts +80 -0
- package/dist/esm/utilities/chat-params.js +96 -0
- package/dist/esm/utilities/chat-params.js.map +1 -0
- package/package.json +3 -3
- package/skills/ai-core/ag-ui-protocol/SKILL.md +46 -3
- package/skills/ai-core/structured-outputs/SKILL.md +92 -1
- package/src/activities/chat/adapter.ts +17 -0
- package/src/activities/chat/index.ts +401 -35
- package/src/activities/chat/messages.ts +44 -4
- package/src/activities/chat/middleware/compose.ts +1 -1
- package/src/activities/chat/middleware/types.ts +12 -1
- package/src/activities/chat/tools/schema-converter.ts +14 -0
- package/src/activities/error-payload.ts +31 -2
- package/src/activities/generateImage/adapter.ts +8 -2
- package/src/activities/generateVideo/adapter.ts +8 -2
- package/src/activities/index.ts +5 -0
- package/src/activities/stream-generation-result.ts +4 -6
- package/src/activities/summarize/adapter.ts +8 -4
- package/src/activities/summarize/chat-stream-summarize.ts +238 -0
- package/src/activities/summarize/index.ts +12 -9
- package/src/index.ts +11 -0
- package/src/types.ts +146 -11
- package/src/utilities/ag-ui-wire.ts +182 -0
- package/src/utilities/chat-params.ts +199 -0
|
@@ -76,6 +76,20 @@ export interface TextAdapter<TModel extends string, TProviderOptions extends Rec
|
|
|
76
76
|
* @returns Promise with the raw data (validation is done in the chat function)
|
|
77
77
|
*/
|
|
78
78
|
structuredOutput: (options: StructuredOutputOptions<TProviderOptions>) => Promise<StructuredOutputResult<unknown>>;
|
|
79
|
+
/**
|
|
80
|
+
* Stream structured output using the provider's native streaming structured
|
|
81
|
+
* output API (stream + response_format json_schema in a single request).
|
|
82
|
+
*
|
|
83
|
+
* Optional — adapters without native streaming JSON omit this method and the
|
|
84
|
+
* activity layer synthesizes a stream around the non-streaming
|
|
85
|
+
* `structuredOutput` call.
|
|
86
|
+
*
|
|
87
|
+
* Implementations must emit standard AG-UI lifecycle events (RUN_STARTED,
|
|
88
|
+
* TEXT_MESSAGE_*, RUN_FINISHED) carrying raw JSON text deltas, plus a final
|
|
89
|
+
* `CUSTOM` event named `structured-output.complete` whose `value` is
|
|
90
|
+
* `{ object, raw, reasoning? }`.
|
|
91
|
+
*/
|
|
92
|
+
structuredOutputStream?: (options: StructuredOutputOptions<TProviderOptions>) => AsyncIterable<StreamChunk>;
|
|
79
93
|
}
|
|
80
94
|
/**
|
|
81
95
|
* A TextAdapter with any/unknown type parameters.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sources":["../../../../src/activities/chat/adapter.ts"],"sourcesContent":["import type {\n DefaultMessageMetadataByModality,\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n} from '../../types'\n\n/**\n * Configuration for adapter instances\n */\nexport interface TextAdapterConfig {\n apiKey?: string\n baseUrl?: string\n timeout?: number\n maxRetries?: number\n headers?: Record<string, string>\n}\n\n/**\n * Options for structured output generation.\n *\n * The internal logger is threaded through `chatOptions.logger` (inherited from\n * `TextOptions`). Adapter implementations must call `logger.request()` before\n * SDK calls, `logger.provider()` for each chunk received, and `logger.errors()`\n * in catch blocks.\n */\nexport interface StructuredOutputOptions<TProviderOptions extends object> {\n /** Text options for the request */\n chatOptions: TextOptions<TProviderOptions>\n /** JSON Schema for structured output - already converted from Zod in the ai layer */\n outputSchema: JSONSchema\n}\n\n/**\n * Result from structured output generation\n */\nexport interface StructuredOutputResult<T = unknown> {\n /** The parsed data conforming to the schema */\n data: T\n /** The raw text response from the model before parsing */\n rawText: string\n}\n\n/**\n * Text adapter interface with pre-resolved generics.\n *\n * An adapter is created by a provider function: `provider('model')` → `adapter`\n * All type resolution happens at the provider call site, not in this interface.\n *\n * Generic parameters:\n * - TModel: The specific model name (e.g., 'gpt-4o')\n * - TProviderOptions: Provider-specific options for this model (already resolved)\n * - TInputModalities: Supported input modalities for this model (already resolved)\n * - TMessageMetadata: Metadata types for content parts (already resolved)\n * - TToolCapabilities: Tuple of tool-kind strings supported by this model, resolved from `supports.tools`\n * - TToolCallMetadata: Metadata type that round-trips with tool calls (e.g. Gemini's `thoughtSignature`)\n */\nexport interface TextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, any>,\n TInputModalities extends ReadonlyArray<Modality>,\n TMessageMetadataByModality extends DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n TToolCallMetadata = unknown,\n> {\n /** Discriminator for adapter kind */\n readonly kind: 'text'\n /** Provider name identifier (e.g., 'openai', 'anthropic') */\n readonly name: string\n /** The model this adapter is configured for */\n readonly model: TModel\n\n /**\n * @internal Type-only properties for inference. Not assigned at runtime.\n */\n '~types': {\n providerOptions: TProviderOptions\n inputModalities: TInputModalities\n messageMetadataByModality: TMessageMetadataByModality\n toolCapabilities: TToolCapabilities\n toolCallMetadata: TToolCallMetadata\n }\n\n /**\n * Stream text completions from the model\n */\n chatStream: (\n options: TextOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\n\n /**\n * Generate structured output using the provider's native structured output API.\n * This method uses stream: false and sends the JSON schema to the provider\n * to ensure the response conforms to the expected structure.\n *\n * @param options - Structured output options containing chat options and JSON schema\n * @returns Promise with the raw data (validation is done in the chat function)\n */\n structuredOutput: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => Promise<StructuredOutputResult<unknown>>\n}\n\n/**\n * A TextAdapter with any/unknown type parameters.\n * Useful as a constraint in generic functions and interfaces.\n */\nexport type AnyTextAdapter = TextAdapter<any, any, any, any, any, any>\n\n/**\n * Abstract base class for text adapters.\n * Extend this class to implement a text adapter for a specific provider.\n *\n * Generic parameters match TextAdapter - all pre-resolved by the provider function.\n */\nexport abstract class BaseTextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, any>,\n TInputModalities extends ReadonlyArray<Modality>,\n TMessageMetadataByModality extends DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n TToolCallMetadata = unknown,\n> implements TextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadataByModality,\n TToolCapabilities,\n TToolCallMetadata\n> {\n readonly kind = 'text' as const\n abstract readonly name: string\n readonly model: TModel\n\n // Type-only property - never assigned at runtime\n declare '~types': {\n providerOptions: TProviderOptions\n inputModalities: TInputModalities\n messageMetadataByModality: TMessageMetadataByModality\n toolCapabilities: TToolCapabilities\n toolCallMetadata: TToolCallMetadata\n }\n\n protected config: TextAdapterConfig\n\n constructor(config: TextAdapterConfig = {}, model: TModel) {\n this.config = config\n this.model = model\n }\n\n abstract chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk>\n\n /**\n * Generate structured output using the provider's native structured output API.\n * Concrete implementations should override this to use provider-specific structured output.\n */\n abstract structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>>\n\n protected generateId(): string {\n return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adapter.js","sources":["../../../../src/activities/chat/adapter.ts"],"sourcesContent":["import type {\n DefaultMessageMetadataByModality,\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n} from '../../types'\n\n/**\n * Configuration for adapter instances\n */\nexport interface TextAdapterConfig {\n apiKey?: string\n baseUrl?: string\n timeout?: number\n maxRetries?: number\n headers?: Record<string, string>\n}\n\n/**\n * Options for structured output generation.\n *\n * The internal logger is threaded through `chatOptions.logger` (inherited from\n * `TextOptions`). Adapter implementations must call `logger.request()` before\n * SDK calls, `logger.provider()` for each chunk received, and `logger.errors()`\n * in catch blocks.\n */\nexport interface StructuredOutputOptions<TProviderOptions extends object> {\n /** Text options for the request */\n chatOptions: TextOptions<TProviderOptions>\n /** JSON Schema for structured output - already converted from Zod in the ai layer */\n outputSchema: JSONSchema\n}\n\n/**\n * Result from structured output generation\n */\nexport interface StructuredOutputResult<T = unknown> {\n /** The parsed data conforming to the schema */\n data: T\n /** The raw text response from the model before parsing */\n rawText: string\n}\n\n/**\n * Text adapter interface with pre-resolved generics.\n *\n * An adapter is created by a provider function: `provider('model')` → `adapter`\n * All type resolution happens at the provider call site, not in this interface.\n *\n * Generic parameters:\n * - TModel: The specific model name (e.g., 'gpt-4o')\n * - TProviderOptions: Provider-specific options for this model (already resolved)\n * - TInputModalities: Supported input modalities for this model (already resolved)\n * - TMessageMetadata: Metadata types for content parts (already resolved)\n * - TToolCapabilities: Tuple of tool-kind strings supported by this model, resolved from `supports.tools`\n * - TToolCallMetadata: Metadata type that round-trips with tool calls (e.g. Gemini's `thoughtSignature`)\n */\nexport interface TextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, any>,\n TInputModalities extends ReadonlyArray<Modality>,\n TMessageMetadataByModality extends DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n TToolCallMetadata = unknown,\n> {\n /** Discriminator for adapter kind */\n readonly kind: 'text'\n /** Provider name identifier (e.g., 'openai', 'anthropic') */\n readonly name: string\n /** The model this adapter is configured for */\n readonly model: TModel\n\n /**\n * @internal Type-only properties for inference. Not assigned at runtime.\n */\n '~types': {\n providerOptions: TProviderOptions\n inputModalities: TInputModalities\n messageMetadataByModality: TMessageMetadataByModality\n toolCapabilities: TToolCapabilities\n toolCallMetadata: TToolCallMetadata\n }\n\n /**\n * Stream text completions from the model\n */\n chatStream: (\n options: TextOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\n\n /**\n * Generate structured output using the provider's native structured output API.\n * This method uses stream: false and sends the JSON schema to the provider\n * to ensure the response conforms to the expected structure.\n *\n * @param options - Structured output options containing chat options and JSON schema\n * @returns Promise with the raw data (validation is done in the chat function)\n */\n structuredOutput: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => Promise<StructuredOutputResult<unknown>>\n\n /**\n * Stream structured output using the provider's native streaming structured\n * output API (stream + response_format json_schema in a single request).\n *\n * Optional — adapters without native streaming JSON omit this method and the\n * activity layer synthesizes a stream around the non-streaming\n * `structuredOutput` call.\n *\n * Implementations must emit standard AG-UI lifecycle events (RUN_STARTED,\n * TEXT_MESSAGE_*, RUN_FINISHED) carrying raw JSON text deltas, plus a final\n * `CUSTOM` event named `structured-output.complete` whose `value` is\n * `{ object, raw, reasoning? }`.\n */\n structuredOutputStream?: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\n}\n\n/**\n * A TextAdapter with any/unknown type parameters.\n * Useful as a constraint in generic functions and interfaces.\n */\nexport type AnyTextAdapter = TextAdapter<any, any, any, any, any, any>\n\n/**\n * Abstract base class for text adapters.\n * Extend this class to implement a text adapter for a specific provider.\n *\n * Generic parameters match TextAdapter - all pre-resolved by the provider function.\n */\nexport abstract class BaseTextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, any>,\n TInputModalities extends ReadonlyArray<Modality>,\n TMessageMetadataByModality extends DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n TToolCallMetadata = unknown,\n> implements TextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadataByModality,\n TToolCapabilities,\n TToolCallMetadata\n> {\n readonly kind = 'text' as const\n abstract readonly name: string\n readonly model: TModel\n\n // Type-only property - never assigned at runtime\n declare '~types': {\n providerOptions: TProviderOptions\n inputModalities: TInputModalities\n messageMetadataByModality: TMessageMetadataByModality\n toolCapabilities: TToolCapabilities\n toolCallMetadata: TToolCallMetadata\n }\n\n protected config: TextAdapterConfig\n\n constructor(config: TextAdapterConfig = {}, model: TModel) {\n this.config = config\n this.model = model\n }\n\n abstract chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk>\n\n /**\n * Generate structured output using the provider's native structured output API.\n * Concrete implementations should override this to use provider-specific structured output.\n */\n abstract structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>>\n\n protected generateId(): string {\n return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n}\n"],"names":[],"mappings":"AAqIO,MAAe,gBAcpB;AAAA,EAgBA,YAAY,SAA4B,CAAA,GAAI,OAAe;AAf3D,SAAS,OAAO;AAgBd,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AAAA,EAcU,aAAqB;AAC7B,WAAO,GAAG,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,EAC9E;AACF;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyTextAdapter } from './adapter.js';
|
|
2
|
-
import { ConstrainedModelMessage, InferSchemaType, SchemaInput, StreamChunk, TextOptions, Tool } from '../../types.js';
|
|
2
|
+
import { ConstrainedModelMessage, InferSchemaType, ModelMessage, SchemaInput, StreamChunk, StructuredOutputStream, TextOptions, Tool, UIMessage } from '../../types.js';
|
|
3
3
|
import { ChatMiddleware } from './middleware/types.js';
|
|
4
4
|
import { DebugOption } from '../../logger/types.js';
|
|
5
5
|
import { ProviderTool } from '../../tools/provider-tool.js';
|
|
@@ -16,8 +16,15 @@ export declare const kind: "text";
|
|
|
16
16
|
export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined, TStream extends boolean> {
|
|
17
17
|
/** The text adapter to use (created by a provider function like openaiText('gpt-4o')) */
|
|
18
18
|
adapter: TAdapter;
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Conversation messages. Accepts:
|
|
21
|
+
* - `ConstrainedModelMessage` — content types constrained by the adapter's input modalities.
|
|
22
|
+
* - `ModelMessage` — unconstrained model message (e.g., forwarded from an AG-UI wire payload).
|
|
23
|
+
* - `UIMessage` — parts-based UI representation; converted internally via `convertMessagesToModelMessages`.
|
|
24
|
+
*
|
|
25
|
+
* The three shapes can be mixed in a single array (e.g., when forwarding a wire payload that includes both anchor UIMessages and AG-UI fan-out ModelMessages).
|
|
26
|
+
*/
|
|
27
|
+
messages?: Array<UIMessage | ModelMessage | ConstrainedModelMessage<{
|
|
21
28
|
inputModalities: TAdapter['~types']['inputModalities'];
|
|
22
29
|
messageMetadataByModality: TAdapter['~types']['messageMetadataByModality'];
|
|
23
30
|
}>>;
|
|
@@ -56,6 +63,8 @@ export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema ex
|
|
|
56
63
|
threadId?: TextOptions['threadId'];
|
|
57
64
|
/** Run ID override for AG-UI protocol. Auto-generated by adapter if not provided. */
|
|
58
65
|
runId?: TextOptions['runId'];
|
|
66
|
+
/** Parent run ID for AG-UI protocol nested run correlation. */
|
|
67
|
+
parentRunId?: TextOptions['parentRunId'];
|
|
59
68
|
/**
|
|
60
69
|
* Optional Standard Schema for structured output.
|
|
61
70
|
* When provided, the activity will:
|
|
@@ -139,11 +148,21 @@ export interface TextActivityOptions<TAdapter extends AnyTextAdapter, TSchema ex
|
|
|
139
148
|
export declare function createChatOptions<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined = undefined, TStream extends boolean = true>(options: TextActivityOptions<TAdapter, TSchema, TStream>): TextActivityOptions<TAdapter, TSchema, TStream>;
|
|
140
149
|
/**
|
|
141
150
|
* Result type for the text activity.
|
|
142
|
-
* - If outputSchema is provided:
|
|
143
|
-
*
|
|
144
|
-
*
|
|
151
|
+
* - If outputSchema is provided AND stream is explicitly true:
|
|
152
|
+
* StructuredOutputStream<InferSchemaType<TSchema>> — yields raw JSON deltas
|
|
153
|
+
* via TEXT_MESSAGE_CONTENT plus a terminal StructuredOutputCompleteEvent
|
|
154
|
+
* carrying the validated object.
|
|
155
|
+
* - If outputSchema is provided without explicit stream:true:
|
|
156
|
+
* Promise<InferSchemaType<TSchema>>.
|
|
157
|
+
* - If stream is explicitly false (no schema): Promise<string>.
|
|
158
|
+
* - Otherwise (default): AsyncIterable<StreamChunk>.
|
|
159
|
+
*
|
|
160
|
+
* `[TStream] extends [true]` is used (not `TStream extends true`) so that the
|
|
161
|
+
* default `boolean` value of `TStream` does *not* match the streaming branch.
|
|
162
|
+
* Without this, plain `chat({ outputSchema })` would type as a stream while
|
|
163
|
+
* the runtime returns a Promise — see issue #526.
|
|
145
164
|
*/
|
|
146
|
-
export type TextActivityResult<TSchema extends SchemaInput | undefined, TStream extends boolean =
|
|
165
|
+
export type TextActivityResult<TSchema extends SchemaInput | undefined, TStream extends boolean = boolean> = TSchema extends SchemaInput ? [TStream] extends [true] ? StructuredOutputStream<InferSchemaType<TSchema>> : Promise<InferSchemaType<TSchema>> : [TStream] extends [false] ? Promise<string> : AsyncIterable<StreamChunk>;
|
|
147
166
|
/**
|
|
148
167
|
* Text activity - handles agentic text generation, one-shot text generation, and agentic structured output.
|
|
149
168
|
*
|
|
@@ -205,6 +224,6 @@ export type TextActivityResult<TSchema extends SchemaInput | undefined, TStream
|
|
|
205
224
|
* // result is { summary: string, keyPoints: string[] }
|
|
206
225
|
* ```
|
|
207
226
|
*/
|
|
208
|
-
export declare function chat<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined = undefined, TStream extends boolean =
|
|
227
|
+
export declare function chat<TAdapter extends AnyTextAdapter, TSchema extends SchemaInput | undefined = undefined, TStream extends boolean = boolean>(options: TextActivityOptions<TAdapter, TSchema, TStream>): TextActivityResult<TSchema, TStream>;
|
|
209
228
|
export type { TextAdapter, TextAdapterConfig, StructuredOutputOptions, StructuredOutputResult, } from './adapter.js';
|
|
210
229
|
export { BaseTextAdapter } from './adapter.js';
|
|
@@ -8,6 +8,7 @@ import { convertSchemaToJsonSchema, isStandardSchema, parseWithStandardSchema }
|
|
|
8
8
|
import { maxIterations } from "./agent-loop-strategies.js";
|
|
9
9
|
import { convertMessagesToModelMessages } from "./messages.js";
|
|
10
10
|
import { MiddlewareRunner } from "./middleware/compose.js";
|
|
11
|
+
import { EventType } from "@ag-ui/core";
|
|
11
12
|
const kind = "text";
|
|
12
13
|
function createChatOptions(options) {
|
|
13
14
|
return options;
|
|
@@ -53,8 +54,9 @@ class TextEngine {
|
|
|
53
54
|
this.streamId = this.createId("stream");
|
|
54
55
|
this.effectiveRequest = config.params.abortController ? { signal: config.params.abortController.signal } : void 0;
|
|
55
56
|
this.effectiveSignal = config.params.abortController?.signal;
|
|
56
|
-
this.threadId = config.params.threadId || this.createId("thread");
|
|
57
|
+
this.threadId = config.params.threadId || config.params.conversationId || this.createId("thread");
|
|
57
58
|
this.runIdOverride = config.params.runId;
|
|
59
|
+
this.parentRunIdOverride = config.params.parentRunId;
|
|
58
60
|
const allMiddleware = [
|
|
59
61
|
devtoolsMiddleware(),
|
|
60
62
|
...config.middleware || [],
|
|
@@ -65,7 +67,10 @@ class TextEngine {
|
|
|
65
67
|
this.middlewareCtx = {
|
|
66
68
|
requestId: this.requestId,
|
|
67
69
|
streamId: this.streamId,
|
|
68
|
-
|
|
70
|
+
threadId: this.threadId,
|
|
71
|
+
// Legacy alias kept on the ctx so middleware that reads
|
|
72
|
+
// `ctx.conversationId` keeps working. Always equals `threadId`.
|
|
73
|
+
conversationId: this.threadId,
|
|
69
74
|
phase: "init",
|
|
70
75
|
iteration: 0,
|
|
71
76
|
chunkIndex: 0,
|
|
@@ -110,7 +115,7 @@ class TextEngine {
|
|
|
110
115
|
async *run() {
|
|
111
116
|
this.beforeRun();
|
|
112
117
|
this.logger.agentLoop("run started", {
|
|
113
|
-
|
|
118
|
+
threadId: this.middlewareCtx.threadId
|
|
114
119
|
});
|
|
115
120
|
try {
|
|
116
121
|
this.middlewareCtx.phase = "init";
|
|
@@ -172,7 +177,7 @@ class TextEngine {
|
|
|
172
177
|
} else {
|
|
173
178
|
this.logger.errors("chat run failed", {
|
|
174
179
|
error,
|
|
175
|
-
|
|
180
|
+
threadId: this.middlewareCtx.threadId
|
|
176
181
|
});
|
|
177
182
|
await this.middlewareRunner.runOnError(this.middlewareCtx, {
|
|
178
183
|
error,
|
|
@@ -268,7 +273,8 @@ class TextEngine {
|
|
|
268
273
|
systemPrompts: this.systemPrompts,
|
|
269
274
|
logger: this.logger,
|
|
270
275
|
threadId: this.threadId,
|
|
271
|
-
runId: this.runIdOverride
|
|
276
|
+
runId: this.runIdOverride,
|
|
277
|
+
parentRunId: this.parentRunIdOverride
|
|
272
278
|
})) {
|
|
273
279
|
if (this.isCancelled()) {
|
|
274
280
|
break;
|
|
@@ -926,19 +932,31 @@ class TextEngine {
|
|
|
926
932
|
}
|
|
927
933
|
function chat(options) {
|
|
928
934
|
const { outputSchema, stream } = options;
|
|
935
|
+
if (outputSchema && stream === true) {
|
|
936
|
+
return runStreamingStructuredOutput({
|
|
937
|
+
...options,
|
|
938
|
+
outputSchema,
|
|
939
|
+
stream
|
|
940
|
+
});
|
|
941
|
+
}
|
|
929
942
|
if (outputSchema) {
|
|
930
|
-
return runAgenticStructuredOutput(
|
|
931
|
-
options
|
|
932
|
-
|
|
943
|
+
return runAgenticStructuredOutput({
|
|
944
|
+
...options,
|
|
945
|
+
outputSchema
|
|
946
|
+
});
|
|
933
947
|
}
|
|
934
948
|
if (stream === false) {
|
|
935
|
-
return runNonStreamingText(
|
|
936
|
-
options
|
|
937
|
-
|
|
949
|
+
return runNonStreamingText({
|
|
950
|
+
...options,
|
|
951
|
+
outputSchema: void 0,
|
|
952
|
+
stream
|
|
953
|
+
});
|
|
938
954
|
}
|
|
939
|
-
return runStreamingText(
|
|
940
|
-
options
|
|
941
|
-
|
|
955
|
+
return runStreamingText({
|
|
956
|
+
...options,
|
|
957
|
+
outputSchema: void 0,
|
|
958
|
+
stream
|
|
959
|
+
});
|
|
942
960
|
}
|
|
943
961
|
async function* runStreamingText(options) {
|
|
944
962
|
const { adapter, middleware, context, debug, ...textOptions } = options;
|
|
@@ -1017,6 +1035,219 @@ async function runAgenticStructuredOutput(options) {
|
|
|
1017
1035
|
}
|
|
1018
1036
|
return result.data;
|
|
1019
1037
|
}
|
|
1038
|
+
async function* fallbackStructuredOutputStream(adapter, options) {
|
|
1039
|
+
const { chatOptions } = options;
|
|
1040
|
+
const runId = chatOptions.runId ?? `mock-${Date.now()}`;
|
|
1041
|
+
const threadId = chatOptions.threadId ?? `mock-${Date.now()}`;
|
|
1042
|
+
const messageId = `mock-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1043
|
+
const model = chatOptions.model;
|
|
1044
|
+
const timestamp = Date.now();
|
|
1045
|
+
yield {
|
|
1046
|
+
type: EventType.RUN_STARTED,
|
|
1047
|
+
runId,
|
|
1048
|
+
threadId,
|
|
1049
|
+
model,
|
|
1050
|
+
timestamp
|
|
1051
|
+
};
|
|
1052
|
+
let result;
|
|
1053
|
+
try {
|
|
1054
|
+
result = await adapter.structuredOutput(options);
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1057
|
+
yield {
|
|
1058
|
+
type: EventType.RUN_ERROR,
|
|
1059
|
+
runId,
|
|
1060
|
+
model,
|
|
1061
|
+
timestamp,
|
|
1062
|
+
message,
|
|
1063
|
+
error: { message }
|
|
1064
|
+
};
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
yield {
|
|
1068
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
1069
|
+
messageId,
|
|
1070
|
+
role: "assistant",
|
|
1071
|
+
model,
|
|
1072
|
+
timestamp
|
|
1073
|
+
};
|
|
1074
|
+
yield {
|
|
1075
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
1076
|
+
messageId,
|
|
1077
|
+
delta: result.rawText,
|
|
1078
|
+
model,
|
|
1079
|
+
timestamp
|
|
1080
|
+
};
|
|
1081
|
+
yield {
|
|
1082
|
+
type: EventType.TEXT_MESSAGE_END,
|
|
1083
|
+
messageId,
|
|
1084
|
+
model,
|
|
1085
|
+
timestamp
|
|
1086
|
+
};
|
|
1087
|
+
yield {
|
|
1088
|
+
type: EventType.CUSTOM,
|
|
1089
|
+
name: "structured-output.complete",
|
|
1090
|
+
value: { object: result.data, raw: result.rawText },
|
|
1091
|
+
model,
|
|
1092
|
+
timestamp
|
|
1093
|
+
};
|
|
1094
|
+
yield {
|
|
1095
|
+
type: EventType.RUN_FINISHED,
|
|
1096
|
+
runId,
|
|
1097
|
+
threadId,
|
|
1098
|
+
model,
|
|
1099
|
+
timestamp,
|
|
1100
|
+
finishReason: "stop"
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
function runStreamingStructuredOutput(options) {
|
|
1104
|
+
const { outputSchema } = options;
|
|
1105
|
+
if (!outputSchema) {
|
|
1106
|
+
throw new Error("outputSchema is required for streaming structured output");
|
|
1107
|
+
}
|
|
1108
|
+
const jsonSchema = convertSchemaToJsonSchema(outputSchema, {
|
|
1109
|
+
forStructuredOutput: true
|
|
1110
|
+
});
|
|
1111
|
+
if (!jsonSchema) {
|
|
1112
|
+
throw new Error("Failed to convert output schema to JSON Schema");
|
|
1113
|
+
}
|
|
1114
|
+
return runStreamingStructuredOutputImpl(
|
|
1115
|
+
options,
|
|
1116
|
+
jsonSchema
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
async function* runStreamingStructuredOutputImpl(options, jsonSchema) {
|
|
1120
|
+
const { adapter, outputSchema, middleware, context, debug, ...textOptions } = options;
|
|
1121
|
+
const model = adapter.model;
|
|
1122
|
+
const logger = resolveDebugOption(debug);
|
|
1123
|
+
const runId = textOptions.runId;
|
|
1124
|
+
let finalMessages = convertMessagesToModelMessages(textOptions.messages ?? []);
|
|
1125
|
+
if (textOptions.tools?.length) {
|
|
1126
|
+
const engine = new TextEngine(
|
|
1127
|
+
{
|
|
1128
|
+
adapter,
|
|
1129
|
+
params: { ...textOptions, model, logger, messages: finalMessages },
|
|
1130
|
+
middleware,
|
|
1131
|
+
context
|
|
1132
|
+
},
|
|
1133
|
+
logger
|
|
1134
|
+
);
|
|
1135
|
+
let agentLoopErrored = false;
|
|
1136
|
+
try {
|
|
1137
|
+
for await (const chunk of engine.run()) {
|
|
1138
|
+
if (chunk.type === "RUN_STARTED" || chunk.type === "RUN_FINISHED") {
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
if (chunk.type === "RUN_ERROR") {
|
|
1142
|
+
agentLoopErrored = true;
|
|
1143
|
+
yield chunk;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
yield chunk;
|
|
1147
|
+
}
|
|
1148
|
+
} catch (engineError) {
|
|
1149
|
+
const message = engineError.message || "Agent loop failed";
|
|
1150
|
+
logger.errors("runStreamingStructuredOutput agent loop failed", {
|
|
1151
|
+
error: engineError,
|
|
1152
|
+
source: "runStreamingStructuredOutput"
|
|
1153
|
+
});
|
|
1154
|
+
yield {
|
|
1155
|
+
type: EventType.RUN_ERROR,
|
|
1156
|
+
runId,
|
|
1157
|
+
model,
|
|
1158
|
+
timestamp: Date.now(),
|
|
1159
|
+
message,
|
|
1160
|
+
code: "agent-loop-failed",
|
|
1161
|
+
error: { message, code: "agent-loop-failed" }
|
|
1162
|
+
};
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
if (agentLoopErrored) {
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
finalMessages = engine.getMessages();
|
|
1169
|
+
}
|
|
1170
|
+
const {
|
|
1171
|
+
tools: _tools,
|
|
1172
|
+
agentLoopStrategy: _als,
|
|
1173
|
+
...structuredTextOptions
|
|
1174
|
+
} = textOptions;
|
|
1175
|
+
logger.request(
|
|
1176
|
+
`activity=chat-structured-stream provider=${adapter.name} model=${model} messages=${finalMessages.length}`,
|
|
1177
|
+
{
|
|
1178
|
+
provider: adapter.name,
|
|
1179
|
+
model,
|
|
1180
|
+
messageCount: finalMessages.length
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
const structuredChatOptions = {
|
|
1184
|
+
...structuredTextOptions,
|
|
1185
|
+
model,
|
|
1186
|
+
messages: finalMessages,
|
|
1187
|
+
logger,
|
|
1188
|
+
request: textOptions.abortController ? { signal: textOptions.abortController.signal } : void 0
|
|
1189
|
+
};
|
|
1190
|
+
const stream = adapter.structuredOutputStream ? adapter.structuredOutputStream({
|
|
1191
|
+
chatOptions: structuredChatOptions,
|
|
1192
|
+
outputSchema: jsonSchema
|
|
1193
|
+
}) : fallbackStructuredOutputStream(adapter, {
|
|
1194
|
+
chatOptions: structuredChatOptions,
|
|
1195
|
+
outputSchema: jsonSchema
|
|
1196
|
+
});
|
|
1197
|
+
for await (const chunk of stream) {
|
|
1198
|
+
if (chunk.type === EventType.CUSTOM && chunk.name === "structured-output.complete") {
|
|
1199
|
+
const value = chunk.value;
|
|
1200
|
+
if (isStandardSchema(outputSchema)) {
|
|
1201
|
+
try {
|
|
1202
|
+
const validated = parseWithStandardSchema(
|
|
1203
|
+
outputSchema,
|
|
1204
|
+
value.object
|
|
1205
|
+
);
|
|
1206
|
+
yield {
|
|
1207
|
+
...chunk,
|
|
1208
|
+
// Forward `reasoning` through schema validation so consumers that
|
|
1209
|
+
// only listen for the terminal event don't lose chain-of-thought.
|
|
1210
|
+
value: {
|
|
1211
|
+
object: validated,
|
|
1212
|
+
raw: value.raw,
|
|
1213
|
+
...value.reasoning ? { reasoning: value.reasoning } : {}
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
continue;
|
|
1217
|
+
} catch (err) {
|
|
1218
|
+
const message = err.message || "Schema validation failed";
|
|
1219
|
+
logger.errors(
|
|
1220
|
+
"runStreamingStructuredOutput schema validation failed",
|
|
1221
|
+
{
|
|
1222
|
+
error: err,
|
|
1223
|
+
source: "runStreamingStructuredOutput",
|
|
1224
|
+
// Include reasoning in error meta so post-mortems can recover
|
|
1225
|
+
// what the model thought through before producing invalid JSON.
|
|
1226
|
+
...value.reasoning ? { reasoning: value.reasoning } : {}
|
|
1227
|
+
}
|
|
1228
|
+
);
|
|
1229
|
+
yield {
|
|
1230
|
+
type: EventType.RUN_ERROR,
|
|
1231
|
+
runId,
|
|
1232
|
+
model: chunk.model ?? model,
|
|
1233
|
+
timestamp: chunk.timestamp ?? Date.now(),
|
|
1234
|
+
message,
|
|
1235
|
+
code: "schema-validation",
|
|
1236
|
+
error: {
|
|
1237
|
+
message,
|
|
1238
|
+
code: "schema-validation",
|
|
1239
|
+
...value.reasoning ? { reasoning: value.reasoning } : {}
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
return;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
yield chunk;
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
yield chunk;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1020
1251
|
export {
|
|
1021
1252
|
chat,
|
|
1022
1253
|
createChatOptions,
|