/** * Cross-environment executor adapter for the OpenAI Responses API. * * @module @nhtio/adk/batteries/llm/openai_responses/adapter * * @remarks * Cross-environment LLM adapter for the OpenAI Responses wire shape — a flat `input: Item[]` array * (not `messages[]`), where a tool call and its result are two SIBLING top-level items * (`function_call` / `function_call_output`), and the system prompt defaults to a top-level * `instructions` string rather than a leading message item. * * The adapter is built around the same three pluggable layers as `openai_chat_completions`: * * 1. **Translation helpers** — the swappable functions exported from `./helpers` turn ADK * primitives into Responses wire shapes. Consumers override individual helpers via * `options.helpers.*`. * 2. **Three-layer options merging** — constructor baseline, per-`executor()` overrides, and * per-iteration `ctx.stash.openaiResponses` overrides combine with key-by-key precedence for * `headers`/`helpers`/`retry` and wholesale replacement for everything else. The merged shape * is re-validated on every iteration. * 3. **Cross-env transport** — hand-rolled `fetch` + SSE parsing, mirroring * `openai_chat_completions/adapter.ts`'s transport exactly. No `openai` SDK dependency. * * This adapter is STATELESS by design: `store: false` is always sent (never a settable option), * and the full `input` array is resent every iteration. There is no `[DONE]` sentinel on the * Responses SSE stream — termination is `response.completed` / `.incomplete` / `.failed`; EOF * without one of these is a best-effort recovery, not a hard failure: it warn-logs and drains * whatever was accumulated rather than nacking with {@link E_OPENAI_RESPONSES_STREAM_ERROR} (that * exception is reserved for a genuine stream-level error, e.g. the connection itself failing). * * Recoverable-failure handling: a 400 whose body matches `invalid_encrypted_content` drops every * reasoning item from the resolved input and retries the request once — a documented real-world * failure mode when a persisted reasoning `Thought` outlives a server-side key rotation. A 400 * matching the reasoning/output-item pairing violation phrases translates to * {@link E_OPENAI_RESPONSES_REASONING_REPLAY_REJECTED} instead of a generic HTTP error. * * `background` is NOT supported: this executor has no polling/resumption loop for a * `queued`/`in_progress` background response (the Responses API's async mode; see * https://platform.openai.com/docs/guides/background), so `background: true` is rejected up front * by the options schema (`E_INVALID_OPENAI_RESPONSES_OPTIONS`, see `validation.ts`) rather than * reaching this executor at all — accepting it here would otherwise fall straight into the * ordinary streaming/non-streaming response handling below and treat the initial * `queued`/`in_progress` response body as a complete, empty answer, silently discarding whatever * the background job eventually produces. */ import type { DispatchExecutorFn } from "../../../dispatch_runner"; import type { OpenAIResponsesAdapterOptions } from "./types"; /** * Opinionated cross-environment LLM adapter for the OpenAI Responses wire shape. * * @remarks * Construction validates options eagerly via {@link validateOptions} and throws * {@link E_INVALID_OPENAI_RESPONSES_OPTIONS} on failure. The returned instance is reusable: call * {@link OpenAIResponsesAdapter.executor} once per `DispatchRunner` configuration. * * Per-iteration overrides live on the active `DispatchContext`'s `stash.openaiResponses` slot and * take highest precedence — they merge into the executor-scope shape on every iteration. * `headers`, `helpers`, and `retry` merge key-by-key across all three layers; every other field is * replaced wholesale at the highest layer that sets it. */ export declare class OpenAIResponsesAdapter { #private; /** * Customary key for per-iteration overrides on `ctx.stash`. The adapter reads * `ctx.stash.get(OpenAIResponsesAdapter.STASH_KEY, {})` at the start of every iteration and * merges the value into the resolved options shape. */ static readonly STASH_KEY: "openaiResponses"; /** * @param options - Constructor-baseline options. Re-validated on every iteration after * per-dispatch and per-iteration overrides are layered in. * @throws {@link E_INVALID_OPENAI_RESPONSES_OPTIONS} when `options` does not satisfy * `openAIResponsesOptionsSchema`. */ constructor(options: unknown); /** * Returns a `DispatchExecutorFn` bound to this adapter's baseline plus optional executor-scope * overrides. The returned function is reusable across iterations — every iteration re-merges * with `ctx.stash[STASH_KEY]` and re-validates the result. * * @param overrides - Optional executor-scope overrides. Higher precedence than the baseline, * lower precedence than `ctx.stash[STASH_KEY]`. * @returns A `DispatchExecutorFn` suitable for `DispatchRunner`. */ executor(overrides?: Partial): DispatchExecutorFn; /** * Returns `true` when `value` is an {@link OpenAIResponsesAdapter} instance. * * @param value - The value to test. * @returns `true` when `value` is an `OpenAIResponsesAdapter` instance. */ static isOpenAIResponsesAdapter(value: unknown): value is OpenAIResponsesAdapter; }