/** * OpenAI ↔ Anthropic format translation. * * Pure functions — no I/O, no side effects. Used by the /v1/chat/completions * and /v1/models routes in server.ts. * * Design note: OpenAI clients always send the full conversation history on * every request. Feeding that directly into Meridian's session system would * classify every turn as "undo" or "diverged" (since the message list keeps * changing). Instead: * 1. The last user message becomes the actual SDK request * 2. Prior turns are packed into a block in the * system prompt so Claude has context * 3. Each chat completions request gets a fresh SDK session * This is intentional — OpenAI-format clients replay full history themselves * and don't benefit from Meridian's session resumption. */ export type OpenAiRole = "system" | "user" | "assistant" | "tool"; export interface OpenAiTextPart { type: "text"; text?: string; } export interface OpenAiImageUrlPart { type: "image_url"; image_url?: { url?: string; }; } export interface OpenAiContentPart { type: string; text?: string; image_url?: { url?: string; }; } export interface OpenAiMessage { role: OpenAiRole; tool_call_id?: string; content: string | OpenAiContentPart[]; tool_calls?: OpenAiCompletionToolCall[]; } export interface OpenAiChatToolFunction { type: "function"; function: { name: string; description?: string; parameters: unknown; strict?: boolean; }; } export interface OpenAiChatToolCustom { type: "custom"; } export type OpenAiChatTool = OpenAiChatToolFunction | OpenAiChatToolCustom; export interface OpenAiChatRequest { model?: string; messages?: OpenAiMessage[]; stream?: boolean; max_tokens?: number; max_completion_tokens?: number; temperature?: number; top_p?: number; tools?: OpenAiChatTool[]; } export interface AnthropicTextBlock { type: "text"; text: string; } export interface AnthropicImageBlock { type: "image"; source: { type: "base64"; media_type: string; data: string; }; } export interface AnthropicMessage { role: "user" | "assistant"; content: string | AnthropicContentBlock[]; } export interface AnthropicTool { name: string; description: string; input_schema: unknown; strict?: boolean; } export interface AnthropicRequestBody { model: string; messages: AnthropicMessage[]; max_tokens: number; stream: boolean; system?: string; temperature?: number; top_p?: number; tools?: AnthropicTool[]; } export interface AnthropicUsage { input_tokens?: number; output_tokens?: number; } export interface AnthropicContentBlockText { type: "text"; text?: string; } export interface AnthropicToolUseBlock { type: "tool_use"; id: string; name: string; input: Record; } export interface AnthropicToolResultBlock { type: "tool_result"; tool_use_id: string; content: string | AnthropicContentBlock[]; } export interface AnthropicThinkingBlock { type: "thinking"; thinking: string; } export type AnthropicContentBlock = AnthropicTextBlock | AnthropicImageBlock | AnthropicThinkingBlock | AnthropicToolResultBlock | AnthropicToolUseBlock; export interface AnthropicResponse { content?: AnthropicContentBlock[]; stop_reason?: string; usage?: AnthropicUsage; } /** * Streaming tool-call delta as emitted in chat.completion.chunk events. * * The OpenAI streaming protocol splits a single tool call across multiple * chunks: a "start" chunk announces the call (id + function name), and * subsequent "args" chunks append `function.arguments` fragments. `index` * correlates fragments back to their parent call. Fields are optional rather * than `DeepPartial` so the type can't represent * nonsense like `{ function: { arguments: undefined } }`. */ export interface OpenAiStreamingToolCallDelta { index: number; type?: "function"; id?: string; function?: { name?: string; arguments?: string; }; } export interface OpenAiStreamChunk { id: string; object: "chat.completion.chunk"; created: number; model: string; choices: Array<{ index: 0; delta: { role?: "assistant"; content?: string; tool_calls?: OpenAiStreamingToolCallDelta[]; reasoning_content?: string; }; finish_reason: "stop" | "length" | "tool_calls" | null; }>; } export interface OpenAiCompletionFunctionToolCall { type: "function"; index?: number; id: string; function: { name: string; arguments: string; }; } export interface OpenAiCompletionCustomToolCall { type: "custom"; } export type OpenAiCompletionToolCall = OpenAiCompletionFunctionToolCall | OpenAiCompletionCustomToolCall; export interface OpenAiCompletion { id: string; object: "chat.completion"; created: number; model: string; choices: Array<{ index: 0; message: { role: "assistant"; content: string | null; reasoning_content?: string; tool_calls?: OpenAiCompletionToolCall[]; }; finish_reason: "stop" | "length" | "tool_calls"; }>; usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number; }; } export interface OpenAiModel { id: string; object: "model"; created: number; owned_by: string; display_name: string; context_window: number; } /** * Normalise an OpenAI message content field to a plain string. * Handles both string content and structured content arrays. * Non-text blocks are summarized so history packing does not silently erase * multimodal context. */ export declare function extractOpenAiContent(content: string | OpenAiContentPart[]): string; /** * Translate an OpenAI /v1/chat/completions request body into an Anthropic * /v1/messages request body. * * Returns null if the request has no messages (caller should return 400). */ export declare function translateOpenAiToAnthropic(body: OpenAiChatRequest): AnthropicRequestBody | null; /** * Translate a complete Anthropic /v1/messages response to OpenAI format. * Currently supports only text, thinking and function call blocks. * * When `thinkingPassthrough` is false, thinking blocks are not * mapped to `reasoning_content` (stripped from the response). */ export declare function translateAnthropicToOpenAi(response: AnthropicResponse, completionId: string, model: string, created: number, options?: { thinkingPassthrough?: boolean; }): OpenAiCompletion; /** * Wire-format SSE event from Anthropic's `/v1/messages` streaming API. * * `content_block` may describe a text block, a tool_use block, or a thinking * block depending on the stream position — only `type` is guaranteed. */ export interface AnthropicSseEvent { type: string; index?: number; delta?: { type?: string; text?: string; stop_reason?: string; partial_json?: string; thinking?: string; }; content_block?: { type: "text"; text?: string; } | { type: "thinking"; thinking?: string; } | AnthropicToolUseBlock; message?: { id?: string; }; } export interface SseTranslator { (event: AnthropicSseEvent): OpenAiStreamChunk | null; } export interface SseTranslatorContext { completionId: string; model: string; created: number; /** When false, thinking blocks are stripped from the response */ thinkingPassthrough?: boolean; } /** * A stateful translator for one OpenAI streaming response. * * Each completion stream gets its own translator instance to keep state out * of server.ts. Internally tracks the current tool-call index so that * `content_block_start` (tool_use) events are emitted as OpenAI tool_call * deltas with monotonically increasing `index` values, matching how * `function.arguments` fragments must correlate back to their parent call. * * Anthropic's wire format signals start/end of each content block; OpenAI's * does not, so we manufacture an index per stream. */ export declare function createSseTranslator(ctx: SseTranslatorContext): SseTranslator; /** * Translate one parsed Anthropic SSE event into an OpenAI stream chunk. * Returns null for events that should be skipped (pings, message_stop, * content_block_stop, text-block content_block_start, etc). * * `toolCallNum` is the OpenAI `tool_calls[].index` value to emit on tool-call * chunks. Callers tracking multiple tools per stream must increment it on * each `content_block_start` with `type: "tool_use"` *before* calling this * function. Use `createSseTranslator` to handle this automatically. * * When `thinkingPassthrough` is false, thinking_delta events are skipped * so the client does not receive reasoning_content. */ export declare function translateAnthropicSseEvent(event: AnthropicSseEvent, completionId: string, model: string, created: number, toolCallNum: number, thinkingPassthrough?: boolean): OpenAiStreamChunk | null; /** * Return the static list of available Claude models in OpenAI format. * Context windows reflect subscription capabilities. */ export declare function buildModelList(isMaxSubscription: boolean, now?: number): OpenAiModel[]; //# sourceMappingURL=openai.d.ts.map