"""Live Anthropic passthrough: the vendor-native route delegations will use. Proves the renderer's body is accepted as-is, that a tool call round-trips through the canonical log, and — per A5 — that prompt caching survives the passthrough rather than being flattened by the proxy. """ import io from pathlib import Path import httpx import pytest from nisse_dev.canonical import AssistantText, ToolCall, ToolResult, UserMessage from nisse_dev.config import AgentConfig from nisse_dev.llm import AnthropicPassthroughClient from nisse_dev.oplog import OpLog from nisse_dev.state import StateStore from nisse_dev.tools import EXECUTOR_TOOLS pytestmark = pytest.mark.integration def store_with(tmp_path: Path, *events) -> StateStore: store = StateStore(tmp_path / "state.db", oplog=OpLog("nisse-dev", io.StringIO())) for event in events: store.append_event(event) return store def test_passthrough_completes_a_plain_turn( tmp_path: Path, passthrough: AnthropicPassthroughClient, live_model: str ) -> None: store = store_with( tmp_path, UserMessage(text="Reply with exactly: pong", source="live") ) result = passthrough.complete( store.read_events(), None, live_model, system="Be terse." ) texts = [e.text for e in result if isinstance(e, AssistantText)] assert texts and "pong" in texts[0].lower() def test_passthrough_round_trips_a_tool_call( tmp_path: Path, passthrough: AnthropicPassthroughClient, live_model: str ) -> None: store = store_with( tmp_path, UserMessage( text="List the files in the current directory using your tools.", source="live", ), ) client = passthrough produced = client.complete( store.read_events(), EXECUTOR_TOOLS, live_model, system="Use a tool. Do not answer in prose.", ) calls = [e for e in produced if isinstance(e, ToolCall)] assert calls, f"expected a tool call, got {produced!r}" # Feed the result back: the follow-up render must be accepted, which is # what proves tool_result placement is right for this vendor. for event in produced: store.append_event(event) store.append_event( ToolResult(call_id=calls[0].call_id, result="app.py\ntests/", success=True) ) follow_up = client.complete(store.read_events(), EXECUTOR_TOOLS, live_model) assert follow_up, "the vendor rejected or ignored the tool_result turn" def test_prompt_caching_survives_the_passthrough( live_config: AgentConfig, live_model: str ) -> None: # A5 asks for this to be verified rather than assumed: a proxy that # flattened the request would silently drop cache control and turn a # cost optimisation into a per-call expense. base = live_config.llm_base_url.rstrip("/") token = live_config.llm_token response = httpx.post( f"{base}/v1/messages", headers={ "Authorization": f"Bearer {token}", "x-api-key": token, "anthropic-version": "2023-06-01", }, json={ "model": live_model, "max_tokens": 16, "messages": [{"role": "user", "content": "Say pong."}], }, timeout=120, ) response.raise_for_status() usage = response.json().get("usage", {}) assert "cache_read_input_tokens" in usage, ( f"cache accounting absent from passthrough usage: {usage}" )