HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import pytest23from aiatlas.connectors.labs.google import DEEPMIND_RSS, DOCS, GOOGLE_AI_RSS, GoogleConnector4from aiatlas.sdk.facts import Target5from tests.conftest import claims_of, entity_names, extract_from_fixture, fixture_path678@pytest.fixture9def connector() -> GoogleConnector:10 return GoogleConnector()111213async def test_models_overview(connector):14 target = Target(url=f"{DOCS}/models", doc_type="model_docs", key="models")15 facts = await extract_from_fixture(connector, target, fixture_path("google", "models.html"))16 models = entity_names(facts, "model")17 assert len(models) >= 4018 assert {"Gemini 3.8 Flash", "Gemini 2.5 Pro", "Nano Banana 2", "Veo 3.1", "Gemini Embedding 2"} <= models19 flash = claims_of(facts, "Gemini 3.8 Flash")20 assert flash["api_model_id"] == "gemini-3.8-flash" and flash["status"] == "active" and flash["family"] == "Gemini 3.8" # versioned family (ontology)21 flash_ref = next(e for e in facts.entities if e.name == "Gemini 3.8 Flash")22 assert flash_ref.family.entity_type == "model_family" and flash_ref.family.name == "Gemini 3.8" and flash_ref.family.organization.name == "Google"23 assert claims_of(facts, "Gemini 3.1 Pro")["status"] == "preview"24 assert claims_of(facts, "Gemini 2.0 Flash")["status"] == "retired"25 ref = next(e for e in facts.entities if e.name == "Nano Banana 2")26 assert ref.identifiers == {"gemini_model_id": "gemini-3.1-flash-image", "google_model_id": "gemini-3.1-flash-image"} # both schemes27 pages = [t for t in facts.targets if t.doc_type == "model_page"]28 assert len(pages) >= 40 and pages[0].url == f"{DOCS}/models/gemini-3.8-flash"29 # several endpoints under one display name: identity = first endpoint, the others are a claim (never aliases)30 transcribe = next(e for e in facts.entities if e.name == "Gemini 3.5 Transcribe")31 assert transcribe.identifiers["gemini_model_id"] == "gemini-3.5-transcribe" and "gemini-3.5-transcribe-live" not in transcribe.aliases32 assert claims_of(facts, "Gemini 3.5 Transcribe")["api_aliases"] == ["gemini-3.5-transcribe-live"]33 assert claims_of(facts, "Gemini 3.5 Transcribe")["api_alias"] == "gemini-3.5-transcribe-live"34 assert len([e for e in facts.entities if e.name == "Gemini Embedding 2"]) == 135 omni = {e.name: e.identifiers["gemini_model_id"] for e in facts.entities if e.entity_type == "model" and e.name.startswith("Gemini Omni Flash")}36 assert omni == {"Gemini Omni Flash": "gemini-omni-flash"} # the GA endpoint row folds into the card's api_aliases37 assert claims_of(facts, "Gemini Omni Flash")["api_aliases"] == ["gemini-omni-1.1-flash"]38 assert sum(1 for c in facts.claims if c.property == "api_aliases" and c.entity.name == "Gemini Omni Flash") == 139 names = [e.name.lower() for e in facts.entities if e.entity_type == "model"]40 assert len(names) == len(set(names)) # every model ref has a distinct name inside the document41 props = [(c.entity.name, c.property) for c in facts.claims if c.property in ("api_model_id", "context_length")]42 assert len(props) == len(set(props))434445async def test_model_pages(connector):46 target = Target(url=f"{DOCS}/models/gemini-3.8-flash", doc_type="model_page", key="model:gemini-3.8-flash", meta={"api_id": "gemini-3.8-flash"})47 facts = await extract_from_fixture(connector, target, fixture_path("google", "model-gemini-3.8-flash.html"))48 m = claims_of(facts, "Gemini 3.8 Flash")49 assert m["context_length"] == 1_048_576 and m["max_output_tokens"] == 65_53650 assert m["modalities_input"] == ["audio", "document", "image", "text", "video"] and m["modalities_output"] == ["text"] # pdf → document (ontology)51 assert m["tool_calling"] is True and m["structured_output"] is True and m["reasoning"] is True and m["vision"] is True and m["audio"] is True52 assert {"function_calling", "structured_output", "reasoning"} <= set(m["capabilities"]) and "image_generation" not in m["capabilities"]53 assert "Function calling" in m["capabilities_raw"] and "Image generation" not in m["capabilities_raw"] # Google's labels kept as raw54 assert m["latest_update"] == "2026-09" and m["versions"] == {"stable": ["gemini-3.8-flash"]}55 target = Target(url=f"{DOCS}/models/gemini-2.5-pro", doc_type="model_page", key="model:gemini-2.5-pro", meta={"api_id": "gemini-2.5-pro"})56 facts = await extract_from_fixture(connector, target, fixture_path("google", "model-gemini-2.5-pro.html"))57 pro = claims_of(facts, "Gemini 2.5 Pro")58 assert pro["knowledge_cutoff"] == "2025-01" and pro["latest_update"] == "2025-06" and pro["context_length"] == 1_048_576596061async def test_pricing(connector):62 target = Target(url=f"{DOCS}/pricing", doc_type="pricing", key="pricing")63 facts = await extract_from_fixture(connector, target, fixture_path("google", "pricing.html"))64 by_id = {p.provider_model_id: p for p in facts.prices}65 assert len(by_id) >= 2566 flash = by_id["gemini-3.8-flash"]67 assert (flash.input_per_mtok, flash.cached_input_per_mtok, flash.output_per_mtok) == (0.75, 0.075, 3.75)68 assert (flash.batch_input_per_mtok, flash.batch_output_per_mtok) == (0.375, 1.875)69 assert flash.features["scheduled_input_per_mtok"] == 1.5 and flash.features["promotional_until"] == "December 31, 2026"70 assert flash.features["priority_input_per_mtok"] == 1.35 and flash.features["free_tier"] is True71 assert flash.provider.identifiers["registry_provider"] == "google-gemini-api" and flash.model.name == "Gemini 3.8 Flash"72 pro = by_id["gemini-2.5-pro"]73 assert (pro.input_per_mtok, pro.cached_input_per_mtok, pro.output_per_mtok) == (1.25, 0.125, 10.0)74 assert pro.features["long_context_input_per_mtok"] == 2.5 and pro.features["long_context_output_per_mtok"] == 15.075 assert pro.features["cache_storage_per_mtok_hour"] == 4.576 lite = by_id["gemini-2.5-flash-lite"]77 assert lite.input_per_mtok == 0.10 and lite.features["audio_input_per_mtok"] == 0.30 # "(text / image / video)" is a modality list, not a unit78 assert by_id["gemini-2.5-flash-image"].per_image == 0.03979 assert by_id["lyria-3.5"].per_request == 0.0880 assert "720p" in str(by_id["veo-3.1-generate-preview"].features["per_second"])81 assert by_id["gemini-embedding-2"].input_per_mtok == 0.20 and by_id["gemini-embedding-2"].features["image_input_per_mtok"] == 0.45828384async def test_changelog(connector):85 target = Target(url=f"{DOCS}/changelog", doc_type="listing", key="changelog")86 facts = await extract_from_fixture(connector, target, fixture_path("google", "changelog.html"))87 events = facts.events88 assert len(events) >= 80 and all(e.effective_at is not None and e.dedupe_key for e in events)89 assert events[0].summary == "Gemini API: Lyria 3.5 in public preview" and events[0].effective_at.date().isoformat() == "2026-09-03"90 ga = next(e for e in events if "Gemini 3.8 Flash generally available" in e.summary)91 assert ga.category == "release" and "gemini-3.8-flash" in ga.meta["models"]92 assert len({e.dedupe_key for e in events}) == len(events)939495async def test_feeds(connector):96 facts = await extract_from_fixture(connector, Target(url=DEEPMIND_RSS, doc_type="feed", key="deepmind_feed"), fixture_path("google", "deepmind.rss.xml"),97 content_type="application/rss+xml")98 assert len(facts.events) == 100 and facts.events[0].entity.name == "Google DeepMind"99 assert any(e.category == "release" and "WeatherNext 3" in e.summary for e in facts.events)100 facts = await extract_from_fixture(connector, Target(url=GOOGLE_AI_RSS, doc_type="feed", key="google_ai_feed"), fixture_path("google", "google-ai-blog.rss.xml"),101 content_type="application/rss+xml")102 assert len(facts.events) == 20 and facts.events[0].entity.name == "Google"103 assert facts.events[0].effective_at.date().isoformat() == "2026-09-10"104