Comprehensive Mapping Between Anthropic Messages API and OpenAI Chat Completions API

July 22, 2025 · View on GitHub

Introduction

This document provides a detailed, field-by-field mapping between Anthropic's Claude v3 Messages API and OpenAI's GPT-4 (Chat Completions API), based on deep research into both APIs. It covers translating requests (Anthropic -> OpenAI) and responses (OpenAI -> Anthropic), focusing on accuracy for features like message roles, content blocks, system prompts, tool usage, and streaming. This mapping is crucial for building a proxy server that allows clients using the Anthropic API format to interact seamlessly with OpenAI's backend. Differences in fields, values, and behavior are noted, along with required transformations and potential gaps.

Scope: Assumes a stateless translator (full context per request) supporting Claude 3 features via OpenAI's equivalent mechanisms. The reference models are Claude 3 and GPT-4/GPT-4-Turbo. The proxy implementation includes dynamic model selection, comprehensive error handling, multimodal support, and token counting functionality.


API Endpoints

  • Anthropic Messages API Endpoint:
    POST /v1/messages
    
  • OpenAI Chat Completions API Endpoint:
    POST /v1/chat/completions
    

Count Tokens Endpoint

  • Anthropic: Provides POST /v1/messages/count_tokens for calculating input token count.
  • OpenAI: No direct HTTP endpoint. Token usage is returned in completion responses. For estimation, use libraries like tiktoken.
  • Proxy Implementation: The proxy implements the count_tokens endpoint using tiktoken with GPT-4 encoding (or cl100k_base fallback) to estimate token counts for Anthropic-format requests. This includes counting tokens for messages, system prompts, tool definitions, and content blocks (with special handling for images and tool results).

Dynamic Model Selection

The proxy implements intelligent model selection based on the requested Claude model name:

  • Claude Opus/Sonnet Models → Maps to the configured "big model" (e.g., gpt-4-turbo)
  • Claude Haiku Models → Maps to the configured "small model" (e.g., gpt-3.5-turbo)
  • Unknown Models → Defaults to small model with a warning logged

Model names are matched using case-insensitive substring matching on "opus", "sonnet", and "haiku". The actual target models are configured via environment variables (BIG_MODEL_NAME, SMALL_MODEL_NAME).


Request Mapping (Anthropic -> OpenAI)

Authentication & Headers

  • Authorization: Translate between header formats.
    • Anthropic: x-api-key: YOUR_ANTHROPIC_API_KEY
    • OpenAI: Authorization: Bearer YOUR_OPENAI_API_KEY
  • API Version: Include Anthropic's version header if needed by the client.
    • Anthropic: anthropic-version: VERSION_STRING (e.g., 2023-06-01)
    • OpenAI: Version is typically tied to the model or API path, not a specific header.

Request Body Parameters

Mapping Anthropic request fields to OpenAI:

Anthropic ParameterOpenAI ParameterMapping and Notes
modelmodelMap the requested Claude model name (e.g., claude-3-opus-20240229) to a corresponding OpenAI model name (e.g., gpt-4-turbo). The proxy dynamically selects models: Claude Opus/Sonnet → big model, Claude Haiku → small model.
system (string)messagesIf present, prepend the system string as the first message in the OpenAI messages array: {"role": "system", "content": "<system_string>"}. If absent, omit this system message.
messagesmessagesTranslate the array of message objects. Roles and content structure require careful conversion (see details below).
max_tokensmax_tokensDirect mapping. The maximum number of tokens to generate in the response. Ensure value respects the target OpenAI model's limits.
stop_sequencesstopDirect mapping. Pass the array of stop strings. Note OpenAI returns finish_reason: "stop" if triggered.
streamstreamDirect mapping (true/false). If true, handle streaming response conversion (see Streaming section).
temperaturetemperatureDirect mapping (float, 0.0 to ~2.0). Both default around 1.0.
top_ptop_pDirect mapping (float, 0.0 to 1.0). OpenAI defaults to 1.0. Anthropic recommends using only one of temperature or top_p.
top_kNot supportedAnthropic-specific sampling parameter. OpenAI Chat API does not support top_k. Action: Ignore/drop this parameter. Behavior cannot be perfectly replicated.
metadata.user_iduserMap the optional metadata.user_id string from Anthropic to OpenAI's top-level user string for tracking/monitoring. Other fields in metadata are not mappable.
toolstoolsMap the array of Anthropic tool definitions to OpenAI's tools array. (See Tool Definitions mapping).
tool_choicetool_choiceMap Anthropic's tool choice mechanism to OpenAI's tool choice control. (See Mapping Tool Choices).
stream_optionsNot directly supportedAnthropic's stream_options (e.g., include_usage) doesn't map directly. Usage in OpenAI streams is not provided per-chunk. Proxy needs to handle usage reporting at the end of the stream.

