/** * OpenAI Responses API <-> Chat Completions translation. * * Some clients speak only the Responses API (`POST /v1/responses`) - the Codex * CLI is the motivating one. Databricks now also exposes native Responses * surfaces (`/serving-endpoints/responses` and `/serving-endpoints/open-responses`), * while most endpoints still speak Chat Completions at `/invocations`. This * module bridges the two, in both directions: * * - {@link responsesToChat} lowers a Responses request body to a chat * completions body (instructions -> system message, typed `input` items -> * `messages`, `tools`/`tool_choice` carried through, function-call outputs * folded back into the transcript). * - {@link chatToResponsesRequest} raises a chat-completions request into a * Responses request (the inverse of {@link responsesToChat}), for routing a * chat client at a Responses-only model like Codex. * - {@link chatToResponse} lifts a non-streaming chat completion back into a * Responses `response` object. * - {@link responseToChatCompletion} lifts a native Responses `response` * back into a chat-completions body (the inverse of {@link chatToResponse}). * - {@link createResponsesStreamTranslator} lifts a streaming chat completion * (an OpenAI SSE `chat.completion.chunk` stream) into the Responses SSE * event stream those clients consume (`response.created`, * `response.output_text.delta`, function-call argument deltas, * `response.completed`). * - {@link readResponsesOutput} reads the other direction: pull the answer * text and citations out of a `response` object returned by an endpoint * that speaks Responses natively (the Databricks native web-search tool). * - {@link sanitizeResponsesTools} keeps only `function` tools on a Responses * request body (for `/open-responses` / Anthropic, which reject Codex * built-ins like `web_search`). * - {@link sanitizeOpenResponsesInput} rewrites `output_*` content part types * in `input` to `input_*`, and drops Claude thinking / reasoning blocks * that Open Responses rejects on replay (`redacted_thinking`, …). * - {@link repairTrailingAssistantInput} drops a trailing assistant turn that * Anthropic reads as a prefill request, the Responses-side counterpart to * `@dbx-tools/appkit-mastra`'s `repairAssistantPrefill`. * * Only the surface real clients exercise is translated; unknown fields are * ignored rather than rejected, so a newer client degrades instead of breaking. * * Browser-safe: pure functions over plain JSON, no transport and no Node * built-ins, so the same translation runs in a proxy, a server route, or a test. * * @module */ /** * Lower a Responses request body to a chat-completions body. Returns the chat * body plus whether the caller asked for streaming (the server needs it to pick * the upstream `accept` header and the translation path). */ export declare function responsesToChat(body: Record): { chat: Record; stream: boolean; }; /** * Raise a chat-completions request into a Responses request body. Inverse of * {@link responsesToChat}: used when a chat client hits a Responses-only * Databricks model (e.g. Codex) so the proxy can POST to * `/serving-endpoints/responses` without the client knowing. */ export declare function chatToResponsesRequest(body: Record): { responses: Record; stream: boolean; }; /** Lift a non-streaming chat completion JSON into a Responses `response` object. */ export declare function chatToResponse(chat: Record, model: string): unknown; /** * Lift a native Responses `response` object into a chat-completions body. * Inverse of {@link chatToResponse}: used after a Responses-only upstream call * when the client spoke Chat Completions. */ export declare function responseToChatCompletion(response: Record, model: string): Record; /** * Incremental chat-chunk -> Responses-SSE translator. `feed` and `finish` both * return the SSE bytes to forward, or `""` when a chunk produced no event. */ export interface ResponsesStreamTranslator { /** Translate one upstream `chat.completion.chunk` object. */ feed(chunk: Record): string; /** Close any open items and emit `response.completed`. Call once, at end of stream. */ finish(): string; } /** * Translate an upstream chat-completions SSE stream into the Responses SSE * event stream a Responses client consumes. * * Upstream chunks are `chat.completion.chunk` objects whose `choices[0].delta` * carries incremental `content` (assistant text) and/or `tool_calls` (function * calls, streamed by `index` with partial `arguments`). We emit the Responses * lifecycle around them: * * response.created * -> per text run: output_item.added → output_text.delta* → output_item.done * -> per tool call: output_item.added → function_call_arguments.delta* * → function_call_arguments.done → output_item.done * response.completed (with the assembled final `response` object) * * The translator is intentionally tolerant: malformed/keepalive lines yield * nothing. */ export declare function createResponsesStreamTranslator(model: string, responseId: string): ResponsesStreamTranslator; /** A source the model cited, as carried by a Responses content-part annotation. */ export interface ResponsesCitation { url: string; title?: string; } /** What {@link readResponsesOutput} pulls out of a `response` object. */ export interface ResponsesOutput { /** The assistant's answer, with the output items concatenated. */ text: string; /** Cited sources in first-seen order, deduplicated by URL. */ citations: ResponsesCitation[]; } /** * Read the answer text and cited sources out of a Responses `response` payload. * * This is the inverse of {@link chatToResponse}: it consumes what an endpoint * speaking Responses natively returns, rather than producing it. The Databricks * native web-search tool answers this way, with each source attached as a * `url_citation` annotation on the content part that used it. * * Prefers the flattened `output_text` when the payload carries it, and falls * back to walking `output[].content[].text`. Every field is optional upstream, * so a payload missing any of it yields empty results rather than throwing. */ export declare function readResponsesOutput(payload: Record): ResponsesOutput; /** * Strip tool types the upstream model doesn't support from a Responses request * body, returning a shallow copy safe to forward (the input is not mutated). * * Clients like the Codex CLI include built-in Responses tools (`web_search`, * `local_shell`, `custom`, ...) alongside their `function` tools. Databricks' * Open Responses surface for Anthropic/Claude (and other non-OpenAI providers) * only accepts `function` tools and hard-errors otherwise: * "Anthropic does not support tool type 'web_search'. Only 'function' is supported." * Call this before forwarding to `/serving-endpoints/open-responses`. The OpenAI * `/serving-endpoints/responses` path should keep built-ins (GPT supports them). * If filtering empties the list, `tools` / `tool_choice` / `parallel_tool_calls` * are dropped so we never send an empty `tools: []`. */ export declare function sanitizeResponsesTools(body: Record): Record; /** * Content-part / input-item types Claude extended thinking uses on the wire. * * Shared so both wire surfaces strip the same set: the Responses path here * ({@link sanitizeOpenResponsesInput}) and the Chat Completions path in * `@dbx-tools/appkit-mastra`'s serving sanitize. Anthropic signs these blocks, * so a replay that mutates one is rejected outright - both paths must agree on * what counts as a reasoning block. */ export declare const REASONING_TYPES: ReadonlySet; /** * Rewrite Responses `input` so Open Responses will accept it. * * Two OpenAI / Codex shapes that Databricks Open Responses (Claude, Gemini, …) * reject: * * 1. Prior assistant turns with `output_text` / `output_*` content parts — * Open Responses only allows `input_text`, `input_image`, `input_file`, * `input_audio`. Map every `output_` part to `input_`. * 2. Replayed extended-thinking blocks (`thinking`, `redacted_thinking`, * `reasoning` content parts, and top-level `reasoning` input items). Claude * signs those; any client round-trip that mutates them (or a cross-provider * replay) fails with e.g. * "messages.N.content.M: Invalid `data` in `redacted_thinking` block" * Strip them — the UI/client already showed the thinking; replay does not * need the signed blob. Same policy as appkit-mastra's serving sanitize. * * Returns a shallow copy when anything changes; otherwise the input body. */ export declare function sanitizeOpenResponsesInput(body: Record): Record; /** * Drop trailing assistant turns so the transcript ends on something Anthropic * will continue from. * * Databricks Open Responses rejects a body whose last item is an assistant * message with `"This model does not support assistant message prefill. The * conversation must end with a user message."` (the upstream Bedrock route * disallows prefill). Two ways a real client hits it: * * 1. The client replays its own last answer. The Codex CLI does this when a turn * ends without a tool call and the next request carries the full transcript. * 2. {@link sanitizeOpenResponsesInput} CREATES the shape: a turn that ended in a * `reasoning` item is stripped for replay compatibility, promoting the * assistant message before it to last. Running this repair after that strip * is what keeps one fix from causing the other failure. * * Dropping is correct rather than lossy: a trailing assistant turn carries text * the model itself just produced, so it is context the provider does not need * repeated in order to continue. Appending a synthetic `"Continue."` user turn * also satisfies the provider, but it puts words in the user's mouth that show * up in the model's context, so this takes the honest option. * * A trailing `function_call` is left ALONE. It is also an assistant-side item, * but Anthropic rejects an unanswered `tool_use` on a different rule * (`tool_use` ids without `tool_result`), and dropping it would silently discard * a tool call the client is about to answer. That is the client's bug to fix, * not something to paper over here. * * Returns a shallow copy when anything changes; otherwise the input body. */ export declare function repairTrailingAssistantInput(body: Record): Record; /** * Full Open Responses request sanitizer: strip non-`function` tools, rewrite * `output_*` content parts, drop thinking / reasoning blocks, and drop a * trailing assistant turn the provider would read as a prefill. Safe no-op * when nothing needs changing. */ export declare function sanitizeOpenResponsesRequest(body: Record): Record;