v1/messages → /responses Parameter Mapping
When you send a request to /v1/messages targeting an openai/* model, or a few Responses-only deployments of other providers such as perplexity/perplexity/sonar, LiteLLM internally routes it through the OpenAI Responses API. A request with stop_sequences, or any request when use_chat_completions_url_for_anthropic_messages is on, goes through chat completions instead. This page documents exactly how every parameter gets translated in both directions.
The transformation lives in litellm/llms/anthropic/messages/responses_adapters/transformation.py.
Request: Anthropic → Responses API
Top-level parameters
Anthropic (/v1/messages) | Responses API | Notes |
|---|---|---|
model | model | Passed through as-is |
messages | input | Structurally transformed — see the messages section below |
system (string) | instructions | Passed as a plain string |
system (list of content blocks) | instructions | Text blocks are joined with \n; non-text blocks are ignored |
max_tokens | max_output_tokens | Renamed |
temperature | temperature | Passed through as-is |
top_p | top_p | Passed through as-is |
tools | tools | Format-translated — see the tools section below |
tool_choice | tool_choice | Type-remapped — see the tool_choice section below |
thinking | reasoning | Budget tokens mapped to effort level — see the thinking section below |
output_format or output_config.format | text | Wrapped as {"format": {"type": "json_schema", "name": "structured_output", "schema": ..., "strict": ...}}. strict is copied from the request's strict flag and defaults to false, so schemas with optional properties pass through; set strict: true to opt into OpenAI strict mode, which requires every property in required |
context_management | context_management | Only compact_20260112 is converted, see the context_management section below. Other edits run in LiteLLM before the request is translated |
metadata.user_id | user | Extracted from the metadata object and truncated to 64 characters |
metadata.user_id | prompt_cache_key | Same value, truncated to 64 characters (OpenAI's key limit), so one user's requests keep hitting the same prompt cache. Not set when user_id is empty or null. A prompt_cache_key sent in the request body wins over the derived one |
stop_sequences | ❌ Not mapped | A request with stop_sequences goes through chat completions instead of this path |
top_k | ❌ Not mapped | Dropped silently |
speed | ❌ Not mapped | Only used to set Anthropic beta headers on the native path |
The chat-completions path (litellm_settings.use_chat_completions_url_for_anthropic_messages: true, and every non-OpenAI provider) derives the same two parameters from metadata.user_id, with two differences: user is passed through untruncated, and prompt_cache_key is only set when the target provider's supported params include it. OpenAI, Azure OpenAI and other OpenAI-compatible providers do; Gemini, Vertex AI, Bedrock and Anthropic do not. litellm_proxy/ deployments (one LiteLLM proxy in front of another) never get the derived key, because the downstream proxy's real provider is unknown and would reject it unless drop_params is set there.
How messages get converted
Each Anthropic message is expanded into one or more Responses API input items. The key difference is that tool_result and tool_use blocks become top-level items in the input array rather than being nested inside a message.
| Anthropic message | Responses API input item |
|---|---|
user role, string content | {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "..."}]} |
user role, {"type": "text"} block | {"type": "input_text", "text": "..."} inside a user message |
user role, {"type": "image", "source": {"type": "base64"}} | {"type": "input_image", "image_url": "data:<media_type>;base64,<data>"} inside a user message |
user role, {"type": "image", "source": {"type": "url"}} | {"type": "input_image", "image_url": "<url>"} inside a user message |
user role, {"type": "tool_result"} block | Top-level {"type": "function_call_output", "call_id": "...", "output": "..."} — pulled out of the message entirely |
assistant role, string content | {"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "..."}]} |
assistant role, {"type": "text"} block | {"type": "output_text", "text": "..."} inside an assistant message |
assistant role, {"type": "tool_use"} block | Top-level {"type": "function_call", "call_id": "<id>", "name": "...", "arguments": "<JSON string>"} — pulled out of the message entirely |
assistant role, {"type": "thinking"} block | {"type": "output_text", "text": "<thinking text>"} inside an assistant message |
tools
| Anthropic tool | Responses API tool |
|---|---|
Any tool where type starts with "web_search" or name == "web_search" | {"type": "web_search_preview"} |
| All other tools | {"type": "function", "name": "...", "description": "...", "parameters": <input_schema>} |
tool_choice
Anthropic tool_choice.type | Responses API tool_choice |
|---|---|
"auto" | {"type": "auto"} |
"any" | {"type": "required"} |
"tool" | {"type": "function", "name": "<tool name>"} |
thinking → reasoning
The budget_tokens value is mapped to a string effort level. summary is always set to "detailed".
thinking.budget_tokens | reasoning.effort |
|---|---|
| >= 10000 | "high" |
| >= 5000 | "medium" |
| >= 2000 | "low" |
| < 2000 | "minimal" |
If thinking.type is anything other than "enabled", the reasoning field is not sent at all.
context_management
Anthropic uses a nested dict with an edits array. OpenAI uses a flat array of compaction objects. Only compact_20260112 becomes a compaction object. LiteLLM runs clear_tool_uses_20250919 on the messages before this translation, as Context Management describes.
Anthropic input:
{
"edits": [
{
"type": "compact_20260112",
"trigger": {"type": "input_tokens", "value": 150000}
}
]
}
Responses API output:
[
{"type": "compaction", "compact_threshold": 150000}
]
Response: Responses API → Anthropic
When the Responses API reply comes back, LiteLLM converts it into an Anthropic AnthropicMessagesResponse.
| Responses API field | Anthropic response field | Notes |
|---|---|---|
response.id | id | |
response.model | model | Falls back to "unknown-model" if missing |
ResponseReasoningItem — summary[*].text | content block {"type": "thinking", "thinking": "..."} | Each non-empty summary text becomes a thinking block |
ResponseOutputMessage — content[*] where type == "output_text" | content block {"type": "text", "text": "..."} | |
ResponseFunctionToolCall — {call_id, name, arguments} | content block {"type": "tool_use", "id": "...", "name": "...", "input": {...}} | arguments is JSON-parsed back into a dict |
Any function_call present in output | stop_reason: "tool_use" | |
response.status == "incomplete" | stop_reason: "max_tokens" | Takes precedence over the default |
| Everything else | stop_reason: "end_turn" | Default |
response.usage.input_tokens | usage.input_tokens | |
response.usage.output_tokens | usage.output_tokens | |
| (hardcoded) | type: "message" | Always set |
| (hardcoded) | role: "assistant" | Always set |
| (hardcoded) | stop_sequence: null | Always null on this path |