Messages and Content Blocks

Anthropic Messages Format

  • role: Must be user or assistant within the messages array.
  • content: Can be a string OR an array of content blocks (text, image, tool_result).
// Anthropic User Message with Text
{
  "role": "user",
  "content": "Hello, Claude."
}

// Anthropic User Message with Image (Requires special handling)
{
  "role": "user",
  "content": [
    {"type": "text", "text": "Describe this image:"},
    {"type": "image", "source": {...}} // GPT-4 Chat API cannot process this directly
  ]
}

// Anthropic User Message with Tool Result (Follow-up after tool_use)
{
  "role": "user",
  "content": [
    {"type": "tool_result", "tool_use_id": "toolu_123", "content": "<tool_output_string_or_JSON>"}
  ]
}

// Anthropic Assistant Message (Response)
{
  "role": "assistant",
  "content": [{"type": "text", "text": "Hi there!"}] // Or tool_use block
}

OpenAI Messages Format

  • role: Can be system, user, assistant, or function.
  • content: Typically a string (for system, user, assistant, function roles). Can be null for assistant messages containing only a function_call.
  • function_call: Optional object in assistant messages indicating a function invocation.
  • name: Required for function role messages, identifying the function whose result is provided.
// OpenAI System Message
{"role": "system", "content": "You are helpful."}

// OpenAI User Message
{"role": "user", "content": "Hello."}

// OpenAI Assistant Message (Text Response)
{"role": "assistant", "content": "Hi there!"}

// OpenAI Assistant Message (Function Call Request)
{"role": "assistant", "content": null, "function_call": {"name": "get_weather", "arguments": "{\"location\": \"Paris\"}"}}

// OpenAI Function Result Message (Provides result back to model)
{"role": "function", "name": "get_weather", "content": "{\"temperature\": 22, \"unit\": \"celsius\"}"}

Mapping Messages (User & Assistant Turns)

  • Roles:
    • Anthropic user -> OpenAI user.
    • Anthropic assistant -> OpenAI assistant.
    • Anthropic only allows user and assistant in the messages array. OpenAI also uses system (mapped from top-level system) and function (mapped from Anthropic tool_result, see below).
  • Content Conversion:
    • Text: If Anthropic content is a string or a single text block, use the text directly as OpenAI content. If multiple text blocks, concatenate them into a single string for OpenAI content.
    • Image Blocks: Anthropic image blocks (type: "image") are supported by OpenAI's Chat Completions API when using vision-capable models. Implementation: The proxy converts base64 image blocks to OpenAI's multimodal format:
      • {"type": "image_url", "image_url": {"url": "data:media_type;base64,data"}}
      • Only base64 source type is supported; other source types are ignored with warnings.
    • Partial Assistant Prefill: Anthropic allows the last message to be role: "assistant" to provide a prefix for the model to continue. OpenAI does not support this "prefill" mechanism directly. Action: This feature cannot be reliably proxied. Best approach is to disallow or ignore such partial assistant messages in the request.

Mapping Tool Result Messages (User Turn)

