From 5f5f6b6ec271eac0ef429eea81f810fc1dbe43e0 Mon Sep 17 00:00:00 2001 From: Yifan Lu Date: Sat, 31 Oct 2026 17:40:43 -0500 Subject: [PATCH 1/2] Price sync: use each model's standard price, not OpenRouter's model-level price OpenRouter's model-level price is one endpoint's price, and often not the model's standard price: Gemini 3.7/3.8 Flash show a 50% introductory price ($0.75/$3.75 or a regional $0.825/$4.125) against Google's announced $1.50/$7.50; DeepSeek V4 Flash shows a host's 35%-off promotion; GLM-5.3 and Kimi K3 show a 4-bit host's price instead of the lab's. The sync now reads each model's per-provider endpoints and takes: - standard-tier endpoints only (no Flex, Priority or batch), active ones when any are; - each endpoint's promotional discount undone (price / (1 - discount)); - the lab's own lowest standard price when the lab sells the model on OpenRouter; - otherwise the median over hosts, without 4-bit quantizations when higher-precision hosts exist. A model whose endpoints can't be read keeps its price and is listed in the report. The snapshot records each price's basis. Prices that double or halve are flagged in the report. Co-Authored-By: Claude Opus 5.5 --- scripts/pricing/sync_openrouter.py | 265 +++++++++++++++++++++++------ tests/test_price_sync.py | 123 +++++++++++-- 2 files changed, 321 insertions(+), 67 deletions(-) diff --git a/scripts/pricing/sync_openrouter.py b/scripts/pricing/sync_openrouter.py index 34abfb98..44f70b8e 100644 --- a/scripts/pricing/sync_openrouter.py +++ b/scripts/pricing/sync_openrouter.py @@ -1,28 +1,41 @@ # SPDX-FileCopyrightText: Copyright contributors to the RouterArena project # SPDX-License-Identifier: Apache-2.0 -"""Sync model prices in ``model_cost/model_profiles.yaml`` with OpenRouter's list prices. - -For every profile that resolves to an OpenRouter model, the profile's price is set to -OpenRouter's current list price (prompt/completion, converted to USD per 1M tokens), and -``as_of`` and ``source`` are updated. ``model_cost.json`` is then regenerated. - -A profile resolves through its ``openrouter_id`` field, or else through an exact match of -its id or one of its aliases against OpenRouter's model ids. Set ``sync: false`` on a -profile to keep its price fixed (a vendor's standard price, a local model, ...). +"""Sync model prices in ``model_cost/model_profiles.yaml`` with each model's standard price on OpenRouter. + +A model is priced at its standard published price, not at a promotion and not at the +cheapest host of the day. For every profile that resolves to an OpenRouter model, the +price is read from OpenRouter's per-provider endpoints: + +* only standard-tier endpoints count (no Flex, Priority or batch tiers), and only active + ones when any are; +* a provider's promotional discount is undone (OpenRouter lists ``discount: 0.5`` next to + a halved price, so the standard price is ``price / (1 - discount)``); +* when the model's own lab sells it on OpenRouter (Google for Gemini, Z.AI for GLM, ...), + the lab's lowest standard price is used; +* otherwise the median over hosts is used, leaving out 4-bit quantizations when + higher-precision hosts exist. + +The profile's ``price``, ``as_of`` and ``source`` are updated and ``model_cost.json`` is +regenerated. A profile resolves through its ``openrouter_id`` field, or else through an +exact match of its id or one of its aliases against OpenRouter's model ids. Set +``sync: false`` on a profile to keep its price fixed (a retired model, a model OpenRouter +does not list, ...). A model whose endpoints cannot be read keeps its price and is listed +in the report. When a price changes, the run also writes ``model_cost/price_snapshots/.json`` with -the OpenRouter prices of every resolved model, so each change point is kept as history +the price and its basis for every resolved model, so each change point is kept as history (``--always-snapshot`` writes one even when nothing changed). Syncing changes the price table only. Leaderboard costs change when a maintainer re-prices the routers, as a separate step (scripts/pricing/reprice.py). Usage: - python scripts/pricing/sync_openrouter.py # fetch, update, write - python scripts/pricing/sync_openrouter.py --dry-run # report only - python scripts/pricing/sync_openrouter.py --models-json f # use a saved API response - python scripts/pricing/sync_openrouter.py --report out.md # also write a Markdown report + python scripts/pricing/sync_openrouter.py # fetch, update, write + python scripts/pricing/sync_openrouter.py --dry-run # report only + python scripts/pricing/sync_openrouter.py --models-json f # use a saved /models response + python scripts/pricing/sync_openrouter.py --endpoints-dir d # use saved endpoint responses + python scripts/pricing/sync_openrouter.py --report out.md # also write a Markdown report """ from __future__ import annotations @@ -31,18 +44,42 @@ import datetime as dt import json import re +import statistics import sys +import time import urllib.request from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Callable, Dict, List, Optional, Tuple sys.path.insert(0, str(Path(__file__).resolve().parent)) import build_model_cost # noqa: E402 OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models" +OPENROUTER_ENDPOINTS_URL = "https://openrouter.ai/api/v1/models/{}/endpoints" SNAPSHOT_DIR = build_model_cost.REPO_ROOT / "model_cost" / "price_snapshots" +# The lab's own provider name(s) on OpenRouter, by the model id's vendor prefix. +FIRST_PARTY = { + "anthropic": {"Anthropic"}, + "deepseek": {"DeepSeek"}, + "google": {"Google", "Google AI Studio"}, + "inclusionai": {"inclusionAI"}, + "minimax": {"Minimax", "MiniMax"}, + "mistralai": {"Mistral"}, + "moonshotai": {"Moonshot AI"}, + "nvidia": {"NVIDIA"}, + "openai": {"OpenAI"}, + "qwen": {"Alibaba"}, + "upstage": {"Upstage"}, + "x-ai": {"xAI"}, + "xiaomi": {"Xiaomi"}, + "z-ai": {"Z.AI"}, +} +# Quantizations below 8 bits: a cheaper, degraded variant, not the model's standard serving. +LOW_BIT = {"fp4", "mxfp4", "nvfp4", "int4"} + Price = Tuple[float, float] +Endpoints = Optional[List[Dict[str, Any]]] def per_million(per_token: Any) -> Optional[float]: @@ -57,17 +94,15 @@ def per_million(per_token: Any) -> Optional[float]: def load_openrouter_prices(source: Optional[str] = None) -> Dict[str, Price]: - """Map OpenRouter model id -> (input, output) USD per 1M tokens. + """Map OpenRouter model id -> (input, output) list price, USD per 1M tokens. - ``source`` is a saved API response (JSON file); by default the live API is fetched.""" + Used to resolve profiles to OpenRouter ids; the price itself comes from + ``standard_price``. ``source`` is a saved API response (JSON file); by default the + live API is fetched.""" if source: payload = json.loads(Path(source).read_text(encoding="utf-8")) else: - request = urllib.request.Request( - OPENROUTER_MODELS_URL, headers={"User-Agent": "routerarena-price-sync"} - ) - with urllib.request.urlopen(request, timeout=60) as response: - payload = json.load(response) + payload = _get_json(OPENROUTER_MODELS_URL) prices: Dict[str, Price] = {} for model in payload.get("data", []): pricing = model.get("pricing") or {} @@ -79,6 +114,89 @@ def load_openrouter_prices(source: Optional[str] = None) -> Dict[str, Price]: return prices +def _get_json(url: str) -> Any: + request = urllib.request.Request( + url, headers={"User-Agent": "routerarena-price-sync"} + ) + with urllib.request.urlopen(request, timeout=60) as response: + return json.load(response) + + +def endpoints_loader(endpoints_dir: Optional[str]) -> Callable[[str], Endpoints]: + """Return a function that gives an OpenRouter id's endpoint list, or None if it + can't be read. Reads ``/__.json`` when a directory is given, + else the live API (with retries).""" + + def from_dir(or_id: str) -> Endpoints: + assert endpoints_dir is not None + path = Path(endpoints_dir) / f"{or_id.replace('/', '__')}.json" + if not path.exists(): + return None + payload = json.loads(path.read_text(encoding="utf-8")) + return (payload.get("data") or {}).get("endpoints") + + def from_api(or_id: str) -> Endpoints: + for attempt in range(3): + try: + payload = _get_json(OPENROUTER_ENDPOINTS_URL.format(or_id)) + return (payload.get("data") or {}).get("endpoints") + except Exception: + time.sleep(2 * (attempt + 1)) + return None + + return from_dir if endpoints_dir else from_api + + +def standard_price(or_id: str, endpoints: Endpoints) -> Optional[Dict[str, Any]]: + """The model's standard price from its OpenRouter endpoints (see the module + docstring), as {"price": (input, output), "basis": str, "discount_removed": float}, + or None if no standard-tier endpoint has a price.""" + standard = [ + e + for e in endpoints or [] + if e.get("service_tier") in (None, "standard") + and per_million((e.get("pricing") or {}).get("prompt")) is not None + and per_million((e.get("pricing") or {}).get("completion")) is not None + ] + active = [e for e in standard if (e.get("status") or 0) >= 0] + candidates = active or standard + if not candidates: + return None + + def undiscounted(endpoint: Dict[str, Any]) -> Tuple[float, float, float]: + pricing = endpoint["pricing"] + discount = float(pricing.get("discount") or 0) + factor = 1 / (1 - discount) if 0 < discount < 1 else 1.0 + return ( + round(float(pricing["prompt"]) * 1e6 * factor, 6), + round(float(pricing["completion"]) * 1e6 * factor, 6), + discount, + ) + + lab = FIRST_PARTY.get(or_id.split("/")[0], set()) + own = [e for e in candidates if e.get("provider_name") in lab] + if own: + best = min(own, key=lambda e: sum(undiscounted(e)[:2])) + i, o, d = undiscounted(best) + return { + "price": (i, o), + "basis": f"{best['provider_name']} (the lab's own price)", + "discount_removed": d, + } + hosts = [ + e for e in candidates if str(e.get("quantization")) not in LOW_BIT + ] or candidates + prices = [undiscounted(e) for e in hosts] + return { + "price": ( + round(statistics.median(p[0] for p in prices), 6), + round(statistics.median(p[1] for p in prices), 6), + ), + "basis": f"median of {len(prices)} host(s)", + "discount_removed": max(p[2] for p in prices), + } + + def resolve_openrouter_id( model_id: str, profile: Dict[str, Any], openrouter: Dict[str, Price] ) -> Optional[str]: @@ -93,16 +211,21 @@ def resolve_openrouter_id( def plan_updates( - profiles: Dict[str, Dict[str, Any]], openrouter: Dict[str, Price] -) -> Tuple[List[Dict[str, Any]], Dict[str, str], List[str]]: - """Return (changes, resolved, unresolved). - - ``changes`` lists profiles whose price differs from OpenRouter's; ``resolved`` maps - profile id -> OpenRouter id for every synced profile; ``unresolved`` lists profiles - that sync but match no OpenRouter model (or whose ``openrouter_id`` is unknown).""" + profiles: Dict[str, Dict[str, Any]], + openrouter: Dict[str, Price], + get_endpoints: Callable[[str], Endpoints], +) -> Tuple[List[Dict[str, Any]], Dict[str, Dict[str, Any]], List[str], List[str]]: + """Return (changes, resolved, unresolved, no_data). + + ``changes`` lists profiles whose price differs from the standard price; + ``resolved`` maps profile id -> {"openrouter_id", "price", "basis", + "discount_removed"} for every synced profile; ``unresolved`` lists profiles that sync + but match no OpenRouter model; ``no_data`` lists profiles whose endpoints could not + be read (they keep their price).""" changes: List[Dict[str, Any]] = [] - resolved: Dict[str, str] = {} + resolved: Dict[str, Dict[str, Any]] = {} unresolved: List[str] = [] + no_data: List[str] = [] for model_id, profile in profiles.items(): if profile.get("sync") is False: continue @@ -110,14 +233,25 @@ def plan_updates( if or_id is None: unresolved.append(model_id) continue - resolved[model_id] = or_id - new = openrouter[or_id] + found = standard_price(or_id, get_endpoints(or_id)) + if found is None: + no_data.append(model_id) + continue + resolved[model_id] = {"openrouter_id": or_id, **found} + new = found["price"] old = (float(profile["price"]["input"]), float(profile["price"]["output"])) if abs(old[0] - new[0]) > 1e-9 or abs(old[1] - new[1]) > 1e-9: changes.append( - {"model": model_id, "openrouter_id": or_id, "old": old, "new": new} + { + "model": model_id, + "openrouter_id": or_id, + "old": old, + "new": new, + "basis": found["basis"], + "discount_removed": found["discount_removed"], + } ) - return changes, resolved, unresolved + return changes, resolved, unresolved, no_data def _fmt(value: float) -> str: @@ -159,37 +293,64 @@ def apply_updates(text: str, changes: List[Dict[str, Any]], today: str) -> str: return "\n".join(lines) -def write_snapshot( - resolved: Dict[str, str], openrouter: Dict[str, Price], today: str -) -> Path: +def write_snapshot(resolved: Dict[str, Dict[str, Any]], today: str) -> Path: SNAPSHOT_DIR.mkdir(parents=True, exist_ok=True) path = SNAPSHOT_DIR / f"{today}.json" - prices = { - or_id: {"input": openrouter[or_id][0], "output": openrouter[or_id][1]} - for or_id in sorted(set(resolved.values())) + prices = {} + for entry in sorted(resolved.values(), key=lambda e: e["openrouter_id"]): + prices[entry["openrouter_id"]] = { + "input": entry["price"][0], + "output": entry["price"][1], + "basis": entry["basis"], + "discount_removed": entry["discount_removed"], + } + payload = { + "date": today, + "source": OPENROUTER_ENDPOINTS_URL.format(""), + "rule": "standard price: the lab's own standard-tier price, else the median " + "over hosts (no 4-bit); promotional discounts undone", + "prices": prices, } - payload = {"date": today, "source": OPENROUTER_MODELS_URL, "prices": prices} path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") return path -def report(changes: List[Dict[str, Any]], unresolved: List[str], today: str) -> str: +def report( + changes: List[Dict[str, Any]], + unresolved: List[str], + no_data: List[str], + today: str, +) -> str: out = [f"## OpenRouter price sync, {today}", ""] if changes: out += [ f"{len(changes)} price(s) changed (USD per 1M tokens, input / output):", "", - "| Model | OpenRouter id | Old | New |", - "| --- | --- | --- | --- |", + "| Model | OpenRouter id | Old | New | Basis |", + "| --- | --- | --- | --- | --- |", ] for c in changes: + basis = c["basis"] + if c["discount_removed"]: + basis += f", {round(100 * c['discount_removed'])}% promotion undone" + ratio = max( + (c["new"][k] / c["old"][k]) if c["old"][k] else 0 for k in (0, 1) + ) + flag = " ⚠️" if ratio >= 2 or ratio <= 0.5 else "" out.append( - f"| `{c['model']}` | `{c['openrouter_id']}` | " + f"| `{c['model']}`{flag} | `{c['openrouter_id']}` | " f"{_fmt(c['old'][0])} / {_fmt(c['old'][1])} | " - f"{_fmt(c['new'][0])} / {_fmt(c['new'][1])} |" + f"{_fmt(c['new'][0])} / {_fmt(c['new'][1])} | {basis} |" ) + out += ["", "⚠️ marks a price that at least doubled or halved."] else: out.append("No price changed.") + if no_data: + out += [ + "", + f"{len(no_data)} profile(s) kept their price because OpenRouter's endpoints " + "could not be read: " + ", ".join(f"`{m}`" for m in no_data), + ] if unresolved: out += [ "", @@ -203,6 +364,10 @@ def report(changes: List[Dict[str, Any]], unresolved: List[str], today: str) -> def main(argv: Optional[List[str]] = None) -> int: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--models-json", help="saved OpenRouter /models response") + parser.add_argument( + "--endpoints-dir", + help="saved /models//endpoints responses, named __.json", + ) parser.add_argument("--date", default=dt.date.today().isoformat()) parser.add_argument( "--dry-run", action="store_true", help="report only; write nothing" @@ -217,8 +382,10 @@ def main(argv: Optional[List[str]] = None) -> int: profiles = build_model_cost.load_profiles(build_model_cost.PROFILES_PATH) openrouter = load_openrouter_prices(args.models_json) - changes, resolved, unresolved = plan_updates(profiles, openrouter) - text = report(changes, unresolved, args.date) + changes, resolved, unresolved, no_data = plan_updates( + profiles, openrouter, endpoints_loader(args.endpoints_dir) + ) + text = report(changes, unresolved, no_data, args.date) print(text) if args.report: Path(args.report).write_text(text, encoding="utf-8") @@ -226,7 +393,7 @@ def main(argv: Optional[List[str]] = None) -> int: return 0 if changes or args.always_snapshot: - write_snapshot(resolved, openrouter, args.date) + write_snapshot(resolved, args.date) if changes: path = build_model_cost.PROFILES_PATH path.write_text( diff --git a/tests/test_price_sync.py b/tests/test_price_sync.py index b1a986e5..ba9e815a 100644 --- a/tests/test_price_sync.py +++ b/tests/test_price_sync.py @@ -55,6 +55,42 @@ } +def endpoint( + provider, prompt, completion, discount=0, tier=None, quant="unknown", status=0 +): + return { + "provider_name": provider, + "service_tier": tier, + "quantization": quant, + "status": status, + "pricing": { + "prompt": str(prompt / 1e6), + "completion": str(completion / 1e6), + "discount": discount, + }, + } + + +# google/model-b is sold by its lab at a 50% introductory discount (standard 1.5 / 7.5); +# the model-level list price above is one regional endpoint's discounted price. +ENDPOINTS = { + "vendor/model-a": [endpoint("SomeHost", 1.0, 2.0)], + "google/model-b": [ + endpoint("Google AI Studio", 0.375, 1.875, 0.5, tier="flex"), + endpoint("Google AI Studio", 0.75, 3.75, 0.5), + endpoint("Google", 0.825, 4.125, 0.5), + endpoint("Google", 1.35, 6.75, 0.5, tier="priority"), + ], +} + + +def write_endpoints(directory, endpoints): + directory.mkdir(exist_ok=True) + for or_id, eps in endpoints.items(): + path = directory / f"{or_id.replace('/', '__')}.json" + path.write_text(json.dumps({"data": {"endpoints": eps}}), encoding="utf-8") + + @pytest.fixture def setup(tmp_path, monkeypatch): profiles = tmp_path / "model_profiles.yaml" @@ -64,9 +100,17 @@ def setup(tmp_path, monkeypatch): monkeypatch.setattr(build_model_cost, "PROFILES_PATH", profiles) monkeypatch.setattr(build_model_cost, "COST_PATH", tmp_path / "model_cost.json") monkeypatch.setattr(sync_openrouter, "SNAPSHOT_DIR", tmp_path / "snapshots") + write_endpoints(tmp_path / "endpoints", ENDPOINTS) return tmp_path, profiles, models +def run(tmp_path, models, *extra): + return sync_openrouter.main( + ["--models-json", str(models), "--endpoints-dir", str(tmp_path / "endpoints")] + + list(extra) + ) + + def test_prices_convert_to_per_million_and_skip_variable_prices(setup): _, _, models = setup prices = sync_openrouter.load_openrouter_prices(str(models)) @@ -75,28 +119,72 @@ def test_prices_convert_to_per_million_and_skip_variable_prices(setup): def test_plan_resolves_by_explicit_id_and_respects_sync_false(setup): - _, profiles, models = setup + tmp_path, profiles, models = setup loaded = build_model_cost.load_profiles(profiles) - changes, resolved, unresolved = sync_openrouter.plan_updates( - loaded, sync_openrouter.load_openrouter_prices(str(models)) + changes, resolved, unresolved, no_data = sync_openrouter.plan_updates( + loaded, + sync_openrouter.load_openrouter_prices(str(models)), + sync_openrouter.endpoints_loader(str(tmp_path / "endpoints")), ) - assert resolved == {"vendor/model-a": "vendor/model-a", "model-b": "google/model-b"} + assert {k: v["openrouter_id"] for k, v in resolved.items()} == { + "vendor/model-a": "vendor/model-a", + "model-b": "google/model-b", + } assert [c["model"] for c in changes] == ["model-b"] # model-a is unchanged assert unresolved == ["local-only"] # "pinned" is skipped, not unresolved + assert no_data == [] + + +def test_standard_price_is_the_labs_own_without_the_promotion(): + found = sync_openrouter.standard_price( + "google/model-b", ENDPOINTS["google/model-b"] + ) + assert found["price"] == (1.5, 7.5) + assert found["discount_removed"] == 0.5 + assert "Google AI Studio" in found["basis"] + + +def test_standard_price_without_the_lab_is_the_median_of_full_precision_hosts(): + eps = [ + endpoint("HostA", 0.14, 0.28, quant="fp8"), + endpoint("HostB", 0.0399, 0.0798, 0.715, quant="fp8"), # promo of 0.14 / 0.28 + endpoint("HostC", 0.2, 0.5), + endpoint("Cheap4bit", 0.01, 0.02, quant="fp4"), + endpoint("Down", 0.01, 0.01, status=-5), + ] + found = sync_openrouter.standard_price("deepseek/model-x", eps) + assert found["price"] == (0.14, 0.28) + assert found["basis"] == "median of 3 host(s)" + + +def test_only_low_bit_hosts_still_give_a_price(): + eps = [endpoint("A", 0.1, 0.2, quant="fp4"), endpoint("B", 0.3, 0.4, quant="int4")] + assert sync_openrouter.standard_price("x/y", eps)["price"] == (0.2, 0.3) + + +def test_no_standard_tier_endpoint_means_no_price(): + eps = [endpoint("OpenAI", 2, 10, tier="flex")] + assert sync_openrouter.standard_price("openai/m", eps) is None + assert sync_openrouter.standard_price("openai/m", None) is None + + +def test_unreadable_endpoints_keep_the_price(setup): + tmp_path, profiles, models = setup + (tmp_path / "endpoints" / "google__model-b.json").unlink() + run(tmp_path, models, "--date", "2026-10-30") + data = yaml.safe_load(profiles.read_text(encoding="utf-8"))["models"] + assert data["model-b"]["price"] == {"input": 0.5, "output": 3} def test_run_updates_only_changed_lines_and_writes_snapshot(setup): tmp_path, profiles, models = setup - assert ( - sync_openrouter.main(["--models-json", str(models), "--date", "2026-10-30"]) - == 0 - ) + assert run(tmp_path, models, "--date", "2026-10-30") == 0 text = profiles.read_text(encoding="utf-8") assert text.startswith("# A comment that must survive.") assert 'note: "keep me"' in text data = yaml.safe_load(text)["models"] - assert data["model-b"]["price"] == {"input": 0.825, "output": 4.125} + assert data["model-b"]["price"] == {"input": 1.5, "output": 7.5} assert data["model-b"]["as_of"] == "2026-10-30" assert data["model-b"]["source"] == "https://openrouter.ai/google/model-b" assert ( @@ -106,15 +194,16 @@ def test_run_updates_only_changed_lines_and_writes_snapshot(setup): snapshot = json.loads((tmp_path / "snapshots" / "2026-10-30.json").read_text()) assert set(snapshot["prices"]) == {"vendor/model-a", "google/model-b"} + assert snapshot["prices"]["google/model-b"]["discount_removed"] == 0.5 table = json.loads((tmp_path / "model_cost.json").read_text()) - assert table["model-b"]["output_token_price_per_million"] == 4.125 + assert table["model-b"]["output_token_price_per_million"] == 7.5 def test_dry_run_writes_nothing(setup): tmp_path, profiles, models = setup before = profiles.read_text(encoding="utf-8") - sync_openrouter.main(["--models-json", str(models), "--dry-run"]) + run(tmp_path, models, "--dry-run") assert profiles.read_text(encoding="utf-8") == before assert not (tmp_path / "snapshots").exists() @@ -137,12 +226,10 @@ def test_missing_source_and_as_of_lines_are_added(setup): def test_no_change_writes_no_snapshot_unless_asked(setup): tmp_path, profiles, models = setup - data = json.loads(models.read_text()) - data["data"][1]["pricing"] = {"prompt": "0.0000005", "completion": "0.000003"} - models.write_text(json.dumps(data)) - sync_openrouter.main(["--models-json", str(models), "--date", "2026-10-30"]) - assert not (tmp_path / "snapshots").exists() - sync_openrouter.main( - ["--models-json", str(models), "--date", "2026-10-30", "--always-snapshot"] + write_endpoints( + tmp_path / "endpoints", {"google/model-b": [endpoint("Google", 0.5, 3)]} ) + run(tmp_path, models, "--date", "2026-10-30") + assert not (tmp_path / "snapshots").exists() + run(tmp_path, models, "--date", "2026-10-30", "--always-snapshot") assert (tmp_path / "snapshots" / "2026-10-30.json").exists() From 34c48af20851dd62152ef85610742428170a5876 Mon Sep 17 00:00:00 2001 From: Yifan Lu Date: Sat, 31 Oct 2026 17:40:43 -0500 Subject: [PATCH 2/2] Re-sync prices with the standard-price rule (OpenRouter data of 2026-10-31) 26 prices change, e.g. Gemini 3.7/3.8 Flash 0.75-0.825/3.75-4.125 -> 1.50/7.50 (Google's standard price), DeepSeek V4 Flash 0.091/0.182 -> 0.14/0.28, GLM-5.3 0.07/1.40 -> 1.40/4.40 and GLM-5.3 Flash 0.015/0.154 -> 0.15/0.50 (Z.AI's price), Kimi K3 0.192/15 -> 3/15 (Moonshot's price). The 2026-10-31 snapshot is rewritten with the prices and their basis. The leaderboard is not re-priced here. Co-Authored-By: Claude Opus 5.5 --- model_cost/model_cost.json | 184 +++++------ model_cost/model_profiles.yaml | 72 ++--- model_cost/price_snapshots/2026-10-31.json | 355 +++++++++++++++------ 3 files changed, 381 insertions(+), 230 deletions(-) diff --git a/model_cost/model_cost.json b/model_cost/model_cost.json index bf12991d..5fdc0dd9 100644 --- a/model_cost/model_cost.json +++ b/model_cost/model_cost.json @@ -96,12 +96,12 @@ "output_token_price_per_million": 4.94 }, "deepseek/deepseek-chat": { - "input_token_price_per_million": 0.2574, - "output_token_price_per_million": 1.0287 + "input_token_price_per_million": 0.286, + "output_token_price_per_million": 1.143 }, "deepseek-chat": { - "input_token_price_per_million": 0.2574, - "output_token_price_per_million": 1.0287 + "input_token_price_per_million": 0.286, + "output_token_price_per_million": 1.143 }, "deepseek/deepseek-reasoner": { "input_token_price_per_million": 0.28, @@ -112,40 +112,40 @@ "output_token_price_per_million": 0.42 }, "deepseek/deepseek-v3.2": { - "input_token_price_per_million": 0.26, - "output_token_price_per_million": 0.38 + "input_token_price_per_million": 0.295, + "output_token_price_per_million": 0.88 }, "deepseek-v3.2": { - "input_token_price_per_million": 0.26, - "output_token_price_per_million": 0.38 + "input_token_price_per_million": 0.295, + "output_token_price_per_million": 0.88 }, "deepseek/deepseek-v4-flash": { - "input_token_price_per_million": 0.091, - "output_token_price_per_million": 0.182 + "input_token_price_per_million": 0.14, + "output_token_price_per_million": 0.28 }, "deepseek/deepseek-v4-flash-0731": { - "input_token_price_per_million": 0.119, - "output_token_price_per_million": 0.238 + "input_token_price_per_million": 0.176, + "output_token_price_per_million": 0.528 }, "deepseek-ai/DeepSeek-V4-Flash-0731": { - "input_token_price_per_million": 0.119, - "output_token_price_per_million": 0.238 + "input_token_price_per_million": 0.176, + "output_token_price_per_million": 0.528 }, "deepseek/deepseek-v4-pro": { "input_token_price_per_million": 1.6, "output_token_price_per_million": 3.2 }, "deepseek/deepseek-v4.1-flash": { - "input_token_price_per_million": 0.045, - "output_token_price_per_million": 0.5 + "input_token_price_per_million": 0.3, + "output_token_price_per_million": 1.2 }, "deepseek/deepseek-v4.1-flash@high": { - "input_token_price_per_million": 0.045, - "output_token_price_per_million": 0.5 + "input_token_price_per_million": 0.3, + "output_token_price_per_million": 1.2 }, "deepseek/deepseek-v4.1-flash@low": { - "input_token_price_per_million": 0.045, - "output_token_price_per_million": 0.5 + "input_token_price_per_million": 0.3, + "output_token_price_per_million": 1.2 }, "gemini-2.0-flash-001": { "input_token_price_per_million": 0.1, @@ -220,23 +220,23 @@ "output_token_price_per_million": 2.5 }, "google/gemini-3.7-flash": { - "input_token_price_per_million": 0.75, - "output_token_price_per_million": 3.75 + "input_token_price_per_million": 1.5, + "output_token_price_per_million": 7.5 }, "google/gemini-3.8-flash": { - "input_token_price_per_million": 0.825, - "output_token_price_per_million": 4.125 + "input_token_price_per_million": 1.5, + "output_token_price_per_million": 7.5 }, "gemini-3.8-flash": { - "input_token_price_per_million": 0.825, - "output_token_price_per_million": 4.125 + "input_token_price_per_million": 1.5, + "output_token_price_per_million": 7.5 }, "google/gemma-3n-e4b-it": { "input_token_price_per_million": 0.02, "output_token_price_per_million": 0.04 }, "google/gemma-4-31b-it": { - "input_token_price_per_million": 0.2, + "input_token_price_per_million": 0.15, "output_token_price_per_million": 0.4 }, "gpt-5-chat": { @@ -252,24 +252,24 @@ "output_token_price_per_million": 27.5 }, "inclusionai/ling-3.0-flash": { - "input_token_price_per_million": 0.021, - "output_token_price_per_million": 0.063 + "input_token_price_per_million": 0.06, + "output_token_price_per_million": 0.18 }, "lingdt-3.0-flash": { - "input_token_price_per_million": 0.021, - "output_token_price_per_million": 0.063 + "input_token_price_per_million": 0.06, + "output_token_price_per_million": 0.18 }, "llama-4-maverick-17b-128e-instruct-fp8": { "input_token_price_per_million": 0.25, "output_token_price_per_million": 1 }, "meta-llama/llama-3.3-70b-instruct": { - "input_token_price_per_million": 0.1, - "output_token_price_per_million": 0.32 + "input_token_price_per_million": 0.4415, + "output_token_price_per_million": 0.715 }, "meta-llama_llama-3.3-70b-instruct": { - "input_token_price_per_million": 0.1, - "output_token_price_per_million": 0.32 + "input_token_price_per_million": 0.4415, + "output_token_price_per_million": 0.715 }, "meta-llama_llama-3.1-405b-instruct": { "input_token_price_per_million": 3.5, @@ -316,19 +316,19 @@ "output_token_price_per_million": 0.22 }, "moonshotai/kimi-k2.5": { - "input_token_price_per_million": 0.5, - "output_token_price_per_million": 2.5 + "input_token_price_per_million": 0.6, + "output_token_price_per_million": 3 }, "moonshotai/kimi-k2.6": { - "input_token_price_per_million": 0.65, - "output_token_price_per_million": 3.41 + "input_token_price_per_million": 0.95, + "output_token_price_per_million": 4 }, "moonshotai/Kimi-K2.6": { - "input_token_price_per_million": 0.65, - "output_token_price_per_million": 3.41 + "input_token_price_per_million": 0.95, + "output_token_price_per_million": 4 }, "moonshotai/kimi-k3": { - "input_token_price_per_million": 0.192, + "input_token_price_per_million": 3, "output_token_price_per_million": 15 }, "nvidia/nemotron-3-nano-30b-a3b": { @@ -364,12 +364,12 @@ "output_token_price_per_million": 1.6 }, "openai/gpt-4.1-nano": { - "input_token_price_per_million": 0.1, - "output_token_price_per_million": 0.4 + "input_token_price_per_million": 0.105, + "output_token_price_per_million": 0.42 }, "gpt-4.1-nano": { - "input_token_price_per_million": 0.1, - "output_token_price_per_million": 0.4 + "input_token_price_per_million": 0.105, + "output_token_price_per_million": 0.42 }, "openai/gpt-4o": { "input_token_price_per_million": 2.5, @@ -520,52 +520,52 @@ "output_token_price_per_million": 0.6 }, "openai/gpt-oss-20b": { - "input_token_price_per_million": 0.018, - "output_token_price_per_million": 0.09 + "input_token_price_per_million": 0.04, + "output_token_price_per_million": 0.15 }, "gpt-oss-20b": { - "input_token_price_per_million": 0.018, - "output_token_price_per_million": 0.09 + "input_token_price_per_million": 0.04, + "output_token_price_per_million": 0.15 }, "qwen/qwen3-235b-a22b-2507": { - "input_token_price_per_million": 0.09, - "output_token_price_per_million": 0.55 + "input_token_price_per_million": 0.145, + "output_token_price_per_million": 0.775 }, "alibaba/qwen3-235b-a22b-instruct-2507": { - "input_token_price_per_million": 0.09, - "output_token_price_per_million": 0.55 + "input_token_price_per_million": 0.145, + "output_token_price_per_million": 0.775 }, "qwen3-235b-a22b-instruct-2507": { - "input_token_price_per_million": 0.09, - "output_token_price_per_million": 0.55 + "input_token_price_per_million": 0.145, + "output_token_price_per_million": 0.775 }, "qwen_qwen3-235b-a22b-2507": { - "input_token_price_per_million": 0.09, - "output_token_price_per_million": 0.55 + "input_token_price_per_million": 0.145, + "output_token_price_per_million": 0.775 }, "qwen/qwen3-30b-a3b-instruct-2507": { - "input_token_price_per_million": 0.1, + "input_token_price_per_million": 0.09, "output_token_price_per_million": 0.3 }, "alibaba/qwen3-30b-a3b-instruct-2507": { - "input_token_price_per_million": 0.1, + "input_token_price_per_million": 0.09, "output_token_price_per_million": 0.3 }, "qwen3-30b-a3b-instruct-2507": { - "input_token_price_per_million": 0.1, + "input_token_price_per_million": 0.09, "output_token_price_per_million": 0.3 }, "qwen/qwen3-coder": { - "input_token_price_per_million": 0.3, - "output_token_price_per_million": 1 + "input_token_price_per_million": 0.35, + "output_token_price_per_million": 1.5 }, "qwen_qwen3-coder": { - "input_token_price_per_million": 0.3, - "output_token_price_per_million": 1 + "input_token_price_per_million": 0.35, + "output_token_price_per_million": 1.5 }, "qwen/qwen3-coder-30b-a3b-instruct": { - "input_token_price_per_million": 0.07, - "output_token_price_per_million": 0.28 + "input_token_price_per_million": 0.11, + "output_token_price_per_million": 0.44 }, "qwen/qwen3-coder-next": { "input_token_price_per_million": 0.12, @@ -576,7 +576,7 @@ "output_token_price_per_million": 0.8 }, "qwen/qwen3-next-80b-a3b-instruct": { - "input_token_price_per_million": 0.09, + "input_token_price_per_million": 0.095, "output_token_price_per_million": 1.1 }, "qwen/qwen3-vl-235b-a22b-instruct": { @@ -588,8 +588,8 @@ "output_token_price_per_million": 1.9 }, "qwen/qwen3.5-9b": { - "input_token_price_per_million": 0.17, - "output_token_price_per_million": 0.25 + "input_token_price_per_million": 0.1, + "output_token_price_per_million": 0.15 }, "qwen/qwen3.5-flash-02-23": { "input_token_price_per_million": 0.065, @@ -624,8 +624,8 @@ "output_token_price_per_million": 0.6 }, "upstage/solar-pro4": { - "input_token_price_per_million": 0.15, - "output_token_price_per_million": 0.6 + "input_token_price_per_million": 0.3, + "output_token_price_per_million": 1.2 }, "x-ai/grok-4.1-fast": { "input_token_price_per_million": 0.2, @@ -660,20 +660,20 @@ "output_token_price_per_million": 0.3 }, "z-ai/glm-4.5-air": { - "input_token_price_per_million": 0.13, - "output_token_price_per_million": 0.85 + "input_token_price_per_million": 0.2, + "output_token_price_per_million": 1.1 }, "glm-4.5-air": { - "input_token_price_per_million": 0.13, - "output_token_price_per_million": 0.85 + "input_token_price_per_million": 0.2, + "output_token_price_per_million": 1.1 }, "z-ai/glm-4.6": { - "input_token_price_per_million": 0.5, - "output_token_price_per_million": 2 + "input_token_price_per_million": 0.6, + "output_token_price_per_million": 2.2 }, "glm-4.6": { - "input_token_price_per_million": 0.5, - "output_token_price_per_million": 2 + "input_token_price_per_million": 0.6, + "output_token_price_per_million": 2.2 }, "z-ai/glm-4.7": { "input_token_price_per_million": 0.6, @@ -688,27 +688,27 @@ "output_token_price_per_million": 3.2 }, "z-ai/glm-5.3": { - "input_token_price_per_million": 0.0699, - "output_token_price_per_million": 1.4 + "input_token_price_per_million": 1.4, + "output_token_price_per_million": 4.4 }, "z-ai/glm-5.3@high": { - "input_token_price_per_million": 0.0699, - "output_token_price_per_million": 1.4 + "input_token_price_per_million": 1.4, + "output_token_price_per_million": 4.4 }, "z-ai/glm-5.3@low": { - "input_token_price_per_million": 0.0699, - "output_token_price_per_million": 1.4 + "input_token_price_per_million": 1.4, + "output_token_price_per_million": 4.4 }, "z-ai/glm-5.3-flash": { - "input_token_price_per_million": 0.0149, - "output_token_price_per_million": 0.1543 + "input_token_price_per_million": 0.15, + "output_token_price_per_million": 0.5 }, "glm-5.3-flash": { - "input_token_price_per_million": 0.0149, - "output_token_price_per_million": 0.1543 + "input_token_price_per_million": 0.15, + "output_token_price_per_million": 0.5 }, "z-ai/glm-5.3-flash@default": { - "input_token_price_per_million": 0.0149, - "output_token_price_per_million": 0.1543 + "input_token_price_per_million": 0.15, + "output_token_price_per_million": 0.5 } } diff --git a/model_cost/model_profiles.yaml b/model_cost/model_profiles.yaml index 4d280dce..f4c45140 100644 --- a/model_cost/model_profiles.yaml +++ b/model_cost/model_profiles.yaml @@ -92,7 +92,7 @@ models: as_of: "2026-09-27" source: null "deepseek/deepseek-chat": - price: {input: 0.2574, output: 1.0287} + price: {input: 0.286, output: 1.143} aliases: ["deepseek-chat"] as_of: "2026-10-31" source: "https://openrouter.ai/deepseek/deepseek-chat" @@ -104,16 +104,16 @@ models: note: "DeepSeek's API name; the model behind it changed over time, so it is not synced." sync: false "deepseek/deepseek-v3.2": - price: {input: 0.26, output: 0.38} + price: {input: 0.295, output: 0.88} aliases: ["deepseek-v3.2"] - as_of: "2026-09-27" - source: null + as_of: "2026-10-31" + source: "https://openrouter.ai/deepseek/deepseek-v3.2" "deepseek/deepseek-v4-flash": - price: {input: 0.091, output: 0.182} + price: {input: 0.14, output: 0.28} as_of: "2026-10-31" source: "https://openrouter.ai/deepseek/deepseek-v4-flash" "deepseek/deepseek-v4-flash-0731": - price: {input: 0.119, output: 0.238} + price: {input: 0.176, output: 0.528} aliases: ["deepseek-ai/DeepSeek-V4-Flash-0731"] as_of: "2026-10-31" source: "https://openrouter.ai/deepseek/deepseek-v4-flash-0731" @@ -122,7 +122,7 @@ models: as_of: "2026-10-31" source: "https://openrouter.ai/deepseek/deepseek-v4-pro" "deepseek/deepseek-v4.1-flash": - price: {input: 0.045, output: 0.5} + price: {input: 0.3, output: 1.2} aliases: ["deepseek/deepseek-v4.1-flash@high", "deepseek/deepseek-v4.1-flash@low"] as_of: "2026-10-31" source: "https://openrouter.ai/deepseek/deepseek-v4.1-flash" @@ -183,11 +183,11 @@ models: as_of: null source: null "google/gemini-3.7-flash": - price: {input: 0.75, output: 3.75} - as_of: null - source: null + price: {input: 1.5, output: 7.5} + as_of: "2026-10-31" + source: "https://openrouter.ai/google/gemini-3.7-flash" "google/gemini-3.8-flash": - price: {input: 0.825, output: 4.125} + price: {input: 1.5, output: 7.5} aliases: ["gemini-3.8-flash"] as_of: "2026-10-31" source: "https://openrouter.ai/google/gemini-3.8-flash" @@ -196,7 +196,7 @@ models: as_of: "2026-09-27" source: null "google/gemma-4-31b-it": - price: {input: 0.2, output: 0.4} + price: {input: 0.15, output: 0.4} as_of: "2026-10-31" source: "https://openrouter.ai/google/gemma-4-31b-it" "gpt-5-chat": @@ -212,7 +212,7 @@ models: as_of: "2026-09-27" source: null "inclusionai/ling-3.0-flash": - price: {input: 0.021, output: 0.063} + price: {input: 0.06, output: 0.18} aliases: ["lingdt-3.0-flash"] as_of: "2026-10-31" source: "https://openrouter.ai/inclusionai/ling-3.0-flash" @@ -222,10 +222,10 @@ models: as_of: "2026-09-27" source: null "meta-llama/llama-3.3-70b-instruct": - price: {input: 0.1, output: 0.32} + price: {input: 0.4415, output: 0.715} aliases: ["meta-llama_llama-3.3-70b-instruct"] - as_of: "2026-09-27" - source: null + as_of: "2026-10-31" + source: "https://openrouter.ai/meta-llama/llama-3.3-70b-instruct" "meta-llama_llama-3.1-405b-instruct": price: {input: 3.5, output: 3.5} as_of: "2026-09-27" @@ -261,16 +261,16 @@ models: as_of: "2026-09-27" source: null "moonshotai/kimi-k2.5": - price: {input: 0.5, output: 2.5} + price: {input: 0.6, output: 3} as_of: "2026-10-31" source: "https://openrouter.ai/moonshotai/kimi-k2.5" "moonshotai/kimi-k2.6": - price: {input: 0.65, output: 3.41} + price: {input: 0.95, output: 4} aliases: ["moonshotai/Kimi-K2.6"] as_of: "2026-10-31" source: "https://openrouter.ai/moonshotai/kimi-k2.6" "moonshotai/kimi-k3": - price: {input: 0.192, output: 15} + price: {input: 3, output: 15} as_of: "2026-10-31" source: "https://openrouter.ai/moonshotai/kimi-k3" "nvidia/nemotron-3-nano-30b-a3b": @@ -295,10 +295,10 @@ models: as_of: "2026-09-27" source: null "openai/gpt-4.1-nano": - price: {input: 0.1, output: 0.4} + price: {input: 0.105, output: 0.42} aliases: ["gpt-4.1-nano"] - as_of: "2026-09-27" - source: null + as_of: "2026-10-31" + source: "https://openrouter.ai/openai/gpt-4.1-nano" "openai/gpt-4o": price: {input: 2.5, output: 10} aliases: ["gpt-4o"] @@ -390,29 +390,29 @@ models: as_of: "2026-09-27" source: null "openai/gpt-oss-20b": - price: {input: 0.018, output: 0.09} + price: {input: 0.04, output: 0.15} aliases: ["gpt-oss-20b"] as_of: "2026-10-31" source: "https://openrouter.ai/openai/gpt-oss-20b" "qwen/qwen3-235b-a22b-2507": - price: {input: 0.09, output: 0.55} + price: {input: 0.145, output: 0.775} aliases: ["alibaba/qwen3-235b-a22b-instruct-2507", "qwen3-235b-a22b-instruct-2507", "qwen_qwen3-235b-a22b-2507"] as_of: "2026-10-31" source: "https://openrouter.ai/qwen/qwen3-235b-a22b-2507" note: "Unified spellings; previous prices: qwen3-235b-a22b-instruct-2507 0.5/2. alibaba/qwen3-235b-a22b-instruct-2507: same model, Alibaba's spelling." "qwen/qwen3-30b-a3b-instruct-2507": - price: {input: 0.1, output: 0.3} + price: {input: 0.09, output: 0.3} aliases: ["alibaba/qwen3-30b-a3b-instruct-2507", "qwen3-30b-a3b-instruct-2507"] as_of: "2026-10-31" source: "https://openrouter.ai/qwen/qwen3-30b-a3b-instruct-2507" note: "Unified spellings; previous prices: qwen/qwen3-30b-a3b-instruct-2507 0.08/0.33." "qwen/qwen3-coder": - price: {input: 0.3, output: 1} + price: {input: 0.35, output: 1.5} aliases: ["qwen_qwen3-coder"] as_of: "2026-10-31" source: "https://openrouter.ai/qwen/qwen3-coder" "qwen/qwen3-coder-30b-a3b-instruct": - price: {input: 0.07, output: 0.28} + price: {input: 0.11, output: 0.44} as_of: "2026-10-31" source: "https://openrouter.ai/qwen/qwen3-coder-30b-a3b-instruct" "qwen/qwen3-coder-next": @@ -421,16 +421,16 @@ models: as_of: "2026-10-31" source: "https://openrouter.ai/qwen/qwen3-coder-next" "qwen/qwen3-next-80b-a3b-instruct": - price: {input: 0.09, output: 1.1} - as_of: "2026-09-27" - source: null + price: {input: 0.095, output: 1.1} + as_of: "2026-10-31" + source: "https://openrouter.ai/qwen/qwen3-next-80b-a3b-instruct" "qwen/qwen3-vl-235b-a22b-instruct": price: {input: 0.21, output: 1.9} aliases: ["qwen_qwen3-vl-235b-a22b-instruct"] as_of: "2026-10-31" source: "https://openrouter.ai/qwen/qwen3-vl-235b-a22b-instruct" "qwen/qwen3.5-9b": - price: {input: 0.17, output: 0.25} + price: {input: 0.1, output: 0.15} as_of: "2026-10-31" source: "https://openrouter.ai/qwen/qwen3.5-9b" "qwen/qwen3.5-flash-02-23": @@ -468,7 +468,7 @@ models: as_of: null source: null "upstage/solar-pro4": - price: {input: 0.15, output: 0.6} + price: {input: 0.3, output: 1.2} as_of: "2026-10-31" source: "https://openrouter.ai/upstage/solar-pro4" "x-ai/grok-4.1-fast": @@ -496,12 +496,12 @@ models: as_of: "2026-09-27" source: null "z-ai/glm-4.5-air": - price: {input: 0.13, output: 0.85} + price: {input: 0.2, output: 1.1} aliases: ["glm-4.5-air"] as_of: "2026-10-31" source: "https://openrouter.ai/z-ai/glm-4.5-air" "z-ai/glm-4.6": - price: {input: 0.5, output: 2} + price: {input: 0.6, output: 2.2} aliases: ["glm-4.6"] as_of: "2026-10-31" source: "https://openrouter.ai/z-ai/glm-4.6" @@ -515,12 +515,12 @@ models: as_of: "2026-09-27" source: null "z-ai/glm-5.3": - price: {input: 0.0699, output: 1.4} + price: {input: 1.4, output: 4.4} aliases: ["z-ai/glm-5.3@high", "z-ai/glm-5.3@low"] as_of: "2026-10-31" source: "https://openrouter.ai/z-ai/glm-5.3" "z-ai/glm-5.3-flash": - price: {input: 0.0149, output: 0.1543} + price: {input: 0.15, output: 0.5} aliases: ["glm-5.3-flash", "z-ai/glm-5.3-flash@default"] as_of: "2026-10-31" source: "https://openrouter.ai/z-ai/glm-5.3-flash" diff --git a/model_cost/price_snapshots/2026-10-31.json b/model_cost/price_snapshots/2026-10-31.json index 3a438eed..db56729a 100644 --- a/model_cost/price_snapshots/2026-10-31.json +++ b/model_cost/price_snapshots/2026-10-31.json @@ -1,306 +1,457 @@ { "date": "2026-10-31", - "source": "https://openrouter.ai/api/v1/models", + "source": "https://openrouter.ai/api/v1/models//endpoints", + "rule": "standard price: the lab's own standard-tier price, else the median over hosts (no 4-bit); promotional discounts undone", "prices": { "anthropic/claude-haiku-4.5": { "input": 1.0, - "output": 5.0 + "output": 5.0, + "basis": "Anthropic (the lab's own price)", + "discount_removed": 0.0 }, "anthropic/claude-haiku-5.5": { "input": 0.1, - "output": 0.5 + "output": 0.5, + "basis": "Anthropic (the lab's own price)", + "discount_removed": 0.0 }, "anthropic/claude-opus-4.1": { "input": 15.0, - "output": 75.0 + "output": 75.0, + "basis": "median of 1 host(s)", + "discount_removed": 0.0 }, "anthropic/claude-opus-4.6": { "input": 5.0, - "output": 25.0 + "output": 25.0, + "basis": "Anthropic (the lab's own price)", + "discount_removed": 0.0 }, "anthropic/claude-opus-4.7": { "input": 5.0, - "output": 25.0 + "output": 25.0, + "basis": "Anthropic (the lab's own price)", + "discount_removed": 0.0 }, "anthropic/claude-opus-5.5": { "input": 4.0, - "output": 20.0 + "output": 20.0, + "basis": "Anthropic (the lab's own price)", + "discount_removed": 0.0 }, "anthropic/claude-sonnet-4.5": { "input": 3.0, - "output": 15.0 + "output": 15.0, + "basis": "Anthropic (the lab's own price)", + "discount_removed": 0.0 }, "anthropic/claude-sonnet-5.5": { "input": 2.0, - "output": 10.0 + "output": 10.0, + "basis": "Anthropic (the lab's own price)", + "discount_removed": 0.0 }, "deepseek/deepseek-chat": { - "input": 0.2574, - "output": 1.0287 + "input": 0.286, + "output": 1.143, + "basis": "median of 1 host(s)", + "discount_removed": 0.1 }, "deepseek/deepseek-v3.2": { - "input": 0.26, - "output": 0.38 + "input": 0.295, + "output": 0.88, + "basis": "median of 8 host(s)", + "discount_removed": 0.28 }, "deepseek/deepseek-v4-flash": { - "input": 0.091, - "output": 0.182 + "input": 0.14, + "output": 0.28, + "basis": "median of 13 host(s)", + "discount_removed": 0.715 }, "deepseek/deepseek-v4-flash-0731": { - "input": 0.119, - "output": 0.238 + "input": 0.176, + "output": 0.528, + "basis": "median of 19 host(s)", + "discount_removed": 0.899 }, "deepseek/deepseek-v4-pro": { "input": 1.6, - "output": 3.2 + "output": 3.2, + "basis": "median of 13 host(s)", + "discount_removed": 0.6923 }, "deepseek/deepseek-v4.1-flash": { - "input": 0.045, - "output": 0.5 + "input": 0.3, + "output": 1.2, + "basis": "median of 30 host(s)", + "discount_removed": 0.851 }, "google/gemini-2.5-flash": { "input": 0.3, - "output": 2.5 + "output": 2.5, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.0 }, "google/gemini-2.5-flash-lite": { "input": 0.1, - "output": 0.4 + "output": 0.4, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.0 }, "google/gemini-2.5-pro": { "input": 1.25, - "output": 10.0 + "output": 10.0, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.0 }, "google/gemini-3-flash-preview": { "input": 0.5, - "output": 3.0 + "output": 3.0, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.0 }, "google/gemini-3.1-flash-lite": { "input": 0.25, - "output": 1.5 + "output": 1.5, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.0 }, "google/gemini-3.1-flash-lite-preview": { "input": 0.25, - "output": 1.5 + "output": 1.5, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.0 }, "google/gemini-3.1-pro-preview": { "input": 2.0, - "output": 12.0 + "output": 12.0, + "basis": "Google (the lab's own price)", + "discount_removed": 0.0 }, "google/gemini-3.5-flash-lite": { "input": 0.3, - "output": 2.5 + "output": 2.5, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.0 }, "google/gemini-3.7-flash": { - "input": 0.75, - "output": 3.75 + "input": 1.5, + "output": 7.5, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.5 }, "google/gemini-3.8-flash": { - "input": 0.825, - "output": 4.125 + "input": 1.5, + "output": 7.5, + "basis": "Google AI Studio (the lab's own price)", + "discount_removed": 0.5 }, "google/gemma-4-31b-it": { - "input": 0.2, - "output": 0.4 + "input": 0.15, + "output": 0.4, + "basis": "median of 7 host(s)", + "discount_removed": 0.05 }, "inclusionai/ling-3.0-flash": { - "input": 0.021, - "output": 0.063 + "input": 0.06, + "output": 0.18, + "basis": "median of 1 host(s)", + "discount_removed": 0.65 }, "meta-llama/llama-3.3-70b-instruct": { - "input": 0.1, - "output": 0.32 + "input": 0.4415, + "output": 0.715, + "basis": "median of 10 host(s)", + "discount_removed": 0.25 }, "minimax/minimax-m3": { "input": 0.3, - "output": 1.2 + "output": 1.2, + "basis": "median of 9 host(s)", + "discount_removed": 0.5 }, "mistralai/ministral-3b-2512": { "input": 0.1, - "output": 0.1 + "output": 0.1, + "basis": "Mistral (the lab's own price)", + "discount_removed": 0.0 }, "mistralai/ministral-8b-2512": { "input": 0.15, - "output": 0.15 + "output": 0.15, + "basis": "Mistral (the lab's own price)", + "discount_removed": 0.0 }, "moonshotai/kimi-k2.5": { - "input": 0.5, - "output": 2.5 + "input": 0.6, + "output": 3.0, + "basis": "median of 3 host(s)", + "discount_removed": 0.05 }, "moonshotai/kimi-k2.6": { - "input": 0.65, - "output": 3.41 + "input": 0.95, + "output": 4.0, + "basis": "Moonshot AI (the lab's own price)", + "discount_removed": 0.0 }, "moonshotai/kimi-k3": { - "input": 0.192, - "output": 15.0 + "input": 3.0, + "output": 15.0, + "basis": "Moonshot AI (the lab's own price)", + "discount_removed": 0.0 }, "nvidia/nemotron-3-nano-30b-a3b": { "input": 0.06, - "output": 0.24 + "output": 0.24, + "basis": "median of 1 host(s)", + "discount_removed": 0.0 }, "openai/gpt-4.1": { "input": 2.0, - "output": 8.0 + "output": 8.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-4.1-mini": { "input": 0.4, - "output": 1.6 + "output": 1.6, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-4.1-nano": { - "input": 0.1, - "output": 0.4 + "input": 0.105, + "output": 0.42, + "basis": "median of 2 host(s)", + "discount_removed": 0.0 }, "openai/gpt-4o": { "input": 2.5, - "output": 10.0 + "output": 10.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-4o-mini": { "input": 0.15, - "output": 0.6 + "output": 0.6, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5": { "input": 1.25, - "output": 10.0 + "output": 10.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5-mini": { "input": 0.25, - "output": 2.0 + "output": 2.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5-nano": { "input": 0.05, - "output": 0.4 + "output": 0.4, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.1": { "input": 1.25, - "output": 10.0 + "output": 10.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.2": { "input": 1.75, - "output": 14.0 + "output": 14.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.2-chat": { "input": 1.75, - "output": 14.0 + "output": 14.0, + "basis": "median of 1 host(s)", + "discount_removed": 0.0 }, "openai/gpt-5.4": { "input": 2.5, - "output": 15.0 + "output": 15.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.4-mini": { "input": 0.75, - "output": 4.5 + "output": 4.5, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.4-nano": { "input": 0.2, - "output": 1.25 + "output": 1.25, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.5": { "input": 5.0, - "output": 30.0 + "output": 30.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.6-luna": { "input": 0.2, - "output": 1.2 + "output": 1.2, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.6-sol": { "input": 4.0, - "output": 20.0 + "output": 20.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-5.6-terra": { "input": 2.0, - "output": 12.0 + "output": 12.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-6-luna": { "input": 0.1, - "output": 0.5 + "output": 0.5, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-6-sol": { "input": 2.0, - "output": 10.0 + "output": 10.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-6.1-sol": { "input": 2.0, - "output": 10.0 + "output": 10.0, + "basis": "OpenAI (the lab's own price)", + "discount_removed": 0.0 }, "openai/gpt-oss-120b": { "input": 0.15, - "output": 0.6 + "output": 0.6, + "basis": "median of 16 host(s)", + "discount_removed": 0.0 }, "openai/gpt-oss-20b": { - "input": 0.018, - "output": 0.09 + "input": 0.04, + "output": 0.15, + "basis": "median of 7 host(s)", + "discount_removed": 0.0 }, "qwen/qwen3-235b-a22b-2507": { - "input": 0.09, - "output": 0.55 + "input": 0.145, + "output": 0.775, + "basis": "median of 4 host(s)", + "discount_removed": 0.75 }, "qwen/qwen3-30b-a3b-instruct-2507": { - "input": 0.1, - "output": 0.3 + "input": 0.09, + "output": 0.3, + "basis": "median of 3 host(s)", + "discount_removed": 0.0 }, "qwen/qwen3-coder": { - "input": 0.3, - "output": 1.0 + "input": 0.35, + "output": 1.5, + "basis": "median of 1 host(s)", + "discount_removed": 0.0 }, "qwen/qwen3-coder-30b-a3b-instruct": { - "input": 0.07, - "output": 0.28 + "input": 0.11, + "output": 0.44, + "basis": "median of 2 host(s)", + "discount_removed": 0.0 }, "qwen/qwen3-coder-next": { "input": 0.12, - "output": 0.8 + "output": 0.8, + "basis": "median of 1 host(s)", + "discount_removed": 0.0 }, "qwen/qwen3-next-80b-a3b-instruct": { - "input": 0.09, - "output": 1.1 + "input": 0.095, + "output": 1.1, + "basis": "median of 2 host(s)", + "discount_removed": 0.0 }, "qwen/qwen3-vl-235b-a22b-instruct": { "input": 0.21, - "output": 1.9 + "output": 1.9, + "basis": "median of 3 host(s)", + "discount_removed": 0.0 }, "qwen/qwen3.5-9b": { - "input": 0.17, - "output": 0.25 + "input": 0.1, + "output": 0.15, + "basis": "median of 5 host(s)", + "discount_removed": 0.0 }, "qwen/qwen3.5-flash-02-23": { "input": 0.065, - "output": 0.26 + "output": 0.26, + "basis": "Alibaba (the lab's own price)", + "discount_removed": 0.0 }, "upstage/solar-pro4": { - "input": 0.15, - "output": 0.6 + "input": 0.3, + "output": 1.2, + "basis": "Upstage (the lab's own price)", + "discount_removed": 0.5 }, "x-ai/grok-4.3": { "input": 1.25, - "output": 2.5 + "output": 2.5, + "basis": "xAI (the lab's own price)", + "discount_removed": 0.0 }, "xiaomi/mimo-v2.5": { "input": 0.4, - "output": 2.0 + "output": 2.0, + "basis": "median of 1 host(s)", + "discount_removed": 0.0 }, "z-ai/glm-4.5-air": { - "input": 0.13, - "output": 0.85 + "input": 0.2, + "output": 1.1, + "basis": "Z.AI (the lab's own price)", + "discount_removed": 0.0 }, "z-ai/glm-4.6": { - "input": 0.5, - "output": 2.0 + "input": 0.6, + "output": 2.2, + "basis": "Z.AI (the lab's own price)", + "discount_removed": 0.0 }, "z-ai/glm-4.7": { "input": 0.6, - "output": 2.2 + "output": 2.2, + "basis": "Z.AI (the lab's own price)", + "discount_removed": 0.0 }, "z-ai/glm-5": { "input": 1.0, - "output": 3.2 + "output": 3.2, + "basis": "Z.AI (the lab's own price)", + "discount_removed": 0.0 }, "z-ai/glm-5.3": { - "input": 0.0699, - "output": 1.4 + "input": 1.4, + "output": 4.4, + "basis": "Z.AI (the lab's own price)", + "discount_removed": 0.0 }, "z-ai/glm-5.3-flash": { - "input": 0.0149, - "output": 0.1543 + "input": 0.15, + "output": 0.5, + "basis": "Z.AI (the lab's own price)", + "discount_removed": 0.0 } } }