feat(agent): add Fireworks pricing entries + routing branch

Fireworks-hosted sessions previously showed estimated_cost_usd = 0
because (a) _OFFICIAL_DOCS_PRICING had no Fireworks entries and (b)
resolve_billing_route() had no branch for provider="fireworks",
falling through to billing_mode="unknown".

Adds entries for the three Fireworks models hermes operators are
most likely to route through (Kimi K2.6, DeepSeek V4 Pro, Qwen3.6-Plus)
and a routing branch that triggers on either explicit
provider="fireworks" or api.fireworks.ai base_url match. Mirrors the
recently-merged MiniMax addition pattern; pricing snapshot sourced
from https://docs.fireworks.ai/serverless/pricing and the per-model
pages on fireworks.ai.

Tests cover: (a) full Fireworks model id resolves to the snapshot
entry, (b) base_url alone is sufficient to route, (c) end-to-end
estimate returns "estimated" status with the expected dollar amount.

A follow-up upstream issue is open proposing a dynamic pricing
source (e.g. litellm's pricing JSON) as a permanent fix to the
PR-per-model treadmill that this snapshot keeps adding to.
This commit is contained in:
Brendan DeBeasi 2026-05-21 07:16:48 -07:00 committed by Teknium
parent 5a39f0501c
commit 365620ab28
2 changed files with 83 additions and 0 deletions

View file

@ -609,6 +609,43 @@ _OFFICIAL_DOCS_PRICING: Dict[tuple[str, str], PricingEntry] = {
source="official_docs_snapshot",
pricing_version="minimax-pricing-2026-04",
),
# Fireworks AI — serverless pricing for the models hermes typically routes
# through when configured with provider="fireworks". Fireworks publishes a
# cached_input rate per model alongside input/output, which maps to
# cache_read_cost_per_million. No separately published cache_write rate.
(
"fireworks",
"kimi-k2p6",
): PricingEntry(
input_cost_per_million=Decimal("0.95"),
output_cost_per_million=Decimal("4.00"),
cache_read_cost_per_million=Decimal("0.16"),
source="official_docs_snapshot",
source_url="https://docs.fireworks.ai/serverless/pricing",
pricing_version="fireworks-pricing-2026-05",
),
(
"fireworks",
"deepseek-v4-pro",
): PricingEntry(
input_cost_per_million=Decimal("1.74"),
output_cost_per_million=Decimal("3.48"),
cache_read_cost_per_million=Decimal("0.145"),
source="official_docs_snapshot",
source_url="https://docs.fireworks.ai/serverless/pricing",
pricing_version="fireworks-pricing-2026-05",
),
(
"fireworks",
"qwen3p6-plus",
): PricingEntry(
input_cost_per_million=Decimal("0.50"),
output_cost_per_million=Decimal("3.00"),
cache_read_cost_per_million=Decimal("0.10"),
source="official_docs_snapshot",
source_url="https://fireworks.ai/models/fireworks/qwen3p6-plus",
pricing_version="fireworks-pricing-2026-05",
),
}
# GPT-5.6 "-pro" high-effort variants bill at the same per-token rates as
@ -672,6 +709,10 @@ def resolve_billing_route(
# the OpenAI-compat endpoint requires so the pricing key matches.
if provider_name == "vertex" or base_url_host_matches(base_url or "", "aiplatform.googleapis.com"):
return BillingRoute(provider="gemini", model=model.split("/")[-1], base_url=base_url or "", billing_mode="official_docs_snapshot")
if provider_name == "fireworks" or base_url_host_matches(base_url or "", "api.fireworks.ai"):
# Fireworks model ids look like accounts/fireworks/models/<name>;
# rsplit("/", 1)[-1] yields just <name> which is what the dict keys on.
return BillingRoute(provider="fireworks", model=model.rsplit("/", 1)[-1], base_url=base_url or "", billing_mode="official_docs_snapshot")
if provider_name in {"custom", "local"} or (base and "localhost" in base):
return BillingRoute(provider=provider_name or "custom", model=model, base_url=base_url or "", billing_mode="unknown")
return BillingRoute(provider=provider_name or "unknown", model=model.split("/")[-1] if model else "", base_url=base_url or "", billing_mode="unknown")

View file

@ -322,3 +322,45 @@ def test_bedrock_claude_cached_session_estimates_cost_not_unknown():
)
assert result.status == "estimated"
assert result.amount_usd is not None
def test_fireworks_kimi_k2p6_resolves_with_full_model_path():
"""Fireworks model ids look like accounts/fireworks/models/<name>;
the routing layer must strip the prefix so the dict lookup succeeds."""
entry = get_pricing_entry(
"accounts/fireworks/models/kimi-k2p6",
provider="fireworks",
base_url="https://api.fireworks.ai/inference/v1",
)
assert entry is not None
assert float(entry.input_cost_per_million) == 0.95
assert float(entry.output_cost_per_million) == 4.00
assert float(entry.cache_read_cost_per_million) == 0.16
assert entry.source == "official_docs_snapshot"
def test_fireworks_base_url_host_match_alone_routes_to_pricing():
"""Provider not explicitly passed; routing infers fireworks from the host."""
entry = get_pricing_entry(
"accounts/fireworks/models/deepseek-v4-pro",
base_url="https://api.fireworks.ai/inference/v1",
)
assert entry is not None
assert float(entry.input_cost_per_million) == 1.74
assert float(entry.output_cost_per_million) == 3.48
def test_fireworks_qwen3p6_plus_estimate_usage_cost():
"""End-to-end: Fireworks Qwen3.6-Plus sessions report a dollar estimate."""
result = estimate_usage_cost(
"accounts/fireworks/models/qwen3p6-plus",
CanonicalUsage(input_tokens=1_000_000, output_tokens=500_000),
provider="fireworks",
base_url="https://api.fireworks.ai/inference/v1",
)
assert result.status == "estimated"
assert result.amount_usd is not None
# 1M input × $0.50/M + 500K output × $3.00/M = $0.50 + $1.50 = $2.00
assert float(result.amount_usd) == 2.00