This is critical for multi-turn tool use:

  • Anthropic Input: A user message containing one or more tool_result content blocks.

    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "toolu_abc",
          "content": "{\"temp\": 72}" // Can be string or JSON object/array
        }
      ]
    }
    
  • OpenAI Output: Map each tool_result block to a separate OpenAI message with role: "function".

    • role: "function"
    • name: The name of the tool/function corresponding to the tool_use_id. The proxy needs to track the mapping between the tool_use_id generated in the previous assistant response and the tool name.
    • content: The output/result provided in the content field of the tool_result. OpenAI expects this to be a string (usually JSON stringified).
    {
      "role": "function",
      "name": "get_current_weather", // Retrieved via toolu_abc mapping
      "content": "{\"temp\": 72}"
    }
    
  • Placement: These function role messages should be placed in the OpenAI messages array immediately after the assistant message that contained the corresponding function_call (which was mapped from Anthropic's tool_use).

System Prompts

  • Anthropic: Uses a top-level system parameter (string).
  • OpenAI: Uses a message with role: "system" at the beginning of the messages array.
  • Mapping: Convert Anthropic's system string into {"role": "system", "content": "<system_string>"} and make it the first element (messages[0]) in the OpenAI request messages list. Ensure this is done for every turn if the conversation is stateful on the client side.

Tools and Functions

Tool Definitions

  • Anthropic tools: Array of objects, each with name, description, input_schema (JSON Schema).
  • OpenAI tools: Array of objects, each with type: "function" and nested function object containing name, description, parameters (JSON Schema).

Mapping Tool Definitions

  • Map each Anthropic tool to an OpenAI tools entry:
    • Wrap in {"type": "function", "function": {...}}
    • name -> function.name
    • description -> function.description
    • input_schema -> function.parameters (both expect JSON Schema format).
  • Built-in Tools: Anthropic mentions beta built-in tools (e.g., bash). OpenAI has no direct equivalent. Proxy should treat these as custom tools if needed, defining the expected schema, or simply not support them.

Tool Usage Control

  • Anthropic tool_choice: Object controlling how the model uses tools (type: auto, any, tool, none).
  • OpenAI tool_choice: String or object controlling tool usage (auto, none, required, {"type": "function", "function": {"name": "..."}}).

Mapping Tool Choices

Anthropic tool_choiceOpenAI tool_choiceNotes
{"type": "auto"} (or omitted)"auto" (or omitted)Model decides whether to call a tool and which one. (Default behavior for both).
{"type": "any"}"auto"Force the model to use any available tool. OpenAI has no direct equivalent. Map to "auto" and potentially add instructions in the system prompt (e.g., "You must use a tool if appropriate").
{"type": "tool", "name": "tool_name"}{"type": "function", "function": {"name": "tool_name"}}Force the model to call the specified tool.
Omitted / DefaultOmitted / Default ("auto")If Anthropic tool_choice is not provided, use OpenAI's default ("auto").
(Note: Anthropic also has a "none" type implied by omitting tools)"none"If no tools are provided, or if explicit prevention is needed, OpenAI can use "none". This doesn't seem to directly map from an Anthropic option but might be needed for specific proxy logic.

Response Mapping (OpenAI -> Anthropic)

Response Body Structure

Anthropic Response Structure

{
  "id": "msg_...", // Message ID
  "type": "message", // Fixed type for successful response
  "role": "assistant", // Fixed role
  "model": "claude-3-opus-...", // Model name requested by client
  "content": [ ... ], // Array of content blocks (text or tool_use)
  "stop_reason": "end_turn", // Reason generation stopped
  "stop_sequence": null, // Sequence that caused stop, if applicable
  "usage": {
    "input_tokens": 10,
    "output_tokens": 25
  }
}

OpenAI Response Structure

{
  "id": "chatcmpl-...", // Completion ID
  "object": "chat.completion",
  "created": 1677652288, // Timestamp
  "model": "gpt-4-turbo-...", // Model name that generated response
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello!", // Can be null if function_call is present
        "function_call": null // Or {"name": "...", "arguments": "..."}
      },
      "logprobs": null,
      "finish_reason": "stop" // Reason generation stopped
    }
    // Potentially more choices if n > 1, but Anthropic only expects one.
  ],
  "usage": {
    "prompt_tokens": 9,
    "completion_tokens": 12,
    "total_tokens": 21
  },
  "system_fingerprint": "fp_..."
}

Mapping Response Fields

Translate fields from the first choice (choices[0]) of the OpenAI response to the Anthropic format:

