OpenAI ⇄ Anthropic Converter
Convert message arrays between OpenAI Chat Completions and Anthropic Messages API formats. Handles system messages, tool calls, image content blocks, multi-turn. In-browser, no API keys.
What is this for?
OpenAI's Chat Completions API and Anthropic's Messages API both let you send a multi-turn conversation, but they shape the JSON differently enough that prompts and tool-call traces don't drop in cleanly when you switch providers. The mismatches are small (system message placement, tool-call vs tool-use, image block shape, role: 'tool' vs content-array tool_result) but each one is a 10-minute rabbit hole when you're under deadline. This converter handles the translation so you can paste an OpenAI conversation and get back an Anthropic-shaped one (or vice versa).
What gets translated
- System messages. OpenAI puts the system instruction as a regular message with
role: "system". Anthropic puts it at the top level as asystemfield on the request. We move it across and concatenate if you have multiple. (Anthropic accepts a single string for system; multiple OpenAI system messages get joined with double newlines.) - Tool calls (assistant side). OpenAI:
tool_calls: [{id, type:'function', function:{name, arguments:'JSON-string'}}]. Anthropic: atool_useblock inside the assistant'scontentarray with{id, name, input}(parsed object, not a string). We parse OpenAI's escaped JSON arguments into the structuredinputAnthropic expects, and the reverse direction re-stringifies. - Tool results. OpenAI: a separate message with
role: "tool",tool_call_id, andcontent. Anthropic: atool_resultblock inside the next user message's content array, withtool_use_id. Consecutive OpenAItoolmessages get coalesced into one Anthropic user message containing multipletool_resultblocks (the shape Anthropic actually wants). - Images. OpenAI:
{type:'image_url', image_url:{url}}inside a user content array. Anthropic:{type:'image', source:{type:'base64',media_type,data}}or{type:'image', source:{type:'url',url}}. We detectdata:URLs and convert them; bare URLs pass through as URL-source images. - Plain text content. OpenAI uses a string; Anthropic accepts both a string and a one-element
[{type:'text', text}]array. We use whichever is simpler.
What doesn't translate
- Streaming events. This converter handles request/response JSON, not the SSE event streams. If you have a stream dump, reconstruct the full final message first.
- Provider-specific fields. Things like OpenAI's
logprobs, Anthropic'sstop_sequences, sampling parameters, etc. live at the request top level, not inside messages — they're not part of the format we touch. - Cache control hints. Anthropic's
cache_control: {type:'ephemeral'}hints have no OpenAI equivalent. They survive going OpenAI → Anthropic (no source to attach them to anyway), but are dropped when going Anthropic → OpenAI. - Tool definitions. The schema of your tools (the
toolsfield on the request) lives outside the messages array. Each provider's tool-definition shape is different (OpenAI: function calling; Anthropic:input_schema). Convert those separately.
Common gotchas
- OpenAI argument escaping. OpenAI's
argumentsis a JSON-encoded string, not a parsed object. If it's malformed (rare, but happens with tiny models), we put it under_rawin the Anthropicinputinstead of throwing. - Anthropic top-level system. When converting OpenAI → Anthropic, the result is
{system, messages}. If you want just the messages array, use the.messagesfield — but remember to attachsystemto your API request separately. - Strict alternation. Anthropic requires user / assistant / user / assistant in strict alternation (after the optional system). The converter mostly preserves this, but if your OpenAI source has two consecutive user messages, the Anthropic output will too — and the API will reject it. Merge them first.
- Multi-modal in OpenAI. An OpenAI user message with images uses a content array instead of a string. We handle this; just make sure your input parses as JSON (no Python-style
None/single quotes). - Privacy. Nothing leaves the page. Conversion is pure JavaScript; no API keys, no network calls.