mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-08 22:21:35 +00:00
Claude Code drives Opus 4.7 with thinking {"type": "adaptive"} plus
output_config {"effort": "max"}. The anthropic-to-openai adapter
forwarded thinking verbatim for Claude models but dropped output_config,
and Bedrock Converse streams zero reasoningContent blocks for adaptive
thinking without an effort tier. Forward the effort subset of
output_config for Bedrock targets, accept it in the converse supported
params, and map it with the model's effort ceiling applied. Re-enable
the skipped e2e compat cell that catches this
97 lines
3.1 KiB
Python
97 lines
3.1 KiB
Python
"""thinking x Bedrock (Converse).
|
|
|
|
Drive the real `claude` CLI against a running LiteLLM proxy that routes
|
|
Claude requests to AWS Bedrock via the unified `Converse` API path,
|
|
enable extended thinking via `--effort high`, and assert that the
|
|
upstream returned a `thinking` content block.
|
|
|
|
The (feature, provider) for this cell is inferred from the file path by
|
|
`tests/e2e/claude_code/conftest.py`:
|
|
|
|
tests/e2e/claude_code/thinking/test_bedrock_converse.py
|
|
^^^^^^^^ ^^^^^^^^^^^^^^^^
|
|
feature_id provider
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Mapping, Sequence
|
|
|
|
import pytest
|
|
|
|
from claude_code._env import require_proxy
|
|
from claude_code.cli_driver import (
|
|
ClaudeCLIError,
|
|
failure_diagnostic,
|
|
run_claude_models_parallel,
|
|
)
|
|
|
|
|
|
BEDROCK_CONVERSE_MODELS = [
|
|
"claude-haiku-4-5-bedrock-converse",
|
|
"claude-sonnet-4-5-bedrock-converse",
|
|
"claude-opus-4-7-bedrock-converse",
|
|
]
|
|
|
|
THINKING_ARGS = ["--effort", "max"]
|
|
THINKING_PROMPT = (
|
|
"I have a 3-gallon jug and a 5-gallon jug. How can I measure "
|
|
"exactly 4 gallons of water? Think through the steps carefully."
|
|
)
|
|
|
|
|
|
def _has_thinking_block(events: Sequence[Mapping[str, Any]]) -> bool:
|
|
for event in events:
|
|
if event.get("type") != "assistant":
|
|
continue
|
|
message = event.get("message") or {}
|
|
content = message.get("content")
|
|
if not isinstance(content, list):
|
|
continue
|
|
for block in content:
|
|
if isinstance(block, dict) and block.get("type") == "thinking":
|
|
return True
|
|
return False
|
|
|
|
|
|
@pytest.mark.covers("llm.messages.bedrock_converse.thinking.nonstream.works")
|
|
def test_thinking_bedrock_converse(compat_result):
|
|
"""Drive the `claude` CLI against the LiteLLM proxy with thinking
|
|
enabled and assert a `thinking` content block was emitted."""
|
|
base_url, api_key = require_proxy(compat_result)
|
|
|
|
outcomes = run_claude_models_parallel(
|
|
models=BEDROCK_CONVERSE_MODELS,
|
|
prompt=THINKING_PROMPT,
|
|
base_url=base_url,
|
|
api_key=api_key,
|
|
extra_args=THINKING_ARGS,
|
|
)
|
|
|
|
failures = []
|
|
for model in BEDROCK_CONVERSE_MODELS:
|
|
outcome = outcomes[model]
|
|
if isinstance(outcome, ClaudeCLIError):
|
|
error = f"[{model}] {outcome}"
|
|
compat_result.add({"status": "fail", "error": error})
|
|
failures.append(error)
|
|
continue
|
|
|
|
if outcome.exit_code != 0:
|
|
error = f"[{model}] claude CLI failed: {failure_diagnostic(outcome)}"
|
|
compat_result.add({"status": "fail", "error": error})
|
|
failures.append(error)
|
|
continue
|
|
|
|
if not _has_thinking_block(outcome.events):
|
|
error = (
|
|
f"[{model}] no `thinking` content block observed in stream-json events"
|
|
)
|
|
compat_result.add({"status": "fail", "error": error})
|
|
failures.append(error)
|
|
continue
|
|
|
|
compat_result.add({"status": "pass"})
|
|
|
|
if failures:
|
|
pytest.fail("; ".join(failures), pytrace=False)
|