@tanstack/ai 0.44.0 → 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 +126 -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 +80 -34
- 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 +155 -49
- 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"}
|
|
@@ -142,6 +142,8 @@ var TextEngine = class {
|
|
|
142
142
|
streamStartTime = 0;
|
|
143
143
|
totalChunkCount = 0;
|
|
144
144
|
currentMessageId = null;
|
|
145
|
+
currentMessageCreatedAt = null;
|
|
146
|
+
streamIdentityCaptured = false;
|
|
145
147
|
accumulatedContent = "";
|
|
146
148
|
accumulatedThinking = [];
|
|
147
149
|
currentThinkingContent = "";
|
|
@@ -185,6 +187,7 @@ var TextEngine = class {
|
|
|
185
187
|
validatedStructuredOutput = void 0;
|
|
186
188
|
hasValidatedStructuredOutput = false;
|
|
187
189
|
finalizationError = null;
|
|
190
|
+
combinedCompleteEmitted = false;
|
|
188
191
|
finalStructuredOutput;
|
|
189
192
|
constructor(config, logger) {
|
|
190
193
|
this.logger = logger;
|
|
@@ -334,27 +337,31 @@ var TextEngine = class {
|
|
|
334
337
|
this.endCycle();
|
|
335
338
|
} while (await this.shouldContinue());
|
|
336
339
|
this.logger.agentLoop("run finished", { finishReason: this.lastFinishReason });
|
|
337
|
-
if (this.finalStructuredOutput && this.toolPhase !== "wait" && !this.isCancelled() && !this.finalizationError
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (this.finalizationError
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
+
}
|
|
358
365
|
}
|
|
359
366
|
} catch (error) {
|
|
360
367
|
if (error instanceof Error && error.name === "InterruptReplaySignal" && "continuationRunId" in error && typeof error.continuationRunId === "string") {
|
|
@@ -444,6 +451,8 @@ var TextEngine = class {
|
|
|
444
451
|
}
|
|
445
452
|
async beginIteration() {
|
|
446
453
|
this.currentMessageId = this.createId("msg");
|
|
454
|
+
this.currentMessageCreatedAt = /* @__PURE__ */ new Date();
|
|
455
|
+
this.streamIdentityCaptured = false;
|
|
447
456
|
this.accumulatedContent = "";
|
|
448
457
|
this.accumulatedThinking = [];
|
|
449
458
|
this.currentThinkingContent = "";
|
|
@@ -494,7 +503,32 @@ var TextEngine = class {
|
|
|
494
503
|
if (this.isCancelled()) break;
|
|
495
504
|
this.totalChunkCount++;
|
|
496
505
|
this.handleStreamChunk(chunk);
|
|
497
|
-
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) {
|
|
498
532
|
this.combinedStartEmitted = true;
|
|
499
533
|
const messageId = typeof chunk.messageId === "string" && chunk.messageId !== "" ? chunk.messageId : generateMessageId();
|
|
500
534
|
this.combinedStructuredMessageId = messageId;
|
|
@@ -513,7 +547,7 @@ var TextEngine = class {
|
|
|
513
547
|
this.middlewareCtx.chunkIndex++;
|
|
514
548
|
}
|
|
515
549
|
}
|
|
516
|
-
const outputChunks = await this.middlewareRunner.runOnChunk(this.middlewareCtx,
|
|
550
|
+
const outputChunks = await this.middlewareRunner.runOnChunk(this.middlewareCtx, outboundChunk);
|
|
517
551
|
const suppressAgentLifecycle = !!this.finalStructuredOutput && this.finalStructuredOutput.yieldChunks && this.finalStructuredOutput.nativeCombined !== true;
|
|
518
552
|
for (const outputChunk of outputChunks) {
|
|
519
553
|
if (suppressAgentLifecycle && (outputChunk.type === EventType.RUN_STARTED || outputChunk.type === EventType.RUN_FINISHED)) continue;
|
|
@@ -533,6 +567,9 @@ var TextEngine = class {
|
|
|
533
567
|
}
|
|
534
568
|
handleStreamChunk(chunk) {
|
|
535
569
|
switch (chunk.type) {
|
|
570
|
+
case "TEXT_MESSAGE_START":
|
|
571
|
+
if (typeof chunk.messageId === "string" && chunk.messageId !== "") this.captureStreamMessageIdentity(chunk.messageId);
|
|
572
|
+
break;
|
|
536
573
|
case "TEXT_MESSAGE_CONTENT":
|
|
537
574
|
this.handleTextMessageContentEvent(chunk);
|
|
538
575
|
break;
|
|
@@ -554,16 +591,7 @@ var TextEngine = class {
|
|
|
554
591
|
case "STEP_STARTED":
|
|
555
592
|
this.handleStepStartedEvent();
|
|
556
593
|
break;
|
|
557
|
-
case "STEP_FINISHED":
|
|
558
|
-
this.handleStepFinishedEvent(chunk);
|
|
559
|
-
break;
|
|
560
|
-
case "TOOL_CALL_RESULT": break;
|
|
561
|
-
case "REASONING_START":
|
|
562
|
-
case "REASONING_MESSAGE_START":
|
|
563
|
-
case "REASONING_MESSAGE_CONTENT":
|
|
564
|
-
case "REASONING_MESSAGE_END":
|
|
565
|
-
case "REASONING_END": break;
|
|
566
|
-
default: break;
|
|
594
|
+
case "STEP_FINISHED": this.handleStepFinishedEvent(chunk);
|
|
567
595
|
}
|
|
568
596
|
}
|
|
569
597
|
handleTextMessageContentEvent(chunk) {
|
|
@@ -571,7 +599,16 @@ var TextEngine = class {
|
|
|
571
599
|
else this.accumulatedContent += chunk.delta;
|
|
572
600
|
this.middlewareCtx.accumulatedContent = this.accumulatedContent;
|
|
573
601
|
}
|
|
602
|
+
captureStreamMessageIdentity(messageId) {
|
|
603
|
+
this.currentMessageId = messageId;
|
|
604
|
+
this.middlewareCtx.currentMessageId = messageId;
|
|
605
|
+
if (!this.streamIdentityCaptured) {
|
|
606
|
+
this.currentMessageCreatedAt = /* @__PURE__ */ new Date();
|
|
607
|
+
this.streamIdentityCaptured = true;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
574
610
|
handleToolCallStartEvent(chunk) {
|
|
611
|
+
if (typeof chunk.parentMessageId === "string" && chunk.parentMessageId !== "") this.captureStreamMessageIdentity(chunk.parentMessageId);
|
|
575
612
|
this.toolCallManager.addToolCallStartEvent(chunk);
|
|
576
613
|
}
|
|
577
614
|
handleToolCallArgsEvent(chunk) {
|
|
@@ -584,8 +621,15 @@ var TextEngine = class {
|
|
|
584
621
|
this.finishedEvent = chunk;
|
|
585
622
|
this.lastFinishReason = chunk.finishReason ?? null;
|
|
586
623
|
}
|
|
587
|
-
handleRunErrorEvent(
|
|
624
|
+
handleRunErrorEvent(chunk) {
|
|
588
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
|
+
}
|
|
589
633
|
}
|
|
590
634
|
finalizeCurrentThinkingStep() {
|
|
591
635
|
if (this.currentThinkingContent) {
|
|
@@ -808,6 +852,8 @@ var TextEngine = class {
|
|
|
808
852
|
role: "assistant",
|
|
809
853
|
content: this.accumulatedContent || null,
|
|
810
854
|
toolCalls,
|
|
855
|
+
id: this.currentMessageId ?? void 0,
|
|
856
|
+
createdAt: this.currentMessageCreatedAt ?? void 0,
|
|
811
857
|
...this.accumulatedThinking.length > 0 && { thinking: this.accumulatedThinking }
|
|
812
858
|
}];
|
|
813
859
|
this.middlewareCtx.messages = this.messages;
|
|
@@ -922,7 +968,7 @@ var TextEngine = class {
|
|
|
922
968
|
const messages = this.messages.map((message, index) => {
|
|
923
969
|
const content = typeof message.content === "string" ? message.content : message.content === null ? void 0 : JSON.stringify(message.content);
|
|
924
970
|
return {
|
|
925
|
-
id: `snapshot_${this.runIdOverride ?? this.requestId}_${index}`,
|
|
971
|
+
id: message.id || `snapshot_${this.runIdOverride ?? this.requestId}_${index}`,
|
|
926
972
|
role: message.role,
|
|
927
973
|
...content !== void 0 ? { content } : {},
|
|
928
974
|
..."toolCalls" in message && message.toolCalls ? { toolCalls: message.toolCalls } : {},
|
|
@@ -1354,7 +1400,7 @@ var TextEngine = class {
|
|
|
1354
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;
|
|
1355
1401
|
return null;
|
|
1356
1402
|
};
|
|
1357
|
-
const buildSynthesizedStart = () => {
|
|
1403
|
+
const buildSynthesizedStart = (timestamp = Date.now()) => {
|
|
1358
1404
|
const idForStart = structuredMessageId ?? generateMessageId();
|
|
1359
1405
|
structuredMessageId = idForStart;
|
|
1360
1406
|
return {
|
|
@@ -1362,7 +1408,7 @@ var TextEngine = class {
|
|
|
1362
1408
|
name: "structured-output.start",
|
|
1363
1409
|
value: { messageId: idForStart },
|
|
1364
1410
|
model: this.params.model,
|
|
1365
|
-
timestamp
|
|
1411
|
+
timestamp,
|
|
1366
1412
|
threadId: this.threadId,
|
|
1367
1413
|
...this.runIdOverride ? { runId: this.runIdOverride } : {}
|
|
1368
1414
|
};
|
|
@@ -1379,7 +1425,7 @@ var TextEngine = class {
|
|
|
1379
1425
|
if (this.finalStructuredOutput.yieldChunks) {
|
|
1380
1426
|
if (!startEmitted && (chunk.type === EventType.TEXT_MESSAGE_START || chunk.type === EventType.TEXT_MESSAGE_CONTENT || chunk.type === EventType.TEXT_MESSAGE_END)) {
|
|
1381
1427
|
startEmitted = true;
|
|
1382
|
-
const synthOutputs = await pipeThroughMiddleware(buildSynthesizedStart());
|
|
1428
|
+
const synthOutputs = await pipeThroughMiddleware(buildSynthesizedStart(chunk.timestamp));
|
|
1383
1429
|
for (const outputChunk of synthOutputs) {
|
|
1384
1430
|
yield outputChunk;
|
|
1385
1431
|
this.middlewareCtx.chunkIndex++;
|
|
@@ -1387,7 +1433,7 @@ var TextEngine = class {
|
|
|
1387
1433
|
}
|
|
1388
1434
|
if (!startEmitted && chunk.type === EventType.RUN_ERROR) {
|
|
1389
1435
|
startEmitted = true;
|
|
1390
|
-
const synthOutputs = await pipeThroughMiddleware(buildSynthesizedStart());
|
|
1436
|
+
const synthOutputs = await pipeThroughMiddleware(buildSynthesizedStart(chunk.timestamp));
|
|
1391
1437
|
for (const outputChunk of synthOutputs) {
|
|
1392
1438
|
yield outputChunk;
|
|
1393
1439
|
this.middlewareCtx.chunkIndex++;
|
|
@@ -1493,25 +1539,32 @@ var TextEngine = class {
|
|
|
1493
1539
|
async *harvestCombinedStructuredOutput() {
|
|
1494
1540
|
if (!this.finalStructuredOutput) throw new Error("harvestCombinedStructuredOutput called without finalStructuredOutput config");
|
|
1495
1541
|
const yieldChunks = this.finalStructuredOutput.yieldChunks;
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
};
|
|
1501
|
-
else try {
|
|
1502
|
-
const parsed = JSON.parse(rawText);
|
|
1503
|
-
const data = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed) : parsed;
|
|
1504
|
-
this.structuredOutputResult = {
|
|
1505
|
-
data,
|
|
1506
|
-
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"
|
|
1507
1546
|
};
|
|
1508
|
-
}
|
|
1509
|
-
const
|
|
1510
|
-
this.finalizationError = {
|
|
1511
|
-
message:
|
|
1512
|
-
code: "structured-output-
|
|
1513
|
-
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"
|
|
1514
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
|
+
}
|
|
1515
1568
|
}
|
|
1516
1569
|
if (this.structuredOutputResult && !this.finalizationError && this.finalStructuredOutput.validate) try {
|
|
1517
1570
|
const validated = this.finalStructuredOutput.validate(this.structuredOutputResult.data);
|
|
@@ -1545,7 +1598,7 @@ var TextEngine = class {
|
|
|
1545
1598
|
this.middlewareCtx.chunkIndex++;
|
|
1546
1599
|
}
|
|
1547
1600
|
}
|
|
1548
|
-
if (this.structuredOutputResult && !this.finalizationError) {
|
|
1601
|
+
if (this.structuredOutputResult && !this.finalizationError && !this.combinedCompleteEmitted) {
|
|
1549
1602
|
const completeChunk = {
|
|
1550
1603
|
type: EventType.CUSTOM,
|
|
1551
1604
|
name: "structured-output.complete",
|
|
@@ -1893,7 +1946,8 @@ async function* streamTextChunks(options, engineRef) {
|
|
|
1893
1946
|
* Runs the full agentic loop (if tools are provided) but returns collected text.
|
|
1894
1947
|
*/
|
|
1895
1948
|
function runNonStreamingText(options) {
|
|
1896
|
-
|
|
1949
|
+
const stream = runStreamingText(options);
|
|
1950
|
+
return streamToText(stream);
|
|
1897
1951
|
}
|
|
1898
1952
|
/**
|
|
1899
1953
|
* Run agentic structured output:
|
|
@@ -1911,6 +1965,7 @@ async function runAgenticStructuredOutput(options) {
|
|
|
1911
1965
|
const normalize = (data) => undoNullWidening(data, nullWideningMap);
|
|
1912
1966
|
const validate = isStandardSchema(outputSchema) ? (data) => parseWithStandardSchema(outputSchema, data) : void 0;
|
|
1913
1967
|
const nativeCombined = adapter.supportsCombinedToolsAndSchema?.(options.modelOptions) === true;
|
|
1968
|
+
const source = adapter.combinedStructuredOutputSource?.(options.modelOptions) ?? "text";
|
|
1914
1969
|
const mcpManager = MCPManager.from(mcp);
|
|
1915
1970
|
const mcpTools = await mcpManager.discover();
|
|
1916
1971
|
if (mcpTools.length > 0) textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
|
|
@@ -1928,7 +1983,8 @@ async function runAgenticStructuredOutput(options) {
|
|
|
1928
1983
|
yieldChunks: false,
|
|
1929
1984
|
normalize,
|
|
1930
1985
|
...validate ? { validate } : {},
|
|
1931
|
-
...nativeCombined ? { nativeCombined: true } : {}
|
|
1986
|
+
...nativeCombined ? { nativeCombined: true } : {},
|
|
1987
|
+
source
|
|
1932
1988
|
}
|
|
1933
1989
|
}, logger);
|
|
1934
1990
|
try {
|
|
@@ -1989,13 +2045,13 @@ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError)
|
|
|
1989
2045
|
const threadId = chatOptions.threadId ?? `fallback-${Date.now()}-${fallbackRand}`;
|
|
1990
2046
|
const messageId = `fallback-${Date.now()}-${fallbackRand}`;
|
|
1991
2047
|
const model = chatOptions.model;
|
|
1992
|
-
const
|
|
2048
|
+
const startedAt = Date.now();
|
|
1993
2049
|
yield {
|
|
1994
2050
|
type: EventType.RUN_STARTED,
|
|
1995
2051
|
runId,
|
|
1996
2052
|
threadId,
|
|
1997
2053
|
model,
|
|
1998
|
-
timestamp
|
|
2054
|
+
timestamp: startedAt
|
|
1999
2055
|
};
|
|
2000
2056
|
let result;
|
|
2001
2057
|
try {
|
|
@@ -2008,7 +2064,7 @@ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError)
|
|
|
2008
2064
|
runId,
|
|
2009
2065
|
threadId,
|
|
2010
2066
|
model,
|
|
2011
|
-
timestamp,
|
|
2067
|
+
timestamp: Date.now(),
|
|
2012
2068
|
message,
|
|
2013
2069
|
error: { message }
|
|
2014
2070
|
};
|
|
@@ -2019,20 +2075,20 @@ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError)
|
|
|
2019
2075
|
messageId,
|
|
2020
2076
|
role: "assistant",
|
|
2021
2077
|
model,
|
|
2022
|
-
timestamp
|
|
2078
|
+
timestamp: Date.now()
|
|
2023
2079
|
};
|
|
2024
2080
|
yield {
|
|
2025
2081
|
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
2026
2082
|
messageId,
|
|
2027
2083
|
delta: result.rawText,
|
|
2028
2084
|
model,
|
|
2029
|
-
timestamp
|
|
2085
|
+
timestamp: Date.now()
|
|
2030
2086
|
};
|
|
2031
2087
|
yield {
|
|
2032
2088
|
type: EventType.TEXT_MESSAGE_END,
|
|
2033
2089
|
messageId,
|
|
2034
2090
|
model,
|
|
2035
|
-
timestamp
|
|
2091
|
+
timestamp: Date.now()
|
|
2036
2092
|
};
|
|
2037
2093
|
yield {
|
|
2038
2094
|
type: EventType.CUSTOM,
|
|
@@ -2042,14 +2098,14 @@ async function* fallbackStructuredOutputStream(adapter, options, onAdapterError)
|
|
|
2042
2098
|
raw: result.rawText
|
|
2043
2099
|
},
|
|
2044
2100
|
model,
|
|
2045
|
-
timestamp
|
|
2101
|
+
timestamp: Date.now()
|
|
2046
2102
|
};
|
|
2047
2103
|
yield {
|
|
2048
2104
|
type: EventType.RUN_FINISHED,
|
|
2049
2105
|
runId,
|
|
2050
2106
|
threadId,
|
|
2051
2107
|
model,
|
|
2052
|
-
timestamp,
|
|
2108
|
+
timestamp: Date.now(),
|
|
2053
2109
|
finishReason: "stop",
|
|
2054
2110
|
...result.usage ? { usage: result.usage } : {}
|
|
2055
2111
|
};
|
|
@@ -2097,6 +2153,7 @@ async function* runStreamingStructuredOutputImpl(options, jsonSchema, normalize,
|
|
|
2097
2153
|
const model = adapter.model;
|
|
2098
2154
|
const logger = resolveDebugOption(debug);
|
|
2099
2155
|
const nativeCombined = adapter.supportsCombinedToolsAndSchema?.(options.modelOptions) === true;
|
|
2156
|
+
const source = adapter.combinedStructuredOutputSource?.(options.modelOptions) ?? "text";
|
|
2100
2157
|
const mcpManager = MCPManager.from(mcp);
|
|
2101
2158
|
const mcpTools = await mcpManager.discover();
|
|
2102
2159
|
if (mcpTools.length > 0) textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
|
|
@@ -2113,7 +2170,8 @@ async function* runStreamingStructuredOutputImpl(options, jsonSchema, normalize,
|
|
|
2113
2170
|
jsonSchema,
|
|
2114
2171
|
yieldChunks: true,
|
|
2115
2172
|
normalize,
|
|
2116
|
-
...nativeCombined ? { nativeCombined: true } : {}
|
|
2173
|
+
...nativeCombined ? { nativeCombined: true } : {},
|
|
2174
|
+
source
|
|
2117
2175
|
}
|
|
2118
2176
|
}, logger);
|
|
2119
2177
|
engineRef.current = engine;
|