@tanstack/openai-base 0.3.0 → 0.3.1

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.
@@ -46,7 +46,8 @@ class OpenAIBaseChatCompletionsTextAdapter extends BaseTextAdapter {
46
46
  runId: aguiState.runId,
47
47
  threadId: aguiState.threadId,
48
48
  model: options.model,
49
- timestamp: Date.now()
49
+ timestamp: Date.now(),
50
+ parentRunId: options.parentRunId
50
51
  };
51
52
  }
52
53
  yield {
@@ -236,7 +237,8 @@ class OpenAIBaseChatCompletionsTextAdapter extends BaseTextAdapter {
236
237
  runId: aguiState.runId,
237
238
  threadId: aguiState.threadId,
238
239
  model: chunk.model || chatOptions.model,
239
- timestamp
240
+ timestamp,
241
+ parentRunId: chatOptions.parentRunId
240
242
  };
241
243
  }
242
244
  const reasoning = this.extractReasoning(chunk);
@@ -378,7 +380,8 @@ class OpenAIBaseChatCompletionsTextAdapter extends BaseTextAdapter {
378
380
  runId: aguiState.runId,
379
381
  threadId: aguiState.threadId,
380
382
  model: chatOptions.model,
381
- timestamp
383
+ timestamp,
384
+ parentRunId: chatOptions.parentRunId
382
385
  };
383
386
  }
384
387
  const isAbort = this.isAbortError(error);
@@ -475,7 +478,8 @@ class OpenAIBaseChatCompletionsTextAdapter extends BaseTextAdapter {
475
478
  runId: aguiState.runId,
476
479
  threadId: aguiState.threadId,
477
480
  model: chunk.model || options.model,
478
- timestamp: Date.now()
481
+ timestamp: Date.now(),
482
+ parentRunId: options.parentRunId
479
483
  };
480
484
  }
481
485
  const reasoning = this.extractReasoning(chunk);
