import type { ProviderAdapter, ProviderRequestOptions, ProviderResponse, StreamChunk, FinishReason, ToolDefinitionSchema, AiMessage } from '../../types.js'; import type { OpenAIConfig } from './config.js'; export declare class OpenAIAdapter implements ProviderAdapter { private readonly config; private readonly model; constructor(config: OpenAIConfig, model: string); private readonly getClient; generate(options: ProviderRequestOptions): Promise; stream(options: ProviderRequestOptions): AsyncIterable; } /** * Repair tool-call ↔ tool-result adjacency before serializing for an * OpenAI-compatible provider. * * Anthropic carries tool results as content blocks inside user turns, so a * loosely-ordered transcript round-trips fine. The OpenAI wire protocol (and * strict implementers like DeepSeek) enforce two hard rules: * * 1. every `role:'tool'` message must immediately follow the `assistant` * message whose `tool_calls` declares its `tool_call_id`, and * 2. every `tool_calls` entry on an assistant message must be answered by a * following `role:'tool'` message before the next assistant/user turn. * * A persist→resume cycle (client-tool pause, approval round-trip, or an app * that re-stores assistant turns without their `toolCalls`) can violate * either rule, yielding `400 Messages with role 'tool' must be a response to * a preceding message with 'tool_calls'` — or its mirror, an unanswered * `tool_calls`. See `docs/plans/2026-06-11-deepseek-tool-transcript-400.md`. * * This pass enforces BOTH directions: * - **Detached / out-of-order results** are pulled up to sit immediately * after their parent assistant, in `tool_calls` order. * - **Unanswered `tool_calls`** get a synthesized stub result so the * request is well-formed (mirrors the placeholder strategy in * `resumePendingToolCalls`). * - **Orphan tool results** — whose `tool_call_id` is declared by no * assistant message anywhere — are dropped; they can never be valid on * the wire. (Lossy only when the app already discarded the parent's * `toolCalls`; the framework can't reconstruct a deleted call.) * * Transcripts that already satisfy the invariant pass through unchanged * (same message object references), so the common single-run path pays only * a linear scan. */ export declare function normalizeToolTranscript(messages: AiMessage[]): AiMessage[]; export declare function toOpenAIMessages(messages: AiMessage[]): unknown[]; export declare function toOpenAITools(tools: ToolDefinitionSchema[]): unknown[]; /** * Map an OpenAI finish reason onto the neutral {@link FinishReason}. Without * this, a `max_tokens` truncation and a content-filter stop both report a clean * `stop`, so a caller cannot tell a complete answer from a cut-off one. */ export declare function mapOpenAIFinishReason(reason: string): FinishReason; //# sourceMappingURL=chat.d.ts.map