Spaces:
Sleeping
Sleeping
| """Tests for api/models/responses.py Pydantic response models.""" | |
| from api.models.anthropic import ( | |
| ContentBlockText, | |
| ContentBlockThinking, | |
| ContentBlockToolUse, | |
| ) | |
| from api.models.responses import MessagesResponse, TokenCountResponse, Usage | |
| class TestUsage: | |
| """Tests for Usage model.""" | |
| def test_required_fields(self): | |
| usage = Usage(input_tokens=10, output_tokens=20) | |
| assert usage.input_tokens == 10 | |
| assert usage.output_tokens == 20 | |
| def test_cache_defaults_zero(self): | |
| usage = Usage(input_tokens=1, output_tokens=2) | |
| assert usage.cache_creation_input_tokens == 0 | |
| assert usage.cache_read_input_tokens == 0 | |
| def test_cache_fields_set(self): | |
| usage = Usage( | |
| input_tokens=10, | |
| output_tokens=20, | |
| cache_creation_input_tokens=5, | |
| cache_read_input_tokens=3, | |
| ) | |
| assert usage.cache_creation_input_tokens == 5 | |
| assert usage.cache_read_input_tokens == 3 | |
| def test_serialization(self): | |
| usage = Usage(input_tokens=10, output_tokens=20) | |
| data = usage.model_dump() | |
| assert data == { | |
| "input_tokens": 10, | |
| "output_tokens": 20, | |
| "cache_creation_input_tokens": 0, | |
| "cache_read_input_tokens": 0, | |
| } | |
| class TestTokenCountResponse: | |
| """Tests for TokenCountResponse model.""" | |
| def test_basic(self): | |
| resp = TokenCountResponse(input_tokens=42) | |
| assert resp.input_tokens == 42 | |
| def test_serialization(self): | |
| resp = TokenCountResponse(input_tokens=100) | |
| data = resp.model_dump() | |
| assert data == {"input_tokens": 100} | |
| class TestMessagesResponse: | |
| """Tests for MessagesResponse model.""" | |
| def test_minimum_fields(self): | |
| resp = MessagesResponse( | |
| id="msg_001", | |
| model="test-model", | |
| content=[ContentBlockText(type="text", text="Hello")], | |
| usage=Usage(input_tokens=10, output_tokens=5), | |
| ) | |
| assert resp.id == "msg_001" | |
| assert resp.model == "test-model" | |
| assert resp.role == "assistant" | |
| assert resp.type == "message" | |
| assert resp.stop_reason is None | |
| assert resp.stop_sequence is None | |
| def test_with_text_content(self): | |
| resp = MessagesResponse( | |
| id="msg_002", | |
| model="model", | |
| content=[ContentBlockText(type="text", text="response")], | |
| usage=Usage(input_tokens=1, output_tokens=1), | |
| ) | |
| assert len(resp.content) == 1 | |
| block = resp.content[0] | |
| assert isinstance(block, ContentBlockText) | |
| assert block.type == "text" | |
| assert block.text == "response" | |
| def test_with_tool_use_content(self): | |
| resp = MessagesResponse( | |
| id="msg_003", | |
| model="model", | |
| content=[ | |
| ContentBlockToolUse( | |
| type="tool_use", | |
| id="tool_1", | |
| name="Read", | |
| input={"path": "test.py"}, | |
| ) | |
| ], | |
| usage=Usage(input_tokens=1, output_tokens=1), | |
| stop_reason="tool_use", | |
| ) | |
| block = resp.content[0] | |
| assert isinstance(block, ContentBlockToolUse) | |
| assert block.type == "tool_use" | |
| assert block.name == "Read" | |
| assert resp.stop_reason == "tool_use" | |
| def test_with_thinking_content(self): | |
| resp = MessagesResponse( | |
| id="msg_004", | |
| model="model", | |
| content=[ | |
| ContentBlockThinking(type="thinking", thinking="Let me reason..."), | |
| ContentBlockText(type="text", text="Answer"), | |
| ], | |
| usage=Usage(input_tokens=5, output_tokens=10), | |
| ) | |
| assert len(resp.content) == 2 | |
| block0 = resp.content[0] | |
| assert isinstance(block0, ContentBlockThinking) | |
| assert block0.type == "thinking" | |
| assert block0.thinking == "Let me reason..." | |
| block1 = resp.content[1] | |
| assert isinstance(block1, ContentBlockText) | |
| assert block1.type == "text" | |
| def test_with_all_content_types(self): | |
| resp = MessagesResponse( | |
| id="msg_005", | |
| model="model", | |
| content=[ | |
| ContentBlockThinking(type="thinking", thinking="hmm"), | |
| ContentBlockText(type="text", text="result"), | |
| ContentBlockToolUse( | |
| type="tool_use", id="t1", name="Bash", input={"command": "ls"} | |
| ), | |
| ], | |
| usage=Usage(input_tokens=10, output_tokens=20), | |
| stop_reason="tool_use", | |
| ) | |
| assert len(resp.content) == 3 | |
| def test_with_dict_content(self): | |
| """Dict content (unknown block type) should be accepted.""" | |
| resp = MessagesResponse( | |
| id="msg_006", | |
| model="model", | |
| content=[{"type": "custom", "data": "value"}], | |
| usage=Usage(input_tokens=1, output_tokens=1), | |
| ) | |
| block = resp.content[0] | |
| assert isinstance(block, dict) | |
| assert block["type"] == "custom" | |
| def test_stop_reason_values(self): | |
| """All valid stop_reason values should be accepted.""" | |
| from typing import Literal | |
| reasons: list[ | |
| Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"] | |
| ] = [ | |
| "end_turn", | |
| "max_tokens", | |
| "stop_sequence", | |
| "tool_use", | |
| ] | |
| for reason in reasons: | |
| resp = MessagesResponse( | |
| id="msg", | |
| model="model", | |
| content=[ContentBlockText(type="text", text="x")], | |
| usage=Usage(input_tokens=1, output_tokens=1), | |
| stop_reason=reason, | |
| ) | |
| assert resp.stop_reason == reason | |
| def test_serialization_round_trip(self): | |
| resp = MessagesResponse( | |
| id="msg_rt", | |
| model="model-v1", | |
| content=[ContentBlockText(type="text", text="hello")], | |
| usage=Usage(input_tokens=10, output_tokens=5), | |
| stop_reason="end_turn", | |
| ) | |
| data = resp.model_dump() | |
| restored = MessagesResponse(**data) | |
| assert restored.id == resp.id | |
| assert restored.model == resp.model | |
| assert restored.stop_reason == resp.stop_reason | |
| def test_empty_content_list(self): | |
| resp = MessagesResponse( | |
| id="msg_empty", | |
| model="model", | |
| content=[], | |
| usage=Usage(input_tokens=0, output_tokens=0), | |
| ) | |
| assert resp.content == [] | |