@@ -1 +1 @@
1
- {"version":3,"file":"chat-completions-text.js","sources":["../../../src/adapters/chat-completions-text.ts"],"sourcesContent":["import { EventType } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { generateId, transformNullsToUndefined } from '@tanstack/ai-utils'\nimport { extractRequestOptions } from '../utils/request-options'\nimport { makeStructuredOutputCompatible } from '../utils/schema-converter'\nimport { convertToolsToChatCompletionsFormat } from './chat-completions-tool-converter'\nimport type OpenAI from 'openai'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type {\n ChatCompletionChunk,\n ChatCompletionContentPart,\n ChatCompletionCreateParamsStreaming,\n ChatCompletionMessageParam,\n} from 'openai/resources/chat/completions/completions'\nimport type {\n ContentPart,\n DefaultMessageMetadataByModality,\n Modality,\n ModelMessage,\n RunFinishedEvent,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\n\n/**\n * Shared implementation of the OpenAI Chat Completions API. Holds the\n * stream-accumulator + AG-UI lifecycle logic and calls the OpenAI SDK\n * directly. Subclasses (ai-openai, ai-grok, ai-groq) construct an OpenAI\n * client with their provider-specific `baseURL` / headers and pass it in.\n */\nexport abstract class OpenAIBaseChatCompletionsTextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, unknown> = Record<string, unknown>,\n TInputModalities extends ReadonlyArray<Modality> = ReadonlyArray<Modality>,\n TMessageMetadata extends DefaultMessageMetadataByModality =\n DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadata,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name: string\n protected client: OpenAI\n\n constructor(model: TModel, name: string, client: OpenAI) {\n super({}, model)\n this.name = name\n this.client = client\n }\n\n async *chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n // AG-UI lifecycle tracking (mutable state object for ESLint compatibility)\n const aguiState = {\n runId: generateId(this.name),\n threadId: options.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n hasEmittedRunStarted: false,\n }\n\n try {\n // mapOptionsToRequest can throw (e.g. fail-loud guards in convertMessage\n // for empty content or unsupported parts). Keep it inside the try so\n // those failures surface as a single RUN_ERROR event, matching every\n // other failure mode here — callers iterating chatStream then only need\n // one error-handling path instead of both a try/catch around iteration\n // and a RUN_ERROR handler.\n const requestParams = this.mapOptionsToRequest(options)\n options.logger.request(\n `activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: this.name, model: this.model },\n )\n const stream = await this.client.chat.completions.create(\n {\n ...requestParams,\n stream: true,\n stream_options: { include_usage: true },\n },\n extractRequestOptions(options.request),\n )\n\n yield* this.processStreamChunks(stream, options, aguiState)\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.chatStream failed`,\n )\n\n // Emit RUN_STARTED if not yet emitted\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: options.model,\n timestamp: Date.now(),\n }\n }\n\n // Emit AG-UI RUN_ERROR\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n code: errorPayload.code,\n error: errorPayload,\n }\n\n options.logger.errors(`${this.name}.chatStream fatal`, {\n error: errorPayload,\n source: `${this.name}.chatStream`,\n })\n }\n }\n\n /**\n * Generate structured output using the provider's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * OpenAI-compatible APIs have strict requirements for structured output:\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply provider-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapOptionsToRequest(chatOptions)\n\n const jsonSchema = this.makeStructuredOutputCompatible(\n outputSchema,\n outputSchema.required,\n )\n\n try {\n // Strip stream_options which is only valid for streaming calls\n const {\n stream_options: _,\n stream: __,\n ...cleanParams\n } = requestParams as any\n chatOptions.logger.request(\n `activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const response = await this.client.chat.completions.create(\n {\n ...cleanParams,\n stream: false,\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n },\n extractRequestOptions(chatOptions.request),\n )\n\n // Extract text content from the response. Fail loud on empty content\n // rather than letting it cascade into a JSON-parse error on '' — the\n // root cause (the model returned no content for the structured request)\n // is then visible in logs.\n const rawText = response.choices[0]?.message.content\n if (typeof rawText !== 'string' || rawText.length === 0) {\n throw new Error(\n `${this.name}.structuredOutput: response contained no content`,\n )\n }\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n // Transform null values to undefined to match original Zod schema expectations\n // Provider returns null for optional fields we made nullable in the schema.\n // Subclasses can override `transformStructuredOutput` to skip this — e.g.\n // OpenRouter historically passed nulls through unchanged.\n const transformed = this.transformStructuredOutput(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {\n error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),\n source: `${this.name}.structuredOutput`,\n })\n throw error\n }\n }\n\n /**\n * Stream structured output. Single Chat Completions request with\n * `response_format: json_schema` + `stream: true`. Emits the standard\n * AG-UI lifecycle (`RUN_STARTED` → `REASONING_*?` → `TEXT_MESSAGE_*`\n * carrying raw JSON deltas → terminal `CUSTOM 'structured-output.complete'`\n * → `RUN_FINISHED`). Subclasses use the same SDK-call / reasoning /\n * structured-output-transform hooks as `chatStream` / `structuredOutput` —\n * no per-subclass override should be needed.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapOptionsToRequest(chatOptions)\n\n const jsonSchema = this.makeStructuredOutputCompatible(\n outputSchema,\n outputSchema.required,\n )\n\n const timestamp = Date.now()\n const aguiState = {\n runId: generateId(this.name),\n threadId: chatOptions.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n let accumulatedContent = ''\n let accumulatedReasoning = ''\n let hasEmittedTextMessageStart = false\n let reasoningMessageId: string | undefined\n let hasClosedReasoning = false\n let stepId: string | undefined\n let lastModel: string | undefined\n let lastUsage:\n | OpenAI.Chat.Completions.ChatCompletionChunk['usage']\n | undefined\n\n const closeReasoningLifecycle = function* (this: {\n name: string\n }): Generator<StreamChunk> {\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model: lastModel || chatOptions.model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model: lastModel || chatOptions.model,\n timestamp,\n }\n if (stepId) {\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model: lastModel || chatOptions.model,\n timestamp,\n content: accumulatedReasoning,\n }\n }\n }\n }.bind(this)\n\n try {\n // Strip stream_options + tools from the base request. Structured output\n // sends `response_format: json_schema` and doesn't carry tools — keeping\n // them in the request can confuse strict-mode validation upstream.\n const {\n stream_options: _so,\n stream: _s,\n tools: _t,\n ...cleanParams\n } = requestParams\n\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n\n const stream = await this.client.chat.completions.create(\n {\n ...cleanParams,\n stream: true,\n stream_options: { include_usage: true },\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n },\n extractRequestOptions(chatOptions.request),\n )\n\n for await (const chunk of stream) {\n const choiceForLog = chunk.choices[0]\n chatOptions.logger.provider(\n `provider=${this.name} finish_reason=${choiceForLog?.finish_reason ?? 'none'} hasContent=${!!choiceForLog?.delta.content} hasUsage=${!!chunk.usage}`,\n { provider: this.name, model: chunk.model },\n )\n\n if (chunk.model) lastModel = chunk.model\n\n // Usage may arrive on a chunk with empty `choices` (OpenAI's\n // include_usage terminal chunk) or piggybacked on a finish chunk\n // (`x_groq.usage` on Groq). Capture from either independent of\n // choices[0].\n const usage =\n chunk.usage ??\n (chunk as { x_groq?: { usage?: typeof chunk.usage } }).x_groq?.usage\n if (usage) lastUsage = usage\n\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || chatOptions.model,\n timestamp,\n }\n }\n\n // Reasoning (via the extractReasoning hook — same hook as chatStream).\n const reasoning = this.extractReasoning(chunk)\n if (reasoning && reasoning.text) {\n if (!reasoningMessageId) {\n reasoningMessageId = generateId(this.name)\n stepId = generateId(this.name)\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model: chunk.model || chatOptions.model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model: chunk.model || chatOptions.model,\n timestamp,\n }\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: chunk.model || chatOptions.model,\n timestamp,\n stepType: 'thinking',\n }\n }\n accumulatedReasoning += reasoning.text\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: reasoning.text,\n model: chunk.model || chatOptions.model,\n timestamp,\n }\n }\n\n const choice = chunk.choices[0]\n if (!choice) continue\n\n const deltaContent = choice.delta.content\n if (deltaContent) {\n yield* closeReasoningLifecycle()\n\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: chunk.model || chatOptions.model,\n timestamp,\n role: 'assistant',\n }\n }\n\n accumulatedContent += deltaContent\n\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model: chunk.model || chatOptions.model,\n timestamp,\n delta: deltaContent,\n content: accumulatedContent,\n }\n }\n }\n\n // Finalisation: close any open lifecycle, parse + validate, emit\n // terminal events. This block always runs unless the loop threw — abort\n // and SDK errors land in the catch block below.\n yield* closeReasoningLifecycle()\n\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: lastModel || chatOptions.model,\n timestamp,\n }\n }\n\n if (accumulatedContent.length === 0) {\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model: lastModel || chatOptions.model,\n timestamp,\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n error: {\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n },\n }\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(accumulatedContent)\n } catch {\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model: lastModel || chatOptions.model,\n timestamp,\n message: `Failed to parse structured output as JSON. Content: ${accumulatedContent.slice(0, 200)}${accumulatedContent.length > 200 ? '...' : ''}`,\n code: 'parse-error',\n error: {\n message: 'Failed to parse structured output as JSON',\n code: 'parse-error',\n },\n }\n return\n }\n\n const transformed = this.transformStructuredOutput(parsed)\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: {\n object: transformed,\n raw: accumulatedContent,\n ...(accumulatedReasoning ? { reasoning: accumulatedReasoning } : {}),\n },\n model: lastModel || chatOptions.model,\n timestamp,\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: lastModel || chatOptions.model,\n timestamp,\n finishReason: 'stop',\n ...(lastUsage && {\n usage: {\n promptTokens: lastUsage.prompt_tokens,\n completionTokens: lastUsage.completion_tokens,\n totalTokens: lastUsage.total_tokens,\n },\n }),\n }\n } catch (error: unknown) {\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chatOptions.model,\n timestamp,\n }\n }\n\n const isAbort = this.isAbortError(error)\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.structuredOutputStream failed`,\n )\n\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model: lastModel || chatOptions.model,\n timestamp,\n message: errorPayload.message,\n code: isAbort ? 'aborted' : errorPayload.code,\n error: { ...errorPayload, ...(isAbort && { code: 'aborted' }) },\n }\n\n chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {\n error: errorPayload,\n source: `${this.name}.structuredOutputStream`,\n })\n }\n }\n\n /**\n * Cross-SDK abort detection for `structuredOutputStream`. Default duck-types\n * on `name === 'APIUserAbortError'` (OpenAI SDK), `code === 'ERR_CANCELED'`,\n * and standard `AbortError`s. Subclasses with proprietary error types (e.g.\n * `@openrouter/sdk`'s `RequestAbortedError`) override to extend the check.\n */\n protected isAbortError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false\n const e = error as { name?: unknown; code?: unknown }\n return (\n e.name === 'APIUserAbortError' ||\n e.name === 'AbortError' ||\n e.code === 'ERR_CANCELED'\n )\n }\n\n /**\n * Applies provider-specific transformations for structured output compatibility.\n * Override this in subclasses to handle provider-specific quirks.\n */\n protected makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ): Record<string, any> {\n return makeStructuredOutputCompatible(schema, originalRequired)\n }\n\n /**\n * Extract reasoning content from a stream chunk. Default returns\n * `undefined` because the OpenAI Chat Completions chunk shape doesn't\n * carry reasoning. The chunk param is typed `unknown` so an override can\n * narrow to its own SDK chunk type without an `as` dance — the base only\n * passes through `processStreamChunks`'s structurally-iterated chunk.\n */\n protected extractReasoning(_chunk: unknown): { text: string } | undefined {\n return undefined\n }\n\n /**\n * Final shaping pass applied to parsed structured-output JSON before it is\n * returned to the caller. Default converts `null` values to `undefined` so\n * the result aligns with the original Zod schema's optional-field\n * semantics. Subclasses with different conventions (OpenRouter historically\n * preserves nulls) can override.\n */\n protected transformStructuredOutput(parsed: unknown): unknown {\n return transformNullsToUndefined(parsed)\n }\n\n /**\n * Processes streamed chunks from the Chat Completions API and yields AG-UI events.\n * Override this in subclasses to handle provider-specific stream behavior.\n */\n protected async *processStreamChunks(\n stream: AsyncIterable<ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n let hasEmittedTextMessageStart = false\n let lastModel: string | undefined\n // Track usage from any chunk that carries it. With\n // `stream_options: { include_usage: true }` OpenAI emits a terminal chunk\n // whose `choices` is `[]` and only the `usage` field is populated; the\n // earlier `finish_reason` chunk does NOT include token counts. We must\n // therefore defer RUN_FINISHED until the iterator is exhausted so we can\n // pick up usage from the trailing chunk regardless of arrival order.\n let lastUsage: ChatCompletionChunk['usage'] | undefined\n let pendingFinishReason:\n | ChatCompletionChunk['choices'][number]['finish_reason']\n | undefined\n\n // Track tool calls being streamed (arguments come in chunks)\n const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean // Track if TOOL_CALL_START has been emitted\n }\n >()\n\n // Reasoning lifecycle (driven by extractReasoning() hook — see method\n // docs). The base wire format (OpenAI Chat Completions) has no reasoning,\n // so these stay unused for openai/grok/groq. OpenRouter etc. opt in.\n let reasoningMessageId: string | undefined\n let hasClosedReasoning = false\n // Legacy STEP_STARTED/STEP_FINISHED pair emitted alongside REASONING_*\n // for back-compat with consumers (UI, devtools) that haven't migrated\n // to the spec REASONING_* events yet.\n let stepId: string | undefined\n let accumulatedReasoning = ''\n // Track whether ANY tool call lifecycle was actually completed across the\n // entire stream. Lets us downgrade a `tool_calls` finish_reason to `stop`\n // when the upstream signalled tool calls but never produced a complete\n // start/end pair — emitting RUN_FINISHED { finishReason: 'tool_calls' }\n // with no matching TOOL_CALL_END would leave consumers waiting for tool\n // results that never arrive.\n let emittedAnyToolCallEnd = false\n\n try {\n for await (const chunk of stream) {\n const choiceForLog = chunk.choices[0]\n options.logger.provider(\n `provider=${this.name} finish_reason=${choiceForLog?.finish_reason ?? 'none'} hasContent=${!!choiceForLog?.delta.content} hasToolCalls=${!!choiceForLog?.delta.tool_calls} hasUsage=${!!chunk.usage}`,\n { provider: this.name, model: chunk.model },\n )\n\n // Capture usage from any chunk (including the terminal usage-only\n // chunk emitted when `stream_options.include_usage` is on).\n if (chunk.usage) {\n lastUsage = chunk.usage\n }\n if (chunk.model) {\n lastModel = chunk.model\n }\n\n // Emit RUN_STARTED on the first chunk of any kind so callers see a\n // run lifecycle even on streams that arrive entirely as usage-only\n // (no choices). Without this, a usage-first stream would skip\n // RUN_STARTED via `if (!choice) continue` below and the post-loop\n // synthetic block would also skip RUN_FINISHED (it gates on\n // `hasEmittedRunStarted`).\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n }\n\n // Reasoning content (extractReasoning() hook). Run before reading\n // choice/delta so reasoning-only chunks (no `choices`) still drive\n // the REASONING_* lifecycle on providers that send reasoning out of\n // band. The base default returns undefined.\n const reasoning = this.extractReasoning(chunk)\n if (reasoning && reasoning.text) {\n if (!reasoningMessageId) {\n reasoningMessageId = generateId(this.name)\n stepId = generateId(this.name)\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n // Legacy STEP_STARTED (single emission, paired with the\n // STEP_FINISHED below when reasoning closes).\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n accumulatedReasoning += reasoning.text\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: reasoning.text,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n }\n\n const choice = chunk.choices[0]\n\n if (!choice) continue\n\n const delta = choice.delta\n const deltaContent = delta.content\n const deltaToolCalls = delta.tool_calls\n\n // Handle content delta\n if (deltaContent) {\n // Close reasoning before text starts so consumers see a clean\n // REASONING_END before any TEXT_MESSAGE_START.\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n if (stepId) {\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n content: accumulatedReasoning,\n }\n }\n }\n\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += deltaContent\n\n // Emit AG-UI TEXT_MESSAGE_CONTENT\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n delta: deltaContent,\n content: accumulatedContent,\n }\n }\n\n // Handle tool calls - they come in as deltas\n if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\n // Initialize or update the tool call in progress\n if (!toolCallsInProgress.has(index)) {\n toolCallsInProgress.set(index, {\n id: toolCallDelta.id || '',\n name: toolCallDelta.function?.name || '',\n arguments: '',\n started: false,\n })\n }\n\n const toolCall = toolCallsInProgress.get(index)!\n\n // Update with any new data from the delta\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments\n }\n\n // Emit TOOL_CALL_START when we have id and name\n if (toolCall.id && toolCall.name && !toolCall.started) {\n toolCall.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n index,\n }\n }\n\n // Emit TOOL_CALL_ARGS for argument deltas\n if (toolCallDelta.function?.arguments && toolCall.started) {\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: toolCall.id,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n delta: toolCallDelta.function.arguments,\n }\n }\n }\n }\n\n // Handle finish reason. We DO emit TOOL_CALL_END and TEXT_MESSAGE_END\n // here because the corresponding _START events have already fired,\n // and tool execution downstream wants to begin as soon as possible.\n // RUN_FINISHED is deferred until the iterator is fully exhausted so\n // we can capture the trailing usage chunk that arrives AFTER this\n // chunk when stream_options.include_usage is on.\n if (choice.finish_reason) {\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n // Skip tool calls that never emitted TOOL_CALL_START — emitting\n // a stray TOOL_CALL_END here would violate AG-UI lifecycle\n // (END without matching START) for partial deltas where the\n // upstream never sent both id and name.\n if (!toolCall.started) continue\n\n // Parse arguments for TOOL_CALL_END. Surface parse failures via\n // the logger so a model emitting malformed JSON for tool args\n // is debuggable instead of silently invoking the tool with {}.\n // Non-object JSON (e.g. a bare string or number) is also coerced\n // to {} so downstream tool execution doesn't receive a primitive\n // input, mirroring the Responses adapter's guard.\n let parsedInput: unknown = {}\n if (toolCall.arguments) {\n try {\n const parsed: unknown = JSON.parse(toolCall.arguments)\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${toolCall.name} (${toolCall.id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n rawArguments: toolCall.arguments,\n },\n )\n parsedInput = {}\n }\n }\n\n // Emit AG-UI TOOL_CALL_END\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n emittedAnyToolCallEnd = true\n }\n // Clear tool-call state after emission so a subsequent\n // `finish_reason: 'stop'` chunk (or the post-loop synthetic\n // block) doesn't see lingering entries and misreport the finish.\n toolCallsInProgress.clear()\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n hasEmittedTextMessageStart = false\n }\n\n // Remember the upstream finish_reason; RUN_FINISHED is emitted at\n // end-of-stream so we pick up the trailing usage-only chunk too.\n pendingFinishReason = choice.finish_reason\n }\n }\n\n // Emit a single terminal RUN_FINISHED after the iterator is exhausted.\n // This both delivers accurate token counts (the trailing usage chunk\n // may arrive AFTER the finish_reason chunk) and gives consumers a\n // guaranteed terminal event even when the upstream cuts off mid-stream\n // (no finish_reason chunk ever arrives).\n if (aguiState.hasEmittedRunStarted) {\n // Close any started tool calls that never got finish_reason. A\n // truncated stream that emitted TOOL_CALL_START but never reached\n // finish_reason would otherwise leave consumers with an unbalanced\n // start. Skip non-started entries (no matching START to close).\n let pendingToolCount = 0\n for (const [, toolCall] of toolCallsInProgress) {\n if (!toolCall.started) continue\n let parsedInput: unknown = {}\n if (toolCall.arguments) {\n try {\n const parsed: unknown = JSON.parse(toolCall.arguments)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n // Mirror the finish_reason path's logger call — a truncated\n // stream emitting malformed tool-call JSON would otherwise\n // silently invoke the tool with `{}`, the exact failure the\n // finish_reason logger was added to prevent.\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed (drain)`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${toolCall.name} (${toolCall.id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n rawArguments: toolCall.arguments,\n },\n )\n parsedInput = {}\n }\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: lastModel || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n pendingToolCount += 1\n emittedAnyToolCallEnd = true\n }\n toolCallsInProgress.clear()\n\n // Make sure the text message lifecycle is closed even on early\n // termination paths where finish_reason never arrives.\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n }\n }\n\n // Close any reasoning lifecycle that text never closed (no text\n // content arrived, or the stream cut off before text started).\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n }\n if (stepId) {\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n content: accumulatedReasoning,\n }\n }\n }\n\n // Map upstream finish_reason to AG-UI's narrower vocabulary.\n // Collapsing length / content_filter to 'stop' would hide why the\n // run terminated — surface it instead. Use `tool_calls` only when\n // a TOOL_CALL_END was actually emitted: an upstream that signalled\n // `tool_calls` but never produced a started/ended pair must NOT\n // surface `tool_calls` here, since downstream consumers wait for\n // tool results that would never arrive. OpenAI's legacy\n // `function_call` value (from the v1 function-calling API) is\n // normalized to `tool_calls` — semantically the same termination.\n const finishReason: NonNullable<RunFinishedEvent['finishReason']> =\n emittedAnyToolCallEnd\n ? 'tool_calls'\n : pendingFinishReason === 'tool_calls'\n ? 'stop'\n : pendingFinishReason === 'function_call'\n ? 'tool_calls'\n : (pendingFinishReason ?? 'stop')\n\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n usage: lastUsage\n ? {\n promptTokens: lastUsage.prompt_tokens || 0,\n completionTokens: lastUsage.completion_tokens || 0,\n totalTokens: lastUsage.total_tokens || 0,\n }\n : undefined,\n finishReason,\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.processStreamChunks failed`,\n )\n options.logger.errors(`${this.name}.processStreamChunks fatal`, {\n error: errorPayload,\n source: `${this.name}.processStreamChunks`,\n })\n\n // Emit AG-UI RUN_ERROR\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n code: errorPayload.code,\n error: errorPayload,\n }\n }\n }\n\n /**\n * Maps common TextOptions to Chat Completions API request format.\n * Override this in subclasses to add provider-specific options.\n */\n protected mapOptionsToRequest(\n options: TextOptions,\n ): ChatCompletionCreateParamsStreaming {\n const tools = options.tools\n ? convertToolsToChatCompletionsFormat(\n options.tools,\n this.makeStructuredOutputCompatible.bind(this),\n )\n : undefined\n\n // Build messages array with system prompts\n const messages: Array<ChatCompletionMessageParam> = []\n\n // Add system prompts first\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n // Convert messages\n for (const message of options.messages) {\n messages.push(this.convertMessage(message))\n }\n\n const modelOptions = options.modelOptions\n\n // Build the request so explicit top-level options win over modelOptions\n // when set, but `undefined` top-level options do NOT clobber values the\n // caller put in modelOptions. Keeping the merge nullish-aware fixes the\n // silent regression where a `modelOptions: { temperature: 0.7 }` setting\n // was overwritten with `temperature: undefined`.\n return {\n ...modelOptions,\n model: options.model,\n messages,\n ...(options.temperature !== undefined && {\n temperature: options.temperature,\n }),\n ...(options.maxTokens !== undefined && {\n max_tokens: options.maxTokens,\n }),\n ...(options.topP !== undefined && { top_p: options.topP }),\n // Conditional spread: `tools: undefined` would clobber any\n // modelOptions.tools the caller set above.\n ...(tools &&\n tools.length > 0 && {\n tools,\n }),\n stream: true,\n }\n }\n\n /**\n * Converts a single ModelMessage to the Chat Completions API message format.\n * Override this in subclasses to handle provider-specific message formats.\n */\n protected convertMessage(message: ModelMessage): ChatCompletionMessageParam {\n // Handle tool messages\n if (message.role === 'tool') {\n return {\n role: 'tool',\n tool_call_id: message.toolCallId || '',\n content:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n }\n }\n\n // Handle assistant messages\n if (message.role === 'assistant') {\n const toolCalls = message.toolCalls?.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === 'string'\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }))\n const hasToolCalls = !!toolCalls && toolCalls.length > 0\n const textContent = this.extractTextContent(message.content)\n\n // Per the OpenAI Chat Completions contract, an assistant message that\n // only carries tool_calls should have `content: null` (or omit content)\n // rather than `content: ''`. Empty-string content interacts oddly with\n // tokenization on some backends; null is the documented shape.\n return {\n role: 'assistant',\n content: hasToolCalls && !textContent ? null : textContent,\n ...(hasToolCalls ? { tool_calls: toolCalls } : {}),\n }\n }\n\n // Handle user messages - support multimodal content\n const contentParts = this.normalizeContent(message.content)\n\n // If only text, use simple string format\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n const text = contentParts[0].content\n if (text.length === 0) {\n // Single empty text part is the same fail-loud condition as below —\n // an empty paid request mask a real intent (caller passed `null`/'',\n // or an upstream step normalised everything to an empty string).\n throw new Error(\n `User message for ${this.name} has empty text content. ` +\n `Empty user messages would produce a paid request with no input; ` +\n `provide non-empty content or omit the message.`,\n )\n }\n return {\n role: 'user',\n content: text,\n }\n }\n\n // Otherwise, use array format for multimodal. Fail fast on unsupported\n // content parts rather than silently dropping them — a message of all\n // unsupported parts would otherwise turn into an empty user prompt and\n // mask a real capability mismatch.\n const parts: Array<ChatCompletionContentPart> = []\n for (const part of contentParts) {\n const converted = this.convertContentPart(part)\n if (!converted) {\n throw new Error(\n `Unsupported content part type for ${this.name}: ${part.type}. ` +\n `Override convertContentPart() in a subclass to handle this type, ` +\n `or remove it from the message.`,\n )\n }\n parts.push(converted)\n }\n\n if (parts.length === 0) {\n // The original message had no content parts at all (e.g. content was\n // explicitly null or []). Sending an empty user message to OpenAI\n // produces a paid request with no signal — fail loud instead.\n throw new Error(\n `User message for ${this.name} has no content parts. ` +\n `Empty user messages would produce a paid request with no input; ` +\n `provide at least one text/image/audio part or omit the message.`,\n )\n }\n\n return {\n role: 'user',\n content: parts,\n }\n }\n\n /**\n * Converts a single ContentPart to the Chat Completions API content part format.\n * Override this in subclasses to handle additional content types or provider-specific metadata.\n */\n protected convertContentPart(\n part: ContentPart,\n ): ChatCompletionContentPart | null {\n if (part.type === 'text') {\n return { type: 'text', text: part.content }\n }\n\n if (part.type === 'image') {\n const imageMetadata = part.metadata as\n | { detail?: 'auto' | 'low' | 'high' }\n | undefined\n\n // For base64 data, construct a data URI using the mimeType from source.\n // Default to a generic octet-stream MIME if the source didn't provide\n // one — interpolating `undefined` into the URI (\"data:undefined;base64,\n // ...\") would produce an invalid URI the API rejects.\n const imageValue = part.source.value\n const imageMime = part.source.mimeType || 'application/octet-stream'\n const imageUrl =\n part.source.type === 'data' && !imageValue.startsWith('data:')\n ? `data:${imageMime};base64,${imageValue}`\n : imageValue\n\n return {\n type: 'image_url',\n image_url: {\n url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n },\n }\n }\n\n // Unsupported content type — subclasses can override to handle more types\n return null\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n protected normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n protected extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // It's an array of ContentPart\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-completions-text.js","sources":["../../../src/adapters/chat-completions-text.ts"],"sourcesContent":["import { EventType } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { generateId, transformNullsToUndefined } from '@tanstack/ai-utils'\nimport { extractRequestOptions } from '../utils/request-options'\nimport { makeStructuredOutputCompatible } from '../utils/schema-converter'\nimport { convertToolsToChatCompletionsFormat } from './chat-completions-tool-converter'\nimport type OpenAI from 'openai'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type {\n ChatCompletionChunk,\n ChatCompletionContentPart,\n ChatCompletionCreateParamsStreaming,\n ChatCompletionMessageParam,\n} from 'openai/resources/chat/completions/completions'\nimport type {\n ContentPart,\n DefaultMessageMetadataByModality,\n Modality,\n ModelMessage,\n RunFinishedEvent,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\n\n/**\n * Shared implementation of the OpenAI Chat Completions API. Holds the\n * stream-accumulator + AG-UI lifecycle logic and calls the OpenAI SDK\n * directly. Subclasses (ai-openai, ai-grok, ai-groq) construct an OpenAI\n * client with their provider-specific `baseURL` / headers and pass it in.\n */\nexport abstract class OpenAIBaseChatCompletionsTextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, unknown> = Record<string, unknown>,\n TInputModalities extends ReadonlyArray<Modality> = ReadonlyArray<Modality>,\n TMessageMetadata extends DefaultMessageMetadataByModality =\n DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadata,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name: string\n protected client: OpenAI\n\n constructor(model: TModel, name: string, client: OpenAI) {\n super({}, model)\n this.name = name\n this.client = client\n }\n\n async *chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n // AG-UI lifecycle tracking (mutable state object for ESLint compatibility)\n const aguiState = {\n runId: generateId(this.name),\n threadId: options.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n hasEmittedRunStarted: false,\n }\n\n try {\n // mapOptionsToRequest can throw (e.g. fail-loud guards in convertMessage\n // for empty content or unsupported parts). Keep it inside the try so\n // those failures surface as a single RUN_ERROR event, matching every\n // other failure mode here — callers iterating chatStream then only need\n // one error-handling path instead of both a try/catch around iteration\n // and a RUN_ERROR handler.\n const requestParams = this.mapOptionsToRequest(options)\n options.logger.request(\n `activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: this.name, model: this.model },\n )\n const stream = await this.client.chat.completions.create(\n {\n ...requestParams,\n stream: true,\n stream_options: { include_usage: true },\n },\n extractRequestOptions(options.request),\n )\n\n yield* this.processStreamChunks(stream, options, aguiState)\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.chatStream failed`,\n )\n\n // Emit RUN_STARTED if not yet emitted\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: options.model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n // Emit AG-UI RUN_ERROR\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n code: errorPayload.code,\n error: errorPayload,\n }\n\n options.logger.errors(`${this.name}.chatStream fatal`, {\n error: errorPayload,\n source: `${this.name}.chatStream`,\n })\n }\n }\n\n /**\n * Generate structured output using the provider's JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * OpenAI-compatible APIs have strict requirements for structured output:\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply provider-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapOptionsToRequest(chatOptions)\n\n const jsonSchema = this.makeStructuredOutputCompatible(\n outputSchema,\n outputSchema.required,\n )\n\n try {\n // Strip stream_options which is only valid for streaming calls\n const {\n stream_options: _,\n stream: __,\n ...cleanParams\n } = requestParams as any\n chatOptions.logger.request(\n `activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const response = await this.client.chat.completions.create(\n {\n ...cleanParams,\n stream: false,\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n },\n extractRequestOptions(chatOptions.request),\n )\n\n // Extract text content from the response. Fail loud on empty content\n // rather than letting it cascade into a JSON-parse error on '' — the\n // root cause (the model returned no content for the structured request)\n // is then visible in logs.\n const rawText = response.choices[0]?.message.content\n if (typeof rawText !== 'string' || rawText.length === 0) {\n throw new Error(\n `${this.name}.structuredOutput: response contained no content`,\n )\n }\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n // Transform null values to undefined to match original Zod schema expectations\n // Provider returns null for optional fields we made nullable in the schema.\n // Subclasses can override `transformStructuredOutput` to skip this — e.g.\n // OpenRouter historically passed nulls through unchanged.\n const transformed = this.transformStructuredOutput(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {\n error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),\n source: `${this.name}.structuredOutput`,\n })\n throw error\n }\n }\n\n /**\n * Stream structured output. Single Chat Completions request with\n * `response_format: json_schema` + `stream: true`. Emits the standard\n * AG-UI lifecycle (`RUN_STARTED` → `REASONING_*?` → `TEXT_MESSAGE_*`\n * carrying raw JSON deltas → terminal `CUSTOM 'structured-output.complete'`\n * → `RUN_FINISHED`). Subclasses use the same SDK-call / reasoning /\n * structured-output-transform hooks as `chatStream` / `structuredOutput` —\n * no per-subclass override should be needed.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapOptionsToRequest(chatOptions)\n\n const jsonSchema = this.makeStructuredOutputCompatible(\n outputSchema,\n outputSchema.required,\n )\n\n const timestamp = Date.now()\n const aguiState = {\n runId: generateId(this.name),\n threadId: chatOptions.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n let accumulatedContent = ''\n let accumulatedReasoning = ''\n let hasEmittedTextMessageStart = false\n let reasoningMessageId: string | undefined\n let hasClosedReasoning = false\n let stepId: string | undefined\n let lastModel: string | undefined\n let lastUsage:\n | OpenAI.Chat.Completions.ChatCompletionChunk['usage']\n | undefined\n\n const closeReasoningLifecycle = function* (this: {\n name: string\n }): Generator<StreamChunk> {\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model: lastModel || chatOptions.model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model: lastModel || chatOptions.model,\n timestamp,\n }\n if (stepId) {\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model: lastModel || chatOptions.model,\n timestamp,\n content: accumulatedReasoning,\n }\n }\n }\n }.bind(this)\n\n try {\n // Strip stream_options + tools from the base request. Structured output\n // sends `response_format: json_schema` and doesn't carry tools — keeping\n // them in the request can confuse strict-mode validation upstream.\n const {\n stream_options: _so,\n stream: _s,\n tools: _t,\n ...cleanParams\n } = requestParams\n\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n\n const stream = await this.client.chat.completions.create(\n {\n ...cleanParams,\n stream: true,\n stream_options: { include_usage: true },\n response_format: {\n type: 'json_schema',\n json_schema: {\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n },\n extractRequestOptions(chatOptions.request),\n )\n\n for await (const chunk of stream) {\n const choiceForLog = chunk.choices[0]\n chatOptions.logger.provider(\n `provider=${this.name} finish_reason=${choiceForLog?.finish_reason ?? 'none'} hasContent=${!!choiceForLog?.delta.content} hasUsage=${!!chunk.usage}`,\n { provider: this.name, model: chunk.model },\n )\n\n if (chunk.model) lastModel = chunk.model\n\n // Usage may arrive on a chunk with empty `choices` (OpenAI's\n // include_usage terminal chunk) or piggybacked on a finish chunk\n // (`x_groq.usage` on Groq). Capture from either independent of\n // choices[0].\n const usage =\n chunk.usage ??\n (chunk as { x_groq?: { usage?: typeof chunk.usage } }).x_groq?.usage\n if (usage) lastUsage = usage\n\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n // Reasoning (via the extractReasoning hook — same hook as chatStream).\n const reasoning = this.extractReasoning(chunk)\n if (reasoning && reasoning.text) {\n if (!reasoningMessageId) {\n reasoningMessageId = generateId(this.name)\n stepId = generateId(this.name)\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model: chunk.model || chatOptions.model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model: chunk.model || chatOptions.model,\n timestamp,\n }\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: chunk.model || chatOptions.model,\n timestamp,\n stepType: 'thinking',\n }\n }\n accumulatedReasoning += reasoning.text\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: reasoning.text,\n model: chunk.model || chatOptions.model,\n timestamp,\n }\n }\n\n const choice = chunk.choices[0]\n if (!choice) continue\n\n const deltaContent = choice.delta.content\n if (deltaContent) {\n yield* closeReasoningLifecycle()\n\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: chunk.model || chatOptions.model,\n timestamp,\n role: 'assistant',\n }\n }\n\n accumulatedContent += deltaContent\n\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model: chunk.model || chatOptions.model,\n timestamp,\n delta: deltaContent,\n content: accumulatedContent,\n }\n }\n }\n\n // Finalisation: close any open lifecycle, parse + validate, emit\n // terminal events. This block always runs unless the loop threw — abort\n // and SDK errors land in the catch block below.\n yield* closeReasoningLifecycle()\n\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: lastModel || chatOptions.model,\n timestamp,\n }\n }\n\n if (accumulatedContent.length === 0) {\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model: lastModel || chatOptions.model,\n timestamp,\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n error: {\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n },\n }\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(accumulatedContent)\n } catch {\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model: lastModel || chatOptions.model,\n timestamp,\n message: `Failed to parse structured output as JSON. Content: ${accumulatedContent.slice(0, 200)}${accumulatedContent.length > 200 ? '...' : ''}`,\n code: 'parse-error',\n error: {\n message: 'Failed to parse structured output as JSON',\n code: 'parse-error',\n },\n }\n return\n }\n\n const transformed = this.transformStructuredOutput(parsed)\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: {\n object: transformed,\n raw: accumulatedContent,\n ...(accumulatedReasoning ? { reasoning: accumulatedReasoning } : {}),\n },\n model: lastModel || chatOptions.model,\n timestamp,\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: lastModel || chatOptions.model,\n timestamp,\n finishReason: 'stop',\n ...(lastUsage && {\n usage: {\n promptTokens: lastUsage.prompt_tokens,\n completionTokens: lastUsage.completion_tokens,\n totalTokens: lastUsage.total_tokens,\n },\n }),\n }\n } catch (error: unknown) {\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chatOptions.model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n const isAbort = this.isAbortError(error)\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.structuredOutputStream failed`,\n )\n\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model: lastModel || chatOptions.model,\n timestamp,\n message: errorPayload.message,\n code: isAbort ? 'aborted' : errorPayload.code,\n error: { ...errorPayload, ...(isAbort && { code: 'aborted' }) },\n }\n\n chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {\n error: errorPayload,\n source: `${this.name}.structuredOutputStream`,\n })\n }\n }\n\n /**\n * Cross-SDK abort detection for `structuredOutputStream`. Default duck-types\n * on `name === 'APIUserAbortError'` (OpenAI SDK), `code === 'ERR_CANCELED'`,\n * and standard `AbortError`s. Subclasses with proprietary error types (e.g.\n * `@openrouter/sdk`'s `RequestAbortedError`) override to extend the check.\n */\n protected isAbortError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false\n const e = error as { name?: unknown; code?: unknown }\n return (\n e.name === 'APIUserAbortError' ||\n e.name === 'AbortError' ||\n e.code === 'ERR_CANCELED'\n )\n }\n\n /**\n * Applies provider-specific transformations for structured output compatibility.\n * Override this in subclasses to handle provider-specific quirks.\n */\n protected makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ): Record<string, any> {\n return makeStructuredOutputCompatible(schema, originalRequired)\n }\n\n /**\n * Extract reasoning content from a stream chunk. Default returns\n * `undefined` because the OpenAI Chat Completions chunk shape doesn't\n * carry reasoning. The chunk param is typed `unknown` so an override can\n * narrow to its own SDK chunk type without an `as` dance — the base only\n * passes through `processStreamChunks`'s structurally-iterated chunk.\n */\n protected extractReasoning(_chunk: unknown): { text: string } | undefined {\n return undefined\n }\n\n /**\n * Final shaping pass applied to parsed structured-output JSON before it is\n * returned to the caller. Default converts `null` values to `undefined` so\n * the result aligns with the original Zod schema's optional-field\n * semantics. Subclasses with different conventions (OpenRouter historically\n * preserves nulls) can override.\n */\n protected transformStructuredOutput(parsed: unknown): unknown {\n return transformNullsToUndefined(parsed)\n }\n\n /**\n * Processes streamed chunks from the Chat Completions API and yields AG-UI events.\n * Override this in subclasses to handle provider-specific stream behavior.\n */\n protected async *processStreamChunks(\n stream: AsyncIterable<ChatCompletionChunk>,\n options: TextOptions,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n let hasEmittedTextMessageStart = false\n let lastModel: string | undefined\n // Track usage from any chunk that carries it. With\n // `stream_options: { include_usage: true }` OpenAI emits a terminal chunk\n // whose `choices` is `[]` and only the `usage` field is populated; the\n // earlier `finish_reason` chunk does NOT include token counts. We must\n // therefore defer RUN_FINISHED until the iterator is exhausted so we can\n // pick up usage from the trailing chunk regardless of arrival order.\n let lastUsage: ChatCompletionChunk['usage'] | undefined\n let pendingFinishReason:\n | ChatCompletionChunk['choices'][number]['finish_reason']\n | undefined\n\n // Track tool calls being streamed (arguments come in chunks)\n const toolCallsInProgress = new Map<\n number,\n {\n id: string\n name: string\n arguments: string\n started: boolean // Track if TOOL_CALL_START has been emitted\n }\n >()\n\n // Reasoning lifecycle (driven by extractReasoning() hook — see method\n // docs). The base wire format (OpenAI Chat Completions) has no reasoning,\n // so these stay unused for openai/grok/groq. OpenRouter etc. opt in.\n let reasoningMessageId: string | undefined\n let hasClosedReasoning = false\n // Legacy STEP_STARTED/STEP_FINISHED pair emitted alongside REASONING_*\n // for back-compat with consumers (UI, devtools) that haven't migrated\n // to the spec REASONING_* events yet.\n let stepId: string | undefined\n let accumulatedReasoning = ''\n // Track whether ANY tool call lifecycle was actually completed across the\n // entire stream. Lets us downgrade a `tool_calls` finish_reason to `stop`\n // when the upstream signalled tool calls but never produced a complete\n // start/end pair — emitting RUN_FINISHED { finishReason: 'tool_calls' }\n // with no matching TOOL_CALL_END would leave consumers waiting for tool\n // results that never arrive.\n let emittedAnyToolCallEnd = false\n\n try {\n for await (const chunk of stream) {\n const choiceForLog = chunk.choices[0]\n options.logger.provider(\n `provider=${this.name} finish_reason=${choiceForLog?.finish_reason ?? 'none'} hasContent=${!!choiceForLog?.delta.content} hasToolCalls=${!!choiceForLog?.delta.tool_calls} hasUsage=${!!chunk.usage}`,\n { provider: this.name, model: chunk.model },\n )\n\n // Capture usage from any chunk (including the terminal usage-only\n // chunk emitted when `stream_options.include_usage` is on).\n if (chunk.usage) {\n lastUsage = chunk.usage\n }\n if (chunk.model) {\n lastModel = chunk.model\n }\n\n // Emit RUN_STARTED on the first chunk of any kind so callers see a\n // run lifecycle even on streams that arrive entirely as usage-only\n // (no choices). Without this, a usage-first stream would skip\n // RUN_STARTED via `if (!choice) continue` below and the post-loop\n // synthetic block would also skip RUN_FINISHED (it gates on\n // `hasEmittedRunStarted`).\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n // Reasoning content (extractReasoning() hook). Run before reading\n // choice/delta so reasoning-only chunks (no `choices`) still drive\n // the REASONING_* lifecycle on providers that send reasoning out of\n // band. The base default returns undefined.\n const reasoning = this.extractReasoning(chunk)\n if (reasoning && reasoning.text) {\n if (!reasoningMessageId) {\n reasoningMessageId = generateId(this.name)\n stepId = generateId(this.name)\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n // Legacy STEP_STARTED (single emission, paired with the\n // STEP_FINISHED below when reasoning closes).\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n accumulatedReasoning += reasoning.text\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId: reasoningMessageId,\n delta: reasoning.text,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n }\n\n const choice = chunk.choices[0]\n\n if (!choice) continue\n\n const delta = choice.delta\n const deltaContent = delta.content\n const deltaToolCalls = delta.tool_calls\n\n // Handle content delta\n if (deltaContent) {\n // Close reasoning before text starts so consumers see a clean\n // REASONING_END before any TEXT_MESSAGE_START.\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n if (stepId) {\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n content: accumulatedReasoning,\n }\n }\n }\n\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += deltaContent\n\n // Emit AG-UI TEXT_MESSAGE_CONTENT\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n delta: deltaContent,\n content: accumulatedContent,\n }\n }\n\n // Handle tool calls - they come in as deltas\n if (deltaToolCalls) {\n for (const toolCallDelta of deltaToolCalls) {\n const index = toolCallDelta.index\n\n // Initialize or update the tool call in progress\n if (!toolCallsInProgress.has(index)) {\n toolCallsInProgress.set(index, {\n id: toolCallDelta.id || '',\n name: toolCallDelta.function?.name || '',\n arguments: '',\n started: false,\n })\n }\n\n const toolCall = toolCallsInProgress.get(index)!\n\n // Update with any new data from the delta\n if (toolCallDelta.id) {\n toolCall.id = toolCallDelta.id\n }\n if (toolCallDelta.function?.name) {\n toolCall.name = toolCallDelta.function.name\n }\n if (toolCallDelta.function?.arguments) {\n toolCall.arguments += toolCallDelta.function.arguments\n }\n\n // Emit TOOL_CALL_START when we have id and name\n if (toolCall.id && toolCall.name && !toolCall.started) {\n toolCall.started = true\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n index,\n }\n }\n\n // Emit TOOL_CALL_ARGS for argument deltas\n if (toolCallDelta.function?.arguments && toolCall.started) {\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: toolCall.id,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n delta: toolCallDelta.function.arguments,\n }\n }\n }\n }\n\n // Handle finish reason. We DO emit TOOL_CALL_END and TEXT_MESSAGE_END\n // here because the corresponding _START events have already fired,\n // and tool execution downstream wants to begin as soon as possible.\n // RUN_FINISHED is deferred until the iterator is fully exhausted so\n // we can capture the trailing usage chunk that arrives AFTER this\n // chunk when stream_options.include_usage is on.\n if (choice.finish_reason) {\n if (\n choice.finish_reason === 'tool_calls' ||\n toolCallsInProgress.size > 0\n ) {\n for (const [, toolCall] of toolCallsInProgress) {\n // Skip tool calls that never emitted TOOL_CALL_START — emitting\n // a stray TOOL_CALL_END here would violate AG-UI lifecycle\n // (END without matching START) for partial deltas where the\n // upstream never sent both id and name.\n if (!toolCall.started) continue\n\n // Parse arguments for TOOL_CALL_END. Surface parse failures via\n // the logger so a model emitting malformed JSON for tool args\n // is debuggable instead of silently invoking the tool with {}.\n // Non-object JSON (e.g. a bare string or number) is also coerced\n // to {} so downstream tool execution doesn't receive a primitive\n // input, mirroring the Responses adapter's guard.\n let parsedInput: unknown = {}\n if (toolCall.arguments) {\n try {\n const parsed: unknown = JSON.parse(toolCall.arguments)\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${toolCall.name} (${toolCall.id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n rawArguments: toolCall.arguments,\n },\n )\n parsedInput = {}\n }\n }\n\n // Emit AG-UI TOOL_CALL_END\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n emittedAnyToolCallEnd = true\n }\n // Clear tool-call state after emission so a subsequent\n // `finish_reason: 'stop'` chunk (or the post-loop synthetic\n // block) doesn't see lingering entries and misreport the finish.\n toolCallsInProgress.clear()\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: chunk.model || options.model,\n timestamp: Date.now(),\n }\n hasEmittedTextMessageStart = false\n }\n\n // Remember the upstream finish_reason; RUN_FINISHED is emitted at\n // end-of-stream so we pick up the trailing usage-only chunk too.\n pendingFinishReason = choice.finish_reason\n }\n }\n\n // Emit a single terminal RUN_FINISHED after the iterator is exhausted.\n // This both delivers accurate token counts (the trailing usage chunk\n // may arrive AFTER the finish_reason chunk) and gives consumers a\n // guaranteed terminal event even when the upstream cuts off mid-stream\n // (no finish_reason chunk ever arrives).\n if (aguiState.hasEmittedRunStarted) {\n // Close any started tool calls that never got finish_reason. A\n // truncated stream that emitted TOOL_CALL_START but never reached\n // finish_reason would otherwise leave consumers with an unbalanced\n // start. Skip non-started entries (no matching START to close).\n let pendingToolCount = 0\n for (const [, toolCall] of toolCallsInProgress) {\n if (!toolCall.started) continue\n let parsedInput: unknown = {}\n if (toolCall.arguments) {\n try {\n const parsed: unknown = JSON.parse(toolCall.arguments)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n // Mirror the finish_reason path's logger call — a truncated\n // stream emitting malformed tool-call JSON would otherwise\n // silently invoke the tool with `{}`, the exact failure the\n // finish_reason logger was added to prevent.\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed (drain)`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${toolCall.name} (${toolCall.id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: toolCall.id,\n toolName: toolCall.name,\n rawArguments: toolCall.arguments,\n },\n )\n parsedInput = {}\n }\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: toolCall.id,\n toolCallName: toolCall.name,\n toolName: toolCall.name,\n model: lastModel || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n pendingToolCount += 1\n emittedAnyToolCallEnd = true\n }\n toolCallsInProgress.clear()\n\n // Make sure the text message lifecycle is closed even on early\n // termination paths where finish_reason never arrives.\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n }\n }\n\n // Close any reasoning lifecycle that text never closed (no text\n // content arrived, or the stream cut off before text started).\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n }\n if (stepId) {\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n content: accumulatedReasoning,\n }\n }\n }\n\n // Map upstream finish_reason to AG-UI's narrower vocabulary.\n // Collapsing length / content_filter to 'stop' would hide why the\n // run terminated — surface it instead. Use `tool_calls` only when\n // a TOOL_CALL_END was actually emitted: an upstream that signalled\n // `tool_calls` but never produced a started/ended pair must NOT\n // surface `tool_calls` here, since downstream consumers wait for\n // tool results that would never arrive. OpenAI's legacy\n // `function_call` value (from the v1 function-calling API) is\n // normalized to `tool_calls` — semantically the same termination.\n const finishReason: NonNullable<RunFinishedEvent['finishReason']> =\n emittedAnyToolCallEnd\n ? 'tool_calls'\n : pendingFinishReason === 'tool_calls'\n ? 'stop'\n : pendingFinishReason === 'function_call'\n ? 'tool_calls'\n : (pendingFinishReason ?? 'stop')\n\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: lastModel || options.model,\n timestamp: Date.now(),\n usage: lastUsage\n ? {\n promptTokens: lastUsage.prompt_tokens || 0,\n completionTokens: lastUsage.completion_tokens || 0,\n totalTokens: lastUsage.total_tokens || 0,\n }\n : undefined,\n finishReason,\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.processStreamChunks failed`,\n )\n options.logger.errors(`${this.name}.processStreamChunks fatal`, {\n error: errorPayload,\n source: `${this.name}.processStreamChunks`,\n })\n\n // Emit AG-UI RUN_ERROR\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n code: errorPayload.code,\n error: errorPayload,\n }\n }\n }\n\n /**\n * Maps common TextOptions to Chat Completions API request format.\n * Override this in subclasses to add provider-specific options.\n */\n protected mapOptionsToRequest(\n options: TextOptions,\n ): ChatCompletionCreateParamsStreaming {\n const tools = options.tools\n ? convertToolsToChatCompletionsFormat(\n options.tools,\n this.makeStructuredOutputCompatible.bind(this),\n )\n : undefined\n\n // Build messages array with system prompts\n const messages: Array<ChatCompletionMessageParam> = []\n\n // Add system prompts first\n if (options.systemPrompts && options.systemPrompts.length > 0) {\n messages.push({\n role: 'system',\n content: options.systemPrompts.join('\\n'),\n })\n }\n\n // Convert messages\n for (const message of options.messages) {\n messages.push(this.convertMessage(message))\n }\n\n const modelOptions = options.modelOptions\n\n // Build the request so explicit top-level options win over modelOptions\n // when set, but `undefined` top-level options do NOT clobber values the\n // caller put in modelOptions. Keeping the merge nullish-aware fixes the\n // silent regression where a `modelOptions: { temperature: 0.7 }` setting\n // was overwritten with `temperature: undefined`.\n return {\n ...modelOptions,\n model: options.model,\n messages,\n ...(options.temperature !== undefined && {\n temperature: options.temperature,\n }),\n ...(options.maxTokens !== undefined && {\n max_tokens: options.maxTokens,\n }),\n ...(options.topP !== undefined && { top_p: options.topP }),\n // Conditional spread: `tools: undefined` would clobber any\n // modelOptions.tools the caller set above.\n ...(tools &&\n tools.length > 0 && {\n tools,\n }),\n stream: true,\n }\n }\n\n /**\n * Converts a single ModelMessage to the Chat Completions API message format.\n * Override this in subclasses to handle provider-specific message formats.\n */\n protected convertMessage(message: ModelMessage): ChatCompletionMessageParam {\n // Handle tool messages\n if (message.role === 'tool') {\n return {\n role: 'tool',\n tool_call_id: message.toolCallId || '',\n content:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n }\n }\n\n // Handle assistant messages\n if (message.role === 'assistant') {\n const toolCalls = message.toolCalls?.map((tc) => ({\n id: tc.id,\n type: 'function' as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === 'string'\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }))\n const hasToolCalls = !!toolCalls && toolCalls.length > 0\n const textContent = this.extractTextContent(message.content)\n\n // Per the OpenAI Chat Completions contract, an assistant message that\n // only carries tool_calls should have `content: null` (or omit content)\n // rather than `content: ''`. Empty-string content interacts oddly with\n // tokenization on some backends; null is the documented shape.\n return {\n role: 'assistant',\n content: hasToolCalls && !textContent ? null : textContent,\n ...(hasToolCalls ? { tool_calls: toolCalls } : {}),\n }\n }\n\n // Handle user messages - support multimodal content\n const contentParts = this.normalizeContent(message.content)\n\n // If only text, use simple string format\n if (contentParts.length === 1 && contentParts[0]?.type === 'text') {\n const text = contentParts[0].content\n if (text.length === 0) {\n // Single empty text part is the same fail-loud condition as below —\n // an empty paid request mask a real intent (caller passed `null`/'',\n // or an upstream step normalised everything to an empty string).\n throw new Error(\n `User message for ${this.name} has empty text content. ` +\n `Empty user messages would produce a paid request with no input; ` +\n `provide non-empty content or omit the message.`,\n )\n }\n return {\n role: 'user',\n content: text,\n }\n }\n\n // Otherwise, use array format for multimodal. Fail fast on unsupported\n // content parts rather than silently dropping them — a message of all\n // unsupported parts would otherwise turn into an empty user prompt and\n // mask a real capability mismatch.\n const parts: Array<ChatCompletionContentPart> = []\n for (const part of contentParts) {\n const converted = this.convertContentPart(part)\n if (!converted) {\n throw new Error(\n `Unsupported content part type for ${this.name}: ${part.type}. ` +\n `Override convertContentPart() in a subclass to handle this type, ` +\n `or remove it from the message.`,\n )\n }\n parts.push(converted)\n }\n\n if (parts.length === 0) {\n // The original message had no content parts at all (e.g. content was\n // explicitly null or []). Sending an empty user message to OpenAI\n // produces a paid request with no signal — fail loud instead.\n throw new Error(\n `User message for ${this.name} has no content parts. ` +\n `Empty user messages would produce a paid request with no input; ` +\n `provide at least one text/image/audio part or omit the message.`,\n )\n }\n\n return {\n role: 'user',\n content: parts,\n }\n }\n\n /**\n * Converts a single ContentPart to the Chat Completions API content part format.\n * Override this in subclasses to handle additional content types or provider-specific metadata.\n */\n protected convertContentPart(\n part: ContentPart,\n ): ChatCompletionContentPart | null {\n if (part.type === 'text') {\n return { type: 'text', text: part.content }\n }\n\n if (part.type === 'image') {\n const imageMetadata = part.metadata as\n | { detail?: 'auto' | 'low' | 'high' }\n | undefined\n\n // For base64 data, construct a data URI using the mimeType from source.\n // Default to a generic octet-stream MIME if the source didn't provide\n // one — interpolating `undefined` into the URI (\"data:undefined;base64,\n // ...\") would produce an invalid URI the API rejects.\n const imageValue = part.source.value\n const imageMime = part.source.mimeType || 'application/octet-stream'\n const imageUrl =\n part.source.type === 'data' && !imageValue.startsWith('data:')\n ? `data:${imageMime};base64,${imageValue}`\n : imageValue\n\n return {\n type: 'image_url',\n image_url: {\n url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n },\n }\n }\n\n // Unsupported content type — subclasses can override to handle more types\n return null\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n protected normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n protected extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // It's an array of ContentPart\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n"],"names":[],"mappings":";;;;;;;AAkCO,MAAe,6CAOZ,gBAMR;AAAA,EAKA,YAAY,OAAe,MAAc,QAAgB;AACvD,UAAM,CAAA,GAAI,KAAK;AALjB,SAAS,OAAO;AAMd,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,OAAO,WACL,SAC4B;AAE5B,UAAM,YAAY;AAAA,MAChB,OAAO,WAAW,KAAK,IAAI;AAAA,MAC3B,UAAU,QAAQ,YAAY,WAAW,KAAK,IAAI;AAAA,MAClD,WAAW,WAAW,KAAK,IAAI;AAAA,MAC/B,sBAAsB;AAAA,IAAA;AAGxB,QAAI;AAOF,YAAM,gBAAgB,KAAK,oBAAoB,OAAO;AACtD,cAAQ,OAAO;AAAA,QACb,0BAA0B,KAAK,IAAI,UAAU,KAAK,KAAK,aAAa,QAAQ,SAAS,MAAM,UAAU,QAAQ,OAAO,UAAU,CAAC;AAAA,QAC/H,EAAE,UAAU,KAAK,MAAM,OAAO,KAAK,MAAA;AAAA,MAAM;AAE3C,YAAM,SAAS,MAAM,KAAK,OAAO,KAAK,YAAY;AAAA,QAChD;AAAA,UACE,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,gBAAgB,EAAE,eAAe,KAAA;AAAA,QAAK;AAAA,QAExC,sBAAsB,QAAQ,OAAO;AAAA,MAAA;AAGvC,aAAO,KAAK,oBAAoB,QAAQ,SAAS,SAAS;AAAA,IAC5D,SAAS,OAAgB;AAGvB,YAAM,eAAe;AAAA,QACnB;AAAA,QACA,GAAG,KAAK,IAAI;AAAA,MAAA;AAId,UAAI,CAAC,UAAU,sBAAsB;AACnC,kBAAU,uBAAuB;AACjC,cAAM;AAAA,UACJ,MAAM,UAAU;AAAA,UAChB,OAAO,UAAU;AAAA,UACjB,UAAU,UAAU;AAAA,UACpB,OAAO,QAAQ;AAAA,UACf,WAAW,KAAK,IAAA;AAAA,UAChB,aAAa,QAAQ;AAAA,QAAA;AAAA,MAEzB;AAGA,YAAM;AAAA,QACJ,MAAM,UAAU;AAAA,QAChB,OAAO,QAAQ;AAAA,QACf,WAAW,KAAK,IAAA;AAAA,QAChB,SAAS,aAAa;AAAA,QACtB,MAAM,aAAa;AAAA,QACnB,OAAO;AAAA,MAAA;AAGT,cAAQ,OAAO,OAAO,GAAG,KAAK,IAAI,qBAAqB;AAAA,QACrD,OAAO;AAAA,QACP,QAAQ,GAAG,KAAK,IAAI;AAAA,MAAA,CACrB;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,iBACJ,SAC0C;AAC1C,UAAM,EAAE,aAAa,aAAA,IAAiB;AACtC,UAAM,gBAAgB,KAAK,oBAAoB,WAAW;AAE1D,UAAM,aAAa,KAAK;AAAA,MACtB;AAAA,MACA,aAAa;AAAA,IAAA;AAGf,QAAI;AAEF,YAAM;AAAA,QACJ,gBAAgB;AAAA,QAChB,QAAQ;AAAA,QACR,GAAG;AAAA,MAAA,IACD;AACJ,kBAAY,OAAO;AAAA,QACjB,sCAAsC,KAAK,IAAI,UAAU,KAAK,KAAK,aAAa,YAAY,SAAS,MAAM;AAAA,QAC3G,EAAE,UAAU,KAAK,MAAM,OAAO,KAAK,MAAA;AAAA,MAAM;AAE3C,YAAM,WAAW,MAAM,KAAK,OAAO,KAAK,YAAY;AAAA,QAClD;AAAA,UACE,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,iBAAiB;AAAA,YACf,MAAM;AAAA,YACN,aAAa;AAAA,cACX,MAAM;AAAA,cACN,QAAQ;AAAA,cACR,QAAQ;AAAA,YAAA;AAAA,UACV;AAAA,QACF;AAAA,QAEF,sBAAsB,YAAY,OAAO;AAAA,MAAA;AAO3C,YAAM,UAAU,SAAS,QAAQ,CAAC,GAAG,QAAQ;AAC7C,UAAI,OAAO,YAAY,YAAY,QAAQ,WAAW,GAAG;AACvD,cAAM,IAAI;AAAA,UACR,GAAG,KAAK,IAAI;AAAA,QAAA;AAAA,MAEhB;AAGA,UAAI;AACJ,UAAI;AACF,iBAAS,KAAK,MAAM,OAAO;AAAA,MAC7B,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,uDAAuD,QAAQ,MAAM,GAAG,GAAG,CAAC,GAAG,QAAQ,SAAS,MAAM,QAAQ,EAAE;AAAA,QAAA;AAAA,MAEpH;AAMA,YAAM,cAAc,KAAK,0BAA0B,MAAM;AAEzD,aAAO;AAAA,QACL,MAAM;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ,SAAS,OAAgB;AAGvB,kBAAY,OAAO,OAAO,GAAG,KAAK,IAAI,2BAA2B;AAAA,QAC/D,OAAO,kBAAkB,OAAO,GAAG,KAAK,IAAI,0BAA0B;AAAA,QACtE,QAAQ,GAAG,KAAK,IAAI;AAAA,MAAA,CACrB;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAO,uBACL,SAC4B;AAC5B,UAAM,EAAE,aAAa,aAAA,IAAiB;AACtC,UAAM,gBAAgB,KAAK,oBAAoB,WAAW;AAE1D,UAAM,aAAa,KAAK;AAAA,MACtB;AAAA,MACA,aAAa;AAAA,IAAA;AAGf,UAAM,YAAY,KAAK,IAAA;AACvB,UAAM,YAAY;AAAA,MAChB,OAAO,WAAW,KAAK,IAAI;AAAA,MAC3B,UAAU,YAAY,YAAY,WAAW,KAAK,IAAI;AAAA,MACtD,WAAW,WAAW,KAAK,IAAI;AAAA,MAC/B;AAAA,MACA,sBAAsB;AAAA,IAAA;AAGxB,QAAI,qBAAqB;AACzB,QAAI,uBAAuB;AAC3B,QAAI,6BAA6B;AACjC,QAAI;AACJ,QAAI,qBAAqB;AACzB,QAAI;AACJ,QAAI;AACJ,QAAI;AAIJ,UAAM,2BAA0B,aAEL;AACzB,UAAI,sBAAsB,CAAC,oBAAoB;AAC7C,6BAAqB;AACrB,cAAM;AAAA,UACJ,MAAM,UAAU;AAAA,UAChB,WAAW;AAAA,UACX,OAAO,aAAa,YAAY;AAAA,UAChC;AAAA,QAAA;AAEF,cAAM;AAAA,UACJ,MAAM,UAAU;AAAA,UAChB,WAAW;AAAA,UACX,OAAO,aAAa,YAAY;AAAA,UAChC;AAAA,QAAA;AAEF,YAAI,QAAQ;AACV,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,UAAU;AAAA,YACV;AAAA,YACA,OAAO,aAAa,YAAY;AAAA,YAChC;AAAA,YACA,SAAS;AAAA,UAAA;AAAA,QAEb;AAAA,MACF;AAAA,IACF,GAAE,KAAK,IAAI;AAEX,QAAI;AAIF,YAAM;AAAA,QACJ,gBAAgB;AAAA,QAChB,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,GAAG;AAAA,MAAA,IACD;AAEJ,kBAAY,OAAO;AAAA,QACjB,4CAA4C,KAAK,IAAI,UAAU,KAAK,KAAK,aAAa,YAAY,SAAS,MAAM;AAAA,QACjH,EAAE,UAAU,KAAK,MAAM,OAAO,KAAK,MAAA;AAAA,MAAM;AAG3C,YAAM,SAAS,MAAM,KAAK,OAAO,KAAK,YAAY;AAAA,QAChD;AAAA,UACE,GAAG;AAAA,UACH,QAAQ;AAAA,UACR,gBAAgB,EAAE,eAAe,KAAA;AAAA,UACjC,iBAAiB;AAAA,YACf,MAAM;AAAA,YACN,aAAa;AAAA,cACX,MAAM;AAAA,cACN,QAAQ;AAAA,cACR,QAAQ;AAAA,YAAA;AAAA,UACV;AAAA,QACF;AAAA,QAEF,sBAAsB,YAAY,OAAO;AAAA,MAAA;AAG3C,uBAAiB,SAAS,QAAQ;AAChC,cAAM,eAAe,MAAM,QAAQ,CAAC;AACpC,oBAAY,OAAO;AAAA,UACjB,YAAY,KAAK,IAAI,kBAAkB,cAAc,iBAAiB,MAAM,eAAe,CAAC,CAAC,cAAc,MAAM,OAAO,aAAa,CAAC,CAAC,MAAM,KAAK;AAAA,UAClJ,EAAE,UAAU,KAAK,MAAM,OAAO,MAAM,MAAA;AAAA,QAAM;AAG5C,YAAI,MAAM,MAAO,aAAY,MAAM;AAMnC,cAAM,QACJ,MAAM,SACL,MAAsD,QAAQ;AACjE,YAAI,MAAO,aAAY;AAEvB,YAAI,CAAC,UAAU,sBAAsB;AACnC,oBAAU,uBAAuB;AACjC,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,OAAO,UAAU;AAAA,YACjB,UAAU,UAAU;AAAA,YACpB,OAAO,MAAM,SAAS,YAAY;AAAA,YAClC;AAAA,YACA,aAAa,YAAY;AAAA,UAAA;AAAA,QAE7B;AAGA,cAAM,YAAY,KAAK,iBAAiB,KAAK;AAC7C,YAAI,aAAa,UAAU,MAAM;AAC/B,cAAI,CAAC,oBAAoB;AACvB,iCAAqB,WAAW,KAAK,IAAI;AACzC,qBAAS,WAAW,KAAK,IAAI;AAC7B,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW;AAAA,cACX,OAAO,MAAM,SAAS,YAAY;AAAA,cAClC;AAAA,YAAA;AAEF,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW;AAAA,cACX,MAAM;AAAA,cACN,OAAO,MAAM,SAAS,YAAY;AAAA,cAClC;AAAA,YAAA;AAEF,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,UAAU;AAAA,cACV;AAAA,cACA,OAAO,MAAM,SAAS,YAAY;AAAA,cAClC;AAAA,cACA,UAAU;AAAA,YAAA;AAAA,UAEd;AACA,kCAAwB,UAAU;AAClC,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,WAAW;AAAA,YACX,OAAO,UAAU;AAAA,YACjB,OAAO,MAAM,SAAS,YAAY;AAAA,YAClC;AAAA,UAAA;AAAA,QAEJ;AAEA,cAAM,SAAS,MAAM,QAAQ,CAAC;AAC9B,YAAI,CAAC,OAAQ;AAEb,cAAM,eAAe,OAAO,MAAM;AAClC,YAAI,cAAc;AAChB,iBAAO,wBAAA;AAEP,cAAI,CAAC,4BAA4B;AAC/B,yCAA6B;AAC7B,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW,UAAU;AAAA,cACrB,OAAO,MAAM,SAAS,YAAY;AAAA,cAClC;AAAA,cACA,MAAM;AAAA,YAAA;AAAA,UAEV;AAEA,gCAAsB;AAEtB,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,WAAW,UAAU;AAAA,YACrB,OAAO,MAAM,SAAS,YAAY;AAAA,YAClC;AAAA,YACA,OAAO;AAAA,YACP,SAAS;AAAA,UAAA;AAAA,QAEb;AAAA,MACF;AAKA,aAAO,wBAAA;AAEP,UAAI,4BAA4B;AAC9B,cAAM;AAAA,UACJ,MAAM,UAAU;AAAA,UAChB,WAAW,UAAU;AAAA,UACrB,OAAO,aAAa,YAAY;AAAA,UAChC;AAAA,QAAA;AAAA,MAEJ;AAEA,UAAI,mBAAmB,WAAW,GAAG;AACnC,cAAM;AAAA,UACJ,MAAM,UAAU;AAAA,UAChB,OAAO,UAAU;AAAA,UACjB,OAAO,aAAa,YAAY;AAAA,UAChC;AAAA,UACA,SAAS,GAAG,KAAK,IAAI;AAAA,UACrB,MAAM;AAAA,UACN,OAAO;AAAA,YACL,SAAS,GAAG,KAAK,IAAI;AAAA,YACrB,MAAM;AAAA,UAAA;AAAA,QACR;AAEF;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,iBAAS,KAAK,MAAM,kBAAkB;AAAA,MACxC,QAAQ;AACN,cAAM;AAAA,UACJ,MAAM,UAAU;AAAA,UAChB,OAAO,UAAU;AAAA,UACjB,OAAO,aAAa,YAAY;AAAA,UAChC;AAAA,UACA,SAAS,uDAAuD,mBAAmB,MAAM,GAAG,GAAG,CAAC,GAAG,mBAAmB,SAAS,MAAM,QAAQ,EAAE;AAAA,UAC/I,MAAM;AAAA,UACN,OAAO;AAAA,YACL,SAAS;AAAA,YACT,MAAM;AAAA,UAAA;AAAA,QACR;AAEF;AAAA,MACF;AAEA,YAAM,cAAc,KAAK,0BAA0B,MAAM;AAEzD,YAAM;AAAA,QACJ,MAAM,UAAU;AAAA,QAChB,MAAM;AAAA,QACN,OAAO;AAAA,UACL,QAAQ;AAAA,UACR,KAAK;AAAA,UACL,GAAI,uBAAuB,EAAE,WAAW,yBAAyB,CAAA;AAAA,QAAC;AAAA,QAEpE,OAAO,aAAa,YAAY;AAAA,QAChC;AAAA,MAAA;AAGF,YAAM;AAAA,QACJ,MAAM,UAAU;AAAA,QAChB,OAAO,UAAU;AAAA,QACjB,UAAU,UAAU;AAAA,QACpB,OAAO,aAAa,YAAY;AAAA,QAChC;AAAA,QACA,cAAc;AAAA,QACd,GAAI,aAAa;AAAA,UACf,OAAO;AAAA,YACL,cAAc,UAAU;AAAA,YACxB,kBAAkB,UAAU;AAAA,YAC5B,aAAa,UAAU;AAAA,UAAA;AAAA,QACzB;AAAA,MACF;AAAA,IAEJ,SAAS,OAAgB;AACvB,UAAI,CAAC,UAAU,sBAAsB;AACnC,kBAAU,uBAAuB;AACjC,cAAM;AAAA,UACJ,MAAM,UAAU;AAAA,UAChB,OAAO,UAAU;AAAA,UACjB,UAAU,UAAU;AAAA,UACpB,OAAO,YAAY;AAAA,UACnB;AAAA,UACA,aAAa,YAAY;AAAA,QAAA;AAAA,MAE7B;AAEA,YAAM,UAAU,KAAK,aAAa,KAAK;AACvC,YAAM,eAAe;AAAA,QACnB;AAAA,QACA,GAAG,KAAK,IAAI;AAAA,MAAA;AAGd,YAAM;AAAA,QACJ,MAAM,UAAU;AAAA,QAChB,OAAO,UAAU;AAAA,QACjB,OAAO,aAAa,YAAY;AAAA,QAChC;AAAA,QACA,SAAS,aAAa;AAAA,QACtB,MAAM,UAAU,YAAY,aAAa;AAAA,QACzC,OAAO,EAAE,GAAG,cAAc,GAAI,WAAW,EAAE,MAAM,UAAA,EAAU;AAAA,MAAG;AAGhE,kBAAY,OAAO,OAAO,GAAG,KAAK,IAAI,iCAAiC;AAAA,QACrE,OAAO;AAAA,QACP,QAAQ,GAAG,KAAK,IAAI;AAAA,MAAA,CACrB;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,aAAa,OAAyB;AAC9C,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,UAAM,IAAI;AACV,WACE,EAAE,SAAS,uBACX,EAAE,SAAS,gBACX,EAAE,SAAS;AAAA,EAEf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,+BACR,QACA,kBACqB;AACrB,WAAO,+BAA+B,QAAQ,gBAAgB;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASU,iBAAiB,QAA+C;AACxE,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASU,0BAA0B,QAA0B;AAC5D,WAAO,0BAA0B,MAAM;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAiB,oBACf,QACA,SACA,WAM4B;AAC5B,QAAI,qBAAqB;AACzB,QAAI,6BAA6B;AACjC,QAAI;AAOJ,QAAI;AACJ,QAAI;AAKJ,UAAM,0CAA0B,IAAA;AAahC,QAAI;AACJ,QAAI,qBAAqB;AAIzB,QAAI;AACJ,QAAI,uBAAuB;AAO3B,QAAI,wBAAwB;AAE5B,QAAI;AACF,uBAAiB,SAAS,QAAQ;AAChC,cAAM,eAAe,MAAM,QAAQ,CAAC;AACpC,gBAAQ,OAAO;AAAA,UACb,YAAY,KAAK,IAAI,kBAAkB,cAAc,iBAAiB,MAAM,eAAe,CAAC,CAAC,cAAc,MAAM,OAAO,iBAAiB,CAAC,CAAC,cAAc,MAAM,UAAU,aAAa,CAAC,CAAC,MAAM,KAAK;AAAA,UACnM,EAAE,UAAU,KAAK,MAAM,OAAO,MAAM,MAAA;AAAA,QAAM;AAK5C,YAAI,MAAM,OAAO;AACf,sBAAY,MAAM;AAAA,QACpB;AACA,YAAI,MAAM,OAAO;AACf,sBAAY,MAAM;AAAA,QACpB;AAQA,YAAI,CAAC,UAAU,sBAAsB;AACnC,oBAAU,uBAAuB;AACjC,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,OAAO,UAAU;AAAA,YACjB,UAAU,UAAU;AAAA,YACpB,OAAO,MAAM,SAAS,QAAQ;AAAA,YAC9B,WAAW,KAAK,IAAA;AAAA,YAChB,aAAa,QAAQ;AAAA,UAAA;AAAA,QAEzB;AAMA,cAAM,YAAY,KAAK,iBAAiB,KAAK;AAC7C,YAAI,aAAa,UAAU,MAAM;AAC/B,cAAI,CAAC,oBAAoB;AACvB,iCAAqB,WAAW,KAAK,IAAI;AACzC,qBAAS,WAAW,KAAK,IAAI;AAC7B,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW;AAAA,cACX,OAAO,MAAM,SAAS,QAAQ;AAAA,cAC9B,WAAW,KAAK,IAAA;AAAA,YAAI;AAEtB,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW;AAAA,cACX,MAAM;AAAA,cACN,OAAO,MAAM,SAAS,QAAQ;AAAA,cAC9B,WAAW,KAAK,IAAA;AAAA,YAAI;AAItB,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,UAAU;AAAA,cACV;AAAA,cACA,OAAO,MAAM,SAAS,QAAQ;AAAA,cAC9B,WAAW,KAAK,IAAA;AAAA,cAChB,UAAU;AAAA,YAAA;AAAA,UAEd;AACA,kCAAwB,UAAU;AAClC,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,WAAW;AAAA,YACX,OAAO,UAAU;AAAA,YACjB,OAAO,MAAM,SAAS,QAAQ;AAAA,YAC9B,WAAW,KAAK,IAAA;AAAA,UAAI;AAAA,QAExB;AAEA,cAAM,SAAS,MAAM,QAAQ,CAAC;AAE9B,YAAI,CAAC,OAAQ;AAEb,cAAM,QAAQ,OAAO;AACrB,cAAM,eAAe,MAAM;AAC3B,cAAM,iBAAiB,MAAM;AAG7B,YAAI,cAAc;AAGhB,cAAI,sBAAsB,CAAC,oBAAoB;AAC7C,iCAAqB;AACrB,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW;AAAA,cACX,OAAO,MAAM,SAAS,QAAQ;AAAA,cAC9B,WAAW,KAAK,IAAA;AAAA,YAAI;AAEtB,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW;AAAA,cACX,OAAO,MAAM,SAAS,QAAQ;AAAA,cAC9B,WAAW,KAAK,IAAA;AAAA,YAAI;AAEtB,gBAAI,QAAQ;AACV,oBAAM;AAAA,gBACJ,MAAM,UAAU;AAAA,gBAChB,UAAU;AAAA,gBACV;AAAA,gBACA,OAAO,MAAM,SAAS,QAAQ;AAAA,gBAC9B,WAAW,KAAK,IAAA;AAAA,gBAChB,SAAS;AAAA,cAAA;AAAA,YAEb;AAAA,UACF;AAGA,cAAI,CAAC,4BAA4B;AAC/B,yCAA6B;AAC7B,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW,UAAU;AAAA,cACrB,OAAO,MAAM,SAAS,QAAQ;AAAA,cAC9B,WAAW,KAAK,IAAA;AAAA,cAChB,MAAM;AAAA,YAAA;AAAA,UAEV;AAEA,gCAAsB;AAGtB,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,WAAW,UAAU;AAAA,YACrB,OAAO,MAAM,SAAS,QAAQ;AAAA,YAC9B,WAAW,KAAK,IAAA;AAAA,YAChB,OAAO;AAAA,YACP,SAAS;AAAA,UAAA;AAAA,QAEb;AAGA,YAAI,gBAAgB;AAClB,qBAAW,iBAAiB,gBAAgB;AAC1C,kBAAM,QAAQ,cAAc;AAG5B,gBAAI,CAAC,oBAAoB,IAAI,KAAK,GAAG;AACnC,kCAAoB,IAAI,OAAO;AAAA,gBAC7B,IAAI,cAAc,MAAM;AAAA,gBACxB,MAAM,cAAc,UAAU,QAAQ;AAAA,gBACtC,WAAW;AAAA,gBACX,SAAS;AAAA,cAAA,CACV;AAAA,YACH;AAEA,kBAAM,WAAW,oBAAoB,IAAI,KAAK;AAG9C,gBAAI,cAAc,IAAI;AACpB,uBAAS,KAAK,cAAc;AAAA,YAC9B;AACA,gBAAI,cAAc,UAAU,MAAM;AAChC,uBAAS,OAAO,cAAc,SAAS;AAAA,YACzC;AACA,gBAAI,cAAc,UAAU,WAAW;AACrC,uBAAS,aAAa,cAAc,SAAS;AAAA,YAC/C;AAGA,gBAAI,SAAS,MAAM,SAAS,QAAQ,CAAC,SAAS,SAAS;AACrD,uBAAS,UAAU;AACnB,oBAAM;AAAA,gBACJ,MAAM,UAAU;AAAA,gBAChB,YAAY,SAAS;AAAA,gBACrB,cAAc,SAAS;AAAA,gBACvB,UAAU,SAAS;AAAA,gBACnB,OAAO,MAAM,SAAS,QAAQ;AAAA,gBAC9B,WAAW,KAAK,IAAA;AAAA,gBAChB;AAAA,cAAA;AAAA,YAEJ;AAGA,gBAAI,cAAc,UAAU,aAAa,SAAS,SAAS;AACzD,oBAAM;AAAA,gBACJ,MAAM,UAAU;AAAA,gBAChB,YAAY,SAAS;AAAA,gBACrB,OAAO,MAAM,SAAS,QAAQ;AAAA,gBAC9B,WAAW,KAAK,IAAA;AAAA,gBAChB,OAAO,cAAc,SAAS;AAAA,cAAA;AAAA,YAElC;AAAA,UACF;AAAA,QACF;AAQA,YAAI,OAAO,eAAe;AACxB,cACE,OAAO,kBAAkB,gBACzB,oBAAoB,OAAO,GAC3B;AACA,uBAAW,CAAA,EAAG,QAAQ,KAAK,qBAAqB;AAK9C,kBAAI,CAAC,SAAS,QAAS;AAQvB,kBAAI,cAAuB,CAAA;AAC3B,kBAAI,SAAS,WAAW;AACtB,oBAAI;AACF,wBAAM,SAAkB,KAAK,MAAM,SAAS,SAAS;AACrD,gCACE,UAAU,OAAO,WAAW,WAAW,SAAS,CAAA;AAAA,gBACpD,SAAS,YAAY;AACnB,0BAAQ,OAAO;AAAA,oBACb,GAAG,KAAK,IAAI;AAAA,oBACZ;AAAA,sBACE,OAAO;AAAA,wBACL;AAAA,wBACA,QAAQ,SAAS,IAAI,KAAK,SAAS,EAAE;AAAA,sBAAA;AAAA,sBAEvC,QAAQ,GAAG,KAAK,IAAI;AAAA,sBACpB,YAAY,SAAS;AAAA,sBACrB,UAAU,SAAS;AAAA,sBACnB,cAAc,SAAS;AAAA,oBAAA;AAAA,kBACzB;AAEF,gCAAc,CAAA;AAAA,gBAChB;AAAA,cACF;AAGA,oBAAM;AAAA,gBACJ,MAAM,UAAU;AAAA,gBAChB,YAAY,SAAS;AAAA,gBACrB,cAAc,SAAS;AAAA,gBACvB,UAAU,SAAS;AAAA,gBACnB,OAAO,MAAM,SAAS,QAAQ;AAAA,gBAC9B,WAAW,KAAK,IAAA;AAAA,gBAChB,OAAO;AAAA,cAAA;AAET,sCAAwB;AAAA,YAC1B;AAIA,gCAAoB,MAAA;AAAA,UACtB;AAGA,cAAI,4BAA4B;AAC9B,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,WAAW,UAAU;AAAA,cACrB,OAAO,MAAM,SAAS,QAAQ;AAAA,cAC9B,WAAW,KAAK,IAAA;AAAA,YAAI;AAEtB,yCAA6B;AAAA,UAC/B;AAIA,gCAAsB,OAAO;AAAA,QAC/B;AAAA,MACF;AAOA,UAAI,UAAU,sBAAsB;AAKlC,YAAI,mBAAmB;AACvB,mBAAW,CAAA,EAAG,QAAQ,KAAK,qBAAqB;AAC9C,cAAI,CAAC,SAAS,QAAS;AACvB,cAAI,cAAuB,CAAA;AAC3B,cAAI,SAAS,WAAW;AACtB,gBAAI;AACF,oBAAM,SAAkB,KAAK,MAAM,SAAS,SAAS;AACrD,4BAAc,UAAU,OAAO,WAAW,WAAW,SAAS,CAAA;AAAA,YAChE,SAAS,YAAY;AAKnB,sBAAQ,OAAO;AAAA,gBACb,GAAG,KAAK,IAAI;AAAA,gBACZ;AAAA,kBACE,OAAO;AAAA,oBACL;AAAA,oBACA,QAAQ,SAAS,IAAI,KAAK,SAAS,EAAE;AAAA,kBAAA;AAAA,kBAEvC,QAAQ,GAAG,KAAK,IAAI;AAAA,kBACpB,YAAY,SAAS;AAAA,kBACrB,UAAU,SAAS;AAAA,kBACnB,cAAc,SAAS;AAAA,gBAAA;AAAA,cACzB;AAEF,4BAAc,CAAA;AAAA,YAChB;AAAA,UACF;AACA,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,YAAY,SAAS;AAAA,YACrB,cAAc,SAAS;AAAA,YACvB,UAAU,SAAS;AAAA,YACnB,OAAO,aAAa,QAAQ;AAAA,YAC5B,WAAW,KAAK,IAAA;AAAA,YAChB,OAAO;AAAA,UAAA;AAET,8BAAoB;AACpB,kCAAwB;AAAA,QAC1B;AACA,4BAAoB,MAAA;AAIpB,YAAI,4BAA4B;AAC9B,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,WAAW,UAAU;AAAA,YACrB,OAAO,aAAa,QAAQ;AAAA,YAC5B,WAAW,KAAK,IAAA;AAAA,UAAI;AAAA,QAExB;AAIA,YAAI,sBAAsB,CAAC,oBAAoB;AAC7C,+BAAqB;AACrB,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,WAAW;AAAA,YACX,OAAO,aAAa,QAAQ;AAAA,YAC5B,WAAW,KAAK,IAAA;AAAA,UAAI;AAEtB,gBAAM;AAAA,YACJ,MAAM,UAAU;AAAA,YAChB,WAAW;AAAA,YACX,OAAO,aAAa,QAAQ;AAAA,YAC5B,WAAW,KAAK,IAAA;AAAA,UAAI;AAEtB,cAAI,QAAQ;AACV,kBAAM;AAAA,cACJ,MAAM,UAAU;AAAA,cAChB,UAAU;AAAA,cACV;AAAA,cACA,OAAO,aAAa,QAAQ;AAAA,cAC5B,WAAW,KAAK,IAAA;AAAA,cAChB,SAAS;AAAA,YAAA;AAAA,UAEb;AAAA,QACF;AAWA,cAAM,eACJ,wBACI,eACA,wBAAwB,eACtB,SACA,wBAAwB,kBACtB,eACC,uBAAuB;AAElC,cAAM;AAAA,UACJ,MAAM,UAAU;AAAA,UAChB,OAAO,UAAU;AAAA,UACjB,UAAU,UAAU;AAAA,UACpB,OAAO,aAAa,QAAQ;AAAA,UAC5B,WAAW,KAAK,IAAA;AAAA,UAChB,OAAO,YACH;AAAA,YACE,cAAc,UAAU,iBAAiB;AAAA,YACzC,kBAAkB,UAAU,qBAAqB;AAAA,YACjD,aAAa,UAAU,gBAAgB;AAAA,UAAA,IAEzC;AAAA,UACJ;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF,SAAS,OAAgB;AAGvB,YAAM,eAAe;AAAA,QACnB;AAAA,QACA,GAAG,KAAK,IAAI;AAAA,MAAA;AAEd,cAAQ,OAAO,OAAO,GAAG,KAAK,IAAI,8BAA8B;AAAA,QAC9D,OAAO;AAAA,QACP,QAAQ,GAAG,KAAK,IAAI;AAAA,MAAA,CACrB;AAGD,YAAM;AAAA,QACJ,MAAM,UAAU;AAAA,QAChB,OAAO,QAAQ;AAAA,QACf,WAAW,KAAK,IAAA;AAAA,QAChB,SAAS,aAAa;AAAA,QACtB,MAAM,aAAa;AAAA,QACnB,OAAO;AAAA,MAAA;AAAA,IAEX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,oBACR,SACqC;AACrC,UAAM,QAAQ,QAAQ,QAClB;AAAA,MACE,QAAQ;AAAA,MACR,KAAK,+BAA+B,KAAK,IAAI;AAAA,IAAA,IAE/C;AAGJ,UAAM,WAA8C,CAAA;AAGpD,QAAI,QAAQ,iBAAiB,QAAQ,cAAc,SAAS,GAAG;AAC7D,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS,QAAQ,cAAc,KAAK,IAAI;AAAA,MAAA,CACzC;AAAA,IACH;AAGA,eAAW,WAAW,QAAQ,UAAU;AACtC,eAAS,KAAK,KAAK,eAAe,OAAO,CAAC;AAAA,IAC5C;AAEA,UAAM,eAAe,QAAQ;AAO7B,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO,QAAQ;AAAA,MACf;AAAA,MACA,GAAI,QAAQ,gBAAgB,UAAa;AAAA,QACvC,aAAa,QAAQ;AAAA,MAAA;AAAA,MAEvB,GAAI,QAAQ,cAAc,UAAa;AAAA,QACrC,YAAY,QAAQ;AAAA,MAAA;AAAA,MAEtB,GAAI,QAAQ,SAAS,UAAa,EAAE,OAAO,QAAQ,KAAA;AAAA;AAAA;AAAA,MAGnD,GAAI,SACF,MAAM,SAAS,KAAK;AAAA,QAClB;AAAA,MAAA;AAAA,MAEJ,QAAQ;AAAA,IAAA;AAAA,EAEZ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,eAAe,SAAmD;AAE1E,QAAI,QAAQ,SAAS,QAAQ;AAC3B,aAAO;AAAA,QACL,MAAM;AAAA,QACN,cAAc,QAAQ,cAAc;AAAA,QACpC,SACE,OAAO,QAAQ,YAAY,WACvB,QAAQ,UACR,KAAK,UAAU,QAAQ,OAAO;AAAA,MAAA;AAAA,IAExC;AAGA,QAAI,QAAQ,SAAS,aAAa;AAChC,YAAM,YAAY,QAAQ,WAAW,IAAI,CAAC,QAAQ;AAAA,QAChD,IAAI,GAAG;AAAA,QACP,MAAM;AAAA,QACN,UAAU;AAAA,UACR,MAAM,GAAG,SAAS;AAAA,UAClB,WACE,OAAO,GAAG,SAAS,cAAc,WAC7B,GAAG,SAAS,YACZ,KAAK,UAAU,GAAG,SAAS,SAAS;AAAA,QAAA;AAAA,MAC5C,EACA;AACF,YAAM,eAAe,CAAC,CAAC,aAAa,UAAU,SAAS;AACvD,YAAM,cAAc,KAAK,mBAAmB,QAAQ,OAAO;AAM3D,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,gBAAgB,CAAC,cAAc,OAAO;AAAA,QAC/C,GAAI,eAAe,EAAE,YAAY,cAAc,CAAA;AAAA,MAAC;AAAA,IAEpD;AAGA,UAAM,eAAe,KAAK,iBAAiB,QAAQ,OAAO;AAG1D,QAAI,aAAa,WAAW,KAAK,aAAa,CAAC,GAAG,SAAS,QAAQ;AACjE,YAAM,OAAO,aAAa,CAAC,EAAE;AAC7B,UAAI,KAAK,WAAW,GAAG;AAIrB,cAAM,IAAI;AAAA,UACR,oBAAoB,KAAK,IAAI;AAAA,QAAA;AAAA,MAIjC;AACA,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,MAAA;AAAA,IAEb;AAMA,UAAM,QAA0C,CAAA;AAChD,eAAW,QAAQ,cAAc;AAC/B,YAAM,YAAY,KAAK,mBAAmB,IAAI;AAC9C,UAAI,CAAC,WAAW;AACd,cAAM,IAAI;AAAA,UACR,qCAAqC,KAAK,IAAI,KAAK,KAAK,IAAI;AAAA,QAAA;AAAA,MAIhE;AACA,YAAM,KAAK,SAAS;AAAA,IACtB;AAEA,QAAI,MAAM,WAAW,GAAG;AAItB,YAAM,IAAI;AAAA,QACR,oBAAoB,KAAK,IAAI;AAAA,MAAA;AAAA,IAIjC;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,IAAA;AAAA,EAEb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,mBACR,MACkC;AAClC,QAAI,KAAK,SAAS,QAAQ;AACxB,aAAO,EAAE,MAAM,QAAQ,MAAM,KAAK,QAAA;AAAA,IACpC;AAEA,QAAI,KAAK,SAAS,SAAS;AACzB,YAAM,gBAAgB,KAAK;AAQ3B,YAAM,aAAa,KAAK,OAAO;AAC/B,YAAM,YAAY,KAAK,OAAO,YAAY;AAC1C,YAAM,WACJ,KAAK,OAAO,SAAS,UAAU,CAAC,WAAW,WAAW,OAAO,IACzD,QAAQ,SAAS,WAAW,UAAU,KACtC;AAEN,aAAO;AAAA,QACL,MAAM;AAAA,QACN,WAAW;AAAA,UACT,KAAK;AAAA,UACL,QAAQ,eAAe,UAAU;AAAA,QAAA;AAAA,MACnC;AAAA,IAEJ;AAGA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMU,iBACR,SACoB;AACpB,QAAI,YAAY,MAAM;AACpB,aAAO,CAAA;AAAA,IACT;AACA,QAAI,OAAO,YAAY,UAAU;AAC/B,aAAO,CAAC,EAAE,MAAM,QAAQ,SAAkB;AAAA,IAC5C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKU,mBACR,SACQ;AACR,QAAI,YAAY,MAAM;AACpB,aAAO;AAAA,IACT;AACA,QAAI,OAAO,YAAY,UAAU;AAC/B,aAAO;AAAA,IACT;AAEA,WAAO,QACJ,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,EAC/B,IAAI,CAAC,MAAM,EAAE,OAAO,EACpB,KAAK,EAAE;AAAA,EACZ;AACF;"}
@@ -51,7 +51,8 @@ class OpenAIBaseResponsesTextAdapter extends BaseTextAdapter {
51
51
  runId: aguiState.runId,
52
52
  threadId: aguiState.threadId,
53
53
  model: options.model,
54
- timestamp: Date.now()
54
+ timestamp: Date.now(),
55
+ parentRunId: options.parentRunId
55
56
  };
56
57
  }