OpenAI FieldAnthropic FieldMapping and Notes
ididUse the OpenAI id (e.g., "chatcmpl-...") or generate a new one in Anthropic format (e.g., "msg_..."). Using OpenAI's ID is simpler for traceability.
object ("chat.completion")typeSet Anthropic type to "message" for successful completions.
modelmodelReturn the Anthropic model name that the client originally requested (e.g., "claude-3-opus-..."), not the OpenAI model name used internally.
choices[0].message.roleroleShould always be "assistant" from OpenAI. Set Anthropic role to "assistant".
choices[0].message.contentcontentMap based on whether it's text or null (see below).
choices[0].message.function_callcontentIf present, map to a tool_use content block (see below).
choices[0].finish_reasonstop_reasonMap the reason code (see Stop/Finish Reasons table).
N/Astop_sequenceSet only if OpenAI finish_reason was "stop" AND a stop sequence from the request was matched. Echo the matched sequence here. OpenAI doesn't return this.
usageusageMap token counts (see Usage Statistics).
created, system_fingerprint, logprobs, choices[0].indexN/AThese OpenAI fields have no equivalent in the Anthropic response. Omit them.

Mapping Assistant Message Content

Map choices[0].message to Anthropic's content array:

Text Content

  • If OpenAI message.content is a non-null string and function_call is null:
    • Create an Anthropic content array containing a single text block:
      "content": [{"type": "text", "text": "<OpenAI message.content string>"}]
      

Tool Use Content (Function Call)

  • If OpenAI message.function_call is present (and content might be null):
    • Create an Anthropic content array containing a single tool_use block:
      "content": [
        {
          "type": "tool_use",
          "id": "toolu_<generated_unique_id>", // Generate a unique ID for this tool call
          "name": "<OpenAI function_call.name>",
          "input": <parsed_arguments_object> // Parse the arguments JSON string into a JSON object/value
        }
      ]
      
    • Crucially:
      • Generate a unique id (e.g., toolu_...) for the tool_use block. This ID must be tracked by the proxy if the client will send back a tool_result referencing it.
      • Parse the arguments string from OpenAI (which is JSON stringified) into an actual JSON object or primitive value for Anthropic's input field.

Stop Reasons and Finish Reasons

Mapping Stop/Finish Reasons

OpenAI finish_reasonAnthropic stop_reasonNotes
"stop""end_turn"Model finished naturally. (Default case)
"stop""stop_sequence"Condition: If the stop occurred because a sequence in stop_sequences was hit. Proxy needs to detect this. Set stop_sequence field too.
"length""max_tokens"Model hit the max_tokens limit.
"function_call""tool_use"Model is requesting a tool/function call. This corresponds to the tool_use content block.
"content_filter""stop_sequence" ?OpenAI flagged content. Anthropic has no direct equivalent. Could map to stop_sequence (as an external stop) or handle as an error.
null (streaming)null (streaming)Generation is ongoing during streaming.

Usage Statistics

Mapping Usage

Map fields from OpenAI's usage object to Anthropic's usage object:

OpenAI Usage FieldAnthropic Usage Field
prompt_tokensinput_tokens
completion_tokensoutput_tokens
total_tokens(Omit)

Resulting Anthropic structure:

"usage": {
  "input_tokens": <OpenAI prompt_tokens>,
  "output_tokens": <OpenAI completion_tokens>
}

Streaming Responses

Both APIs use Server-Sent Events (SSE), but formats differ significantly. The proxy must translate OpenAI SSE chunks into Anthropic SSE events.

Anthropic Streaming Format

  • Event-based, with named events (message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop, ping).
  • Structured JSON payloads for each event type.
  • Sends message metadata (message_start), then content blocks incrementally (content_block_* events), then closing metadata (message_delta, message_stop).
  • Text deltas (content_block_delta with delta.type: "text_delta") and tool argument deltas (delta.type: "input_json_delta") are possible.
event: message_start
data: {"type": "message_start", "message": {"id": "msg_123", "type": "message", "role": "assistant", ...}}

event: content_block_start
data: {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Hello"}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": " world"}}

event: content_block_stop
data: {"type": "content_block_stop", "index": 0}

# If tool call occurs...
event: content_block_start
data: {"type": "content_block_start", "index": 1, "content_block": {"type": "tool_use", "id": "toolu_abc", "name": "...", "input": {}}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 1, "delta": {"type": "input_json_delta", "partial_json": "{\"location\":\""}}

event: content_block_delta
data: {"type": "content_block_delta", "index": 1, "delta": {"type": "input_json_delta", "partial_json": "Paris\"}"}}

