test: freeze tool-call contract semantics

Lock the current Anthropic streaming asymmetry so future refactors do not silently synthesize tool blocks. Align schema and docs with the actual support level to avoid over-promising forced-tool fallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
GitHub Actions
2026-04-22 10:56:21 +08:00
parent e3d3a63492
commit d081743924
6 changed files with 55 additions and 9 deletions

View File

@@ -662,6 +662,43 @@ class ToolCallBridgeTests(unittest.IsolatedAsyncioTestCase):
self.assertIn('"type": "tool_use"', body)
self.assertIn('"stop_reason": "tool_use"', body)
async def test_anthropic_stream_does_not_fallback_to_synthetic_tool_blocks_for_forced_tool(self) -> None:
fake_client = _FakeClient(
stream_events=[
{"type": "text", "text": '```json\n{"input": {"k": "v"}, "result": {"value": 1}}\n```'}
],
complete_result={},
)
req = AnthropicMessagesRequest(
model="claude-3-5-sonnet-20241022",
max_tokens=256,
messages=[{"role": "user", "content": "hi"}],
stream=True,
tools=[{"name": "lookup", "input_schema": {"type": "object", "properties": {}}}],
tool_choice={"type": "tool", "name": "lookup"},
)
with (
patch.object(main, "pool", _FakePool(_FakeInstance(fake_client))),
patch.object(main, "chat_guard", _FakeGuard()),
patch.object(main, "_ensure_instance_logged_in", AsyncMock(return_value={"id": "u"})),
patch.object(main.stats_collector, "record_chat", AsyncMock(return_value=None)),
patch.object(main.settings, "api_keys", ["test-key"]),
):
response = await main.v1_messages(
req,
_make_request(
"/v1/messages",
headers={"x-api-key": "test-key", "anthropic-version": "2023-06-01"},
),
)
body = await _collect_stream(response)
self.assertNotIn('"type": "tool_use"', body)
self.assertNotIn('"type": "tool_result"', body)
self.assertIn('"type": "text_delta"', body)
self.assertIn('"stop_reason": "end_turn"', body)
async def test_anthropic_stream_bridges_tool_and_text_events(self) -> None:
fake_client = _FakeClient(
stream_events=[