57
58
  yield {
@@ -265,7 +266,8 @@ class OpenAIBaseResponsesTextAdapter extends BaseTextAdapter {
265
266
  runId: aguiState.runId,
266
267
  threadId: aguiState.threadId,
267
268
  model,
268
- timestamp
269
+ timestamp,
270
+ parentRunId: chatOptions.parentRunId
269
271
  };
270
272
  }
271
273
  if (chunk.type === "response.created" || chunk.type === "response.in_progress") {
@@ -426,7 +428,8 @@ class OpenAIBaseResponsesTextAdapter extends BaseTextAdapter {
426
428
  runId: aguiState.runId,
427
429
  threadId: aguiState.threadId,
428
430
  model,
429
- timestamp
431
+ timestamp,
432
+ parentRunId: chatOptions.parentRunId
430
433
  };
431
434
  }
432
435
  const isAbort = this.isAbortError(error);
@@ -555,7 +558,8 @@ class OpenAIBaseResponsesTextAdapter extends BaseTextAdapter {
555
558
  runId: aguiState.runId,
556
559
  threadId: aguiState.threadId,
557
560
  model: model || options.model,
558
- timestamp: Date.now()
561
+ timestamp: Date.now(),
562
+ parentRunId: options.parentRunId
559
563
  };
560
564
  }
