fix(anthropic): map stop_sequences to stop in /v1/messages adapter

This commit is contained in:
Devin AI 2026-07-13 15:07:38 +00:00
parent c2141b1113
commit f6f3c3938f
2 changed files with 55 additions and 0 deletions

View file

@ -324,6 +324,7 @@ class LiteLLMAnthropicMessagesAdapter:
return [
"messages",
"metadata",
"stop_sequences",
"system",
"tool_choice",
"tools",
@ -903,6 +904,21 @@ class LiteLLMAnthropicMessagesAdapter:
# metadata will be passed to litellm.acompletion(), it's a litellm_param
new_kwargs["metadata"] = anthropic_message_request.pop("litellm_metadata")
def _translate_stop_sequences_to_openai(
self,
anthropic_message_request: AnthropicMessagesRequest,
new_kwargs: ChatCompletionRequest,
) -> None:
"""Translate Anthropic ``stop_sequences`` to OpenAI ``stop``.
Anthropic-native backends map OpenAI ``stop`` back to ``stop_sequences``,
while OpenAI-format providers reject the ``stop_sequences`` field, so the
adapter must normalize to ``stop`` rather than passing it through verbatim.
"""
stop_sequences = anthropic_message_request.get("stop_sequences")
if stop_sequences:
new_kwargs["stop"] = stop_sequences
def _translate_tool_choice_to_openai(
self,
anthropic_message_request: AnthropicMessagesRequest,
@ -1082,6 +1098,11 @@ class LiteLLMAnthropicMessagesAdapter:
anthropic_message_request=anthropic_message_request,
new_kwargs=new_kwargs,
)
## CONVERT STOP SEQUENCES
self._translate_stop_sequences_to_openai(
anthropic_message_request=anthropic_message_request,
new_kwargs=new_kwargs,
)
## CONVERT TOOL CHOICE
self._translate_tool_choice_to_openai(
anthropic_message_request=anthropic_message_request,

View file

@ -3111,3 +3111,37 @@ def test_translate_anthropic_tools_to_openai_preserves_parameters_type():
params = new_tools[0]["function"]["parameters"]
assert params["type"] == "object"
assert new_tools[0]["type"] == "function"
def test_translate_anthropic_to_openai_maps_stop_sequences_to_stop():
"""Regression for #33075: Anthropic `stop_sequences` must be translated to
the OpenAI `stop` param, otherwise OpenAI-format providers 400 on the
unknown `stop_sequences` field."""
adapter = LiteLLMAnthropicMessagesAdapter()
request = {
"model": "azure_ai/gpt-4o",
"max_tokens": 100,
"messages": [{"role": "user", "content": "hi"}],
"stop_sequences": ["\n\nHuman:", "END"],
}
result, _ = adapter.translate_anthropic_to_openai(anthropic_message_request=cast(Any, request))
assert result["stop"] == ["\n\nHuman:", "END"]
assert "stop_sequences" not in result
def test_translate_anthropic_to_openai_omits_stop_when_no_stop_sequences():
"""No `stop` key should be added when the Anthropic request has no
`stop_sequences`."""
adapter = LiteLLMAnthropicMessagesAdapter()
request = {
"model": "azure_ai/gpt-4o",
"max_tokens": 100,
"messages": [{"role": "user", "content": "hi"}],
}
result, _ = adapter.translate_anthropic_to_openai(anthropic_message_request=cast(Any, request))
assert "stop" not in result
assert "stop_sequences" not in result