event: content_block_stop
data: {"type": "content_block_stop", "index": 1}


event: message_delta # Contains stop_reason, usage updates
data: {"type": "message_delta", "delta": {"stop_reason": "tool_use", ...}, "usage": {"output_tokens": 15}}

event: message_stop
data: {"type": "message_stop"}

OpenAI Streaming Format

  • Single stream of unnamed data: events containing chat.completion.chunk JSON objects.
  • Each chunk has a delta field with incremental changes (role, content, or function_call fragments).
  • First chunk usually contains delta: {"role": "assistant"}.
  • Subsequent chunks contain delta: {"content": "..."} or delta: {"function_call": {"name": "...", "arguments": "..."}} (arguments often streamed as partial JSON string fragments).
  • Final chunk has finish_reason set.
  • Stream ends with data: [DONE]. Usage is NOT included in SSE chunks.
data: {"id":"...", "object":"chat.completion.chunk", "choices":[{"index":0, "delta":{"role":"assistant"}, "finish_reason":null}]}

data: {"id":"...", "object":"chat.completion.chunk", "choices":[{"index":0, "delta":{"content":"Hello"}, "finish_reason":null}]}

data: {"id":"...", "object":"chat.completion.chunk", "choices":[{"index":0, "delta":{"content":" world"}, "finish_reason":null}]}

# Function call streaming example
data: {"id":"...", "object":"chat.completion.chunk", "choices":[{"index":0, "delta":{"function_call": {"name": "get_weather"}}, "finish_reason":null}]}

data: {"id":"...", "object":"chat.completion.chunk", "choices":[{"index":0, "delta":{"function_call": {"arguments": "{\"loca"}}, "finish_reason":null}]}

data: {"id":"...", "object":"chat.completion.chunk", "choices":[{"index":0, "delta":{"function_call": {"arguments": "tion\":\"P"}}, "finish_reason":null}]}

data: {"id":"...", "object":"chat.completion.chunk", "choices":[{"index":0, "delta":{"function_call": {"arguments": "aris\"}"}}, "finish_reason":null}]}


data: {"id":"...", "object":"chat.completion.chunk", "choices":[{"index":0, "delta":{}, "finish_reason":"function_call"}]}

data: [DONE]

Mapping Streaming Responses (OpenAI -> Anthropic)

The proxy must maintain state during streaming to construct Anthropic events:

  1. On first OpenAI chunk (delta.role): Send Anthropic message_start event containing initial message metadata (generate message_id, set role: 'assistant', include model). Send initial ping? (Optional, depends on client needs).
  2. On first OpenAI delta.content chunk: Send Anthropic content_block_start for index 0 (type: "text").
  3. On subsequent delta.content chunks: Send Anthropic content_block_delta with delta.type: "text_delta" and the content fragment.
  4. On first OpenAI delta.function_call.name chunk: Send Anthropic content_block_start for the next available index (e.g., 1) (type: "tool_use", generate tool_use_id, include name). Accumulate arguments internally.
  5. On OpenAI delta.function_call.arguments chunks: Send Anthropic content_block_delta for the tool's index with delta.type: "input_json_delta" and the partial_json fragment. Reconstruct the full arguments JSON internally.
  6. When OpenAI stream provides finish_reason:
    • If text content was streaming, send content_block_stop for index 0.
    • If tool call was streaming, send content_block_stop for the tool's index.
    • Map OpenAI finish_reason to Anthropic stop_reason.
    • Send message_delta containing the final stop_reason and potentially calculated usage (input tokens known from request, output tokens counted from stream).
    • Send message_stop.
  7. If OpenAI stream ends (data: [DONE]): Ensure all pending content_block_stop, message_delta, and message_stop events have been sent.
  8. Handling Multiple Blocks: If OpenAI hypothetically interleaved text and tool calls (unlikely but possible), manage multiple content blocks with correct indexing for content_block_* events.
  9. Usage: Since OpenAI doesn't stream usage, the proxy must calculate output tokens by summing streamed content/argument tokens (using a tokenizer like tiktoken) and report it in the final message_delta. Input tokens are calculated from the original request.

Error Handling

Map error responses between the APIs.

Error Response Structures

Anthropic Error Structure

{
  "type": "error",
  "error": {
    "type": "invalid_request_error", // Specific error type
    "message": "Error message details."
  }
}

