@tanstack/ai 0.59.0 → 0.61.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 +9 -0
- package/dist/esm/activities/chat/adapter.js +1 -0
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.js +134 -22
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +5 -1
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js +9 -2
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +0 -1
- package/dist/esm/activities/chat/stream/processor.js +15 -12
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.js +2 -0
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/embed/adapter.d.ts +7 -0
- package/dist/esm/activities/embed/adapter.js +1 -0
- package/dist/esm/activities/embed/adapter.js.map +1 -1
- package/dist/esm/activities/embed/index.js +2 -0
- package/dist/esm/activities/embed/index.js.map +1 -1
- package/dist/esm/activities/files/adapter.d.ts +97 -0
- package/dist/esm/activities/files/adapter.js +45 -0
- package/dist/esm/activities/files/adapter.js.map +1 -0
- package/dist/esm/activities/files/index.d.ts +66 -0
- package/dist/esm/activities/files/index.js +78 -0
- package/dist/esm/activities/files/index.js.map +1 -0
- package/dist/esm/activities/generateImage/adapter.d.ts +8 -0
- package/dist/esm/activities/generateImage/adapter.js +1 -0
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.js +2 -0
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.d.ts +8 -0
- package/dist/esm/activities/generateVideo/adapter.js +1 -0
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.js +3 -0
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/generateWorld/adapter.d.ts +4 -2
- package/dist/esm/activities/generateWorld/adapter.js.map +1 -1
- package/dist/esm/activities/generateWorld/index.d.ts +4 -3
- package/dist/esm/activities/generateWorld/index.js +5 -4
- package/dist/esm/activities/generateWorld/index.js.map +1 -1
- package/dist/esm/activities/index.d.ts +6 -3
- package/dist/esm/activities/index.js +13 -11
- package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +2 -0
- package/dist/esm/activities/summarize/chat-stream-summarize.js +8 -8
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/client.d.ts +2 -0
- package/dist/esm/client.js +2 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +4 -2
- package/dist/esm/types.d.ts +72 -13
- package/dist/esm/utilities/ag-ui-wire.js +34 -13
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/content-source.d.ts +60 -0
- package/dist/esm/utilities/content-source.js +85 -0
- package/dist/esm/utilities/content-source.js.map +1 -0
- package/dist/esm/utilities/provider-executed.d.ts +7 -0
- package/dist/esm/utilities/provider-executed.js +10 -1
- package/dist/esm/utilities/provider-executed.js.map +1 -1
- package/dist/esm/utilities/tool-result.d.ts +12 -2
- package/dist/esm/utilities/tool-result.js +23 -3
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/package.json +2 -2
- package/skills/ai-core/adapter-configuration/SKILL.md +62 -0
- package/skills/ai-core/chat-experience/SKILL.md +14 -0
- package/skills/ai-core/media-generation/SKILL.md +8 -0
- package/src/activities/chat/adapter.ts +10 -0
- package/src/activities/chat/index.ts +226 -40
- package/src/activities/chat/messages.ts +12 -1
- package/src/activities/chat/stream/message-updaters.ts +24 -2
- package/src/activities/chat/stream/processor.ts +24 -22
- package/src/activities/chat/tools/tool-calls.ts +6 -0
- package/src/activities/embed/adapter.ts +7 -0
- package/src/activities/embed/index.ts +5 -0
- package/src/activities/files/adapter.ts +120 -0
- package/src/activities/files/index.ts +113 -0
- package/src/activities/generateImage/adapter.ts +8 -0
- package/src/activities/generateImage/index.ts +4 -0
- package/src/activities/generateVideo/adapter.ts +8 -0
- package/src/activities/generateVideo/index.ts +7 -0
- package/src/activities/generateWorld/adapter.ts +4 -2
- package/src/activities/generateWorld/index.ts +7 -6
- package/src/activities/index.ts +25 -1
- package/src/activities/summarize/chat-stream-summarize.ts +22 -12
- package/src/client.ts +7 -0
- package/src/index.ts +16 -0
- package/src/types.ts +76 -13
- package/src/utilities/ag-ui-wire.ts +60 -16
- package/src/utilities/content-source.ts +138 -0
- package/src/utilities/provider-executed.ts +13 -0
- package/src/utilities/tool-result.ts +38 -2
|
@@ -69,6 +69,14 @@ export interface TextAdapter<TModel extends string, TProviderOptions extends Rec
|
|
|
69
69
|
* this is the declaration/validation surface only.
|
|
70
70
|
*/
|
|
71
71
|
readonly requires?: ReadonlyArray<CapabilityHandle>;
|
|
72
|
+
/**
|
|
73
|
+
* Declares that this adapter can consume `{ type: 'file' }` content sources
|
|
74
|
+
* (provider Files API references). `chat()` rejects file sources in preflight
|
|
75
|
+
* for adapters that don't declare this, so an adapter written before the
|
|
76
|
+
* file arm existed fails closed instead of silently mis-mapping a reference
|
|
77
|
+
* onto its URL/data branch.
|
|
78
|
+
*/
|
|
79
|
+
readonly supportsFileSources?: boolean;
|
|
72
80
|
/**
|
|
73
81
|
* @internal Type-only properties for inference. Not assigned at runtime.
|
|
74
82
|
*/
|
|
@@ -156,6 +164,7 @@ export declare abstract class BaseTextAdapter<TModel extends string, TProviderOp
|
|
|
156
164
|
abstract readonly name: string;
|
|
157
165
|
readonly model: TModel;
|
|
158
166
|
readonly requires?: ReadonlyArray<CapabilityHandle>;
|
|
167
|
+
readonly supportsFileSources: boolean;
|
|
159
168
|
'~types': {
|
|
160
169
|
providerOptions: TProviderOptions;
|
|
161
170
|
inputModalities: TInputModalities;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","names":[],"sources":["../../../../src/activities/chat/adapter.ts"],"sourcesContent":["import type {\n DefaultMessageMetadataByModality,\n JSONSchema,\n Modality,\n TextOptions,\n TokenUsage,\n} from '../../types'\nimport type { AdapterYieldChunk } from '../../utilities/adapter-yield-chunk'\nimport type { CapabilityHandle } from './middleware/capabilities'\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 /** Token usage information (if provided by the adapter) */\n usage?: TokenUsage\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 * - TSystemPromptMetadata: Provider-typed metadata accepted on each\n * `systemPrompts[i]` entry (e.g. Anthropic `cache_control`). Defaults to\n * `never` — adapters without per-prompt metadata reject the `metadata`\n * field at the call site.\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 TSystemPromptMetadata = never,\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 * Capabilities this adapter requires at runtime. `chat()` validates that the\n * configured middleware provides each one. Model adapters omit this; harness\n * adapters (e.g. a future `claudeCode()`) declare e.g. `[sandboxCapability]`.\n * Runtime access to capabilities from inside the adapter is not yet wired —\n * this is the declaration/validation surface only.\n */\n readonly requires?: ReadonlyArray<CapabilityHandle>\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 systemPromptMetadata: TSystemPromptMetadata\n }\n\n /**\n * Stream text completions from the model\n */\n chatStream: (\n options: TextOptions<TProviderOptions>,\n ) => AsyncIterable<AdapterYieldChunk>\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? }`. Events must be timestamped when emitted so\n * their timestamps follow stream order.\n */\n structuredOutputStream?: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => AsyncIterable<AdapterYieldChunk>\n\n /**\n * Declares whether the adapter supports combining `tools` and a\n * schema-constrained final answer in a single streaming request.\n *\n * When `true`, the engine wires `outputSchema` into the regular\n * `chatStream()` call and skips the separate `runStructuredFinalization`\n * round-trip. The model's natural final turn carries the\n * schema-constrained JSON text and the engine harvests it from the agent\n * loop's accumulated content.\n *\n * When `false`, `undefined`, or the method is omitted, the engine runs\n * the agent loop without `outputSchema` and then issues a separate\n * `structuredOutput` / `structuredOutputStream` call against the JSON\n * schema for finalization (the legacy path).\n *\n * The method receives the per-call `modelOptions` so providers whose\n * support depends on the resolved upstream model (e.g. OpenRouter) can\n * answer per-request. Most adapters can return a constant.\n */\n supportsCombinedToolsAndSchema?: (\n modelOptions?: TProviderOptions | undefined,\n ) => boolean\n\n /**\n * Where native-combined structured output is taken from.\n *\n * - `'text'` (default when omitted): the agent loop's accumulated\n * assistant text is schema JSON. The engine parses it after the loop.\n * HTTP adapters use this.\n * - `'event'`: the adapter emits `structured-output.complete` during\n * `chatStream`. The engine must not parse accumulated prose. Harness\n * adapters use this.\n */\n combinedStructuredOutputSource?: (\n modelOptions?: TProviderOptions | undefined,\n ) => 'text' | 'event'\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, 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 TSystemPromptMetadata = never,\n> implements TextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadataByModality,\n TToolCapabilities,\n TToolCallMetadata,\n TSystemPromptMetadata\n> {\n readonly kind = 'text' as const\n abstract readonly name: string\n readonly model: TModel\n readonly requires?: ReadonlyArray<CapabilityHandle> = undefined\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 systemPromptMetadata: TSystemPromptMetadata\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<AdapterYieldChunk>\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"],"mappings":";;;;;;;AA8LA,IAAsB,kBAAtB,MAgBE;CACA,OAAgB;CAEhB;CACA,WAAsD,KAAA;CAYtD;CAEA,YAAY,SAA4B,CAAC,GAAG,OAAe;EACzD,KAAK,SAAS;EACd,KAAK,QAAQ;CACf;CAcA,aAA+B;EAC7B,OAAO,GAAG,KAAK,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,UAAU,CAAC;CAC7E;AACF"}
|
|
1
|
+
{"version":3,"file":"adapter.js","names":[],"sources":["../../../../src/activities/chat/adapter.ts"],"sourcesContent":["import type {\n DefaultMessageMetadataByModality,\n JSONSchema,\n Modality,\n TextOptions,\n TokenUsage,\n} from '../../types'\nimport type { AdapterYieldChunk } from '../../utilities/adapter-yield-chunk'\nimport type { CapabilityHandle } from './middleware/capabilities'\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 /** Token usage information (if provided by the adapter) */\n usage?: TokenUsage\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 * - TSystemPromptMetadata: Provider-typed metadata accepted on each\n * `systemPrompts[i]` entry (e.g. Anthropic `cache_control`). Defaults to\n * `never` — adapters without per-prompt metadata reject the `metadata`\n * field at the call site.\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 TSystemPromptMetadata = never,\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 * Capabilities this adapter requires at runtime. `chat()` validates that the\n * configured middleware provides each one. Model adapters omit this; harness\n * adapters (e.g. a future `claudeCode()`) declare e.g. `[sandboxCapability]`.\n * Runtime access to capabilities from inside the adapter is not yet wired —\n * this is the declaration/validation surface only.\n */\n readonly requires?: ReadonlyArray<CapabilityHandle>\n\n /**\n * Declares that this adapter can consume `{ type: 'file' }` content sources\n * (provider Files API references). `chat()` rejects file sources in preflight\n * for adapters that don't declare this, so an adapter written before the\n * file arm existed fails closed instead of silently mis-mapping a reference\n * onto its URL/data branch.\n */\n readonly supportsFileSources?: boolean\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 systemPromptMetadata: TSystemPromptMetadata\n }\n\n /**\n * Stream text completions from the model\n */\n chatStream: (\n options: TextOptions<TProviderOptions>,\n ) => AsyncIterable<AdapterYieldChunk>\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? }`. Events must be timestamped when emitted so\n * their timestamps follow stream order.\n */\n structuredOutputStream?: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => AsyncIterable<AdapterYieldChunk>\n\n /**\n * Declares whether the adapter supports combining `tools` and a\n * schema-constrained final answer in a single streaming request.\n *\n * When `true`, the engine wires `outputSchema` into the regular\n * `chatStream()` call and skips the separate `runStructuredFinalization`\n * round-trip. The model's natural final turn carries the\n * schema-constrained JSON text and the engine harvests it from the agent\n * loop's accumulated content.\n *\n * When `false`, `undefined`, or the method is omitted, the engine runs\n * the agent loop without `outputSchema` and then issues a separate\n * `structuredOutput` / `structuredOutputStream` call against the JSON\n * schema for finalization (the legacy path).\n *\n * The method receives the per-call `modelOptions` so providers whose\n * support depends on the resolved upstream model (e.g. OpenRouter) can\n * answer per-request. Most adapters can return a constant.\n */\n supportsCombinedToolsAndSchema?: (\n modelOptions?: TProviderOptions | undefined,\n ) => boolean\n\n /**\n * Where native-combined structured output is taken from.\n *\n * - `'text'` (default when omitted): the agent loop's accumulated\n * assistant text is schema JSON. The engine parses it after the loop.\n * HTTP adapters use this.\n * - `'event'`: the adapter emits `structured-output.complete` during\n * `chatStream`. The engine must not parse accumulated prose. Harness\n * adapters use this.\n */\n combinedStructuredOutputSource?: (\n modelOptions?: TProviderOptions | undefined,\n ) => 'text' | 'event'\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, 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 TSystemPromptMetadata = never,\n> implements TextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadataByModality,\n TToolCapabilities,\n TToolCallMetadata,\n TSystemPromptMetadata\n> {\n readonly kind = 'text' as const\n abstract readonly name: string\n readonly model: TModel\n readonly requires?: ReadonlyArray<CapabilityHandle> = undefined\n readonly supportsFileSources: boolean = false\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 systemPromptMetadata: TSystemPromptMetadata\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<AdapterYieldChunk>\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"],"mappings":";;;;;;;AAuMA,IAAsB,kBAAtB,MAgBE;CACA,OAAgB;CAEhB;CACA,WAAsD,KAAA;CACtD,sBAAwC;CAYxC;CAEA,YAAY,SAA4B,CAAC,GAAG,OAAe;EACzD,KAAK,SAAS;EACd,KAAK,QAAQ;CACf;CAcA,aAA+B;EAC7B,OAAO,GAAG,KAAK,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,UAAU,CAAC;CAC7E;AACF"}
|
|
@@ -17,12 +17,13 @@ import { hashSchemaInput, normalizeApprovalSchema } from "./tools/approval-schem
|
|
|
17
17
|
import { INTERRUPT_BINDING_METADATA_KEY, InterruptResumeValidationError, readInterruptBinding, readUnopenedInterruptBinding, validateInterruptResumeBatch } from "../../interrupt-resume.js";
|
|
18
18
|
import { INTERRUPT_PAYLOAD_METADATA_KEY, createInterruptBinding, rehydrateInterruptRequest } from "../../interrupt-definition.js";
|
|
19
19
|
import { readGenericInterruptContinuation } from "../../generic-interrupt-continuation.js";
|
|
20
|
-
import { normalizeToolResult } from "../../utilities/tool-result.js";
|
|
21
|
-
import { subagentHostMessageId } from "../../utilities/subagent-wire.js";
|
|
22
20
|
import { isProviderExecutedToolCall } from "../../utilities/provider-executed.js";
|
|
21
|
+
import { normalizeToolResult, parseToolOutput, toolResultErrorText } from "../../utilities/tool-result.js";
|
|
22
|
+
import { subagentHostMessageId } from "../../utilities/subagent-wire.js";
|
|
23
23
|
import { appendUiResourceToModelMessages, convertMessagesToModelMessages, generateMessageId, modelMessagesToUIMessages, safeJsonStringify, uiResourcePartFromCustomValue } from "./messages.js";
|
|
24
24
|
import { uiMessagesToWire } from "../../utilities/ag-ui-wire.js";
|
|
25
25
|
import { restorePublicUsage } from "../../utilities/restore-inbound-chunk.js";
|
|
26
|
+
import { assertMessagesFileSourceSupport } from "../../utilities/content-source.js";
|
|
26
27
|
import { LazyToolManager } from "./tools/lazy-tool-manager.js";
|
|
27
28
|
import { assertUniqueToolNames } from "./tools/unique-tool-names.js";
|
|
28
29
|
import { MiddlewareAbortError, ToolCallManager, executeToolCalls } from "./tools/tool-calls.js";
|
|
@@ -50,12 +51,6 @@ var kind = "text";
|
|
|
50
51
|
var interruptBindingMetadataKey = INTERRUPT_BINDING_METADATA_KEY;
|
|
51
52
|
/** Resume entries a subagent tool call owns. The parent run skips them. */
|
|
52
53
|
var CHILD_RESUME_IDS = Symbol("tanstack.ai.childResumeIds");
|
|
53
|
-
function assertNoFileSources(adapterName, messages) {
|
|
54
|
-
for (const message of messages) {
|
|
55
|
-
if (!Array.isArray(message.content)) continue;
|
|
56
|
-
for (const part of message.content) if ("source" in part && part.source.type === "file") throw new Error(`${adapterName} does not support provider file-handle sources ({ type: 'file' }). Pass a data or url source.`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
54
|
function isInterruptSubmissionError(value) {
|
|
60
55
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
61
56
|
if (!("scope" in value) || !("code" in value) || !("message" in value) || !("source" in value) || !("retryable" in value) || !("threadId" in value) || !("interruptedRunId" in value) || !("generation" in value) || typeof value.code !== "string" || typeof value.message !== "string" || typeof value.retryable !== "boolean" || typeof value.threadId !== "string" || typeof value.interruptedRunId !== "string" || typeof value.generation !== "number") return false;
|
|
@@ -143,6 +138,15 @@ var TextEngine = class {
|
|
|
143
138
|
streamIdentityCaptured = false;
|
|
144
139
|
accumulatedContent = "";
|
|
145
140
|
accumulatedThinking = [];
|
|
141
|
+
/**
|
|
142
|
+
* Arrival order of this iteration's thinking steps, text and tool calls.
|
|
143
|
+
* A ModelMessage keeps `thinking` apart from `content`/`toolCalls`, so a
|
|
144
|
+
* provider turn that thinks between provider-executed tools would otherwise
|
|
145
|
+
* be recorded as "all thinking, then text, then tools" and the provider
|
|
146
|
+
* rejects the replay (signed thinking must keep its position). `null` once
|
|
147
|
+
* the order could no longer be tracked (callers fall back to one message).
|
|
148
|
+
*/
|
|
149
|
+
turnParts = [];
|
|
146
150
|
currentThinkingContent = "";
|
|
147
151
|
currentThinkingSignature = "";
|
|
148
152
|
eventOptions;
|
|
@@ -501,6 +505,7 @@ var TextEngine = class {
|
|
|
501
505
|
this.streamIdentityCaptured = false;
|
|
502
506
|
this.accumulatedContent = "";
|
|
503
507
|
this.accumulatedThinking = [];
|
|
508
|
+
this.turnParts = [];
|
|
504
509
|
this.currentThinkingContent = "";
|
|
505
510
|
this.currentThinkingSignature = "";
|
|
506
511
|
this.finishedEvent = null;
|
|
@@ -531,7 +536,7 @@ var TextEngine = class {
|
|
|
531
536
|
const { approvals } = this.collectClientState();
|
|
532
537
|
const adapterApprovals = /* @__PURE__ */ new Map();
|
|
533
538
|
for (const [approvalId, resolution] of approvals) adapterApprovals.set(approvalId, typeof resolution === "boolean" ? resolution : resolution.approved);
|
|
534
|
-
|
|
539
|
+
assertMessagesFileSourceSupport(this.adapter, this.messages);
|
|
535
540
|
for await (const raw of this.adapter.chatStream({
|
|
536
541
|
model: this.params.model,
|
|
537
542
|
messages: this.providerMessages,
|
|
@@ -666,9 +671,23 @@ var TextEngine = class {
|
|
|
666
671
|
}
|
|
667
672
|
handleTextMessageContentEvent(chunk) {
|
|
668
673
|
const extra = chunk;
|
|
674
|
+
const before = this.accumulatedContent;
|
|
669
675
|
if (typeof extra.content === "string" && extra.content !== "") this.accumulatedContent = extra.content;
|
|
670
676
|
else this.accumulatedContent += chunk.delta;
|
|
671
677
|
this.middlewareCtx.accumulatedContent = this.accumulatedContent;
|
|
678
|
+
if (!this.turnParts) return;
|
|
679
|
+
if (!this.accumulatedContent.startsWith(before)) {
|
|
680
|
+
this.turnParts = null;
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const delta = this.accumulatedContent.slice(before.length);
|
|
684
|
+
if (delta === "") return;
|
|
685
|
+
const last = this.turnParts[this.turnParts.length - 1];
|
|
686
|
+
if (last && last.type === "text") last.content += delta;
|
|
687
|
+
else this.turnParts.push({
|
|
688
|
+
type: "text",
|
|
689
|
+
content: delta
|
|
690
|
+
});
|
|
672
691
|
}
|
|
673
692
|
captureStreamMessageIdentity(messageId) {
|
|
674
693
|
this.currentMessageId = messageId;
|
|
@@ -685,6 +704,11 @@ var TextEngine = class {
|
|
|
685
704
|
handleToolCallStartEvent(chunk) {
|
|
686
705
|
if (typeof chunk.parentMessageId === "string" && chunk.parentMessageId !== "") this.captureStreamMessageIdentity(chunk.parentMessageId);
|
|
687
706
|
this.toolCallManager.addToolCallStartEvent(chunk);
|
|
707
|
+
if (this.turnParts && !this.turnParts.some((part) => part.type === "call" && part.id === chunk.toolCallId)) this.turnParts.push({
|
|
708
|
+
type: "call",
|
|
709
|
+
id: chunk.toolCallId,
|
|
710
|
+
providerExecuted: isProviderExecutedToolCall({ metadata: chunk.metadata })
|
|
711
|
+
});
|
|
688
712
|
const metadata = chunk.metadata;
|
|
689
713
|
const thoughtSignature = metadata != null && typeof metadata === "object" && "thoughtSignature" in metadata && typeof metadata.thoughtSignature === "string" && metadata.thoughtSignature !== "" ? metadata.thoughtSignature : void 0;
|
|
690
714
|
if (thoughtSignature === void 0) return;
|
|
@@ -746,17 +770,42 @@ var TextEngine = class {
|
|
|
746
770
|
content: this.currentThinkingContent,
|
|
747
771
|
...this.currentThinkingSignature && { signature: this.currentThinkingSignature }
|
|
748
772
|
});
|
|
773
|
+
if (this.turnParts) {
|
|
774
|
+
const placeholder = [...this.turnParts].reverse().find((part) => part.type === "thinking" && part.index === -1);
|
|
775
|
+
const index = this.accumulatedThinking.length - 1;
|
|
776
|
+
if (placeholder) placeholder.index = index;
|
|
777
|
+
else this.turnParts.push({
|
|
778
|
+
type: "thinking",
|
|
779
|
+
index
|
|
780
|
+
});
|
|
781
|
+
}
|
|
749
782
|
this.currentThinkingContent = "";
|
|
750
783
|
this.currentThinkingSignature = "";
|
|
751
784
|
}
|
|
752
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* Record where the current thinking step sits among this turn's parts. A
|
|
788
|
+
* step is finalized only when the next step starts (or the turn ends), by
|
|
789
|
+
* which time later tool calls have already arrived, so the position has to
|
|
790
|
+
* be noted when the step's first content or signature shows up.
|
|
791
|
+
*/
|
|
792
|
+
noteThinkingStepPosition() {
|
|
793
|
+
if (this.turnParts && this.currentThinkingContent === "" && this.currentThinkingSignature === "") this.turnParts.push({
|
|
794
|
+
type: "thinking",
|
|
795
|
+
index: -1
|
|
796
|
+
});
|
|
797
|
+
}
|
|
753
798
|
handleStepStartedEvent() {
|
|
754
799
|
this.finalizeCurrentThinkingStep();
|
|
755
800
|
}
|
|
756
801
|
handleStepFinishedEvent(chunk) {
|
|
757
|
-
if (typeof chunk.signature === "string" && chunk.signature !== "")
|
|
802
|
+
if (typeof chunk.signature === "string" && chunk.signature !== "") {
|
|
803
|
+
this.noteThinkingStepPosition();
|
|
804
|
+
this.currentThinkingSignature = chunk.signature;
|
|
805
|
+
}
|
|
758
806
|
}
|
|
759
807
|
handleReasoningMessageContentEvent(chunk) {
|
|
808
|
+
this.noteThinkingStepPosition();
|
|
760
809
|
this.currentThinkingContent += chunk.delta;
|
|
761
810
|
}
|
|
762
811
|
handleReasoningEncryptedValueEvent(chunk) {
|
|
@@ -768,6 +817,7 @@ var TextEngine = class {
|
|
|
768
817
|
};
|
|
769
818
|
return;
|
|
770
819
|
}
|
|
820
|
+
this.noteThinkingStepPosition();
|
|
771
821
|
this.currentThinkingSignature = chunk.encryptedValue;
|
|
772
822
|
}
|
|
773
823
|
/**
|
|
@@ -1013,16 +1063,67 @@ var TextEngine = class {
|
|
|
1013
1063
|
shouldExecuteToolPhase() {
|
|
1014
1064
|
return this.lastFinishReason === "tool_calls" && this.tools.length > 0 && this.toolCallManager.hasToolCalls();
|
|
1015
1065
|
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Split this iteration into assistant ModelMessages that keep the provider's
|
|
1068
|
+
* block order: a new segment starts at every thinking step that follows a
|
|
1069
|
+
* provider-executed tool call (the rule buildAssistantMessages applies to
|
|
1070
|
+
* UIMessages). Segments after the first get `${id}-segment-${n}` ids.
|
|
1071
|
+
* Returns null when no split is needed or the order could not be tracked,
|
|
1072
|
+
* so callers fall back to the single-message shape.
|
|
1073
|
+
*/
|
|
1074
|
+
buildOrderedAssistantSegments(toolCalls, id, createdAt) {
|
|
1075
|
+
const parts = this.turnParts;
|
|
1076
|
+
if (!parts) return null;
|
|
1077
|
+
const providerCallIds = new Set(parts.flatMap((part) => part.type === "call" && part.providerExecuted ? [part.id] : []));
|
|
1078
|
+
let current = {
|
|
1079
|
+
thinking: [],
|
|
1080
|
+
text: "",
|
|
1081
|
+
callIds: []
|
|
1082
|
+
};
|
|
1083
|
+
const segments = [current];
|
|
1084
|
+
let split = false;
|
|
1085
|
+
for (const part of parts) if (part.type === "thinking") {
|
|
1086
|
+
const thinking = this.accumulatedThinking[part.index];
|
|
1087
|
+
if (!thinking) return null;
|
|
1088
|
+
if (current.callIds.some((callId) => providerCallIds.has(callId))) {
|
|
1089
|
+
current = {
|
|
1090
|
+
thinking: [thinking],
|
|
1091
|
+
text: "",
|
|
1092
|
+
callIds: []
|
|
1093
|
+
};
|
|
1094
|
+
segments.push(current);
|
|
1095
|
+
split = true;
|
|
1096
|
+
} else current.thinking.push(thinking);
|
|
1097
|
+
} else if (part.type === "text") current.text += part.content;
|
|
1098
|
+
else current.callIds.push(part.id);
|
|
1099
|
+
if (!split) return null;
|
|
1100
|
+
if (segments.map((segment) => segment.text).join("") !== this.accumulatedContent) return null;
|
|
1101
|
+
if (segments.reduce((n, segment) => n + segment.thinking.length, 0) !== this.accumulatedThinking.length) return null;
|
|
1102
|
+
const placed = new Set(segments.flatMap((segment) => segment.callIds));
|
|
1103
|
+
for (const toolCall of toolCalls) if (!placed.has(toolCall.id)) current.callIds.push(toolCall.id);
|
|
1104
|
+
return segments.map((segment, index) => {
|
|
1105
|
+
const segmentCalls = toolCalls.filter((toolCall) => segment.callIds.includes(toolCall.id));
|
|
1106
|
+
return {
|
|
1107
|
+
role: "assistant",
|
|
1108
|
+
content: segment.text || null,
|
|
1109
|
+
...segmentCalls.length > 0 && { toolCalls: segmentCalls },
|
|
1110
|
+
id: id === void 0 ? void 0 : index === 0 ? id : `${id}-segment-${index}`,
|
|
1111
|
+
createdAt,
|
|
1112
|
+
...segment.thinking.length > 0 && { thinking: segment.thinking }
|
|
1113
|
+
};
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1016
1116
|
addAssistantToolCallMessage(toolCalls) {
|
|
1017
1117
|
this.finalizeCurrentThinkingStep();
|
|
1018
|
-
|
|
1118
|
+
const segments = this.buildOrderedAssistantSegments(toolCalls, this.currentMessageId ?? void 0, this.currentMessageCreatedAt ?? void 0);
|
|
1119
|
+
this.messages = [...this.messages, ...segments ?? [{
|
|
1019
1120
|
role: "assistant",
|
|
1020
1121
|
content: this.accumulatedContent || null,
|
|
1021
1122
|
toolCalls,
|
|
1022
1123
|
id: this.currentMessageId ?? void 0,
|
|
1023
1124
|
createdAt: this.currentMessageCreatedAt ?? void 0,
|
|
1024
1125
|
...this.accumulatedThinking.length > 0 && { thinking: this.accumulatedThinking }
|
|
1025
|
-
}];
|
|
1126
|
+
}]];
|
|
1026
1127
|
this.middlewareCtx.messages = this.messages;
|
|
1027
1128
|
}
|
|
1028
1129
|
addTerminalAssistantMessages() {
|
|
@@ -1063,13 +1164,17 @@ var TextEngine = class {
|
|
|
1063
1164
|
...thinking ? { thinking } : {}
|
|
1064
1165
|
});
|
|
1065
1166
|
} else {
|
|
1066
|
-
if (!currentTurnAlreadyRecorded && (this.accumulatedContent !== "" || thinking))
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1167
|
+
if (!currentTurnAlreadyRecorded && (this.accumulatedContent !== "" || thinking)) {
|
|
1168
|
+
const id = this.currentMessageId ?? this.createId("msg");
|
|
1169
|
+
const createdAt = this.currentMessageCreatedAt ?? /* @__PURE__ */ new Date();
|
|
1170
|
+
messages.push(...this.buildOrderedAssistantSegments(this.toolCallManager.getToolCalls(), id, createdAt) ?? [{
|
|
1171
|
+
role: "assistant",
|
|
1172
|
+
content: this.accumulatedContent || null,
|
|
1173
|
+
id,
|
|
1174
|
+
createdAt,
|
|
1175
|
+
...thinking ? { thinking } : {}
|
|
1176
|
+
}]);
|
|
1177
|
+
}
|
|
1073
1178
|
if (structuredOutput) messages.push({
|
|
1074
1179
|
role: "assistant",
|
|
1075
1180
|
content: raw || null,
|
|
@@ -1360,6 +1465,7 @@ var TextEngine = class {
|
|
|
1360
1465
|
const approvalRequests = [];
|
|
1361
1466
|
const clientRequests = [];
|
|
1362
1467
|
for (const toolCall of toolCalls) {
|
|
1468
|
+
if (isProviderExecutedToolCall(toolCall)) continue;
|
|
1363
1469
|
const tool = this.resolveExecutableTools([toolCall]).find((candidate) => candidate.name === toolCall.function.name);
|
|
1364
1470
|
if (!tool) continue;
|
|
1365
1471
|
let input = {};
|
|
@@ -1460,7 +1566,8 @@ var TextEngine = class {
|
|
|
1460
1566
|
const newToolMessage = {
|
|
1461
1567
|
role: "tool",
|
|
1462
1568
|
content,
|
|
1463
|
-
toolCallId: result.toolCallId
|
|
1569
|
+
toolCallId: result.toolCallId,
|
|
1570
|
+
...result.state === "output-error" && { error: toolResultErrorText(parseToolOutput(wireContent)) }
|
|
1464
1571
|
};
|
|
1465
1572
|
if (placeholderIdx >= 0) this.messages = [
|
|
1466
1573
|
...this.messages.slice(0, placeholderIdx),
|
|
@@ -1682,7 +1789,7 @@ var TextEngine = class {
|
|
|
1682
1789
|
tools: baseConfig.tools
|
|
1683
1790
|
}));
|
|
1684
1791
|
this.applyMiddlewareConfig(postOnConfig);
|
|
1685
|
-
|
|
1792
|
+
assertMessagesFileSourceSupport(this.adapter, this.messages);
|
|
1686
1793
|
const structuredCallOptions = {
|
|
1687
1794
|
chatOptions: {
|
|
1688
1795
|
model: this.params.model,
|
|
@@ -1832,7 +1939,12 @@ var TextEngine = class {
|
|
|
1832
1939
|
async *harvestCombinedStructuredOutput() {
|
|
1833
1940
|
if (!this.finalStructuredOutput) throw new Error("harvestCombinedStructuredOutput called without finalStructuredOutput config");
|
|
1834
1941
|
const yieldChunks = this.finalStructuredOutput.yieldChunks;
|
|
1835
|
-
|
|
1942
|
+
const source = this.finalStructuredOutput.source ?? "text";
|
|
1943
|
+
if (this.lastFinishReason === "length") this.finalizationError = {
|
|
1944
|
+
message: "The response was cut off because the maximum token limit was reached (finish_reason=length); raise the output token limit.",
|
|
1945
|
+
code: "max_tokens"
|
|
1946
|
+
};
|
|
1947
|
+
else if (source === "event") {
|
|
1836
1948
|
if (!this.structuredOutputResult) this.finalizationError = {
|
|
1837
1949
|
message: "missing structured result",
|
|
1838
1950
|
code: "structured-output-missing-result"
|