fix: pass together_ai replay/streaming tests under the thinking contract

Controlled base-vs-head run of the full llms test shard exposed two
together_ai anthropic-messages tests broken by the thinking_disabled
contract change in this PR:

- test_anthropic_messages_replays_tool_loop: an unsigned thinking block
  in replayed history was being dropped entirely. Unsigned thinking texts
  now map to the provider-facing reasoning_content field (keeping the
  signature-400 defense: they still stay out of thinking_blocks).
- replay + streaming tests asserted provider reasoning is surfaced
  without a thinking param on the request; per the PR contract that is
  suppressed. Updated both to assert the suppression (sending
  thinking=enabled would fail together's parameter validation via
  reasoning_effort).
This commit is contained in:
Daniel Cherubini 2026-09-14 20:21:57 +02:00
parent 63323a1dfd
commit b7deea30aa
2 changed files with 24 additions and 2 deletions

View file

@ -495,6 +495,7 @@ class LiteLLMAnthropicMessagesAdapter:
has_cache_control_in_text = False
tool_calls: list[ChatCompletionAssistantToolCall] = []
thinking_blocks: list[ChatCompletionThinkingBlock | ChatCompletionRedactedThinkingBlock] = []
unsigned_thinking_texts: list[str] = []
if m["role"] == "assistant":
if isinstance(m.get("content"), str):
assistant_message_str = str(m.get("content", ""))
@ -555,6 +556,15 @@ class LiteLLMAnthropicMessagesAdapter:
signature=content.get("signature") or "",
)
thinking_blocks.append(thinking_block)
else:
# Unsigned text is NOT dropped: it is
# replayed as the flat reasoning_content field
# below (the provider-visible form), while
# staying out of thinking_blocks, so the
# signature-400 stays avoided.
unsigned_text = str(content.get("thinking") or "")
if unsigned_text:
unsigned_thinking_texts.append(unsigned_text)
elif content.get("type") == "redacted_thinking":
redacted_thinking_block = ChatCompletionRedactedThinkingBlock(
type="redacted_thinking",
@ -587,6 +597,9 @@ class LiteLLMAnthropicMessagesAdapter:
if len(thinking_blocks) > 0:
assistant_message["thinking_blocks"] = thinking_blocks
reasoning_content = reasoning_content_from_thinking_blocks(thinking_blocks)
if unsigned_thinking_texts:
unsigned = "\n".join(unsigned_thinking_texts)
reasoning_content = f"{reasoning_content}\n{unsigned}" if reasoning_content else unsigned
if reasoning_content:
assistant_message["reasoning_content"] = reasoning_content
new_messages.append(assistant_message)

View file

@ -1013,7 +1013,11 @@ def test_anthropic_messages_replays_tool_loop_and_maps_reasoning_to_thinking_blo
assert tool_turn["content"] == "Sunny, 18C"
blocks = {block["type"]: block for block in response["content"]}
assert blocks["thinking"]["thinking"] == "Tool said sunny."
# Contract (thinking param absent): the provider's reasoning is NOT
# surfaced as an Anthropic thinking block. The mock response still
# carries reasoning, and this asserts it is suppressed.
assert "thinking" not in blocks
assert blocks["text"]["text"] == "Sunny in SF."
assert response["stop_reason"] == "end_turn"
@ -1031,6 +1035,9 @@ def test_anthropic_messages_streams_together_tool_call_as_input_json_delta():
captured_requests: list[httpx.Request] = []
client = _sync_client(captured_requests, _sse_response(*PARALLEL_TOOL_CALL_STREAM))
# Explicit thinking= would be needed to surface provider reasoning
# (the pass-through contract suppresses it when the thinking param is
# absent); this test asserts the streaming translation only.
events = _anthropic_sse_events(
litellm.anthropic.messages.create(
model=f"together_ai/{UNMAPPED_MODEL}",
@ -1067,7 +1074,9 @@ def test_anthropic_messages_streams_together_tool_call_as_input_json_delta():
for event in events
if event["type"] == "content_block_delta" and event["delta"]["type"] == "thinking_delta"
)
assert thinking_text == "Need weather and time."
# thinking param absent in the request: provider reasoning is
# suppressed per the contract, so no thinking block may appear.
assert thinking_text == ""
assert [event["delta"]["stop_reason"] for event in events if event["type"] == "message_delta"] == ["tool_use"]