1from aiatlas.connectors.providers.provider_pricing import (2 FireworksPricingConnector,3 GroqPricingConnector,4 TogetherPricingConnector,5)6from aiatlas.sdk.facts import Target7from tests.conftest import claims_of, extract_from_fixture, fixture_path8910async def test_groq_models_table():11 c = GroqPricingConnector()12 target = Target(url="https://console.groq.com/docs/models", doc_type="model_docs", key="groq_models")13 facts = await extract_from_fixture(c, target, fixture_path("provider_pricing", "groq-models.html"))14 p = next(p for p in facts.prices if p.provider_model_id == "openai/gpt-oss-120b")15 assert (p.input_per_mtok, p.output_per_mtok, p.context_length, p.max_output_tokens) == (0.15, 0.60, 131072, 65536)16 assert p.provider.name == "GroqCloud" and p.model.name == "GPT OSS 120B" and p.model.organization.name == "OpenAI"17 assert p.features["output_tokens_per_second"] == 500 and p.features["groq_section"] == "production"18 assert p.model.identifiers == {"groq_model_id": "openai/gpt-oss-120b"}19 llama = claims_of(facts, "Llama 3.1 8B")20 assert "context_length" not in llama and "groq_pricing_note" in llama # "Contact Sales" → no price row; limits stay on price rows21 assert p.features["context_length"] == 13107222 assert not any(p.provider_model_id == "whisper-large-v3" for p in facts.prices) # per-hour pricing is not per-token23 assert len(facts.prices) >= 6 and c.tier == 1242526async def test_groq_pricing_page_is_client_rendered():27 c = GroqPricingConnector()28 targets = await c.discover(None) # type: ignore[arg-type]29 assert next(t for t in targets if t.key == "groq_pricing").escalate is True30 facts = await extract_from_fixture(c, targets[1], fixture_path("provider_pricing", "groq-pricing.html"))31 assert facts.prices == [] and [e.entity_type for e in facts.entities] == ["provider"]323334async def test_together_tables():35 c = TogetherPricingConnector()36 target = Target(url="https://www.together.ai/pricing", doc_type="pricing", key="together_pricing")37 facts = await extract_from_fixture(c, target, fixture_path("provider_pricing", "together-pricing.html"))38 by_slug = {p.provider_model_id: p for p in facts.prices}39 m3 = by_slug["minimax-m3"]40 assert (m3.input_per_mtok, m3.cached_input_per_mtok, m3.output_per_mtok) == (0.30, 0.06, 1.20)41 assert (m3.batch_input_per_mtok, m3.batch_output_per_mtok) == (0.30, 1.20) and m3.model.organization.name == "MiniMax"42 assert m3.model.identifiers == {"together_ai_model_slug": "minimax-m3"} and m3.provider.name == "Together AI"43 assert by_slug["deepseek-v4-flash-0731"].input_per_mtok == 0.14 and len(facts.prices) >= 2044 assert len({p.provider_model_id for p in facts.prices}) == len(facts.prices) # one price row per model454647async def test_fireworks_markdown():48 c = FireworksPricingConnector()49 target = Target(url="https://docs.fireworks.ai/serverless/pricing.md", doc_type="pricing", key="fireworks_serverless")50 facts = await extract_from_fixture(c, target, fixture_path("provider_pricing", "fireworks-serverless-pricing.md"), content_type="text/markdown")51 by_id = {p.provider_model_id: p for p in facts.prices}52 k3 = by_id["fireworks/kimi-k3"]53 assert (k3.input_per_mtok, k3.cached_input_per_mtok, k3.output_per_mtok) == (3.0, 0.30, 15.0)54 assert k3.features["priority"] == {"input_per_mtok": 3.75, "cached_input_per_mtok": 0.375, "output_per_mtok": 18.75} and k3.features["reserved_throughput"] is True55 assert k3.model.organization.name == "Moonshot AI" and k3.model.identifiers == {"fireworks-ai_model_id": "fireworks/kimi-k3"}56 fast = by_id["fireworks/kimi-k3:fast"]57 assert fast.input_per_mtok == 4.5 and fast.model is k3.model and fast.features["serving_path"] == "fast"58 assert by_id["fireworks/gpt-oss-120b"].output_per_mtok == 0.60 and len(facts.prices) == 2159