diff --git a/agent/usage_pricing.py b/agent/usage_pricing.py index fcf4f6228346..61da51e22732 100644 --- a/agent/usage_pricing.py +++ b/agent/usage_pricing.py @@ -382,6 +382,50 @@ class CostResult: pricing_version="deepseek-pricing-2026-05-12", ), # Google Gemini + ( + "google", + "gemini-3.5-flash", + ): PricingEntry( + input_cost_per_million=Decimal("1.50"), + output_cost_per_million=Decimal("9.00"), + cache_read_cost_per_million=Decimal("0.15"), + source="official_docs_snapshot", + source_url="https://ai.google.dev/pricing", + pricing_version="google-pricing-2026-05-19", + ), + ( + "google", + "gemini-3.1-flash-lite", + ): PricingEntry( + input_cost_per_million=Decimal("0.25"), + output_cost_per_million=Decimal("1.50"), + cache_read_cost_per_million=Decimal("0.025"), + source="official_docs_snapshot", + source_url="https://ai.google.dev/pricing", + pricing_version="google-pricing-2026-05-19", + ), + ( + "google", + "gemini-3.1-flash-lite-preview", + ): PricingEntry( + input_cost_per_million=Decimal("0.25"), + output_cost_per_million=Decimal("1.50"), + cache_read_cost_per_million=Decimal("0.025"), + source="official_docs_snapshot", + source_url="https://ai.google.dev/pricing", + pricing_version="google-pricing-2026-05-19", + ), + ( + "google", + "gemini-3-flash-preview", + ): PricingEntry( + input_cost_per_million=Decimal("0.50"), + output_cost_per_million=Decimal("3.00"), + cache_read_cost_per_million=Decimal("0.05"), + source="official_docs_snapshot", + source_url="https://ai.google.dev/pricing", + pricing_version="google-pricing-2026-05-19", + ), ( "google", "gemini-2.5-pro", @@ -546,6 +590,8 @@ def resolve_billing_route( return BillingRoute(provider="anthropic", model=model.split("/")[-1], base_url=base_url or "", billing_mode="official_docs_snapshot") if provider_name == "openai": return BillingRoute(provider="openai", model=model.split("/")[-1], base_url=base_url or "", billing_mode="official_docs_snapshot") + if provider_name in {"google", "gemini", "google-gemini", "google-ai-studio"}: + return BillingRoute(provider="google", model=model.split("/")[-1], base_url=base_url or "", billing_mode="official_docs_snapshot") if provider_name in {"minimax", "minimax-cn"}: return BillingRoute(provider=provider_name, model=model.split("/")[-1], base_url=base_url or "", billing_mode="official_docs_snapshot") if provider_name in {"custom", "local"} or (base and "localhost" in base): diff --git a/tests/agent/test_usage_pricing.py b/tests/agent/test_usage_pricing.py index 5c84b124a2e3..cec8269e15be 100644 --- a/tests/agent/test_usage_pricing.py +++ b/tests/agent/test_usage_pricing.py @@ -224,3 +224,66 @@ def test_deepseek_v4_pro_estimate_usage_cost(): assert result.amount_usd is not None # 1M input × $1.74/M + 500K output × $3.48/M = $1.74 + $1.74 = $3.48 assert float(result.amount_usd) == 3.48 + + +def test_gemini_35_flash_pricing_entry_exists(): + """Ensure gemini-3.5-flash has a pricing entry and provider mapping works.""" + entry = get_pricing_entry( + "gemini-3.5-flash", + provider="gemini", + ) + + assert entry is not None + assert entry.input_cost_per_million is not None + assert entry.output_cost_per_million is not None + assert entry.cache_read_cost_per_million is not None + assert float(entry.input_cost_per_million) == 1.50 + assert float(entry.output_cost_per_million) == 9.00 + assert float(entry.cache_read_cost_per_million) == 0.15 + + +def test_gemini_35_flash_estimate_usage_cost(): + """Ensure gemini-3.5-flash sessions get a dollar estimate with mapped provider.""" + result = estimate_usage_cost( + "gemini-3.5-flash", + CanonicalUsage(input_tokens=1000000, output_tokens=500000, cache_read_tokens=200000), + provider="gemini", + ) + + assert result.status == "estimated" + assert result.amount_usd is not None + # 1M input × $1.50/M + 500K output × $9.00/M + 200K cache_read × $0.15/M + # = $1.50 + $4.50 + $0.03 = $6.03 + assert float(result.amount_usd) == 6.03 + + +def test_gemini_31_flash_lite_pricing_entry_exists(): + """Ensure gemini-3.1-flash-lite has a pricing entry and provider mapping works.""" + entry = get_pricing_entry( + "gemini-3.1-flash-lite", + provider="gemini", + ) + + assert entry is not None + assert entry.input_cost_per_million is not None + assert entry.output_cost_per_million is not None + assert entry.cache_read_cost_per_million is not None + assert float(entry.input_cost_per_million) == 0.25 + assert float(entry.output_cost_per_million) == 1.50 + assert float(entry.cache_read_cost_per_million) == 0.025 + + +def test_gemini_3_flash_preview_pricing_entry_exists(): + """Ensure gemini-3-flash-preview has a pricing entry and provider mapping works.""" + entry = get_pricing_entry( + "gemini-3-flash-preview", + provider="gemini", + ) + + assert entry is not None + assert entry.input_cost_per_million is not None + assert entry.output_cost_per_million is not None + assert entry.cache_read_cost_per_million is not None + assert float(entry.input_cost_per_million) == 0.50 + assert float(entry.output_cost_per_million) == 3.00 + assert float(entry.cache_read_cost_per_million) == 0.05