OpenAI Error Structure

{
  "error": {
    "message": "Error message details.",
    "type": "invalid_request_error", // Specific error type
    "param": null, // Parameter causing error, if applicable
    "code": null // Specific error code, if applicable
  }
}

HTTP Status Code Mapping

Translate HTTP status codes and inherent error types:

OpenAI HTTP CodeOpenAI Error Type (error.type)Anthropic HTTP CodeAnthropic Error Type (error.type)Notes
400invalid_request_error400invalid_request_errorBad request (syntax, missing fields, etc.)
401authentication_error401authentication_errorInvalid API key
403permission_denied_error403permission_errorInsufficient permissions/access
404not_found_error404not_found_errorResource/model not found
429rate_limit_error429rate_limit_errorRate limit exceeded
500internal_server_error, api_error500api_errorInternal server error on provider side
503service_unavailable_error, overloaded_error529overloaded_errorServer overloaded / temporarily unavailable
400e.g., context length exceeded400invalid_request_errorMap specific 400s appropriately

Note: Error type names might vary slightly depending on specific SDK versions. Use the canonical types where possible.

API Client Errors

Translate common client-side errors (network issues, timeouts) consistently:

OpenAI Client ExceptionAnthropic Equivalent ContextNotes
openai.APIConnectionErrorNetwork connectivity issueCould not connect to OpenAI API.
openai.APITimeoutErrorRequest timeout issueRequest to OpenAI timed out.
openai.RateLimitErrorRate limit exceededReceived 429 from OpenAI.
openai.BadRequestErrorInvalid requestReceived 400 from OpenAI (validation, etc.).
openai.AuthenticationErrorAuthentication failedReceived 401 from OpenAI.
openai.PermissionDeniedErrorPermission issueReceived 403 from OpenAI.
openai.NotFoundErrorResource not foundReceived 404 from OpenAI.
openai.InternalServerErrorUpstream server errorReceived 5xx from OpenAI.

The proxy should catch OpenAI client errors and return corresponding Anthropic-style HTTP errors and JSON bodies.

Implementation Considerations

  • Preserve Messages: Include the original OpenAI error message within the translated Anthropic error structure for debugging.
  • Request IDs: Pass through relevant request IDs (X-Request-ID, etc.) if available.
  • Proxy Context: Add context indicating the error originated from the upstream provider (OpenAI).
  • Provider Error Details: The proxy extracts detailed error information from OpenAI responses, including provider name and raw error data when available (e.g., from OpenRouter metadata).
  • Streaming Errors: Handle errors that occur after the stream has started (e.g., network drop). The proxy formats errors as SSE events with proper Anthropic error structure.
  • Retries: Implement appropriate retry logic (e.g., exponential backoff) for transient errors like rate limits (429) or server issues (5xx).

Important Considerations & Gaps

  • Model Behavior: Mapping APIs doesn't guarantee identical model performance, reasoning, alignment, or adherence to instructions. Claude and GPT-4 have inherent differences.
  • Unsupported Anthropic Features:
    • top_k: Cannot be mapped.
    • Partial Assistant Prefill: Cannot be mapped.
    • Built-in Tools (beta): Require custom mapping or are unsupported.
  • Image Handling:
    • Images: OpenAI Chat API supports image inputs with vision-capable models. The proxy converts Anthropic base64 image blocks to OpenAI's multimodal message format automatically.
  • Role Mapping for Tool Results: The conversion between Anthropic's user role + tool_result content and OpenAI's function role is crucial and requires careful state management in the proxy.
  • Tool Choice any: Anthropic's {"type": "any"} cannot be directly enforced in OpenAI; mapping to "auto" is the closest functional equivalent.
  • System Prompt Handling: Ensure the Anthropic system prompt is consistently prepended as the first system role message in the OpenAI request history for every turn.
  • Streaming Usage: OpenAI does not provide usage stats in stream chunks. Proxy must calculate and append at the end.
  • Error Granularity: Error type details might differ slightly. Aim for the closest conceptual match.

This mapping provides a comprehensive guide for translating between the Anthropic Messages API and OpenAI Chat Completions API, leveraging the detailed information from the provided research. Careful implementation considering the nuances and gaps identified is essential for a functional proxy.