Agentic schematic validation: datasheet extraction via Claude Console Skills, netlist/BOM design graph, per-IC direct datasheet review with page citations, capacitor derating, Next.js report UI. Extracted from the Pinscope cloud codebase. Auth and billing live in the private gateway repo behind stable seams (billing_hook.py, adapter files listed in CLAUDE.md).
82 lines
3.3 KiB
Python
82 lines
3.3 KiB
Python
"""Tests for the Anthropic cache_control breakpoint cap.
|
|
|
|
Anthropic rejects requests with >4 cache_control blocks. The review path adds
|
|
one per cacheable PDF excerpt, so a hub IC fetching two interface excerpts hit
|
|
5 (system + initial PDF + initial context + 2 excerpts) and the API returned
|
|
"A maximum of 4 blocks with cache_control may be provided. Found 5." The
|
|
provider caps message-block breakpoints so system(1) + messages never exceed 4.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from backend.services.llm.anthropic_provider import _enforce_cache_breakpoint_limit
|
|
|
|
|
|
def _cc(block: dict) -> bool:
|
|
return "cache_control" in block
|
|
|
|
|
|
def _count(messages: list[dict]) -> int:
|
|
return sum(_cc(b) for m in messages for b in m["content"])
|
|
|
|
|
|
def _ephemeral() -> dict:
|
|
return {"cache_control": {"type": "ephemeral"}}
|
|
|
|
|
|
def test_u2_two_excerpts_capped_to_three():
|
|
"""The exact failure: initial PDF + context + 2 excerpts = 4 message
|
|
breakpoints (5 with system). Cap to 3 so the total lands at 4."""
|
|
initial_pdf = {"type": "document", "source": {}, **_ephemeral()}
|
|
initial_text = {"type": "text", "text": "ctx", **_ephemeral()}
|
|
excerpt1 = {"type": "document", "source": {}, **_ephemeral()}
|
|
excerpt2 = {"type": "document", "source": {}, **_ephemeral()}
|
|
messages = [
|
|
{"role": "user", "content": [initial_pdf, initial_text]},
|
|
{"role": "assistant", "content": [{"type": "tool_use", "id": "a", "name": "x", "input": {}}]},
|
|
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "a", "content": "."}, excerpt1]},
|
|
{"role": "assistant", "content": [{"type": "tool_use", "id": "b", "name": "x", "input": {}}]},
|
|
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": "b", "content": "."}, excerpt2]},
|
|
]
|
|
|
|
_enforce_cache_breakpoint_limit(messages)
|
|
|
|
assert _count(messages) == 3 # +1 system = 4 total, within the limit
|
|
# The stable full-datasheet anchor and the two most recent excerpts survive;
|
|
# the initial context block (cheap to reprocess) loses its breakpoint.
|
|
assert _cc(initial_pdf)
|
|
assert not _cc(initial_text)
|
|
assert _cc(excerpt1)
|
|
assert _cc(excerpt2)
|
|
|
|
|
|
def test_heavy_fanout_keeps_anchor_and_tail():
|
|
"""A heavy-fanout review (many excerpts) still caps to 3 and always keeps
|
|
the first cacheable block (the full-datasheet anchor)."""
|
|
anchor = {"type": "document", "source": {}, **_ephemeral()}
|
|
messages = [{"role": "user", "content": [anchor, {"type": "text", "text": "x", **_ephemeral()}]}]
|
|
excerpts = []
|
|
for _ in range(6):
|
|
ex = {"type": "document", "source": {}, **_ephemeral()}
|
|
excerpts.append(ex)
|
|
messages.append({"role": "user", "content": [ex]})
|
|
|
|
_enforce_cache_breakpoint_limit(messages)
|
|
|
|
assert _count(messages) == 3
|
|
assert _cc(anchor)
|
|
# Two most-recent excerpts retained for incremental tail caching.
|
|
assert _cc(excerpts[-1]) and _cc(excerpts[-2])
|
|
|
|
|
|
def test_under_limit_untouched():
|
|
"""Three or fewer message breakpoints are left exactly as-is."""
|
|
pdf = {"type": "document", "source": {}, **_ephemeral()}
|
|
text = {"type": "text", "text": "x", **_ephemeral()}
|
|
messages = [{"role": "user", "content": [pdf, text]}]
|
|
|
|
_enforce_cache_breakpoint_limit(messages)
|
|
|
|
assert _cc(pdf) and _cc(text)
|
|
assert _count(messages) == 2
|