from aiatlas.connectors.providers.provider_pricing import ( FireworksPricingConnector, GroqPricingConnector, TogetherPricingConnector, ) from aiatlas.sdk.facts import Target from tests.conftest import claims_of, extract_from_fixture, fixture_path async def test_groq_models_table(): c = GroqPricingConnector() target = Target(url="https://console.groq.com/docs/models", doc_type="model_docs", key="groq_models") facts = await extract_from_fixture(c, target, fixture_path("provider_pricing", "groq-models.html")) p = next(p for p in facts.prices if p.provider_model_id == "openai/gpt-oss-120b") assert (p.input_per_mtok, p.output_per_mtok, p.context_length, p.max_output_tokens) == (0.15, 0.60, 131072, 65536) assert p.provider.name == "GroqCloud" and p.model.name == "GPT OSS 120B" and p.model.organization.name == "OpenAI" assert p.features["output_tokens_per_second"] == 500 and p.features["groq_section"] == "production" assert p.model.identifiers == {"groq_model_id": "openai/gpt-oss-120b"} and p.model.family.name == "gpt-oss" llama = claims_of(facts, "Llama 3.1 8B") llama_ref = next(e for e in facts.entities if e.name == "Llama 3.1 8B") assert llama_ref.organization.name == "Meta AI" # first-party pattern on the id, not a guess assert "context_length" not in llama and "groq_pricing_note" in llama # "Contact Sales" → no price row; limits stay on price rows assert p.features["context_length"] == 131072 assert not any(p.provider_model_id == "whisper-large-v3" for p in facts.prices) # per-hour pricing is not per-token assert len(facts.prices) >= 6 and c.tier == 1 async def test_groq_pricing_page_is_client_rendered(): c = GroqPricingConnector() targets = await c.discover(None) # type: ignore[arg-type] assert next(t for t in targets if t.key == "groq_pricing").escalate is True facts = await extract_from_fixture(c, targets[1], fixture_path("provider_pricing", "groq-pricing.html")) assert facts.prices == [] and [e.entity_type for e in facts.entities] == ["provider"] async def test_together_tables(): c = TogetherPricingConnector() target = Target(url="https://www.together.ai/pricing", doc_type="pricing", key="together_pricing") facts = await extract_from_fixture(c, target, fixture_path("provider_pricing", "together-pricing.html")) by_slug = {p.provider_model_id: p for p in facts.prices} m3 = by_slug["minimax-m3"] assert (m3.input_per_mtok, m3.cached_input_per_mtok, m3.output_per_mtok) == (0.30, 0.06, 1.20) assert (m3.batch_input_per_mtok, m3.batch_output_per_mtok) == (0.30, 1.20) and m3.model.organization.name == "MiniMax" assert m3.model.identifiers == {"together_ai_model_slug": "minimax-m3"} and m3.provider.name == "Together AI" assert by_slug["deepseek-v4-flash-0731"].input_per_mtok == 0.14 and len(facts.prices) >= 20 assert len({p.provider_model_id for p in facts.prices}) == len(facts.prices) # one price row per model async def test_fireworks_markdown(): c = FireworksPricingConnector() target = Target(url="https://docs.fireworks.ai/serverless/pricing.md", doc_type="pricing", key="fireworks_serverless") facts = await extract_from_fixture(c, target, fixture_path("provider_pricing", "fireworks-serverless-pricing.md"), content_type="text/markdown") by_id = {p.provider_model_id: p for p in facts.prices} k3 = by_id["fireworks/kimi-k3"] assert (k3.input_per_mtok, k3.cached_input_per_mtok, k3.output_per_mtok) == (3.0, 0.30, 15.0) 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 True assert k3.model.organization.name == "Moonshot AI" assert k3.model.identifiers == {"fireworks_model_id": "fireworks/kimi-k3", "fireworks-ai_model_id": "fireworks/kimi-k3"} # canonical + legacy scheme assert k3.model.family is not None and k3.model.family.name == "Kimi" and k3.model.identity_confidence == "medium" fast = by_id["fireworks/kimi-k3:fast"] assert fast.input_per_mtok == 4.5 and fast.model is k3.model and fast.features["serving_path"] == "fast" assert by_id["fireworks/gpt-oss-120b"].output_per_mtok == 0.60 and len(facts.prices) == 21