mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-07-03 14:05:26 +02:00
203 lines
6.5 KiB
Python
203 lines
6.5 KiB
Python
"""Pydantic passthrough of Anthropic protocol fields (e.g. ``cache_control``)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from api.models.anthropic import (
|
|
ContentBlockServerToolUse,
|
|
ContentBlockText,
|
|
ContentBlockWebSearchToolResult,
|
|
Message,
|
|
MessagesRequest,
|
|
SystemContent,
|
|
Tool,
|
|
)
|
|
from config.constants import ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS
|
|
from core.anthropic.native_messages_request import (
|
|
build_base_native_anthropic_request_body,
|
|
)
|
|
|
|
|
|
def test_cache_control_preserved_on_parsed_user_text_system_and_tool() -> None:
|
|
raw = {
|
|
"model": "m",
|
|
"max_tokens": 20,
|
|
"messages": [
|
|
{
|
|
"role": "user",
|
|
"content": [
|
|
{
|
|
"type": "text",
|
|
"text": "hi",
|
|
"cache_control": {"type": "ephemeral"},
|
|
}
|
|
],
|
|
}
|
|
],
|
|
"system": [
|
|
{
|
|
"type": "text",
|
|
"text": "be brief",
|
|
"cache_control": {"type": "ephemeral"},
|
|
}
|
|
],
|
|
"tools": [
|
|
{
|
|
"name": "alpha",
|
|
"input_schema": {"type": "object"},
|
|
"cache_control": {"type": "ephemeral"},
|
|
}
|
|
],
|
|
}
|
|
req = MessagesRequest.model_validate(raw)
|
|
block = req.messages[0].content[0]
|
|
assert isinstance(block, ContentBlockText)
|
|
assert block.model_dump()["cache_control"] == {"type": "ephemeral"}
|
|
s0 = req.system[0] if isinstance(req.system, list) else None
|
|
assert isinstance(s0, SystemContent)
|
|
assert s0.model_dump()["cache_control"] == {"type": "ephemeral"}
|
|
t0 = req.tools[0] if req.tools else None
|
|
assert isinstance(t0, Tool)
|
|
assert t0.model_dump()["cache_control"] == {"type": "ephemeral"}
|
|
|
|
|
|
def test_build_base_native_body_includes_cache_control() -> None:
|
|
req = MessagesRequest(
|
|
model="m",
|
|
max_tokens=20,
|
|
messages=[
|
|
Message(
|
|
role="user",
|
|
content=[
|
|
ContentBlockText.model_validate(
|
|
{
|
|
"type": "text",
|
|
"text": "x",
|
|
"cache_control": {"type": "ephemeral"},
|
|
}
|
|
)
|
|
],
|
|
)
|
|
],
|
|
system=[
|
|
SystemContent.model_validate(
|
|
{
|
|
"type": "text",
|
|
"text": "s",
|
|
"cache_control": {"type": "ephemeral"},
|
|
}
|
|
)
|
|
],
|
|
tools=[
|
|
Tool.model_validate(
|
|
{
|
|
"name": "n",
|
|
"input_schema": {"type": "object"},
|
|
"cache_control": {"type": "ephemeral"},
|
|
}
|
|
)
|
|
],
|
|
)
|
|
body = build_base_native_anthropic_request_body(
|
|
req,
|
|
default_max_tokens=ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
thinking_enabled=False,
|
|
)
|
|
assert body["messages"][0]["content"][0]["cache_control"] == {"type": "ephemeral"}
|
|
assert body["system"][0]["cache_control"] == {"type": "ephemeral"}
|
|
assert body["tools"][0]["cache_control"] == {"type": "ephemeral"}
|
|
|
|
|
|
def test_build_base_native_body_drops_unknown_top_level_client_hints() -> None:
|
|
raw = {
|
|
"model": "m",
|
|
"max_tokens": 20,
|
|
"messages": [{"role": "user", "content": "x"}],
|
|
"reasoning_effort": "none",
|
|
"unknown_client_hint": {"mode": "local"},
|
|
}
|
|
req = MessagesRequest.model_validate(raw)
|
|
body = build_base_native_anthropic_request_body(
|
|
req,
|
|
default_max_tokens=ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
thinking_enabled=False,
|
|
)
|
|
assert "reasoning_effort" not in body
|
|
assert "unknown_client_hint" not in body
|
|
|
|
|
|
def test_pydantic_discriminator_still_distinguishes_blocks() -> None:
|
|
m = Message.model_validate(
|
|
{
|
|
"role": "user",
|
|
"content": [{"type": "text", "text": "a", "z": 1}],
|
|
}
|
|
)
|
|
b = m.content[0]
|
|
assert isinstance(b, ContentBlockText)
|
|
assert b.model_dump()["z"] == 1
|
|
|
|
|
|
def test_server_tool_assistant_blocks_round_trip_in_native_body() -> None:
|
|
"""Local server-tool responses must parse as valid history for a follow-up request."""
|
|
raw = {
|
|
"model": "m",
|
|
"max_tokens": 20,
|
|
"messages": [
|
|
{
|
|
"role": "assistant",
|
|
"content": [
|
|
{
|
|
"type": "server_tool_use",
|
|
"id": "srvtoolu_1",
|
|
"name": "web_search",
|
|
"input": {"query": "q"},
|
|
},
|
|
{
|
|
"type": "web_search_tool_result",
|
|
"tool_use_id": "srvtoolu_1",
|
|
"content": [
|
|
{
|
|
"type": "web_search_result",
|
|
"title": "T",
|
|
"url": "https://example.com",
|
|
}
|
|
],
|
|
},
|
|
],
|
|
}
|
|
],
|
|
"mcp_servers": [{"type": "url", "url": "https://example.com/mcp"}],
|
|
}
|
|
req = MessagesRequest.model_validate(raw)
|
|
assert len(req.messages) == 1
|
|
blocks = req.messages[0].content
|
|
assert isinstance(blocks, list)
|
|
assert isinstance(blocks[0], ContentBlockServerToolUse)
|
|
assert isinstance(blocks[1], ContentBlockWebSearchToolResult)
|
|
body = build_base_native_anthropic_request_body(
|
|
req,
|
|
default_max_tokens=ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
thinking_enabled=False,
|
|
)
|
|
assert body["mcp_servers"][0]["type"] == "url"
|
|
assert body["messages"][0]["content"][0]["type"] == "server_tool_use"
|
|
assert body["messages"][0]["content"][1]["type"] == "web_search_tool_result"
|
|
|
|
|
|
def test_native_body_preserves_context_and_output_config() -> None:
|
|
raw = {
|
|
"model": "m",
|
|
"max_tokens": 20,
|
|
"messages": [{"role": "user", "content": "x"}],
|
|
"context_management": {"edits": [{"type": "clear"}]},
|
|
"output_config": {"some": "hint"},
|
|
}
|
|
req = MessagesRequest.model_validate(raw)
|
|
body = build_base_native_anthropic_request_body(
|
|
req,
|
|
default_max_tokens=ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
thinking_enabled=False,
|
|
)
|
|
assert body["context_management"] == raw["context_management"]
|
|
assert body["output_config"] == raw["output_config"]
|