Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ def translatable_anthropic_params(self) -> List:
"thinking",
"output_format",
"output_config",
"stop_sequences",
]

def _is_web_search_tool(self, tool: Dict[str, Any]) -> bool:
Expand Down Expand Up @@ -615,7 +616,7 @@ def translate_anthropic_thinking_to_reasoning_effort(
thinking_type = thinking.get("type", "disabled")

if thinking_type == "disabled":
return None
return "none"
Comment thread
cursor[bot] marked this conversation as resolved.
Resolved
elif thinking_type == "enabled":
return reasoning_effort_from_thinking_budget(thinking.get("budget_tokens", 0))
elif thinking_type == "adaptive":
Expand Down Expand Up @@ -919,6 +920,18 @@ def _translate_tool_choice_to_openai(
tool_choice=cast(AnthropicMessagesToolChoice, tool_choice)
)

def _translate_stop_sequences_to_openai(
self,
anthropic_message_request: AnthropicMessagesRequest,
new_kwargs: ChatCompletionRequest,
) -> None:
if "stop_sequences" not in anthropic_message_request:
return
stop_sequences = anthropic_message_request["stop_sequences"]
if not stop_sequences:
return
new_kwargs["stop"] = stop_sequences

def _translate_tools_to_openai(
self,
anthropic_message_request: AnthropicMessagesRequest,
Expand Down Expand Up @@ -976,12 +989,18 @@ def _translate_thinking_to_openai(
if not reasoning_effort:
return

thinking_type = thinking.get("type") if isinstance(thinking, dict) else None

# For adaptive thinking, override with output_config.effort if available
if isinstance(thinking, dict) and thinking.get("type") == "adaptive":
if thinking_type == "adaptive":
output_config = anthropic_message_request.get("output_config")
if isinstance(output_config, dict) and output_config.get("effort"):
reasoning_effort = output_config["effort"]

if thinking_type == "disabled":
new_kwargs["reasoning_effort"] = reasoning_effort
return

summary = thinking.get("summary") if isinstance(thinking, dict) else None
auto_summary = is_reasoning_auto_summary_enabled()
if summary:
Expand Down Expand Up @@ -1098,6 +1117,11 @@ def translate_anthropic_to_openai(
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 OUTPUT_FORMAT to RESPONSE_FORMAT
self._translate_output_format_to_openai(
anthropic_message_request=anthropic_message_request,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,67 @@ def test_thinking_still_translated_to_reasoning_effort_for_non_claude_model():
assert new_kwargs["reasoning_effort"] == "low"


def test_thinking_disabled_translated_to_reasoning_effort_none_for_non_claude_model():
adapter = LiteLLMAnthropicMessagesAdapter()
thinking = {"type": "disabled"}

new_kwargs = {"model": CACHE_CONTROL_NON_ANTHROPIC_MODEL}
adapter._translate_thinking_to_openai(cast(Any, {"thinking": thinking}), cast(Any, new_kwargs))

assert "thinking" not in new_kwargs
assert new_kwargs["reasoning_effort"] == "none"


def test_thinking_disabled_stays_plain_string_when_auto_summary_enabled():
import litellm

adapter = LiteLLMAnthropicMessagesAdapter()
thinking = {"type": "disabled"}

original = litellm.reasoning_auto_summary
try:
litellm.reasoning_auto_summary = True
new_kwargs = {"model": CACHE_CONTROL_NON_ANTHROPIC_MODEL}
adapter._translate_thinking_to_openai(cast(Any, {"thinking": thinking}), cast(Any, new_kwargs))
finally:
litellm.reasoning_auto_summary = original

assert new_kwargs["reasoning_effort"] == "none"


def test_stop_sequences_translated_to_stop_for_non_claude_model():
from litellm.types.llms.anthropic import AnthropicMessagesRequest

anthropic_request = AnthropicMessagesRequest(
model=CACHE_CONTROL_NON_ANTHROPIC_MODEL,
max_tokens=1024,
messages=[{"role": "user", "content": "hi"}],
stop_sequences=["</block>"],
)

adapter = LiteLLMAnthropicMessagesAdapter()
openai_request, _ = adapter.translate_anthropic_to_openai(anthropic_message_request=anthropic_request)

assert openai_request["stop"] == ["</block>"]
assert "stop_sequences" not in openai_request


def test_empty_stop_sequences_does_not_set_stop():
from litellm.types.llms.anthropic import AnthropicMessagesRequest

anthropic_request = AnthropicMessagesRequest(
model=CACHE_CONTROL_NON_ANTHROPIC_MODEL,
max_tokens=1024,
messages=[{"role": "user", "content": "hi"}],
stop_sequences=[],
)

adapter = LiteLLMAnthropicMessagesAdapter()
openai_request, _ = adapter.translate_anthropic_to_openai(anthropic_message_request=anthropic_request)

assert "stop" not in openai_request


def test_cache_control_preserved_in_image_content_for_claude():
"""Cache control should be preserved in image content for Claude models."""
anthropic_messages = [
Expand Down
Loading