561
565
  const handleContentPart = (contentPart) => {
@@ -1 +1 @@
1
- {"version":3,"file":"responses-text.js","sources":["../../../src/adapters/responses-text.ts"],"sourcesContent":["import { EventType } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { generateId, transformNullsToUndefined } from '@tanstack/ai-utils'\nimport { extractRequestOptions } from '../utils/request-options'\nimport { makeStructuredOutputCompatible } from '../utils/schema-converter'\nimport { convertToolsToResponsesFormat } from './responses-tool-converter'\nimport type OpenAI from 'openai'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type {\n Response,\n ResponseCreateParams,\n ResponseInput,\n ResponseInputContent,\n ResponseStreamEvent,\n} from 'openai/resources/responses/responses'\nimport type {\n ContentPart,\n DefaultMessageMetadataByModality,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\n\n/**\n * Shared implementation of the OpenAI Responses API. Holds the stream-event\n * accumulator + AG-UI lifecycle and calls the OpenAI SDK directly. Subclasses\n * (today: ai-openai) construct an OpenAI client with their provider-specific\n * `baseURL` / headers and pass it in.\n */\nexport abstract class OpenAIBaseResponsesTextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, unknown> = Record<string, unknown>,\n TInputModalities extends ReadonlyArray<Modality> = ReadonlyArray<Modality>,\n TMessageMetadata extends DefaultMessageMetadataByModality =\n DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadata,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name: string\n protected client: OpenAI\n\n constructor(model: TModel, name: string, client: OpenAI) {\n super({}, model)\n this.name = name\n this.client = client\n }\n\n async *chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n // Track tool call metadata by unique ID\n // Responses API streams tool calls with deltas — first chunk has ID/name,\n // subsequent chunks only have args.\n // We assign our own indices as we encounter unique tool call IDs.\n const toolCallMetadata = new Map<\n string,\n {\n index: number\n name: string\n started: boolean\n // Set once TOOL_CALL_END has been emitted (via args.done or the\n // output_item.done backfill) so the two paths don't double-emit.\n ended?: boolean\n // Set when args.done arrives before TOOL_CALL_START could fire\n // (output_item.added lacked a name). output_item.done picks these\n // up to emit the missing END.\n pendingArguments?: string\n }\n >()\n\n // AG-UI lifecycle tracking\n const aguiState = {\n runId: generateId(this.name),\n threadId: options.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n hasEmittedRunStarted: false,\n }\n\n try {\n // mapOptionsToRequest can throw on caller-side validation failures\n // (empty user content, unsupported parts, webSearchTool() rejection in\n // the OpenRouter override). Keep it inside the try so those failures\n // surface as RUN_ERROR events instead of iterator throws.\n const requestParams = this.mapOptionsToRequest(options)\n options.logger.request(\n `activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: this.name, model: this.model },\n )\n const response = await this.client.responses.create(\n {\n ...requestParams,\n stream: true,\n },\n extractRequestOptions(options.request),\n )\n\n yield* this.processStreamChunks(\n response,\n toolCallMetadata,\n options,\n aguiState,\n )\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.chatStream failed`,\n )\n\n // Emit RUN_STARTED if not yet emitted\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: options.model,\n timestamp: Date.now(),\n }\n }\n\n // Emit AG-UI RUN_ERROR\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n code: errorPayload.code,\n error: errorPayload,\n }\n\n options.logger.errors(`${this.name}.chatStream fatal`, {\n error: errorPayload,\n source: `${this.name}.chatStream`,\n })\n }\n }\n\n /**\n * Generate structured output using the provider's native JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * OpenAI-compatible Responses APIs have strict requirements for structured output:\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply provider-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapOptionsToRequest(chatOptions)\n\n // Apply provider-specific transformations for structured output compatibility\n const jsonSchema = this.makeStructuredOutputCompatible(\n outputSchema,\n outputSchema.required,\n )\n\n try {\n // Strip streaming-only fields a subclass override of mapOptionsToRequest\n // might have returned (parallel to chat-completions's structuredOutput\n // cleanup) — sending stream_options to a non-streaming call is a 4xx.\n const {\n stream: _stream,\n stream_options: _streamOptions,\n ...cleanParams\n } = requestParams as Record<string, unknown>\n void _stream\n void _streamOptions\n chatOptions.logger.request(\n `activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const response = await this.client.responses.create(\n {\n ...(cleanParams as Omit<ResponseCreateParams, 'stream'>),\n stream: false,\n // Configure structured output via text.format\n text: {\n format: {\n type: 'json_schema',\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n },\n extractRequestOptions(chatOptions.request),\n )\n\n // Extract text content from the response. `stream: false` narrows the\n // SDK return type to `Response`, but the explicit annotation makes\n // that contract local rather than relying on inference through the\n // overloaded `client.responses.create` signature.\n const rawText = this.extractTextFromResponse(response satisfies Response)\n\n // Fail loud on empty content rather than letting it cascade into a\n // confusing \"Failed to parse JSON. Content: \" error — the root cause\n // (the model returned no text content for the structured request) is\n // then visible in logs. Mirrors the chat-completions sibling.\n if (rawText.length === 0) {\n throw new Error(\n `${this.name}.structuredOutput: response contained no content`,\n )\n }\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n // Apply the provider-specific post-parse shaping (default: null →\n // undefined to align with the original Zod schema's optional-field\n // semantics; subclasses with different conventions can override\n // `transformStructuredOutput`, mirroring the chat-completions base's\n // hook so OpenRouter and other providers that preserve nulls in\n // structured output can opt out without forking `structuredOutput`).\n const transformed = this.transformStructuredOutput(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {\n error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),\n source: `${this.name}.structuredOutput`,\n })\n throw error\n }\n }\n\n /**\n * Stream structured output via the Responses API: single request with\n * `text.format: json_schema` + `stream: true`. Consumes Responses-API\n * events (`response.output_text.delta`, `response.reasoning_text.delta`,\n * `response.reasoning_summary_text.delta`, `response.refusal.delta`,\n * `response.completed`, `response.failed`) and re-emits the standard AG-UI\n * lifecycle ending with `CUSTOM 'structured-output.complete'`.\n *\n * Tools are stripped (structured output is mutually exclusive with tool\n * calls in this path). Reasoning text is accumulated and surfaced both as\n * REASONING_* lifecycle events during the stream and on the terminal\n * CUSTOM event's `value.reasoning`.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapOptionsToRequest(chatOptions)\n\n const jsonSchema = this.makeStructuredOutputCompatible(\n outputSchema,\n outputSchema.required,\n )\n\n const timestamp = Date.now()\n const aguiState = {\n runId: generateId(this.name),\n threadId: chatOptions.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n let accumulatedContent = ''\n let accumulatedReasoning = ''\n let hasEmittedTextMessageStart = false\n let reasoningMessageId: string | undefined\n let stepId: string | undefined\n let hasClosedReasoning = false\n let model: string = chatOptions.model\n let usage: OpenAI.Responses.Response['usage'] | undefined\n\n const closeReasoning = function* (this: {\n name: string\n }): Generator<StreamChunk> {\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n if (stepId) {\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model,\n timestamp,\n content: accumulatedReasoning,\n }\n }\n }\n }.bind(this)\n\n const openReasoning = function* (this: {\n name: string\n }): Generator<StreamChunk> {\n if (reasoningMessageId) return\n reasoningMessageId = generateId(this.name)\n stepId = generateId(this.name)\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model,\n timestamp,\n }\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model,\n timestamp,\n stepType: 'thinking',\n }\n }.bind(this)\n\n try {\n const { tools: _tools, ...cleanParams } = requestParams\n void _tools\n\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n\n const stream = await this.client.responses.create(\n {\n ...cleanParams,\n stream: true,\n text: {\n format: {\n type: 'json_schema',\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n },\n extractRequestOptions(chatOptions.request),\n )\n\n for await (const chunk of stream) {\n chatOptions.logger.provider(\n `provider=${this.name} type=${chunk.type}`,\n { provider: this.name, type: chunk.type },\n )\n\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model,\n timestamp,\n }\n }\n\n if (\n chunk.type === 'response.created' ||\n chunk.type === 'response.in_progress'\n ) {\n const responseModel = (chunk as { response?: { model?: string } })\n .response?.model\n if (responseModel) model = responseModel\n continue\n }\n\n if (chunk.type === 'response.refusal.delta') {\n const delta =\n typeof (chunk as { delta?: unknown }).delta === 'string'\n ? (chunk as { delta: string }).delta\n : ''\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message: `Model refused: ${delta}`,\n code: 'refusal',\n error: { message: `Model refused: ${delta}`, code: 'refusal' },\n }\n return\n }\n\n if (\n chunk.type === 'response.reasoning_text.delta' ||\n chunk.type === 'response.reasoning_summary_text.delta'\n ) {\n const raw = (chunk as { delta?: unknown }).delta\n const reasoningDelta = Array.isArray(raw)\n ? raw.join('')\n : typeof raw === 'string'\n ? raw\n : ''\n if (!reasoningDelta) continue\n yield* openReasoning()\n // openReasoning() guarantees reasoningMessageId is set on first call;\n // TS can't see through the generator side-effect.\n const messageId = reasoningMessageId!\n accumulatedReasoning += reasoningDelta\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId,\n delta: reasoningDelta,\n model,\n timestamp,\n }\n continue\n }\n\n if (chunk.type === 'response.output_text.delta') {\n const raw = (chunk as { delta?: unknown }).delta\n const textDelta = Array.isArray(raw)\n ? raw.join('')\n : typeof raw === 'string'\n ? raw\n : ''\n if (!textDelta) continue\n\n yield* closeReasoning()\n\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model,\n timestamp,\n role: 'assistant',\n }\n }\n accumulatedContent += textDelta\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model,\n timestamp,\n delta: textDelta,\n content: accumulatedContent,\n }\n continue\n }\n\n if (chunk.type === 'response.completed') {\n const response = chunk.response\n if (response.usage) usage = response.usage\n if (response.model) model = response.model\n continue\n }\n\n if (chunk.type === 'response.failed') {\n const response = (\n chunk as {\n response?: { error?: { message?: string; code?: string } }\n }\n ).response\n const message =\n response?.error?.message || 'Responses API stream failed'\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message,\n code: response?.error?.code,\n error: { message, code: response?.error?.code },\n }\n return\n }\n }\n\n yield* closeReasoning()\n\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model,\n timestamp,\n }\n }\n\n if (accumulatedContent.length === 0) {\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n error: {\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n },\n }\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(accumulatedContent)\n } catch {\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message: `Failed to parse structured output as JSON. Content: ${accumulatedContent.slice(0, 200)}${accumulatedContent.length > 200 ? '...' : ''}`,\n code: 'parse-error',\n error: {\n message: 'Failed to parse structured output as JSON',\n code: 'parse-error',\n },\n }\n return\n }\n\n const transformed = transformNullsToUndefined(parsed)\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: {\n object: transformed,\n raw: accumulatedContent,\n ...(accumulatedReasoning ? { reasoning: accumulatedReasoning } : {}),\n },\n model,\n timestamp,\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model,\n timestamp,\n finishReason: 'stop',\n ...(usage && {\n usage: {\n promptTokens: usage.input_tokens,\n completionTokens: usage.output_tokens,\n totalTokens: usage.total_tokens,\n },\n }),\n }\n } catch (error: unknown) {\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model,\n timestamp,\n }\n }\n\n const isAbort = this.isAbortError(error)\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.structuredOutputStream failed`,\n )\n\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message: errorPayload.message,\n code: isAbort ? 'aborted' : errorPayload.code,\n error: { ...errorPayload, ...(isAbort && { code: 'aborted' }) },\n }\n\n chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {\n error: errorPayload,\n source: `${this.name}.structuredOutputStream`,\n })\n }\n }\n\n /**\n * Cross-SDK abort detection for `structuredOutputStream`. Mirrors the\n * Chat Completions base; subclasses with proprietary error types override.\n */\n protected isAbortError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false\n const e = error as { name?: unknown; code?: unknown }\n return (\n e.name === 'APIUserAbortError' ||\n e.name === 'AbortError' ||\n e.code === 'ERR_CANCELED'\n )\n }\n\n /**\n * Applies provider-specific transformations for structured output compatibility.\n * Override this in subclasses to handle provider-specific quirks.\n */\n protected makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ): Record<string, any> {\n return makeStructuredOutputCompatible(schema, originalRequired)\n }\n\n /**\n * Final shaping pass applied to parsed structured-output JSON before it is\n * returned to the caller. Default converts `null` values to `undefined` so\n * the result aligns with the original Zod schema's optional-field\n * semantics. Subclasses with different conventions (OpenRouter historically\n * preserves nulls) can override — mirrors the chat-completions base's hook\n * so a subclass that opts out of null-stripping doesn't have to fork the\n * whole `structuredOutput` method.\n */\n protected transformStructuredOutput(parsed: unknown): unknown {\n return transformNullsToUndefined(parsed)\n }\n\n /**\n * Extract text content from a non-streaming Responses API response.\n * Override this in subclasses for provider-specific response shapes.\n */\n protected extractTextFromResponse(response: Response): string {\n let textContent = ''\n let refusal: string | undefined\n let sawMessageItem = false\n const observedItemTypes = new Set<string>()\n\n for (const item of response.output) {\n observedItemTypes.add(item.type)\n if (item.type === 'message') {\n sawMessageItem = true\n for (const part of item.content) {\n // Cast off the discriminated union before the type discrimination\n // so future SDK variants (e.g. `output_audio`, `output_image`) hit\n // the explicit error path rather than being misreported as refusals\n // when they get added to the union. Mirrors the streaming side's\n // handleContentPart.\n const partType = (part as { type: string }).type\n if (partType === 'output_text') {\n textContent += (part as { text?: string }).text ?? ''\n } else if (partType === 'refusal') {\n const refusalText = (part as { refusal?: string }).refusal\n refusal = refusalText || refusal || 'Refused without explanation'\n } else {\n throw new Error(\n `${this.name}.extractTextFromResponse: unsupported message content part type \"${partType}\"`,\n )\n }\n }\n }\n }\n\n // Surface refusals as an explicit error so callers don't see a generic\n // \"Failed to parse structured output as JSON. Content: \" when the model\n // refused for safety / content-policy reasons.\n if (!textContent && refusal !== undefined) {\n const err = new Error(`Model refused to respond: ${refusal}`)\n ;(err as Error & { code?: string }).code = 'refusal'\n throw err\n }\n\n // Response had items but none carried message text (e.g. only\n // function_call or reasoning items). Surface that explicitly so a\n // downstream structured-output caller doesn't see a misleading\n // \"Failed to parse JSON. Content: \" from an empty string.\n if (!textContent && response.output.length > 0 && !sawMessageItem) {\n throw new Error(\n `${this.name}.extractTextFromResponse: response.output contained items of type(s) [${[...observedItemTypes].sort().join(', ')}] but no message text — the model returned a non-text response`,\n )\n }\n\n return textContent\n }\n\n /**\n * Processes streamed chunks from the Responses API and yields AG-UI events.\n * Override this in subclasses to handle provider-specific stream behavior.\n *\n * Handles the following event types:\n * - response.created / response.incomplete / response.failed\n * - response.output_text.delta\n * - response.reasoning_text.delta\n * - response.reasoning_summary_text.delta\n * - response.content_part.added / response.content_part.done\n * - response.output_item.added\n * - response.function_call_arguments.delta / response.function_call_arguments.done\n * - response.completed\n * - error\n */\n protected async *processStreamChunks(\n stream: AsyncIterable<ResponseStreamEvent>,\n toolCallMetadata: Map<\n string,\n {\n index: number\n name: string\n started: boolean\n ended?: boolean\n pendingArguments?: string\n }\n >,\n options: TextOptions<TProviderOptions>,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n let accumulatedReasoning = ''\n\n // Track if we've been streaming deltas to avoid duplicating content from done events\n let hasStreamedContentDeltas = false\n let hasStreamedReasoningDeltas = false\n\n // Preserve response metadata across events\n let model: string = options.model\n\n // AG-UI lifecycle tracking\n let stepId: string | null = null\n let hasEmittedTextMessageStart = false\n let hasEmittedStepStarted = false\n // Track whether we've emitted a terminal RUN_FINISHED so the\n // end-of-stream fallback below knows to synthesise one when the upstream\n // cuts off without a response.completed event.\n let runFinishedEmitted = false\n\n try {\n for await (const chunk of stream) {\n options.logger.provider(`provider=${this.name} type=${chunk.type}`, {\n provider: this.name,\n type: chunk.type,\n })\n\n // Emit RUN_STARTED on first chunk\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: model || options.model,\n timestamp: Date.now(),\n }\n }\n\n const handleContentPart = (contentPart: {\n type: string\n text?: string\n refusal?: string\n }): StreamChunk => {\n if (contentPart.type === 'output_text') {\n accumulatedContent += contentPart.text || ''\n return {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: contentPart.text || '',\n content: accumulatedContent,\n }\n }\n\n if (contentPart.type === 'reasoning_text') {\n accumulatedReasoning += contentPart.text || ''\n // Cache the fallback stepId rather than generating a fresh one\n // on every call — otherwise multiple reasoning chunks arriving\n // before STEP_STARTED was emitted (e.g. via response.content_part.done\n // alone) would each get a different stepId and break correlation.\n if (!stepId) {\n stepId = generateId(this.name)\n }\n return {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: contentPart.text || '',\n content: accumulatedReasoning,\n }\n }\n // Either a real refusal or an unknown content_part type. Surface\n // the part type in the error so unknown parts are debuggable\n // instead of being misreported as \"Unknown refusal\".\n const isRefusal = contentPart.type === 'refusal'\n const message = isRefusal\n ? contentPart.refusal || 'Refused without explanation'\n : `Unsupported response content_part type: ${contentPart.type}`\n const code = isRefusal ? 'refusal' : contentPart.type\n return {\n type: EventType.RUN_ERROR,\n model: model || options.model,\n timestamp: Date.now(),\n message,\n code,\n error: { message, code },\n }\n }\n\n // Capture model metadata from any of these events (created starts\n // the run; failed/incomplete signal terminal failure).\n if (\n chunk.type === 'response.created' ||\n chunk.type === 'response.incomplete' ||\n chunk.type === 'response.failed'\n ) {\n model = chunk.response.model\n }\n\n // response.created marks the start of a fresh run — safe to reset\n // the per-run accumulators here.\n if (chunk.type === 'response.created') {\n hasStreamedContentDeltas = false\n hasStreamedReasoningDeltas = false\n hasEmittedTextMessageStart = false\n hasEmittedStepStarted = false\n accumulatedContent = ''\n accumulatedReasoning = ''\n }\n\n // response.failed and response.incomplete are TERMINAL events for\n // the current response. Close any open AG-UI message lifecycle FIRST\n // so consumers tracking start/end pairs don't see an unbalanced\n // TEXT_MESSAGE_START. Then surface the error and mark the run as\n // finished so the post-loop synthetic terminal block doesn't emit\n // a duplicate RUN_FINISHED on top of RUN_ERROR.\n if (\n chunk.type === 'response.failed' ||\n chunk.type === 'response.incomplete'\n ) {\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: chunk.response.model,\n timestamp: Date.now(),\n }\n hasEmittedTextMessageStart = false\n }\n // Coalesce error + incomplete_details into a single RUN_ERROR\n // payload — emitting two distinct events for one terminal upstream\n // event would force consumers to handle a non-existent ordering.\n const errorMessage =\n chunk.response.error?.message ||\n chunk.response.incomplete_details?.reason ||\n (chunk.type === 'response.failed'\n ? 'Response failed'\n : 'Response ended incomplete')\n const errorCode =\n chunk.response.error?.code ??\n (chunk.response.incomplete_details ? 'incomplete' : undefined) ??\n undefined\n // Always emit RUN_ERROR for terminal failure events, even when the\n // upstream omitted both `error` and `incomplete_details`. Skipping\n // emission on a `response.incomplete` with no detail would let the\n // post-loop synthetic block silently coerce the run to a clean\n // `RUN_FINISHED { finishReason: 'stop' }` — masking the failure.\n yield {\n type: EventType.RUN_ERROR,\n model: chunk.response.model,\n timestamp: Date.now(),\n message: errorMessage,\n ...(errorCode !== undefined && { code: errorCode }),\n error: {\n message: errorMessage,\n ...(errorCode !== undefined && { code: errorCode }),\n },\n }\n // RUN_ERROR is the terminal event for this run; stop processing\n // any further chunks the iterator might still deliver.\n runFinishedEmitted = true\n return\n }\n\n // Handle output text deltas (token-by-token streaming)\n // response.output_text.delta provides incremental text updates\n if (chunk.type === 'response.output_text.delta' && chunk.delta) {\n // Delta can be an array of strings or a single string\n const textDelta = Array.isArray(chunk.delta)\n ? chunk.delta.join('')\n : typeof chunk.delta === 'string'\n ? chunk.delta\n : ''\n\n if (textDelta) {\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += textDelta\n hasStreamedContentDeltas = true\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: textDelta,\n content: accumulatedContent,\n }\n }\n }\n\n // Handle reasoning deltas (token-by-token thinking/reasoning streaming)\n // response.reasoning_text.delta provides incremental reasoning updates\n if (chunk.type === 'response.reasoning_text.delta' && chunk.delta) {\n // Delta can be an array of strings or a single string\n const reasoningDelta = Array.isArray(chunk.delta)\n ? chunk.delta.join('')\n : typeof chunk.delta === 'string'\n ? chunk.delta\n : ''\n\n if (reasoningDelta) {\n // Emit STEP_STARTED on first reasoning content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedReasoning += reasoningDelta\n hasStreamedReasoningDeltas = true\n const fallbackStepId = stepId || generateId(this.name)\n yield {\n type: EventType.STEP_FINISHED,\n stepName: fallbackStepId,\n stepId: fallbackStepId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: reasoningDelta,\n content: accumulatedReasoning,\n }\n }\n }\n\n // Handle reasoning summary deltas (when using reasoning.summary option)\n // response.reasoning_summary_text.delta provides incremental summary updates\n if (\n chunk.type === 'response.reasoning_summary_text.delta' &&\n chunk.delta\n ) {\n const summaryDelta =\n typeof chunk.delta === 'string' ? chunk.delta : ''\n\n if (summaryDelta) {\n // Emit STEP_STARTED on first reasoning content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedReasoning += summaryDelta\n hasStreamedReasoningDeltas = true\n const fallbackStepId = stepId || generateId(this.name)\n yield {\n type: EventType.STEP_FINISHED,\n stepName: fallbackStepId,\n stepId: fallbackStepId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: summaryDelta,\n content: accumulatedReasoning,\n }\n }\n }\n\n // handle content_part added events for text, reasoning and refusals\n if (chunk.type === 'response.content_part.added') {\n const contentPart = chunk.part\n // Emit TEXT_MESSAGE_START if this is text content\n if (\n contentPart.type === 'output_text' &&\n !hasEmittedTextMessageStart\n ) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n // Emit STEP_STARTED if this is reasoning content\n if (contentPart.type === 'reasoning_text' && !hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n // Mark whichever stream we just emitted into so a subsequent\n // `content_part.done` doesn't duplicate the same text. Without\n // this flag, an `added` event carrying the full text followed by\n // a matching `done` event would emit TEXT_MESSAGE_CONTENT twice.\n if (contentPart.type === 'output_text') {\n hasStreamedContentDeltas = true\n } else if (contentPart.type === 'reasoning_text') {\n hasStreamedReasoningDeltas = true\n }\n const partChunk = handleContentPart(contentPart)\n yield partChunk\n // handleContentPart returns RUN_ERROR for refusals / unknown\n // content_part types — those are terminal events. Don't keep\n // processing more chunks (and don't let the post-loop synthetic\n // block emit a second terminal event).\n if (partChunk.type === 'RUN_ERROR') {\n runFinishedEmitted = true\n return\n }\n }\n\n if (chunk.type === 'response.content_part.done') {\n const contentPart = chunk.part\n\n // Skip emitting chunks for content parts that we've already streamed via deltas\n // The done event is just a completion marker, not new content\n if (contentPart.type === 'output_text' && hasStreamedContentDeltas) {\n // Content already accumulated from deltas, skip\n continue\n }\n if (\n contentPart.type === 'reasoning_text' &&\n hasStreamedReasoningDeltas\n ) {\n // Reasoning already accumulated from deltas, skip\n continue\n }\n\n // Upstreams that emit `content_part.done` without any preceding\n // deltas (or `content_part.added`) still need a START event before\n // CONTENT — otherwise consumers tracking start/end pairs see content\n // without a start and never see an end. Emit the lifecycle opener\n // for whichever stream this content_part belongs to before yielding\n // the CONTENT chunk; the post-loop block emits the matching END.\n if (\n contentPart.type === 'output_text' &&\n !hasEmittedTextMessageStart\n ) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n } else if (\n contentPart.type === 'reasoning_text' &&\n !hasEmittedStepStarted\n ) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n // Only emit if we haven't been streaming deltas (e.g., for non-streaming responses)\n const doneChunk = handleContentPart(contentPart)\n yield doneChunk\n if (doneChunk.type === 'RUN_ERROR') {\n runFinishedEmitted = true\n return\n }\n }\n\n // handle output_item.added to capture function call metadata (name)\n if (chunk.type === 'response.output_item.added') {\n const item = chunk.item\n if (item.type === 'function_call' && item.id) {\n const existing = toolCallMetadata.get(item.id)\n // Track the item as soon as we see it so subsequent arg deltas\n // aren't logged as orphans, but only emit TOOL_CALL_START when\n // both id AND name are populated. Emitting START with an empty\n // name would propagate into TOOL_CALL_END (which reads the same\n // metadata) and route the tool call to whatever name happens to\n // match `''` downstream — a silent misroute.\n if (!existing) {\n toolCallMetadata.set(item.id, {\n index: chunk.output_index,\n name: item.name || '',\n started: false,\n })\n } else if (!existing.name && item.name) {\n // A later output_item.added for the same id finally carries\n // the name. Update so the gated emission below can fire.\n existing.name = item.name\n }\n const metadata = toolCallMetadata.get(item.id)!\n if (!metadata.started && metadata.name) {\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: item.id,\n toolCallName: metadata.name,\n toolName: metadata.name,\n model: model || options.model,\n timestamp: Date.now(),\n index: chunk.output_index,\n }\n metadata.started = true\n }\n }\n }\n\n // Handle function call arguments delta (streaming). Drop the\n // previously-emitted `args` field — it had inverted polarity\n // (populated only when metadata was MISSING, i.e. when the\n // matching TOOL_CALL_START hadn't fired) and the chat-completions\n // adapter never emitted it, so it leaked partial deltas as\n // pseudo-args only on the orphan path. Consumers should accumulate\n // `delta` themselves.\n //\n // Guard with `metadata?.started`: the matching TOOL_CALL_START fires\n // from `output_item.added`, and emitting TOOL_CALL_ARGS before that\n // would violate the AG-UI lifecycle (ARGS without START). The .done\n // handler below applies the same guard.\n if (\n chunk.type === 'response.function_call_arguments.delta' &&\n chunk.delta\n ) {\n const metadata = toolCallMetadata.get(chunk.item_id)\n if (!metadata?.started) {\n options.logger.errors(\n `${this.name}.processStreamChunks orphan function_call_arguments.delta`,\n {\n source: `${this.name}.processStreamChunks`,\n toolCallId: chunk.item_id,\n rawDelta: chunk.delta,\n },\n )\n continue\n }\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: chunk.item_id,\n model: model || options.model,\n timestamp: Date.now(),\n delta: chunk.delta,\n }\n }\n\n if (chunk.type === 'response.function_call_arguments.done') {\n const { item_id } = chunk\n\n // Get the function name from metadata (captured in output_item.added)\n const metadata = toolCallMetadata.get(item_id)\n // If the matching START was never emitted (the upstream sent an\n // `output_item.added` without a name and no later event has filled\n // it in yet), defer END until `output_item.done` or\n // `response.completed` can backfill the name. We stash the raw\n // arguments so the late emission has them. Emitting END without\n // START would produce an unbalanced AG-UI lifecycle event\n // downstream consumers can't pair.\n if (!metadata?.started) {\n if (metadata) {\n metadata.pendingArguments = chunk.arguments\n }\n options.logger.errors(\n `${this.name}.processStreamChunks deferring function_call_arguments.done — TOOL_CALL_START not yet emitted (waiting for name)`,\n {\n source: `${this.name}.processStreamChunks`,\n toolCallId: item_id,\n rawArguments: chunk.arguments,\n },\n )\n continue\n }\n // The output_item.done backstop may have already emitted END (when\n // it arrived before args.done with a populated item.arguments).\n // Skip so we never produce a duplicate close for the same id.\n if (metadata.ended) continue\n const name = metadata.name || ''\n metadata.ended = true\n\n // Parse arguments. Surface parse failures via the logger so a\n // model emitting malformed JSON is debuggable instead of silently\n // invoking the tool with {}.\n let parsedInput: unknown = {}\n if (chunk.arguments) {\n try {\n const parsed = JSON.parse(chunk.arguments)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${name} (${item_id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: item_id,\n toolName: name,\n rawArguments: chunk.arguments,\n },\n )\n parsedInput = {}\n }\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: item_id,\n toolCallName: name,\n toolName: name,\n model: model || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n\n // `output_item.done` is the last point at which a function_call's\n // name is guaranteed to be on the wire — it carries the fully-formed\n // ResponseFunctionToolCall. Use it as a backstop to recover any\n // tool call whose name was missing from `output_item.added` (and\n // whose START + END therefore never fired).\n if (chunk.type === 'response.output_item.done') {\n const item = chunk.item\n if (item.type === 'function_call' && item.id) {\n const metadata = toolCallMetadata.get(item.id) ?? {\n index: chunk.output_index,\n name: item.name || '',\n started: false,\n }\n if (!toolCallMetadata.has(item.id)) {\n toolCallMetadata.set(item.id, metadata)\n } else if (!metadata.name && item.name) {\n metadata.name = item.name\n }\n // Emit gated START if we now have a name and never started.\n if (!metadata.started && metadata.name) {\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: item.id,\n toolCallName: metadata.name,\n toolName: metadata.name,\n model: model || options.model,\n timestamp: Date.now(),\n index: metadata.index,\n }\n metadata.started = true\n }\n // Emit END if we have args (either from a previously-deferred\n // args.done OR from item.arguments) and haven't already ended.\n const rawArgs =\n typeof item.arguments === 'string' && item.arguments.length > 0\n ? item.arguments\n : metadata.pendingArguments\n if (metadata.started && !metadata.ended && rawArgs !== undefined) {\n const name = metadata.name || ''\n let parsedInput: unknown = {}\n if (rawArgs) {\n try {\n const parsed = JSON.parse(rawArgs)\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed (output_item.done backfill)`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${name} (${item.id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: item.id,\n toolName: name,\n rawArguments: rawArgs,\n },\n )\n parsedInput = {}\n }\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: item.id,\n toolCallName: name,\n toolName: name,\n model: model || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n metadata.ended = true\n metadata.pendingArguments = undefined\n }\n }\n }\n\n if (chunk.type === 'response.completed') {\n // Final backstop for function_call lifecycle: if a function_call\n // appears in `response.output[]` but was never matched by an\n // output_item.added/done with a name, recover the missing START\n // (and END if args were pending). Without this, a tool call could\n // be silently dropped from the AG-UI stream while `hasFunctionCalls`\n // below still routes the run's finishReason to 'tool_calls' —\n // leaving consumers waiting for tool results they never saw start.\n for (const item of chunk.response.output) {\n if (item.type !== 'function_call' || !item.id) continue\n const metadata = toolCallMetadata.get(item.id) ?? {\n index: 0,\n name: item.name || '',\n started: false,\n }\n if (!toolCallMetadata.has(item.id)) {\n toolCallMetadata.set(item.id, metadata)\n } else if (!metadata.name && item.name) {\n metadata.name = item.name\n }\n if (!metadata.started && metadata.name) {\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: item.id,\n toolCallName: metadata.name,\n toolName: metadata.name,\n model: model || options.model,\n timestamp: Date.now(),\n index: metadata.index,\n }\n metadata.started = true\n }\n const rawArgs =\n typeof item.arguments === 'string' && item.arguments.length > 0\n ? item.arguments\n : metadata.pendingArguments\n if (metadata.started && !metadata.ended) {\n const name = metadata.name || ''\n let parsedInput: unknown = {}\n if (rawArgs) {\n try {\n const parsed = JSON.parse(rawArgs)\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed (response.completed backfill)`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${name} (${item.id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: item.id,\n toolName: name,\n rawArguments: rawArgs,\n },\n )\n parsedInput = {}\n }\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: item.id,\n toolCallName: name,\n toolName: name,\n model: model || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n metadata.ended = true\n metadata.pendingArguments = undefined\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n }\n hasEmittedTextMessageStart = false\n }\n\n // Determine finish reason. Function-call output → tool_calls.\n // Otherwise surface incomplete_details.reason when present so\n // callers can distinguish length-limit / content-filter cutoffs\n // from a clean stop, mirroring the chat-completions adapter.\n // The Responses API's incomplete_details.reason ('max_output_tokens'\n // | 'content_filter') maps to the AG-UI finishReason vocabulary:\n // max_output_tokens → 'length', content_filter → 'content_filter'.\n const hasFunctionCalls = chunk.response.output.some(\n (item: unknown) =>\n (item as { type: string }).type === 'function_call',\n )\n const incompleteReason = chunk.response.incomplete_details?.reason\n const finishReason:\n | 'tool_calls'\n | 'length'\n | 'content_filter'\n | 'stop' = hasFunctionCalls\n ? 'tool_calls'\n : incompleteReason === 'max_output_tokens'\n ? 'length'\n : incompleteReason === 'content_filter'\n ? 'content_filter'\n : 'stop'\n\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: model || options.model,\n timestamp: Date.now(),\n usage: {\n promptTokens: chunk.response.usage?.input_tokens || 0,\n completionTokens: chunk.response.usage?.output_tokens || 0,\n totalTokens: chunk.response.usage?.total_tokens || 0,\n },\n finishReason,\n }\n runFinishedEmitted = true\n }\n\n if (chunk.type === 'error') {\n yield {\n type: EventType.RUN_ERROR,\n model: model || options.model,\n timestamp: Date.now(),\n message: chunk.message,\n code: chunk.code ?? undefined,\n error: {\n message: chunk.message,\n code: chunk.code ?? undefined,\n },\n }\n // RUN_ERROR is terminal — don't let the synthetic RUN_FINISHED\n // block fire after a top-level stream error event, and stop\n // processing further chunks so no in-flight lifecycle events\n // (TEXT_MESSAGE_CONTENT, TOOL_CALL_*) leak past the terminal\n // error. Mirrors the `response.failed` / `response.incomplete`\n // branches above which return after their RUN_ERROR emission.\n runFinishedEmitted = true\n return\n }\n }\n\n // Synthetic terminal RUN_FINISHED if the stream ended without a\n // response.completed event (e.g. truncated upstream connection). This\n // mirrors the chat-completions adapter's behavior so consumers always\n // see a terminal event for every started run.\n if (!runFinishedEmitted && aguiState.hasEmittedRunStarted) {\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n }\n }\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: model || options.model,\n timestamp: Date.now(),\n usage: undefined,\n finishReason: toolCallMetadata.size > 0 ? 'tool_calls' : 'stop',\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.processStreamChunks failed`,\n )\n options.logger.errors(`${this.name}.processStreamChunks fatal`, {\n error: errorPayload,\n source: `${this.name}.processStreamChunks`,\n })\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n code: errorPayload.code,\n error: errorPayload,\n }\n }\n }\n\n /**\n * Maps common TextOptions to Responses API request format.\n * Override this in subclasses to add provider-specific options.\n */\n protected mapOptionsToRequest(\n options: TextOptions<TProviderOptions>,\n ): Omit<ResponseCreateParams, 'stream'> {\n const input = this.convertMessagesToInput(options.messages)\n\n const tools = options.tools\n ? convertToolsToResponsesFormat(\n options.tools,\n this.makeStructuredOutputCompatible.bind(this),\n )\n : undefined\n\n const modelOptions = options.modelOptions\n\n // Spread modelOptions first, then explicit top-level options when set.\n // Mirrors the chat-completions base adapter's precedence so callers\n // tuning either backend get identical behaviour. Leaving `modelOptions`\n // last (its previous behavior) silently shadowed the canonical\n // `options.temperature`/`maxTokens` fields, while spreading first\n // without nullish-aware merge would clobber `modelOptions.temperature`\n // with `undefined` whenever the caller didn't set the top-level option.\n return {\n ...modelOptions,\n model: options.model,\n ...(options.temperature !== undefined && {\n temperature: options.temperature,\n }),\n ...(options.maxTokens !== undefined && {\n max_output_tokens: options.maxTokens,\n }),\n ...(options.topP !== undefined && { top_p: options.topP }),\n ...(options.metadata !== undefined && { metadata: options.metadata }),\n ...(options.systemPrompts &&\n options.systemPrompts.length > 0 && {\n instructions: options.systemPrompts.join('\\n'),\n }),\n input,\n // Conditional spread: `tools: undefined` would clobber any\n // modelOptions.tools the caller set above.\n ...(tools && tools.length > 0 && { tools }),\n }\n }\n\n /**\n * Converts ModelMessage[] to Responses API ResponseInput format.\n * Override this in subclasses for provider-specific message format quirks.\n *\n * Key differences from Chat Completions:\n * - Tool results use `function_call_output` type (not `tool` role)\n * - Assistant tool calls are `function_call` objects (not nested in `tool_calls`)\n * - User content uses `input_text`, `input_image`, `input_file` types\n * - System prompts go in `instructions`, not as messages\n */\n protected convertMessagesToInput(\n messages: Array<ModelMessage>,\n ): ResponseInput {\n const result: ResponseInput = []\n\n for (const message of messages) {\n // Handle tool messages - convert to FunctionToolCallOutput\n if (message.role === 'tool') {\n result.push({\n type: 'function_call_output',\n call_id: message.toolCallId || '',\n output:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n })\n continue\n }\n\n // Handle assistant messages\n if (message.role === 'assistant') {\n // If the assistant message has tool calls, add them as FunctionToolCall objects\n // Responses API expects arguments as a string (JSON string)\n if (message.toolCalls && message.toolCalls.length > 0) {\n for (const toolCall of message.toolCalls) {\n // Keep arguments as string for Responses API\n const argumentsString =\n typeof toolCall.function.arguments === 'string'\n ? toolCall.function.arguments\n : JSON.stringify(toolCall.function.arguments)\n\n result.push({\n type: 'function_call',\n call_id: toolCall.id,\n name: toolCall.function.name,\n arguments: argumentsString,\n })\n }\n }\n\n // Add the assistant's text message if there is content\n if (message.content) {\n const contentStr = this.extractTextContent(message.content)\n if (contentStr) {\n result.push({\n type: 'message',\n role: 'assistant',\n content: contentStr,\n })\n }\n }\n\n continue\n }\n\n // Handle user messages (default case) — support multimodal content\n const contentParts = this.normalizeContent(message.content)\n const inputContent: Array<ResponseInputContent> = []\n\n for (const part of contentParts) {\n inputContent.push(this.convertContentPartToInput(part))\n }\n\n if (inputContent.length === 0) {\n // Fail loud rather than silently sending an empty user message —\n // mirrors the chat-completions adapter, where a paid-but-empty\n // request would mask the real intent (caller passed `null` content\n // or a normalize step dropped everything).\n throw new Error(\n `User message for ${this.name} has no content parts. ` +\n `Empty user messages would produce a paid request with no input; ` +\n `provide at least one text/image/audio part or omit the message.`,\n )\n }\n\n result.push({\n type: 'message',\n role: 'user',\n content: inputContent,\n })\n }\n\n return result\n }\n\n /**\n * Converts a ContentPart to Responses API input content item.\n * Handles text, image, and audio content parts.\n * Override this in subclasses for additional content types or provider-specific metadata.\n */\n protected convertContentPartToInput(part: ContentPart): ResponseInputContent {\n switch (part.type) {\n case 'text':\n return {\n type: 'input_text',\n text: part.content,\n }\n case 'image': {\n const imageMetadata = part.metadata as\n | { detail?: 'auto' | 'low' | 'high' }\n | undefined\n if (part.source.type === 'url') {\n return {\n type: 'input_image',\n image_url: part.source.value,\n detail: imageMetadata?.detail || 'auto',\n }\n }\n // For base64 data, construct a data URI using the mimeType from\n // source. Default to a generic octet-stream MIME if the source\n // didn't supply one — letting `undefined` interpolate would produce\n // an invalid URI like \"data:undefined;base64,...\".\n const imageValue = part.source.value\n const imageMime = part.source.mimeType || 'application/octet-stream'\n const imageUrl = imageValue.startsWith('data:')\n ? imageValue\n : `data:${imageMime};base64,${imageValue}`\n return {\n type: 'input_image',\n image_url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n }\n }\n case 'audio': {\n if (part.source.type === 'url') {\n return {\n type: 'input_file',\n file_url: part.source.value,\n }\n }\n // Wrap raw base64 in a data URL — `input_file` rejects bare base64\n // payloads (matches the image branch above which already does this).\n // Default the MIME if missing so we never interpolate `undefined`.\n const audioValue = part.source.value\n const audioMime = part.source.mimeType || 'application/octet-stream'\n const audioFileData = audioValue.startsWith('data:')\n ? audioValue\n : `data:${audioMime};base64,${audioValue}`\n return {\n type: 'input_file',\n file_data: audioFileData,\n }\n }\n\n default:\n throw new Error(`Unsupported content part type: ${part.type}`)\n }\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n protected normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n protected extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // It's an array of ContentPart\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"responses-text.js","sources":["../../../src/adapters/responses-text.ts"],"sourcesContent":["import { EventType } from '@tanstack/ai'\nimport { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { toRunErrorPayload } from '@tanstack/ai/adapter-internals'\nimport { generateId, transformNullsToUndefined } from '@tanstack/ai-utils'\nimport { extractRequestOptions } from '../utils/request-options'\nimport { makeStructuredOutputCompatible } from '../utils/schema-converter'\nimport { convertToolsToResponsesFormat } from './responses-tool-converter'\nimport type OpenAI from 'openai'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type {\n Response,\n ResponseCreateParams,\n ResponseInput,\n ResponseInputContent,\n ResponseStreamEvent,\n} from 'openai/resources/responses/responses'\nimport type {\n ContentPart,\n DefaultMessageMetadataByModality,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\n\n/**\n * Shared implementation of the OpenAI Responses API. Holds the stream-event\n * accumulator + AG-UI lifecycle and calls the OpenAI SDK directly. Subclasses\n * (today: ai-openai) construct an OpenAI client with their provider-specific\n * `baseURL` / headers and pass it in.\n */\nexport abstract class OpenAIBaseResponsesTextAdapter<\n TModel extends string,\n TProviderOptions extends Record<string, unknown> = Record<string, unknown>,\n TInputModalities extends ReadonlyArray<Modality> = ReadonlyArray<Modality>,\n TMessageMetadata extends DefaultMessageMetadataByModality =\n DefaultMessageMetadataByModality,\n TToolCapabilities extends ReadonlyArray<string> = ReadonlyArray<string>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n TMessageMetadata,\n TToolCapabilities\n> {\n readonly kind = 'text' as const\n readonly name: string\n protected client: OpenAI\n\n constructor(model: TModel, name: string, client: OpenAI) {\n super({}, model)\n this.name = name\n this.client = client\n }\n\n async *chatStream(\n options: TextOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n // Track tool call metadata by unique ID\n // Responses API streams tool calls with deltas — first chunk has ID/name,\n // subsequent chunks only have args.\n // We assign our own indices as we encounter unique tool call IDs.\n const toolCallMetadata = new Map<\n string,\n {\n index: number\n name: string\n started: boolean\n // Set once TOOL_CALL_END has been emitted (via args.done or the\n // output_item.done backfill) so the two paths don't double-emit.\n ended?: boolean\n // Set when args.done arrives before TOOL_CALL_START could fire\n // (output_item.added lacked a name). output_item.done picks these\n // up to emit the missing END.\n pendingArguments?: string\n }\n >()\n\n // AG-UI lifecycle tracking\n const aguiState = {\n runId: generateId(this.name),\n threadId: options.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n hasEmittedRunStarted: false,\n }\n\n try {\n // mapOptionsToRequest can throw on caller-side validation failures\n // (empty user content, unsupported parts, webSearchTool() rejection in\n // the OpenRouter override). Keep it inside the try so those failures\n // surface as RUN_ERROR events instead of iterator throws.\n const requestParams = this.mapOptionsToRequest(options)\n options.logger.request(\n `activity=chat provider=${this.name} model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,\n { provider: this.name, model: this.model },\n )\n const response = await this.client.responses.create(\n {\n ...requestParams,\n stream: true,\n },\n extractRequestOptions(options.request),\n )\n\n yield* this.processStreamChunks(\n response,\n toolCallMetadata,\n options,\n aguiState,\n )\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.chatStream failed`,\n )\n\n // Emit RUN_STARTED if not yet emitted\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: options.model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n // Emit AG-UI RUN_ERROR\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n code: errorPayload.code,\n error: errorPayload,\n }\n\n options.logger.errors(`${this.name}.chatStream fatal`, {\n error: errorPayload,\n source: `${this.name}.chatStream`,\n })\n }\n }\n\n /**\n * Generate structured output using the provider's native JSON Schema response format.\n * Uses stream: false to get the complete response in one call.\n *\n * OpenAI-compatible Responses APIs have strict requirements for structured output:\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for all objects\n *\n * The outputSchema is already JSON Schema (converted in the ai layer).\n * We apply provider-specific transformations for structured output compatibility.\n */\n async structuredOutput(\n options: StructuredOutputOptions<TProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapOptionsToRequest(chatOptions)\n\n // Apply provider-specific transformations for structured output compatibility\n const jsonSchema = this.makeStructuredOutputCompatible(\n outputSchema,\n outputSchema.required,\n )\n\n try {\n // Strip streaming-only fields a subclass override of mapOptionsToRequest\n // might have returned (parallel to chat-completions's structuredOutput\n // cleanup) — sending stream_options to a non-streaming call is a 4xx.\n const {\n stream: _stream,\n stream_options: _streamOptions,\n ...cleanParams\n } = requestParams as Record<string, unknown>\n void _stream\n void _streamOptions\n chatOptions.logger.request(\n `activity=structuredOutput provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n const response = await this.client.responses.create(\n {\n ...(cleanParams as Omit<ResponseCreateParams, 'stream'>),\n stream: false,\n // Configure structured output via text.format\n text: {\n format: {\n type: 'json_schema',\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n },\n extractRequestOptions(chatOptions.request),\n )\n\n // Extract text content from the response. `stream: false` narrows the\n // SDK return type to `Response`, but the explicit annotation makes\n // that contract local rather than relying on inference through the\n // overloaded `client.responses.create` signature.\n const rawText = this.extractTextFromResponse(response satisfies Response)\n\n // Fail loud on empty content rather than letting it cascade into a\n // confusing \"Failed to parse JSON. Content: \" error — the root cause\n // (the model returned no text content for the structured request) is\n // then visible in logs. Mirrors the chat-completions sibling.\n if (rawText.length === 0) {\n throw new Error(\n `${this.name}.structuredOutput: response contained no content`,\n )\n }\n\n // Parse the JSON response\n let parsed: unknown\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n\n // Apply the provider-specific post-parse shaping (default: null →\n // undefined to align with the original Zod schema's optional-field\n // semantics; subclasses with different conventions can override\n // `transformStructuredOutput`, mirroring the chat-completions base's\n // hook so OpenRouter and other providers that preserve nulls in\n // structured output can opt out without forking `structuredOutput`).\n const transformed = this.transformStructuredOutput(parsed)\n\n return {\n data: transformed,\n rawText,\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n chatOptions.logger.errors(`${this.name}.structuredOutput fatal`, {\n error: toRunErrorPayload(error, `${this.name}.structuredOutput failed`),\n source: `${this.name}.structuredOutput`,\n })\n throw error\n }\n }\n\n /**\n * Stream structured output via the Responses API: single request with\n * `text.format: json_schema` + `stream: true`. Consumes Responses-API\n * events (`response.output_text.delta`, `response.reasoning_text.delta`,\n * `response.reasoning_summary_text.delta`, `response.refusal.delta`,\n * `response.completed`, `response.failed`) and re-emits the standard AG-UI\n * lifecycle ending with `CUSTOM 'structured-output.complete'`.\n *\n * Tools are stripped (structured output is mutually exclusive with tool\n * calls in this path). Reasoning text is accumulated and surfaced both as\n * REASONING_* lifecycle events during the stream and on the terminal\n * CUSTOM event's `value.reasoning`.\n */\n async *structuredOutputStream(\n options: StructuredOutputOptions<TProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n const { chatOptions, outputSchema } = options\n const requestParams = this.mapOptionsToRequest(chatOptions)\n\n const jsonSchema = this.makeStructuredOutputCompatible(\n outputSchema,\n outputSchema.required,\n )\n\n const timestamp = Date.now()\n const aguiState = {\n runId: generateId(this.name),\n threadId: chatOptions.threadId ?? generateId(this.name),\n messageId: generateId(this.name),\n timestamp,\n hasEmittedRunStarted: false,\n }\n\n let accumulatedContent = ''\n let accumulatedReasoning = ''\n let hasEmittedTextMessageStart = false\n let reasoningMessageId: string | undefined\n let stepId: string | undefined\n let hasClosedReasoning = false\n let model: string = chatOptions.model\n let usage: OpenAI.Responses.Response['usage'] | undefined\n\n const closeReasoning = function* (this: {\n name: string\n }): Generator<StreamChunk> {\n if (reasoningMessageId && !hasClosedReasoning) {\n hasClosedReasoning = true\n yield {\n type: EventType.REASONING_MESSAGE_END,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_END,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n if (stepId) {\n yield {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model,\n timestamp,\n content: accumulatedReasoning,\n }\n }\n }\n }.bind(this)\n\n const openReasoning = function* (this: {\n name: string\n }): Generator<StreamChunk> {\n if (reasoningMessageId) return\n reasoningMessageId = generateId(this.name)\n stepId = generateId(this.name)\n yield {\n type: EventType.REASONING_START,\n messageId: reasoningMessageId,\n model,\n timestamp,\n }\n yield {\n type: EventType.REASONING_MESSAGE_START,\n messageId: reasoningMessageId,\n role: 'reasoning' as const,\n model,\n timestamp,\n }\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model,\n timestamp,\n stepType: 'thinking',\n }\n }.bind(this)\n\n try {\n const { tools: _tools, ...cleanParams } = requestParams\n void _tools\n\n chatOptions.logger.request(\n `activity=structuredOutputStream provider=${this.name} model=${this.model} messages=${chatOptions.messages.length}`,\n { provider: this.name, model: this.model },\n )\n\n const stream = await this.client.responses.create(\n {\n ...cleanParams,\n stream: true,\n text: {\n format: {\n type: 'json_schema',\n name: 'structured_output',\n schema: jsonSchema,\n strict: true,\n },\n },\n },\n extractRequestOptions(chatOptions.request),\n )\n\n for await (const chunk of stream) {\n chatOptions.logger.provider(\n `provider=${this.name} type=${chunk.type}`,\n { provider: this.name, type: chunk.type },\n )\n\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n if (\n chunk.type === 'response.created' ||\n chunk.type === 'response.in_progress'\n ) {\n const responseModel = (chunk as { response?: { model?: string } })\n .response?.model\n if (responseModel) model = responseModel\n continue\n }\n\n if (chunk.type === 'response.refusal.delta') {\n const delta =\n typeof (chunk as { delta?: unknown }).delta === 'string'\n ? (chunk as { delta: string }).delta\n : ''\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message: `Model refused: ${delta}`,\n code: 'refusal',\n error: { message: `Model refused: ${delta}`, code: 'refusal' },\n }\n return\n }\n\n if (\n chunk.type === 'response.reasoning_text.delta' ||\n chunk.type === 'response.reasoning_summary_text.delta'\n ) {\n const raw = (chunk as { delta?: unknown }).delta\n const reasoningDelta = Array.isArray(raw)\n ? raw.join('')\n : typeof raw === 'string'\n ? raw\n : ''\n if (!reasoningDelta) continue\n yield* openReasoning()\n // openReasoning() guarantees reasoningMessageId is set on first call;\n // TS can't see through the generator side-effect.\n const messageId = reasoningMessageId!\n accumulatedReasoning += reasoningDelta\n yield {\n type: EventType.REASONING_MESSAGE_CONTENT,\n messageId,\n delta: reasoningDelta,\n model,\n timestamp,\n }\n continue\n }\n\n if (chunk.type === 'response.output_text.delta') {\n const raw = (chunk as { delta?: unknown }).delta\n const textDelta = Array.isArray(raw)\n ? raw.join('')\n : typeof raw === 'string'\n ? raw\n : ''\n if (!textDelta) continue\n\n yield* closeReasoning()\n\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model,\n timestamp,\n role: 'assistant',\n }\n }\n accumulatedContent += textDelta\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model,\n timestamp,\n delta: textDelta,\n content: accumulatedContent,\n }\n continue\n }\n\n if (chunk.type === 'response.completed') {\n const response = chunk.response\n if (response.usage) usage = response.usage\n if (response.model) model = response.model\n continue\n }\n\n if (chunk.type === 'response.failed') {\n const response = (\n chunk as {\n response?: { error?: { message?: string; code?: string } }\n }\n ).response\n const message =\n response?.error?.message || 'Responses API stream failed'\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message,\n code: response?.error?.code,\n error: { message, code: response?.error?.code },\n }\n return\n }\n }\n\n yield* closeReasoning()\n\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model,\n timestamp,\n }\n }\n\n if (accumulatedContent.length === 0) {\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n error: {\n message: `${this.name}.structuredOutputStream: response contained no content`,\n code: 'empty-response',\n },\n }\n return\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(accumulatedContent)\n } catch {\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message: `Failed to parse structured output as JSON. Content: ${accumulatedContent.slice(0, 200)}${accumulatedContent.length > 200 ? '...' : ''}`,\n code: 'parse-error',\n error: {\n message: 'Failed to parse structured output as JSON',\n code: 'parse-error',\n },\n }\n return\n }\n\n const transformed = transformNullsToUndefined(parsed)\n\n yield {\n type: EventType.CUSTOM,\n name: 'structured-output.complete',\n value: {\n object: transformed,\n raw: accumulatedContent,\n ...(accumulatedReasoning ? { reasoning: accumulatedReasoning } : {}),\n },\n model,\n timestamp,\n }\n\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model,\n timestamp,\n finishReason: 'stop',\n ...(usage && {\n usage: {\n promptTokens: usage.input_tokens,\n completionTokens: usage.output_tokens,\n totalTokens: usage.total_tokens,\n },\n }),\n }\n } catch (error: unknown) {\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model,\n timestamp,\n parentRunId: chatOptions.parentRunId,\n }\n }\n\n const isAbort = this.isAbortError(error)\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.structuredOutputStream failed`,\n )\n\n yield {\n type: EventType.RUN_ERROR,\n runId: aguiState.runId,\n model,\n timestamp,\n message: errorPayload.message,\n code: isAbort ? 'aborted' : errorPayload.code,\n error: { ...errorPayload, ...(isAbort && { code: 'aborted' }) },\n }\n\n chatOptions.logger.errors(`${this.name}.structuredOutputStream fatal`, {\n error: errorPayload,\n source: `${this.name}.structuredOutputStream`,\n })\n }\n }\n\n /**\n * Cross-SDK abort detection for `structuredOutputStream`. Mirrors the\n * Chat Completions base; subclasses with proprietary error types override.\n */\n protected isAbortError(error: unknown): boolean {\n if (!error || typeof error !== 'object') return false\n const e = error as { name?: unknown; code?: unknown }\n return (\n e.name === 'APIUserAbortError' ||\n e.name === 'AbortError' ||\n e.code === 'ERR_CANCELED'\n )\n }\n\n /**\n * Applies provider-specific transformations for structured output compatibility.\n * Override this in subclasses to handle provider-specific quirks.\n */\n protected makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired?: Array<string>,\n ): Record<string, any> {\n return makeStructuredOutputCompatible(schema, originalRequired)\n }\n\n /**\n * Final shaping pass applied to parsed structured-output JSON before it is\n * returned to the caller. Default converts `null` values to `undefined` so\n * the result aligns with the original Zod schema's optional-field\n * semantics. Subclasses with different conventions (OpenRouter historically\n * preserves nulls) can override — mirrors the chat-completions base's hook\n * so a subclass that opts out of null-stripping doesn't have to fork the\n * whole `structuredOutput` method.\n */\n protected transformStructuredOutput(parsed: unknown): unknown {\n return transformNullsToUndefined(parsed)\n }\n\n /**\n * Extract text content from a non-streaming Responses API response.\n * Override this in subclasses for provider-specific response shapes.\n */\n protected extractTextFromResponse(response: Response): string {\n let textContent = ''\n let refusal: string | undefined\n let sawMessageItem = false\n const observedItemTypes = new Set<string>()\n\n for (const item of response.output) {\n observedItemTypes.add(item.type)\n if (item.type === 'message') {\n sawMessageItem = true\n for (const part of item.content) {\n // Cast off the discriminated union before the type discrimination\n // so future SDK variants (e.g. `output_audio`, `output_image`) hit\n // the explicit error path rather than being misreported as refusals\n // when they get added to the union. Mirrors the streaming side's\n // handleContentPart.\n const partType = (part as { type: string }).type\n if (partType === 'output_text') {\n textContent += (part as { text?: string }).text ?? ''\n } else if (partType === 'refusal') {\n const refusalText = (part as { refusal?: string }).refusal\n refusal = refusalText || refusal || 'Refused without explanation'\n } else {\n throw new Error(\n `${this.name}.extractTextFromResponse: unsupported message content part type \"${partType}\"`,\n )\n }\n }\n }\n }\n\n // Surface refusals as an explicit error so callers don't see a generic\n // \"Failed to parse structured output as JSON. Content: \" when the model\n // refused for safety / content-policy reasons.\n if (!textContent && refusal !== undefined) {\n const err = new Error(`Model refused to respond: ${refusal}`)\n ;(err as Error & { code?: string }).code = 'refusal'\n throw err\n }\n\n // Response had items but none carried message text (e.g. only\n // function_call or reasoning items). Surface that explicitly so a\n // downstream structured-output caller doesn't see a misleading\n // \"Failed to parse JSON. Content: \" from an empty string.\n if (!textContent && response.output.length > 0 && !sawMessageItem) {\n throw new Error(\n `${this.name}.extractTextFromResponse: response.output contained items of type(s) [${[...observedItemTypes].sort().join(', ')}] but no message text — the model returned a non-text response`,\n )\n }\n\n return textContent\n }\n\n /**\n * Processes streamed chunks from the Responses API and yields AG-UI events.\n * Override this in subclasses to handle provider-specific stream behavior.\n *\n * Handles the following event types:\n * - response.created / response.incomplete / response.failed\n * - response.output_text.delta\n * - response.reasoning_text.delta\n * - response.reasoning_summary_text.delta\n * - response.content_part.added / response.content_part.done\n * - response.output_item.added\n * - response.function_call_arguments.delta / response.function_call_arguments.done\n * - response.completed\n * - error\n */\n protected async *processStreamChunks(\n stream: AsyncIterable<ResponseStreamEvent>,\n toolCallMetadata: Map<\n string,\n {\n index: number\n name: string\n started: boolean\n ended?: boolean\n pendingArguments?: string\n }\n >,\n options: TextOptions<TProviderOptions>,\n aguiState: {\n runId: string\n threadId: string\n messageId: string\n hasEmittedRunStarted: boolean\n },\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n let accumulatedReasoning = ''\n\n // Track if we've been streaming deltas to avoid duplicating content from done events\n let hasStreamedContentDeltas = false\n let hasStreamedReasoningDeltas = false\n\n // Preserve response metadata across events\n let model: string = options.model\n\n // AG-UI lifecycle tracking\n let stepId: string | null = null\n let hasEmittedTextMessageStart = false\n let hasEmittedStepStarted = false\n // Track whether we've emitted a terminal RUN_FINISHED so the\n // end-of-stream fallback below knows to synthesise one when the upstream\n // cuts off without a response.completed event.\n let runFinishedEmitted = false\n\n try {\n for await (const chunk of stream) {\n options.logger.provider(`provider=${this.name} type=${chunk.type}`, {\n provider: this.name,\n type: chunk.type,\n })\n\n // Emit RUN_STARTED on first chunk\n if (!aguiState.hasEmittedRunStarted) {\n aguiState.hasEmittedRunStarted = true\n yield {\n type: EventType.RUN_STARTED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: model || options.model,\n timestamp: Date.now(),\n parentRunId: options.parentRunId,\n }\n }\n\n const handleContentPart = (contentPart: {\n type: string\n text?: string\n refusal?: string\n }): StreamChunk => {\n if (contentPart.type === 'output_text') {\n accumulatedContent += contentPart.text || ''\n return {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: contentPart.text || '',\n content: accumulatedContent,\n }\n }\n\n if (contentPart.type === 'reasoning_text') {\n accumulatedReasoning += contentPart.text || ''\n // Cache the fallback stepId rather than generating a fresh one\n // on every call — otherwise multiple reasoning chunks arriving\n // before STEP_STARTED was emitted (e.g. via response.content_part.done\n // alone) would each get a different stepId and break correlation.\n if (!stepId) {\n stepId = generateId(this.name)\n }\n return {\n type: EventType.STEP_FINISHED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: contentPart.text || '',\n content: accumulatedReasoning,\n }\n }\n // Either a real refusal or an unknown content_part type. Surface\n // the part type in the error so unknown parts are debuggable\n // instead of being misreported as \"Unknown refusal\".\n const isRefusal = contentPart.type === 'refusal'\n const message = isRefusal\n ? contentPart.refusal || 'Refused without explanation'\n : `Unsupported response content_part type: ${contentPart.type}`\n const code = isRefusal ? 'refusal' : contentPart.type\n return {\n type: EventType.RUN_ERROR,\n model: model || options.model,\n timestamp: Date.now(),\n message,\n code,\n error: { message, code },\n }\n }\n\n // Capture model metadata from any of these events (created starts\n // the run; failed/incomplete signal terminal failure).\n if (\n chunk.type === 'response.created' ||\n chunk.type === 'response.incomplete' ||\n chunk.type === 'response.failed'\n ) {\n model = chunk.response.model\n }\n\n // response.created marks the start of a fresh run — safe to reset\n // the per-run accumulators here.\n if (chunk.type === 'response.created') {\n hasStreamedContentDeltas = false\n hasStreamedReasoningDeltas = false\n hasEmittedTextMessageStart = false\n hasEmittedStepStarted = false\n accumulatedContent = ''\n accumulatedReasoning = ''\n }\n\n // response.failed and response.incomplete are TERMINAL events for\n // the current response. Close any open AG-UI message lifecycle FIRST\n // so consumers tracking start/end pairs don't see an unbalanced\n // TEXT_MESSAGE_START. Then surface the error and mark the run as\n // finished so the post-loop synthetic terminal block doesn't emit\n // a duplicate RUN_FINISHED on top of RUN_ERROR.\n if (\n chunk.type === 'response.failed' ||\n chunk.type === 'response.incomplete'\n ) {\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: chunk.response.model,\n timestamp: Date.now(),\n }\n hasEmittedTextMessageStart = false\n }\n // Coalesce error + incomplete_details into a single RUN_ERROR\n // payload — emitting two distinct events for one terminal upstream\n // event would force consumers to handle a non-existent ordering.\n const errorMessage =\n chunk.response.error?.message ||\n chunk.response.incomplete_details?.reason ||\n (chunk.type === 'response.failed'\n ? 'Response failed'\n : 'Response ended incomplete')\n const errorCode =\n chunk.response.error?.code ??\n (chunk.response.incomplete_details ? 'incomplete' : undefined) ??\n undefined\n // Always emit RUN_ERROR for terminal failure events, even when the\n // upstream omitted both `error` and `incomplete_details`. Skipping\n // emission on a `response.incomplete` with no detail would let the\n // post-loop synthetic block silently coerce the run to a clean\n // `RUN_FINISHED { finishReason: 'stop' }` — masking the failure.\n yield {\n type: EventType.RUN_ERROR,\n model: chunk.response.model,\n timestamp: Date.now(),\n message: errorMessage,\n ...(errorCode !== undefined && { code: errorCode }),\n error: {\n message: errorMessage,\n ...(errorCode !== undefined && { code: errorCode }),\n },\n }\n // RUN_ERROR is the terminal event for this run; stop processing\n // any further chunks the iterator might still deliver.\n runFinishedEmitted = true\n return\n }\n\n // Handle output text deltas (token-by-token streaming)\n // response.output_text.delta provides incremental text updates\n if (chunk.type === 'response.output_text.delta' && chunk.delta) {\n // Delta can be an array of strings or a single string\n const textDelta = Array.isArray(chunk.delta)\n ? chunk.delta.join('')\n : typeof chunk.delta === 'string'\n ? chunk.delta\n : ''\n\n if (textDelta) {\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n\n accumulatedContent += textDelta\n hasStreamedContentDeltas = true\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: textDelta,\n content: accumulatedContent,\n }\n }\n }\n\n // Handle reasoning deltas (token-by-token thinking/reasoning streaming)\n // response.reasoning_text.delta provides incremental reasoning updates\n if (chunk.type === 'response.reasoning_text.delta' && chunk.delta) {\n // Delta can be an array of strings or a single string\n const reasoningDelta = Array.isArray(chunk.delta)\n ? chunk.delta.join('')\n : typeof chunk.delta === 'string'\n ? chunk.delta\n : ''\n\n if (reasoningDelta) {\n // Emit STEP_STARTED on first reasoning content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedReasoning += reasoningDelta\n hasStreamedReasoningDeltas = true\n const fallbackStepId = stepId || generateId(this.name)\n yield {\n type: EventType.STEP_FINISHED,\n stepName: fallbackStepId,\n stepId: fallbackStepId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: reasoningDelta,\n content: accumulatedReasoning,\n }\n }\n }\n\n // Handle reasoning summary deltas (when using reasoning.summary option)\n // response.reasoning_summary_text.delta provides incremental summary updates\n if (\n chunk.type === 'response.reasoning_summary_text.delta' &&\n chunk.delta\n ) {\n const summaryDelta =\n typeof chunk.delta === 'string' ? chunk.delta : ''\n\n if (summaryDelta) {\n // Emit STEP_STARTED on first reasoning content\n if (!hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n accumulatedReasoning += summaryDelta\n hasStreamedReasoningDeltas = true\n const fallbackStepId = stepId || generateId(this.name)\n yield {\n type: EventType.STEP_FINISHED,\n stepName: fallbackStepId,\n stepId: fallbackStepId,\n model: model || options.model,\n timestamp: Date.now(),\n delta: summaryDelta,\n content: accumulatedReasoning,\n }\n }\n }\n\n // handle content_part added events for text, reasoning and refusals\n if (chunk.type === 'response.content_part.added') {\n const contentPart = chunk.part\n // Emit TEXT_MESSAGE_START if this is text content\n if (\n contentPart.type === 'output_text' &&\n !hasEmittedTextMessageStart\n ) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n }\n // Emit STEP_STARTED if this is reasoning content\n if (contentPart.type === 'reasoning_text' && !hasEmittedStepStarted) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n // Mark whichever stream we just emitted into so a subsequent\n // `content_part.done` doesn't duplicate the same text. Without\n // this flag, an `added` event carrying the full text followed by\n // a matching `done` event would emit TEXT_MESSAGE_CONTENT twice.\n if (contentPart.type === 'output_text') {\n hasStreamedContentDeltas = true\n } else if (contentPart.type === 'reasoning_text') {\n hasStreamedReasoningDeltas = true\n }\n const partChunk = handleContentPart(contentPart)\n yield partChunk\n // handleContentPart returns RUN_ERROR for refusals / unknown\n // content_part types — those are terminal events. Don't keep\n // processing more chunks (and don't let the post-loop synthetic\n // block emit a second terminal event).\n if (partChunk.type === 'RUN_ERROR') {\n runFinishedEmitted = true\n return\n }\n }\n\n if (chunk.type === 'response.content_part.done') {\n const contentPart = chunk.part\n\n // Skip emitting chunks for content parts that we've already streamed via deltas\n // The done event is just a completion marker, not new content\n if (contentPart.type === 'output_text' && hasStreamedContentDeltas) {\n // Content already accumulated from deltas, skip\n continue\n }\n if (\n contentPart.type === 'reasoning_text' &&\n hasStreamedReasoningDeltas\n ) {\n // Reasoning already accumulated from deltas, skip\n continue\n }\n\n // Upstreams that emit `content_part.done` without any preceding\n // deltas (or `content_part.added`) still need a START event before\n // CONTENT — otherwise consumers tracking start/end pairs see content\n // without a start and never see an end. Emit the lifecycle opener\n // for whichever stream this content_part belongs to before yielding\n // the CONTENT chunk; the post-loop block emits the matching END.\n if (\n contentPart.type === 'output_text' &&\n !hasEmittedTextMessageStart\n ) {\n hasEmittedTextMessageStart = true\n yield {\n type: EventType.TEXT_MESSAGE_START,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n role: 'assistant',\n }\n } else if (\n contentPart.type === 'reasoning_text' &&\n !hasEmittedStepStarted\n ) {\n hasEmittedStepStarted = true\n stepId = generateId(this.name)\n yield {\n type: EventType.STEP_STARTED,\n stepName: stepId,\n stepId,\n model: model || options.model,\n timestamp: Date.now(),\n stepType: 'thinking',\n }\n }\n\n // Only emit if we haven't been streaming deltas (e.g., for non-streaming responses)\n const doneChunk = handleContentPart(contentPart)\n yield doneChunk\n if (doneChunk.type === 'RUN_ERROR') {\n runFinishedEmitted = true\n return\n }\n }\n\n // handle output_item.added to capture function call metadata (name)\n if (chunk.type === 'response.output_item.added') {\n const item = chunk.item\n if (item.type === 'function_call' && item.id) {\n const existing = toolCallMetadata.get(item.id)\n // Track the item as soon as we see it so subsequent arg deltas\n // aren't logged as orphans, but only emit TOOL_CALL_START when\n // both id AND name are populated. Emitting START with an empty\n // name would propagate into TOOL_CALL_END (which reads the same\n // metadata) and route the tool call to whatever name happens to\n // match `''` downstream — a silent misroute.\n if (!existing) {\n toolCallMetadata.set(item.id, {\n index: chunk.output_index,\n name: item.name || '',\n started: false,\n })\n } else if (!existing.name && item.name) {\n // A later output_item.added for the same id finally carries\n // the name. Update so the gated emission below can fire.\n existing.name = item.name\n }\n const metadata = toolCallMetadata.get(item.id)!\n if (!metadata.started && metadata.name) {\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: item.id,\n toolCallName: metadata.name,\n toolName: metadata.name,\n model: model || options.model,\n timestamp: Date.now(),\n index: chunk.output_index,\n }\n metadata.started = true\n }\n }\n }\n\n // Handle function call arguments delta (streaming). Drop the\n // previously-emitted `args` field — it had inverted polarity\n // (populated only when metadata was MISSING, i.e. when the\n // matching TOOL_CALL_START hadn't fired) and the chat-completions\n // adapter never emitted it, so it leaked partial deltas as\n // pseudo-args only on the orphan path. Consumers should accumulate\n // `delta` themselves.\n //\n // Guard with `metadata?.started`: the matching TOOL_CALL_START fires\n // from `output_item.added`, and emitting TOOL_CALL_ARGS before that\n // would violate the AG-UI lifecycle (ARGS without START). The .done\n // handler below applies the same guard.\n if (\n chunk.type === 'response.function_call_arguments.delta' &&\n chunk.delta\n ) {\n const metadata = toolCallMetadata.get(chunk.item_id)\n if (!metadata?.started) {\n options.logger.errors(\n `${this.name}.processStreamChunks orphan function_call_arguments.delta`,\n {\n source: `${this.name}.processStreamChunks`,\n toolCallId: chunk.item_id,\n rawDelta: chunk.delta,\n },\n )\n continue\n }\n yield {\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: chunk.item_id,\n model: model || options.model,\n timestamp: Date.now(),\n delta: chunk.delta,\n }\n }\n\n if (chunk.type === 'response.function_call_arguments.done') {\n const { item_id } = chunk\n\n // Get the function name from metadata (captured in output_item.added)\n const metadata = toolCallMetadata.get(item_id)\n // If the matching START was never emitted (the upstream sent an\n // `output_item.added` without a name and no later event has filled\n // it in yet), defer END until `output_item.done` or\n // `response.completed` can backfill the name. We stash the raw\n // arguments so the late emission has them. Emitting END without\n // START would produce an unbalanced AG-UI lifecycle event\n // downstream consumers can't pair.\n if (!metadata?.started) {\n if (metadata) {\n metadata.pendingArguments = chunk.arguments\n }\n options.logger.errors(\n `${this.name}.processStreamChunks deferring function_call_arguments.done — TOOL_CALL_START not yet emitted (waiting for name)`,\n {\n source: `${this.name}.processStreamChunks`,\n toolCallId: item_id,\n rawArguments: chunk.arguments,\n },\n )\n continue\n }\n // The output_item.done backstop may have already emitted END (when\n // it arrived before args.done with a populated item.arguments).\n // Skip so we never produce a duplicate close for the same id.\n if (metadata.ended) continue\n const name = metadata.name || ''\n metadata.ended = true\n\n // Parse arguments. Surface parse failures via the logger so a\n // model emitting malformed JSON is debuggable instead of silently\n // invoking the tool with {}.\n let parsedInput: unknown = {}\n if (chunk.arguments) {\n try {\n const parsed = JSON.parse(chunk.arguments)\n parsedInput = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${name} (${item_id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: item_id,\n toolName: name,\n rawArguments: chunk.arguments,\n },\n )\n parsedInput = {}\n }\n }\n\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: item_id,\n toolCallName: name,\n toolName: name,\n model: model || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n }\n\n // `output_item.done` is the last point at which a function_call's\n // name is guaranteed to be on the wire — it carries the fully-formed\n // ResponseFunctionToolCall. Use it as a backstop to recover any\n // tool call whose name was missing from `output_item.added` (and\n // whose START + END therefore never fired).\n if (chunk.type === 'response.output_item.done') {\n const item = chunk.item\n if (item.type === 'function_call' && item.id) {\n const metadata = toolCallMetadata.get(item.id) ?? {\n index: chunk.output_index,\n name: item.name || '',\n started: false,\n }\n if (!toolCallMetadata.has(item.id)) {\n toolCallMetadata.set(item.id, metadata)\n } else if (!metadata.name && item.name) {\n metadata.name = item.name\n }\n // Emit gated START if we now have a name and never started.\n if (!metadata.started && metadata.name) {\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: item.id,\n toolCallName: metadata.name,\n toolName: metadata.name,\n model: model || options.model,\n timestamp: Date.now(),\n index: metadata.index,\n }\n metadata.started = true\n }\n // Emit END if we have args (either from a previously-deferred\n // args.done OR from item.arguments) and haven't already ended.\n const rawArgs =\n typeof item.arguments === 'string' && item.arguments.length > 0\n ? item.arguments\n : metadata.pendingArguments\n if (metadata.started && !metadata.ended && rawArgs !== undefined) {\n const name = metadata.name || ''\n let parsedInput: unknown = {}\n if (rawArgs) {\n try {\n const parsed = JSON.parse(rawArgs)\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed (output_item.done backfill)`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${name} (${item.id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: item.id,\n toolName: name,\n rawArguments: rawArgs,\n },\n )\n parsedInput = {}\n }\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: item.id,\n toolCallName: name,\n toolName: name,\n model: model || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n metadata.ended = true\n metadata.pendingArguments = undefined\n }\n }\n }\n\n if (chunk.type === 'response.completed') {\n // Final backstop for function_call lifecycle: if a function_call\n // appears in `response.output[]` but was never matched by an\n // output_item.added/done with a name, recover the missing START\n // (and END if args were pending). Without this, a tool call could\n // be silently dropped from the AG-UI stream while `hasFunctionCalls`\n // below still routes the run's finishReason to 'tool_calls' —\n // leaving consumers waiting for tool results they never saw start.\n for (const item of chunk.response.output) {\n if (item.type !== 'function_call' || !item.id) continue\n const metadata = toolCallMetadata.get(item.id) ?? {\n index: 0,\n name: item.name || '',\n started: false,\n }\n if (!toolCallMetadata.has(item.id)) {\n toolCallMetadata.set(item.id, metadata)\n } else if (!metadata.name && item.name) {\n metadata.name = item.name\n }\n if (!metadata.started && metadata.name) {\n yield {\n type: EventType.TOOL_CALL_START,\n toolCallId: item.id,\n toolCallName: metadata.name,\n toolName: metadata.name,\n model: model || options.model,\n timestamp: Date.now(),\n index: metadata.index,\n }\n metadata.started = true\n }\n const rawArgs =\n typeof item.arguments === 'string' && item.arguments.length > 0\n ? item.arguments\n : metadata.pendingArguments\n if (metadata.started && !metadata.ended) {\n const name = metadata.name || ''\n let parsedInput: unknown = {}\n if (rawArgs) {\n try {\n const parsed = JSON.parse(rawArgs)\n parsedInput =\n parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n options.logger.errors(\n `${this.name}.processStreamChunks tool-args JSON parse failed (response.completed backfill)`,\n {\n error: toRunErrorPayload(\n parseError,\n `tool ${name} (${item.id}) returned malformed JSON arguments`,\n ),\n source: `${this.name}.processStreamChunks`,\n toolCallId: item.id,\n toolName: name,\n rawArguments: rawArgs,\n },\n )\n parsedInput = {}\n }\n }\n yield {\n type: EventType.TOOL_CALL_END,\n toolCallId: item.id,\n toolCallName: name,\n toolName: name,\n model: model || options.model,\n timestamp: Date.now(),\n input: parsedInput,\n }\n metadata.ended = true\n metadata.pendingArguments = undefined\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n }\n hasEmittedTextMessageStart = false\n }\n\n // Determine finish reason. Function-call output → tool_calls.\n // Otherwise surface incomplete_details.reason when present so\n // callers can distinguish length-limit / content-filter cutoffs\n // from a clean stop, mirroring the chat-completions adapter.\n // The Responses API's incomplete_details.reason ('max_output_tokens'\n // | 'content_filter') maps to the AG-UI finishReason vocabulary:\n // max_output_tokens → 'length', content_filter → 'content_filter'.\n const hasFunctionCalls = chunk.response.output.some(\n (item: unknown) =>\n (item as { type: string }).type === 'function_call',\n )\n const incompleteReason = chunk.response.incomplete_details?.reason\n const finishReason:\n | 'tool_calls'\n | 'length'\n | 'content_filter'\n | 'stop' = hasFunctionCalls\n ? 'tool_calls'\n : incompleteReason === 'max_output_tokens'\n ? 'length'\n : incompleteReason === 'content_filter'\n ? 'content_filter'\n : 'stop'\n\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: model || options.model,\n timestamp: Date.now(),\n usage: {\n promptTokens: chunk.response.usage?.input_tokens || 0,\n completionTokens: chunk.response.usage?.output_tokens || 0,\n totalTokens: chunk.response.usage?.total_tokens || 0,\n },\n finishReason,\n }\n runFinishedEmitted = true\n }\n\n if (chunk.type === 'error') {\n yield {\n type: EventType.RUN_ERROR,\n model: model || options.model,\n timestamp: Date.now(),\n message: chunk.message,\n code: chunk.code ?? undefined,\n error: {\n message: chunk.message,\n code: chunk.code ?? undefined,\n },\n }\n // RUN_ERROR is terminal — don't let the synthetic RUN_FINISHED\n // block fire after a top-level stream error event, and stop\n // processing further chunks so no in-flight lifecycle events\n // (TEXT_MESSAGE_CONTENT, TOOL_CALL_*) leak past the terminal\n // error. Mirrors the `response.failed` / `response.incomplete`\n // branches above which return after their RUN_ERROR emission.\n runFinishedEmitted = true\n return\n }\n }\n\n // Synthetic terminal RUN_FINISHED if the stream ended without a\n // response.completed event (e.g. truncated upstream connection). This\n // mirrors the chat-completions adapter's behavior so consumers always\n // see a terminal event for every started run.\n if (!runFinishedEmitted && aguiState.hasEmittedRunStarted) {\n if (hasEmittedTextMessageStart) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n messageId: aguiState.messageId,\n model: model || options.model,\n timestamp: Date.now(),\n }\n }\n yield {\n type: EventType.RUN_FINISHED,\n runId: aguiState.runId,\n threadId: aguiState.threadId,\n model: model || options.model,\n timestamp: Date.now(),\n usage: undefined,\n finishReason: toolCallMetadata.size > 0 ? 'tool_calls' : 'stop',\n }\n }\n } catch (error: unknown) {\n // Narrow before logging: raw SDK errors can carry request metadata\n // (including auth headers) which we must never surface to user loggers.\n const errorPayload = toRunErrorPayload(\n error,\n `${this.name}.processStreamChunks failed`,\n )\n options.logger.errors(`${this.name}.processStreamChunks fatal`, {\n error: errorPayload,\n source: `${this.name}.processStreamChunks`,\n })\n yield {\n type: EventType.RUN_ERROR,\n model: options.model,\n timestamp: Date.now(),\n message: errorPayload.message,\n code: errorPayload.code,\n error: errorPayload,\n }\n }\n }\n\n /**\n * Maps common TextOptions to Responses API request format.\n * Override this in subclasses to add provider-specific options.\n */\n protected mapOptionsToRequest(\n options: TextOptions<TProviderOptions>,\n ): Omit<ResponseCreateParams, 'stream'> {\n const input = this.convertMessagesToInput(options.messages)\n\n const tools = options.tools\n ? convertToolsToResponsesFormat(\n options.tools,\n this.makeStructuredOutputCompatible.bind(this),\n )\n : undefined\n\n const modelOptions = options.modelOptions\n\n // Spread modelOptions first, then explicit top-level options when set.\n // Mirrors the chat-completions base adapter's precedence so callers\n // tuning either backend get identical behaviour. Leaving `modelOptions`\n // last (its previous behavior) silently shadowed the canonical\n // `options.temperature`/`maxTokens` fields, while spreading first\n // without nullish-aware merge would clobber `modelOptions.temperature`\n // with `undefined` whenever the caller didn't set the top-level option.\n return {\n ...modelOptions,\n model: options.model,\n ...(options.temperature !== undefined && {\n temperature: options.temperature,\n }),\n ...(options.maxTokens !== undefined && {\n max_output_tokens: options.maxTokens,\n }),\n ...(options.topP !== undefined && { top_p: options.topP }),\n ...(options.metadata !== undefined && { metadata: options.metadata }),\n ...(options.systemPrompts &&\n options.systemPrompts.length > 0 && {\n instructions: options.systemPrompts.join('\\n'),\n }),\n input,\n // Conditional spread: `tools: undefined` would clobber any\n // modelOptions.tools the caller set above.\n ...(tools && tools.length > 0 && { tools }),\n }\n }\n\n /**\n * Converts ModelMessage[] to Responses API ResponseInput format.\n * Override this in subclasses for provider-specific message format quirks.\n *\n * Key differences from Chat Completions:\n * - Tool results use `function_call_output` type (not `tool` role)\n * - Assistant tool calls are `function_call` objects (not nested in `tool_calls`)\n * - User content uses `input_text`, `input_image`, `input_file` types\n * - System prompts go in `instructions`, not as messages\n */\n protected convertMessagesToInput(\n messages: Array<ModelMessage>,\n ): ResponseInput {\n const result: ResponseInput = []\n\n for (const message of messages) {\n // Handle tool messages - convert to FunctionToolCallOutput\n if (message.role === 'tool') {\n result.push({\n type: 'function_call_output',\n call_id: message.toolCallId || '',\n output:\n typeof message.content === 'string'\n ? message.content\n : JSON.stringify(message.content),\n })\n continue\n }\n\n // Handle assistant messages\n if (message.role === 'assistant') {\n // If the assistant message has tool calls, add them as FunctionToolCall objects\n // Responses API expects arguments as a string (JSON string)\n if (message.toolCalls && message.toolCalls.length > 0) {\n for (const toolCall of message.toolCalls) {\n // Keep arguments as string for Responses API\n const argumentsString =\n typeof toolCall.function.arguments === 'string'\n ? toolCall.function.arguments\n : JSON.stringify(toolCall.function.arguments)\n\n result.push({\n type: 'function_call',\n call_id: toolCall.id,\n name: toolCall.function.name,\n arguments: argumentsString,\n })\n }\n }\n\n // Add the assistant's text message if there is content\n if (message.content) {\n const contentStr = this.extractTextContent(message.content)\n if (contentStr) {\n result.push({\n type: 'message',\n role: 'assistant',\n content: contentStr,\n })\n }\n }\n\n continue\n }\n\n // Handle user messages (default case) — support multimodal content\n const contentParts = this.normalizeContent(message.content)\n const inputContent: Array<ResponseInputContent> = []\n\n for (const part of contentParts) {\n inputContent.push(this.convertContentPartToInput(part))\n }\n\n if (inputContent.length === 0) {\n // Fail loud rather than silently sending an empty user message —\n // mirrors the chat-completions adapter, where a paid-but-empty\n // request would mask the real intent (caller passed `null` content\n // or a normalize step dropped everything).\n throw new Error(\n `User message for ${this.name} has no content parts. ` +\n `Empty user messages would produce a paid request with no input; ` +\n `provide at least one text/image/audio part or omit the message.`,\n )\n }\n\n result.push({\n type: 'message',\n role: 'user',\n content: inputContent,\n })\n }\n\n return result\n }\n\n /**\n * Converts a ContentPart to Responses API input content item.\n * Handles text, image, and audio content parts.\n * Override this in subclasses for additional content types or provider-specific metadata.\n */\n protected convertContentPartToInput(part: ContentPart): ResponseInputContent {\n switch (part.type) {\n case 'text':\n return {\n type: 'input_text',\n text: part.content,\n }\n case 'image': {\n const imageMetadata = part.metadata as\n | { detail?: 'auto' | 'low' | 'high' }\n | undefined\n if (part.source.type === 'url') {\n return {\n type: 'input_image',\n image_url: part.source.value,\n detail: imageMetadata?.detail || 'auto',\n }\n }\n // For base64 data, construct a data URI using the mimeType from\n // source. Default to a generic octet-stream MIME if the source\n // didn't supply one — letting `undefined` interpolate would produce\n // an invalid URI like \"data:undefined;base64,...\".\n const imageValue = part.source.value\n const imageMime = part.source.mimeType || 'application/octet-stream'\n const imageUrl = imageValue.startsWith('data:')\n ? imageValue\n : `data:${imageMime};base64,${imageValue}`\n return {\n type: 'input_image',\n image_url: imageUrl,\n detail: imageMetadata?.detail || 'auto',\n }\n }\n case 'audio': {\n if (part.source.type === 'url') {\n return {\n type: 'input_file',\n file_url: part.source.value,\n }\n }\n // Wrap raw base64 in a data URL — `input_file` rejects bare base64\n // payloads (matches the image branch above which already does this).\n // Default the MIME if missing so we never interpolate `undefined`.\n const audioValue = part.source.value\n const audioMime = part.source.mimeType || 'application/octet-stream'\n const audioFileData = audioValue.startsWith('data:')\n ? audioValue\n : `data:${audioMime};base64,${audioValue}`\n return {\n type: 'input_file',\n file_data: audioFileData,\n }\n }\n\n default:\n throw new Error(`Unsupported content part type: ${part.type}`)\n }\n }\n\n /**\n * Normalizes message content to an array of ContentPart.\n * Handles backward compatibility with string content.\n */\n protected normalizeContent(\n content: string | null | Array<ContentPart>,\n ): Array<ContentPart> {\n if (content === null) {\n return []\n }\n if (typeof content === 'string') {\n return [{ type: 'text', content: content }]\n }\n return content\n }\n\n /**\n * Extracts text content from a content value that may be string, null, or ContentPart array.\n */\n protected extractTextContent(\n content: string | null | Array<ContentPart>,\n ): string {\n if (content === null) {\n return ''\n }\n if (typeof content === 'string') {\n return content\n }\n // It's an array of ContentPart\n return content\n .filter((p) => p.type === 'text')\n .map((p) => p.content)\n .join('')\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/openai-base",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Shared base adapters for OpenAI-SDK-backed providers in TanStack AI (Chat Completions and Responses)",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -33,13 +33,13 @@
33
33
  "@tanstack/ai-utils": "0.2.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "@tanstack/ai": "^0.17.0"
36
+ "@tanstack/ai": "^0.18.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@vitest/coverage-v8": "4.0.14",
40
40
  "vite": "^7.2.7",
41
41
  "zod": "^4.2.0",
42
- "@tanstack/ai": "0.17.0"
42
+ "@tanstack/ai": "0.18.0"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "vite build",
@@ -106,6 +106,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
106
106
  threadId: aguiState.threadId,
107
107
  model: options.model,
108
108
  timestamp: Date.now(),
109
+ parentRunId: options.parentRunId,
109
110
  }
110
111
  }
111
112
 
@@ -347,6 +348,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
347
348
  threadId: aguiState.threadId,
348
349
  model: chunk.model || chatOptions.model,
349
350
  timestamp,
351
+ parentRunId: chatOptions.parentRunId,
350
352
  }
351
353
  }
352
354
 
@@ -506,6 +508,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
506
508
  threadId: aguiState.threadId,
507
509
  model: chatOptions.model,
508
510
  timestamp,
511
+ parentRunId: chatOptions.parentRunId,
509
512
  }
510
513
  }
511
514
 
@@ -669,6 +672,7 @@ export abstract class OpenAIBaseChatCompletionsTextAdapter<
669
672
  threadId: aguiState.threadId,
670
673
  model: chunk.model || options.model,
671
674
  timestamp: Date.now(),
675
+ parentRunId: options.parentRunId,
672
676
  }
673
677
  }
674
678
 
@@ -128,6 +128,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
128
128
  threadId: aguiState.threadId,
129
129
  model: options.model,
130
130
  timestamp: Date.now(),
131
+ parentRunId: options.parentRunId,
131
132
  }
132
133
  }
133
134
 
@@ -393,6 +394,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
393
394
  threadId: aguiState.threadId,
394
395
  model,
395
396
  timestamp,
397
+ parentRunId: chatOptions.parentRunId,
396
398
  }
397
399
  }
398
400
 
@@ -594,6 +596,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
594
596
  threadId: aguiState.threadId,
595
597
  model,
596
598
  timestamp,
599
+ parentRunId: chatOptions.parentRunId,
597
600
  }
598
601
  }
599
602
 
@@ -785,6 +788,7 @@ export abstract class OpenAIBaseResponsesTextAdapter<
785
788
  threadId: aguiState.threadId,
786
789
  model: model || options.model,
787
790
  timestamp: Date.now(),
791
+ parentRunId: options.parentRunId,
788
792
  }
789
793
  }
790
794