@tanstack/ai 0.44.1 → 0.45.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 +13 -1
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.js +108 -68
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +24 -27
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.js +14 -13
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/approval-schema.js +11 -8
- package/dist/esm/activities/chat/tools/approval-schema.js.map +1 -1
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.js +40 -31
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.js +3 -2
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +2 -0
- package/dist/esm/adapter-internals.js +3 -1
- package/dist/esm/interrupt-resume.js +24 -20
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/interrupts.js +2 -1
- package/dist/esm/interrupts.js.map +1 -1
- package/dist/esm/logger/console-logger.js +1 -3
- package/dist/esm/logger/console-logger.js.map +1 -1
- package/dist/esm/logger/resolve.js +2 -1
- package/dist/esm/logger/resolve.js.map +1 -1
- package/dist/esm/stream-durability.js +1 -1
- package/dist/esm/stream-durability.js.map +1 -1
- package/dist/esm/types.d.ts +19 -16
- package/dist/esm/utilities/chat-params.js +1 -3
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/media-prompt.js +1 -3
- package/dist/esm/utilities/media-prompt.js.map +1 -1
- package/dist/esm/utilities/structured-output-events.d.ts +17 -0
- package/dist/esm/utilities/structured-output-events.js +32 -0
- package/dist/esm/utilities/structured-output-events.js.map +1 -0
- package/dist/esm/utilities/structured-output-text.d.ts +7 -0
- package/dist/esm/utilities/structured-output-text.js +50 -0
- package/dist/esm/utilities/structured-output-text.js.map +1 -0
- package/package.json +2 -2
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +6 -2
- package/skills/ai-core/media-generation/SKILL.md +33 -19
- package/skills/ai-core/structured-outputs/SKILL.md +73 -1
- package/skills/ai-core/tool-calling/SKILL.md +1 -1
- package/src/activities/chat/adapter.ts +16 -1
- package/src/activities/chat/index.ts +128 -47
- package/src/activities/chat/stream/processor.ts +6 -3
- package/src/activities/chat/tools/tool-calls.ts +12 -4
- package/src/adapter-internals.ts +8 -0
- package/src/types.ts +27 -19
- package/src/utilities/structured-output-events.ts +44 -0
- package/src/utilities/structured-output-text.ts +63 -0
|
@@ -103,7 +103,8 @@ export interface TextAdapter<TModel extends string, TProviderOptions extends Rec
|
|
|
103
103
|
* Implementations must emit standard AG-UI lifecycle events (RUN_STARTED,
|
|
104
104
|
* TEXT_MESSAGE_*, RUN_FINISHED) carrying raw JSON text deltas, plus a final
|
|
105
105
|
* `CUSTOM` event named `structured-output.complete` whose `value` is
|
|
106
|
-
* `{ object, raw, reasoning? }`.
|
|
106
|
+
* `{ object, raw, reasoning? }`. Events must be timestamped when emitted so
|
|
107
|
+
* their timestamps follow stream order.
|
|
107
108
|
*/
|
|
108
109
|
structuredOutputStream?: (options: StructuredOutputOptions<TProviderOptions>) => AsyncIterable<StreamChunk>;
|
|
109
110
|
/**
|
|
@@ -126,6 +127,17 @@ export interface TextAdapter<TModel extends string, TProviderOptions extends Rec
|
|
|
126
127
|
* answer per-request. Most adapters can return a constant.
|
|
127
128
|
*/
|
|
128
129
|
supportsCombinedToolsAndSchema?: (modelOptions?: TProviderOptions | undefined) => boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Where native-combined structured output is taken from.
|
|
132
|
+
*
|
|
133
|
+
* - `'text'` (default when omitted): the agent loop's accumulated
|
|
134
|
+
* assistant text is schema JSON. The engine parses it after the loop.
|
|
135
|
+
* HTTP adapters use this.
|
|
136
|
+
* - `'event'`: the adapter emits `structured-output.complete` during
|
|
137
|
+
* `chatStream`. The engine must not parse accumulated prose. Harness
|
|
138
|
+
* adapters use this.
|
|
139
|
+
*/
|
|
140
|
+
combinedStructuredOutputSource?: (modelOptions?: TProviderOptions | undefined) => 'text' | 'event';
|
|
129
141
|
}
|
|
130
142
|
/**
|
|
131
143
|
* A TextAdapter with any/unknown type parameters.
|
|
@@ -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 StreamChunk,\n TextOptions,\n TokenUsage,\n} from '../../types'\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<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? }
|
|
1
|
+
{"version":3,"file":"adapter.js","names":[],"sources":["../../../../src/activities/chat/adapter.ts"],"sourcesContent":["import type {\n DefaultMessageMetadataByModality,\n JSONSchema,\n Modality,\n StreamChunk,\n TextOptions,\n TokenUsage,\n} from '../../types'\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<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? }`. Events must be timestamped when emitted so\n * their timestamps follow stream order.\n */\n structuredOutputStream?: (\n options: StructuredOutputOptions<TProviderOptions>,\n ) => AsyncIterable<StreamChunk>\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<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"],"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"}
|
|
@@ -187,6 +187,7 @@ var TextEngine = class {
|
|
|
187
187
|
validatedStructuredOutput = void 0;
|
|
188
188
|
hasValidatedStructuredOutput = false;
|
|
189
189
|
finalizationError = null;
|
|
190
|
+
combinedCompleteEmitted = false;
|
|
190
191
|
finalStructuredOutput;
|
|
191
192
|
constructor(config, logger) {
|
|
192
193
|
this.logger = logger;
|
|
@@ -336,27 +337,31 @@ var TextEngine = class {
|
|
|
336
337
|
this.endCycle();
|
|
337
338
|
} while (await this.shouldContinue());
|
|
338
339
|
this.logger.agentLoop("run finished", { finishReason: this.lastFinishReason });
|
|
339
|
-
if (this.finalStructuredOutput && this.toolPhase !== "wait" && !this.isCancelled() && !this.finalizationError
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (this.finalizationError
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
340
|
+
if (this.finalStructuredOutput && this.toolPhase !== "wait" && !this.isCancelled() && !this.finalizationError && !this.earlyTermination) {
|
|
341
|
+
if (this.finalStructuredOutput.nativeCombined === true) yield* this.harvestCombinedStructuredOutput();
|
|
342
|
+
else yield* this.runStructuredFinalization();
|
|
343
|
+
}
|
|
344
|
+
if (!this.terminalHookCalled && this.toolPhase !== "wait" && !this.isCancelled()) {
|
|
345
|
+
if (this.finalizationError) {
|
|
346
|
+
this.terminalHookCalled = true;
|
|
347
|
+
const errForHook = new Error(this.finalizationError.message, this.finalizationError.cause !== void 0 ? { cause: this.finalizationError.cause } : void 0);
|
|
348
|
+
if (this.finalizationError.code !== void 0) Object.defineProperty(errForHook, "code", {
|
|
349
|
+
value: this.finalizationError.code,
|
|
350
|
+
enumerable: true
|
|
351
|
+
});
|
|
352
|
+
await this.middlewareRunner.runOnError(this.middlewareCtx, {
|
|
353
|
+
error: errForHook,
|
|
354
|
+
duration: Date.now() - this.streamStartTime
|
|
355
|
+
});
|
|
356
|
+
} else {
|
|
357
|
+
this.terminalHookCalled = true;
|
|
358
|
+
await this.middlewareRunner.runOnFinish(this.middlewareCtx, {
|
|
359
|
+
finishReason: this.lastFinishReason,
|
|
360
|
+
duration: Date.now() - this.streamStartTime,
|
|
361
|
+
content: this.accumulatedContent,
|
|
362
|
+
usage: this.finishedEvent?.usage
|
|
363
|
+
});
|
|
364
|
+
}
|
|
360
365
|
}
|
|
361
366
|
} catch (error) {
|
|
362
367
|
if (error instanceof Error && error.name === "InterruptReplaySignal" && "continuationRunId" in error && typeof error.continuationRunId === "string") {
|
|
@@ -498,7 +503,32 @@ var TextEngine = class {
|
|
|
498
503
|
if (this.isCancelled()) break;
|
|
499
504
|
this.totalChunkCount++;
|
|
500
505
|
this.handleStreamChunk(chunk);
|
|
501
|
-
if (
|
|
506
|
+
if (chunk.type === EventType.CUSTOM && chunk.name === "structured-output.start") {
|
|
507
|
+
this.combinedStartEmitted = true;
|
|
508
|
+
const startValue = chunk.value;
|
|
509
|
+
if (startValue && typeof startValue === "object" && "messageId" in startValue && typeof startValue.messageId === "string") this.combinedStructuredMessageId = startValue.messageId;
|
|
510
|
+
}
|
|
511
|
+
let outboundChunk = chunk;
|
|
512
|
+
if (this.finalStructuredOutput?.source === "event" && chunk.type === EventType.CUSTOM && chunk.name === "structured-output.complete") {
|
|
513
|
+
const parsed = readStructuredOutputCompleteValue(chunk.value);
|
|
514
|
+
if (parsed) {
|
|
515
|
+
const object = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed.object) : parsed.object;
|
|
516
|
+
this.structuredOutputResult = {
|
|
517
|
+
data: object,
|
|
518
|
+
rawText: parsed.raw
|
|
519
|
+
};
|
|
520
|
+
this.combinedCompleteEmitted = true;
|
|
521
|
+
const value = chunk.value;
|
|
522
|
+
if (object !== parsed.object && value && typeof value === "object") outboundChunk = {
|
|
523
|
+
...chunk,
|
|
524
|
+
value: {
|
|
525
|
+
...value,
|
|
526
|
+
object
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (this.finalStructuredOutput?.nativeCombined === true && this.finalStructuredOutput.yieldChunks && this.finalStructuredOutput.source !== "event" && !this.combinedStartEmitted && chunk.type === EventType.TEXT_MESSAGE_START) {
|
|
502
532
|
this.combinedStartEmitted = true;
|
|
503
533
|
const messageId = typeof chunk.messageId === "string" && chunk.messageId !== "" ? chunk.messageId : generateMessageId();
|
|
504
534
|
this.combinedStructuredMessageId = messageId;
|
|
@@ -517,7 +547,7 @@ var TextEngine = class {
|
|
|
517
547
|
this.middlewareCtx.chunkIndex++;
|
|
518
548
|
}
|
|
519
549
|
}
|
|
520
|
-
const outputChunks = await this.middlewareRunner.runOnChunk(this.middlewareCtx,
|
|
550
|
+
const outputChunks = await this.middlewareRunner.runOnChunk(this.middlewareCtx, outboundChunk);
|
|
521
551
|
const suppressAgentLifecycle = !!this.finalStructuredOutput && this.finalStructuredOutput.yieldChunks && this.finalStructuredOutput.nativeCombined !== true;
|
|
522
552
|
for (const outputChunk of outputChunks) {
|
|
523
553
|
if (suppressAgentLifecycle && (outputChunk.type === EventType.RUN_STARTED || outputChunk.type === EventType.RUN_FINISHED)) continue;
|
|
@@ -561,16 +591,7 @@ var TextEngine = class {
|
|
|
561
591
|
case "STEP_STARTED":
|
|
562
592
|
this.handleStepStartedEvent();
|
|
563
593
|
break;
|
|
564
|
-
case "STEP_FINISHED":
|
|
565
|
-
this.handleStepFinishedEvent(chunk);
|
|
566
|
-
break;
|
|
567
|
-
case "TOOL_CALL_RESULT": break;
|
|
568
|
-
case "REASONING_START":
|
|
569
|
-
case "REASONING_MESSAGE_START":
|
|
570
|
-
case "REASONING_MESSAGE_CONTENT":
|
|
571
|
-
case "REASONING_MESSAGE_END":
|
|
572
|
-
case "REASONING_END": break;
|
|
573
|
-
default: break;
|
|
594
|
+
case "STEP_FINISHED": this.handleStepFinishedEvent(chunk);
|
|
574
595
|
}
|
|
575
596
|
}
|
|
576
597
|
handleTextMessageContentEvent(chunk) {
|
|
@@ -600,8 +621,15 @@ var TextEngine = class {
|
|
|
600
621
|
this.finishedEvent = chunk;
|
|
601
622
|
this.lastFinishReason = chunk.finishReason ?? null;
|
|
602
623
|
}
|
|
603
|
-
handleRunErrorEvent(
|
|
624
|
+
handleRunErrorEvent(chunk) {
|
|
604
625
|
this.earlyTermination = true;
|
|
626
|
+
if (this.finalStructuredOutput && this.finalizationError === null) {
|
|
627
|
+
const message = chunk.message || chunk.error?.message || "Run failed before structured output completed";
|
|
628
|
+
this.finalizationError = {
|
|
629
|
+
message,
|
|
630
|
+
...chunk.code !== void 0 ? { code: chunk.code } : chunk.error?.code !== void 0 ? { code: chunk.error.code } : {}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
605
633
|
}
|
|
606
634
|
finalizeCurrentThinkingStep() {
|
|
607
635
|
if (this.currentThinkingContent) {
|
|
@@ -940,7 +968,7 @@ var TextEngine = class {
|
|
|
940
968
|
const messages = this.messages.map((message, index) => {
|
|
941
969
|
const content = typeof message.content === "string" ? message.content : message.content === null ? void 0 : JSON.stringify(message.content);
|
|
942
970
|
return {
|
|
943
|
-
id: `snapshot_${this.runIdOverride ?? this.requestId}_${index}`,
|
|
971
|
+
id: message.id || `snapshot_${this.runIdOverride ?? this.requestId}_${index}`,
|
|
944
972
|
role: message.role,
|
|
945
973
|
...content !== void 0 ? { content } : {},
|
|
946
974
|
..."toolCalls" in message && message.toolCalls ? { toolCalls: message.toolCalls } : {},
|
|
@@ -1372,7 +1400,7 @@ var TextEngine = class {
|
|
|
1372
1400
|
if (c.type === EventType.TEXT_MESSAGE_START || c.type === EventType.TEXT_MESSAGE_CONTENT || c.type === EventType.TEXT_MESSAGE_END) return typeof c.messageId === "string" && c.messageId !== "" ? c.messageId : null;
|
|
1373
1401
|
return null;
|
|
1374
1402
|
};
|
|
1375
|
-
const buildSynthesizedStart = () => {
|
|
1403
|
+
const buildSynthesizedStart = (timestamp = Date.now()) => {
|
|
1376
1404
|
const idForStart = structuredMessageId ?? generateMessageId();
|
|
1377
1405
|
structuredMessageId = idForStart;
|
|
1378
1406
|
return {
|
|
@@ -1380,7 +1408,7 @@ var TextEngine = class {
|
|
|
1380
1408
|
name: "structured-output.start",
|
|
1381
1409
|
value: { messageId: idForStart },
|
|
1382
1410
|
model: this.params.model,
|
|
1383
|
-
timestamp
|
|
1411
|
+
timestamp,
|
|
1384
1412
|
threadId: this.threadId,
|
|
1385
1413
|
...this.runIdOverride ? { runId: this.runIdOverride } : {}
|
|
1386
1414
|
};
|
|
@@ -1397,7 +1425,7 @@ var TextEngine = class {
|
|
|
1397
1425
|
if (this.finalStructuredOutput.yieldChunks) {
|
|
1398
1426
|
if (!startEmitted && (chunk.type === EventType.TEXT_MESSAGE_START || chunk.type === EventType.TEXT_MESSAGE_CONTENT || chunk.type === EventType.TEXT_MESSAGE_END)) {
|
|
1399
1427
|
startEmitted = true;
|
|
1400
|
-
const synthOutputs = await pipeThroughMiddleware(buildSynthesizedStart());
|
|
1428
|
+
const synthOutputs = await pipeThroughMiddleware(buildSynthesizedStart(chunk.timestamp));
|
|
1401
1429
|
for (const outputChunk of synthOutputs) {
|
|
1402
1430
|
yield outputChunk;
|
|
1403
1431
|
this.middlewareCtx.chunkIndex++;
|
|
@@ -1405,7 +1433,7 @@ var TextEngine = class {
|
|
|
1405
1433
|
}
|
|
1406
1434
|
if (!startEmitted && chunk.type === EventType.RUN_ERROR) {
|
|
1407
1435
|
startEmitted = true;
|
|
1408
|
-
const synthOutputs = await pipeThroughMiddleware(buildSynthesizedStart());
|
|
1436
|
+
const synthOutputs = await pipeThroughMiddleware(buildSynthesizedStart(chunk.timestamp));
|
|
1409
1437
|
for (const outputChunk of synthOutputs) {
|
|
1410
1438
|
yield outputChunk;
|
|
1411
1439
|
this.middlewareCtx.chunkIndex++;
|
|
@@ -1511,25 +1539,32 @@ var TextEngine = class {
|
|
|
1511
1539
|
async *harvestCombinedStructuredOutput() {
|
|
1512
1540
|
if (!this.finalStructuredOutput) throw new Error("harvestCombinedStructuredOutput called without finalStructuredOutput config");
|
|
1513
1541
|
const yieldChunks = this.finalStructuredOutput.yieldChunks;
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
};
|
|
1519
|
-
else try {
|
|
1520
|
-
const parsed = JSON.parse(rawText);
|
|
1521
|
-
const data = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed) : parsed;
|
|
1522
|
-
this.structuredOutputResult = {
|
|
1523
|
-
data,
|
|
1524
|
-
rawText
|
|
1542
|
+
if ((this.finalStructuredOutput.source ?? "text") === "event") {
|
|
1543
|
+
if (!this.structuredOutputResult) this.finalizationError = {
|
|
1544
|
+
message: "missing structured result",
|
|
1545
|
+
code: "structured-output-missing-result"
|
|
1525
1546
|
};
|
|
1526
|
-
}
|
|
1527
|
-
const
|
|
1528
|
-
this.finalizationError = {
|
|
1529
|
-
message:
|
|
1530
|
-
code: "structured-output-
|
|
1531
|
-
cause: err
|
|
1547
|
+
} else {
|
|
1548
|
+
const rawText = this.accumulatedContent;
|
|
1549
|
+
if (rawText.length === 0) this.finalizationError = {
|
|
1550
|
+
message: "missing structured result",
|
|
1551
|
+
code: "structured-output-missing-result"
|
|
1532
1552
|
};
|
|
1553
|
+
else try {
|
|
1554
|
+
const parsed = JSON.parse(rawText);
|
|
1555
|
+
const data = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed) : parsed;
|
|
1556
|
+
this.structuredOutputResult = {
|
|
1557
|
+
data,
|
|
1558
|
+
rawText
|
|
1559
|
+
};
|
|
1560
|
+
} catch (err) {
|
|
1561
|
+
const detail = rawText.slice(0, 200) + (rawText.length > 200 ? "..." : "");
|
|
1562
|
+
this.finalizationError = {
|
|
1563
|
+
message: `Failed to parse structured output as JSON. Content: ${detail}`,
|
|
1564
|
+
code: "structured-output-parse-failed",
|
|
1565
|
+
cause: err
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1533
1568
|
}
|
|
1534
1569
|
if (this.structuredOutputResult && !this.finalizationError && this.finalStructuredOutput.validate) try {
|
|
1535
1570
|
const validated = this.finalStructuredOutput.validate(this.structuredOutputResult.data);
|
|
@@ -1563,7 +1598,7 @@ var TextEngine = class {
|
|
|
1563
1598
|
this.middlewareCtx.chunkIndex++;
|
|
1564
1599
|
}
|
|
1565
1600
|
}
|
|
1566
|
-
if (this.structuredOutputResult && !this.finalizationError) {
|
|
1601
|
+
if (this.structuredOutputResult && !this.finalizationError && !this.combinedCompleteEmitted) {
|
|
1567
1602
|
const completeChunk = {
|
|
1568
1603
|
type: EventType.CUSTOM,
|
|
1569
1604
|
name: "structured-output.complete",
|
|
@@ -1911,7 +1946,8 @@ async function* streamTextChunks(options, engineRef) {
|
|
|
1911
1946
|
* Runs the full agentic loop (if tools are provided) but returns collected text.
|
|
1912
1947
|
*/
|
|
1913
1948
|
function runNonStreamingText(options) {
|
|
1914
|
-
|
|
1949
|
+
const stream = runStreamingText(options);
|
|
1950
|
+
return streamToText(stream);
|
|
1915
1951
|
}
|
|
1916
1952
|
/**
|
|
1917
1953
|
* Run agentic structured output:
|
|
@@ -1929,6 +1965,7 @@ async function runAgenticStructuredOutput(options) {
|
|
|
1929
1965
|
const normalize = (data) => undoNullWidening(data, nullWideningMap);
|
|
1930
1966
|
const validate = isStandardSchema(outputSchema) ? (data) => parseWithStandardSchema(outputSchema, data) : void 0;
|
|
1931
1967
|
const nativeCombined = adapter.supportsCombinedToolsAndSchema?.(options.modelOptions) === true;
|
|
1968
|
+
const source = adapter.combinedStructuredOutputSource?.(options.modelOptions) ?? "text";
|
|
1932
1969
|
const mcpManager = MCPManager.from(mcp);
|
|
1933
1970
|
const mcpTools = await mcpManager.discover();
|
|
1934
1971
|
if (mcpTools.length > 0) textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
|
|
@@ -1946,7 +1983,8 @@ async function runAgenticStructuredOutput(options) {
|
|
|
1946
1983
|
yieldChunks: false,
|
|
1947
1984
|
normalize,
|
|
1948
1985
|
...validate ? { validate } : {},
|
|
1949
|
-
...nativeCombined ? { nativeCombined: true } : {}
|
|
1986
|
+
...nativeCombined ? { nativeCombined: true } : {},
|
|
1987
|
+
source
|
|
1950
1988
|
}
|
|
1951
1989
|
}, logger);
|
|
1952
1990
|
try {
|
|
@@ -2007,13 +2045,13 @@ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError)
|
|
|
2007
2045
|
const threadId = chatOptions.threadId ?? `fallback-${Date.now()}-${fallbackRand}`;
|
|
2008
2046
|
const messageId = `fallback-${Date.now()}-${fallbackRand}`;
|
|
2009
2047
|
const model = chatOptions.model;
|
|
2010
|
-
const
|
|
2048
|
+
const startedAt = Date.now();
|
|
2011
2049
|
yield {
|
|
2012
2050
|
type: EventType.RUN_STARTED,
|
|
2013
2051
|
runId,
|
|
2014
2052
|
threadId,
|
|
2015
2053
|
model,
|
|
2016
|
-
timestamp
|
|
2054
|
+
timestamp: startedAt
|
|
2017
2055
|
};
|
|
2018
2056
|
let result;
|
|
2019
2057
|
try {
|
|
@@ -2026,7 +2064,7 @@ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError)
|
|
|
2026
2064
|
runId,
|
|
2027
2065
|
threadId,
|
|
2028
2066
|
model,
|
|
2029
|
-
timestamp,
|
|
2067
|
+
timestamp: Date.now(),
|
|
2030
2068
|
message,
|
|
2031
2069
|
error: { message }
|
|
2032
2070
|
};
|
|
@@ -2037,20 +2075,20 @@ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError)
|
|
|
2037
2075
|
messageId,
|
|
2038
2076
|
role: "assistant",
|
|
2039
2077
|
model,
|
|
2040
|
-
timestamp
|
|
2078
|
+
timestamp: Date.now()
|
|
2041
2079
|
};
|
|
2042
2080
|
yield {
|
|
2043
2081
|
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
2044
2082
|
messageId,
|
|
2045
2083
|
delta: result.rawText,
|
|
2046
2084
|
model,
|
|
2047
|
-
timestamp
|
|
2085
|
+
timestamp: Date.now()
|
|
2048
2086
|
};
|
|
2049
2087
|
yield {
|
|
2050
2088
|
type: EventType.TEXT_MESSAGE_END,
|
|
2051
2089
|
messageId,
|
|
2052
2090
|
model,
|
|
2053
|
-
timestamp
|
|
2091
|
+
timestamp: Date.now()
|
|
2054
2092
|
};
|
|
2055
2093
|
yield {
|
|
2056
2094
|
type: EventType.CUSTOM,
|
|
@@ -2060,14 +2098,14 @@ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError)
|
|
|
2060
2098
|
raw: result.rawText
|
|
2061
2099
|
},
|
|
2062
2100
|
model,
|
|
2063
|
-
timestamp
|
|
2101
|
+
timestamp: Date.now()
|
|
2064
2102
|
};
|
|
2065
2103
|
yield {
|
|
2066
2104
|
type: EventType.RUN_FINISHED,
|
|
2067
2105
|
runId,
|
|
2068
2106
|
threadId,
|
|
2069
2107
|
model,
|
|
2070
|
-
timestamp,
|
|
2108
|
+
timestamp: Date.now(),
|
|
2071
2109
|
finishReason: "stop",
|
|
2072
2110
|
...result.usage ? { usage: result.usage } : {}
|
|
2073
2111
|
};
|
|
@@ -2115,6 +2153,7 @@ async function* runStreamingStructuredOutputImpl(options, jsonSchema, normalize,
|
|
|
2115
2153
|
const model = adapter.model;
|
|
2116
2154
|
const logger = resolveDebugOption(debug);
|
|
2117
2155
|
const nativeCombined = adapter.supportsCombinedToolsAndSchema?.(options.modelOptions) === true;
|
|
2156
|
+
const source = adapter.combinedStructuredOutputSource?.(options.modelOptions) ?? "text";
|
|
2118
2157
|
const mcpManager = MCPManager.from(mcp);
|
|
2119
2158
|
const mcpTools = await mcpManager.discover();
|
|
2120
2159
|
if (mcpTools.length > 0) textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
|
|
@@ -2131,7 +2170,8 @@ async function* runStreamingStructuredOutputImpl(options, jsonSchema, normalize,
|
|
|
2131
2170
|
jsonSchema,
|
|
2132
2171
|
yieldChunks: true,
|
|
2133
2172
|
normalize,
|
|
2134
|
-
...nativeCombined ? { nativeCombined: true } : {}
|
|
2173
|
+
...nativeCombined ? { nativeCombined: true } : {},
|
|
2174
|
+
source
|
|
2135
2175
|
}
|
|
2136
2176
|
}, logger);
|
|
2137
2177
|
engineRef.current = engine;
|