Comprehensive Mapping Between Anthropic Messages API and OpenAI Chat Completions API
July 22, 2025 · View on GitHub
- Comprehensive Mapping Between Anthropic Messages API and OpenAI Chat Completions API
- Introduction
- API Endpoints
- Count Tokens Endpoint
- Request Mapping (Anthropic -> OpenAI)
- Response Mapping (OpenAI -> Anthropic)
- Streaming Responses
- Error Handling
- Important Considerations & Gaps
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_tokensfor 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
- Anthropic:
- 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.
- Anthropic:
Request Body Parameters
Mapping Anthropic request fields to OpenAI:
| Anthropic Parameter | OpenAI Parameter | Mapping and Notes |
|---|---|---|
model | model | Map 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) | messages | If 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. |
messages | messages | Translate the array of message objects. Roles and content structure require careful conversion (see details below). |
max_tokens | max_tokens | Direct mapping. The maximum number of tokens to generate in the response. Ensure value respects the target OpenAI model's limits. |
stop_sequences | stop | Direct mapping. Pass the array of stop strings. Note OpenAI returns finish_reason: "stop" if triggered. |
stream | stream | Direct mapping (true/false). If true, handle streaming response conversion (see Streaming section). |
temperature | temperature | Direct mapping (float, 0.0 to ~2.0). Both default around 1.0. |
top_p | top_p | Direct mapping (float, 0.0 to 1.0). OpenAI defaults to 1.0. Anthropic recommends using only one of temperature or top_p. |
top_k | Not supported | Anthropic-specific sampling parameter. OpenAI Chat API does not support top_k. Action: Ignore/drop this parameter. Behavior cannot be perfectly replicated. |
metadata.user_id | user | Map 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. |
tools | tools | Map the array of Anthropic tool definitions to OpenAI's tools array. (See Tool Definitions mapping). |
tool_choice | tool_choice | Map Anthropic's tool choice mechanism to OpenAI's tool choice control. (See Mapping Tool Choices). |
stream_options | Not directly supported | Anthropic'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 beuserorassistantwithin themessagesarray.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 besystem,user,assistant, orfunction.content: Typically a string (forsystem,user,assistant,functionroles). Can benullforassistantmessages containing only afunction_call.function_call: Optional object inassistantmessages indicating a function invocation.name: Required forfunctionrole 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-> OpenAIuser. - Anthropic
assistant-> OpenAIassistant. - Anthropic only allows
userandassistantin themessagesarray. OpenAI also usessystem(mapped from top-levelsystem) andfunction(mapped from Anthropictool_result, see below).
- Anthropic
- Content Conversion:
- Text: If Anthropic
contentis a string or a singletextblock, use the text directly as OpenAIcontent. If multipletextblocks, concatenate them into a single string for OpenAIcontent. - Image Blocks: Anthropic
imageblocks (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.
- Text: If Anthropic
Mapping Tool Result Messages (User Turn)
This is critical for multi-turn tool use:
-
Anthropic Input: A
usermessage containing one or moretool_resultcontent 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_resultblock to a separate OpenAI message withrole: "function".role:"function"name: The name of the tool/function corresponding to thetool_use_id. The proxy needs to track the mapping between thetool_use_idgenerated in the previous assistant response and the tool name.content: The output/result provided in thecontentfield of thetool_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
functionrole messages should be placed in the OpenAImessagesarray immediately after theassistantmessage that contained the correspondingfunction_call(which was mapped from Anthropic'stool_use).
System Prompts
- Anthropic: Uses a top-level
systemparameter (string). - OpenAI: Uses a message with
role: "system"at the beginning of themessagesarray. - Mapping: Convert Anthropic's
systemstring into{"role": "system", "content": "<system_string>"}and make it the first element (messages[0]) in the OpenAI requestmessageslist. 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 withname,description,input_schema(JSON Schema). - OpenAI
tools: Array of objects, each withtype: "function"and nestedfunctionobject containingname,description,parameters(JSON Schema).
Mapping Tool Definitions
- Map each Anthropic
toolto an OpenAItoolsentry:- Wrap in
{"type": "function", "function": {...}} name->function.namedescription->function.descriptioninput_schema->function.parameters(both expect JSON Schema format).
- Wrap in
- 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_choice | OpenAI tool_choice | Notes |
|---|---|---|
{"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 / Default | Omitted / 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 Field | Anthropic Field | Mapping and Notes |
|---|---|---|
id | id | Use 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") | type | Set Anthropic type to "message" for successful completions. |
model | model | Return the Anthropic model name that the client originally requested (e.g., "claude-3-opus-..."), not the OpenAI model name used internally. |
choices[0].message.role | role | Should always be "assistant" from OpenAI. Set Anthropic role to "assistant". |
choices[0].message.content | content | Map based on whether it's text or null (see below). |
choices[0].message.function_call | content | If present, map to a tool_use content block (see below). |
choices[0].finish_reason | stop_reason | Map the reason code (see Stop/Finish Reasons table). |
| N/A | stop_sequence | Set 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. |
usage | usage | Map token counts (see Usage Statistics). |
created, system_fingerprint, logprobs, choices[0].index | N/A | These 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.contentis a non-null string andfunction_callis null:- Create an Anthropic
contentarray containing a singletextblock:"content": [{"type": "text", "text": "<OpenAI message.content string>"}]
- Create an Anthropic
Tool Use Content (Function Call)
- If OpenAI
message.function_callis present (andcontentmight be null):- Create an Anthropic
contentarray containing a singletool_useblock:"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 thetool_useblock. This ID must be tracked by the proxy if the client will send back atool_resultreferencing it. - Parse the
argumentsstring from OpenAI (which is JSON stringified) into an actual JSON object or primitive value for Anthropic'sinputfield.
- Generate a unique
- Create an Anthropic
Stop Reasons and Finish Reasons
Mapping Stop/Finish Reasons
OpenAI finish_reason | Anthropic stop_reason | Notes |
|---|---|---|
"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 Field | Anthropic Usage Field |
|---|---|
prompt_tokens | input_tokens |
completion_tokens | output_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_deltawithdelta.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 containingchat.completion.chunkJSON objects. - Each chunk has a
deltafield with incremental changes (role,content, orfunction_callfragments). - First chunk usually contains
delta: {"role": "assistant"}. - Subsequent chunks contain
delta: {"content": "..."}ordelta: {"function_call": {"name": "...", "arguments": "..."}}(arguments often streamed as partial JSON string fragments). - Final chunk has
finish_reasonset. - 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:
- On first OpenAI chunk (
delta.role): Send Anthropicmessage_startevent containing initial message metadata (generatemessage_id, setrole: 'assistant', include model). Send initialping? (Optional, depends on client needs). - On first OpenAI
delta.contentchunk: Send Anthropiccontent_block_startfor index 0 (type: "text"). - On subsequent
delta.contentchunks: Send Anthropiccontent_block_deltawithdelta.type: "text_delta"and the content fragment. - On first OpenAI
delta.function_call.namechunk: Send Anthropiccontent_block_startfor the next available index (e.g., 1) (type: "tool_use", generatetool_use_id, includename). Accumulate arguments internally. - On OpenAI
delta.function_call.argumentschunks: Send Anthropiccontent_block_deltafor the tool's index withdelta.type: "input_json_delta"and thepartial_jsonfragment. Reconstruct the full arguments JSON internally. - When OpenAI stream provides
finish_reason:- If text content was streaming, send
content_block_stopfor index 0. - If tool call was streaming, send
content_block_stopfor the tool's index. - Map OpenAI
finish_reasonto Anthropicstop_reason. - Send
message_deltacontaining the finalstop_reasonand potentially calculatedusage(input tokens known from request, output tokens counted from stream). - Send
message_stop.
- If text content was streaming, send
- If OpenAI stream ends (
data: [DONE]): Ensure all pendingcontent_block_stop,message_delta, andmessage_stopevents have been sent. - 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. - 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 finalmessage_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 Code | OpenAI Error Type (error.type) | Anthropic HTTP Code | Anthropic Error Type (error.type) | Notes |
|---|---|---|---|---|
| 400 | invalid_request_error | 400 | invalid_request_error | Bad request (syntax, missing fields, etc.) |
| 401 | authentication_error | 401 | authentication_error | Invalid API key |
| 403 | permission_denied_error | 403 | permission_error | Insufficient permissions/access |
| 404 | not_found_error | 404 | not_found_error | Resource/model not found |
| 429 | rate_limit_error | 429 | rate_limit_error | Rate limit exceeded |
| 500 | internal_server_error, api_error | 500 | api_error | Internal server error on provider side |
| 503 | service_unavailable_error, overloaded_error | 529 | overloaded_error | Server overloaded / temporarily unavailable |
| 400 | e.g., context length exceeded | 400 | invalid_request_error | Map 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 Exception | Anthropic Equivalent Context | Notes |
|---|---|---|
openai.APIConnectionError | Network connectivity issue | Could not connect to OpenAI API. |
openai.APITimeoutError | Request timeout issue | Request to OpenAI timed out. |
openai.RateLimitError | Rate limit exceeded | Received 429 from OpenAI. |
openai.BadRequestError | Invalid request | Received 400 from OpenAI (validation, etc.). |
openai.AuthenticationError | Authentication failed | Received 401 from OpenAI. |
openai.PermissionDeniedError | Permission issue | Received 403 from OpenAI. |
openai.NotFoundError | Resource not found | Received 404 from OpenAI. |
openai.InternalServerError | Upstream server error | Received 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
userrole +tool_resultcontent and OpenAI'sfunctionrole 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
systemprompt is consistently prepended as the firstsystemrole 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.