import pytest from aiatlas.connectors.labs.anthropic import AnthropicConnector, _display_name, _name_variants from aiatlas.sdk.facts import Target from tests.conftest import claims_of, entity_names, extract_from_fixture, fixture_path @pytest.fixture def connector() -> AnthropicConnector: return AnthropicConnector() async def test_models_overview(connector): target = Target(url="https://docs.claude.com/en/docs/about-claude/models/overview.md", doc_type="model_docs", key="models") facts = await extract_from_fixture(connector, target, fixture_path("anthropic", "models-overview.md"), content_type="text/markdown") models = entity_names(facts, "model") assert {"Claude Opus 5", "Claude Sonnet 5", "Claude Haiku 4.5"} <= models opus = claims_of(facts, "Claude Opus 5") assert opus["context_length"] == 1_000_000 and opus["max_output_tokens"] == 128_000 assert opus["api_model_id"] == "claude-opus-5" and opus["openness"] == "proprietary" assert opus["knowledge_cutoff"] == "2026-05" and opus["retirement_date"] == "2027-07-24" and opus["retirement_tentative"] is True haiku = claims_of(facts, "Claude Haiku 4.5") assert haiku["context_length"] == 200_000 and haiku["api_model_id"] == "claude-haiku-4-5-20251001" assert any(t.doc_type == "model_page" for t in facts.targets) assert any(r.predicate == "develops" for r in facts.relations) async def test_pricing(connector): target = Target(url="https://docs.claude.com/en/docs/about-claude/pricing.md", doc_type="pricing", key="pricing") facts = await extract_from_fixture(connector, target, fixture_path("anthropic", "pricing.md"), content_type="text/markdown") by_model = {p.model.name: p for p in facts.prices} assert by_model["Claude Opus 5"].input_per_mtok == 5 and by_model["Claude Opus 5"].output_per_mtok == 25 assert by_model["Claude Opus 5"].cached_input_per_mtok == 0.5 and by_model["Claude Opus 5"].cache_write_per_mtok == 6.25 assert by_model["Claude Haiku 3.5"].input_per_mtok == 0.8 assert claims_of(facts, "Claude Opus 4.1")["status"] == "retired" assert claims_of(facts, "Claude Mythos 5.1")["status"] == "limited-availability" assert len(facts.prices) >= 15 async def test_deprecations(connector): target = Target(url="https://docs.claude.com/en/docs/about-claude/model-deprecations.md", doc_type="model_docs", key="deprecations") facts = await extract_from_fixture(connector, target, fixture_path("anthropic", "model-deprecations.md"), content_type="text/markdown") sonnet37 = claims_of(facts, "Claude 3.7 Sonnet") assert sonnet37["status"] == "retired" and sonnet37["deprecation_date"] == "2025-10-28" and sonnet37["retirement_date"] == "2026-02-19" opus5 = claims_of(facts, "Claude Opus 5") assert opus5["status"] == "active" and opus5["retirement_tentative"] is True async def test_news(connector): target = Target(url="https://www.anthropic.com/news", doc_type="listing", key="news") facts = await extract_from_fixture(connector, target, fixture_path("anthropic", "news.html")) events = [e for e in facts.events if e.event_type == "ANNOUNCEMENT"] assert len(events) >= 8 opus = next(e for e in events if "Opus 5" in e.summary) assert opus.effective_at is not None and opus.effective_at.year == 2026 and opus.category == "release" assert any(t.needs_llm for t in facts.targets) def test_display_name(): assert _display_name("claude-opus-4-5-20251101") == "Claude Opus 4.5" assert _display_name("claude-3-7-sonnet-20250219") == "Claude 3.7 Sonnet" assert _display_name("claude-fable-5-1") == "Claude Fable 5.1" assert _name_variants("Claude 3.5 Haiku") == ["Claude Haiku 3.5"]