diff --git a/.github/workflows/aigateway-prices-refresh.yaml b/.github/workflows/aigateway-prices-refresh.yaml new file mode 100644 index 00000000000..53e9234403d --- /dev/null +++ b/.github/workflows/aigateway-prices-refresh.yaml @@ -0,0 +1,177 @@ +# Refreshes the AI Gateway price book from live upstream data (models.dev) +# once a week and opens a pull request when the generated artifacts change. +# +# The price book seeds customer-visible cost numbers, so the refresh is never +# merged automatically. The workflow only ever proposes a change; a human +# reviews and merges it. +# +# Behavior: +# - Runs every Monday. If regeneration produces no diff, the run ends +# without opening anything. +# - Reuses a single branch and pull request, force-pushing each week, so at +# most one refresh PR is open and it always carries the newest snapshot. +# - Fails loudly when the generator refuses to run, which it does by design +# when upstream drops a model pinned in overrides.jq or curated in +# curation.json. Failures are announced in Slack. +name: aigateway-prices-refresh + +on: + schedule: + # 09:00 UTC every Monday, so the PR is waiting when EU and US start the week. + - cron: "0 9 * * 1" + workflow_dispatch: # allows manual runs for testing + # TEMPORARY: schedule and workflow_dispatch only fire from the default + # branch, so this trigger is the only way to exercise the workflow before + # it merges. Drop this commit once the run has been verified. + push: + branches: + - yevhenii/aigov-578-automate-updates-to-the-shipped-ai-model-price-book + +permissions: {} + +concurrency: + group: aigateway-prices-refresh + +env: + REFRESH_BRANCH: bot/aigateway-prices-refresh + PRICES_FILE: coderd/aibridge/prices/data/prices.json + CATALOG_FILE: site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json + +jobs: + refresh: + name: Refresh price book + runs-on: ubuntu-latest + permissions: {} + steps: + - name: Harden Runner + uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up mise tools + uses: ./.github/actions/setup-mise + with: + install-args: "go node pnpm" + + # Needed by catalog generation, which formats its output with biome. + - name: Install pnpm dependencies + uses: ./.github/actions/pnpm-install + + - name: Snapshot the current price book + run: cp "${PRICES_FILE}" "${RUNNER_TEMP}/prices-before.json" + + - name: Regenerate price book and model catalog + run: make gen/aibridge-prices + + - name: Detect changes + id: detect + run: | + set -euo pipefail + if git diff --quiet -- "${PRICES_FILE}" "${CATALOG_FILE}"; then + # exit 0 => NO differences => nothing to propose + echo "Price book already matches upstream; nothing to propose." + echo "changed=false" >> "$GITHUB_OUTPUT" + else + # exit 1 => differences found + git diff --stat -- "${PRICES_FILE}" "${CATALOG_FILE}" + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Build pull request body + if: steps.detect.outputs.changed == 'true' + run: | + set -euo pipefail + go run ./scripts/aibridgepricesdiff \ + -old "${RUNNER_TEMP}/prices-before.json" \ + -new "${PRICES_FILE}" > "${RUNNER_TEMP}/summary.md" + + { + cat "${RUNNER_TEMP}/summary.md" + echo + echo "## Review notes" + echo + echo "Regenerated by \`make gen/aibridge-prices\` from the live" + echo "[models.dev](https://models.dev) catalog. Both artifacts come from one" + echo "snapshot, so they ship together:" + echo + echo "- \`${PRICES_FILE}\`" + echo "- \`${CATALOG_FILE}\`" + echo + echo "These are customer-visible cost numbers taken from upstream data, so this" + echo "PR is never merged automatically. The summary above lists what moved; check" + echo "the diff for exact figures before approving." + echo + echo "Opened automatically by the [aigateway-prices-refresh workflow](${RUN_URL})." + } > "${RUNNER_TEMP}/body.md" + + cat "${RUNNER_TEMP}/body.md" + env: + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + + - name: Open or update the refresh pull request + if: steps.detect.outputs.changed == 'true' + env: + # Use cdrci's token instead of the default GITHUB_TOKEN: PRs opened + # with GITHUB_TOKEN do not trigger workflow runs, so the refresh + # would arrive without CI signal. + GH_TOKEN: ${{ secrets.CDRCI_GITHUB_TOKEN }} + PR_TITLE: "chore: refresh AI model price book" + run: | + set -euo pipefail + + # persist-credentials is disabled on checkout, so authenticate the + # push explicitly. + git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git checkout -b "${REFRESH_BRANCH}" + git add -- "${PRICES_FILE}" "${CATALOG_FILE}" + git commit -m "${PR_TITLE}" + + # Force-push: the branch is regenerated from the newest upstream + # snapshot each week, so the previous contents are always stale. + git push --force origin "refs/heads/${REFRESH_BRANCH}" + + # REST, not `gh pr`: those go through GraphQL, which needs a read:org + # scope that cdrci's token lacks. + owner="${GITHUB_REPOSITORY%%/*}" + pr_number="$(gh api "repos/${GITHUB_REPOSITORY}/pulls?state=open&base=main&head=${owner}:${REFRESH_BRANCH}" --jq '.[0].number // empty')" + if [ -n "${pr_number}" ]; then + gh api --method PATCH "repos/${GITHUB_REPOSITORY}/pulls/${pr_number}" \ + -f title="${PR_TITLE}" \ + -f body="$(cat "${RUNNER_TEMP}/body.md")" \ + --silent + echo "Updated existing PR #${pr_number}." + else + gh api --method POST "repos/${GITHUB_REPOSITORY}/pulls" \ + -f title="${PR_TITLE}" \ + -f head="${REFRESH_BRANCH}" \ + -f base=main \ + -f body="$(cat "${RUNNER_TEMP}/body.md")" \ + --jq '.html_url' + fi + + - name: Send Slack notification on failure + if: failure() + env: + SLACK_WEBHOOK: ${{ secrets.AIGATEWAY_PRICES_SLACK_WEBHOOK }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + set -euo pipefail + if [ -z "${SLACK_WEBHOOK}" ]; then + echo "::error::AIGATEWAY_PRICES_SLACK_WEBHOOK is not set; the failure alert could not be sent." + exit 1 + fi + # printf, not a double-quoted literal: bash leaves \n as two + # characters, and jq --arg then escapes the backslash, so Slack + # would print \n as text instead of breaking the line. + text="$(printf ':warning: *AI model price book refresh failed.*\nThe generator fails by design when upstream drops a model pinned in scripts/aibridgepricesgen/overrides.jq or curated in curation.json. Logs: %s' "${RUN_URL}")" + payload="$(jq -nc --arg text "${text}" '{text: $text}')" + curl -fsSL -X POST -H 'Content-type: application/json' -d "${payload}" "${SLACK_WEBHOOK}" + echo "Sent Slack notification" diff --git a/CODEOWNERS b/CODEOWNERS index 513013605a2..cfbfb004893 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -40,3 +40,10 @@ coderd/database/queries/ai_provider_keys.sql @ibetitsmike @johnstcn coderd/database/queries/aicostcontrol.sql @ibetitsmike @johnstcn codersdk/aiproviders.go @ibetitsmike @johnstcn codersdk/aiproviders_bedrock.go @ibetitsmike @johnstcn + +# Generated price book and frontend model catalog. The +# aigateway-prices-refresh workflow proposes updates to both from live +# models.dev data, and they carry customer-visible cost numbers, so every +# refresh needs a review from someone who owns them. +coderd/aibridge/prices/data/prices.json @evgeniy-scherbina +site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json @evgeniy-scherbina diff --git a/coderd/aibridge/prices/data/prices.json b/coderd/aibridge/prices/data/prices.json index 7570561b618..75bf170f0e9 100644 --- a/coderd/aibridge/prices/data/prices.json +++ b/coderd/aibridge/prices/data/prices.json @@ -31,22 +31,6 @@ "cache_read_price": 1000000, "cache_write_price": 12500000 }, - { - "provider": "anthropic", - "model": "claude-opus-4-1", - "input_price": 15000000, - "output_price": 75000000, - "cache_read_price": 1500000, - "cache_write_price": 18750000 - }, - { - "provider": "anthropic", - "model": "claude-opus-4-1-20250805", - "input_price": 15000000, - "output_price": 75000000, - "cache_read_price": 1500000, - "cache_write_price": 18750000 - }, { "provider": "anthropic", "model": "claude-opus-4-5", @@ -143,6 +127,14 @@ "cache_read_price": 100000, "cache_write_price": 1250000 }, + { + "provider": "azure", + "model": "claude-mythos-5", + "input_price": 10000000, + "output_price": 50000000, + "cache_read_price": 1000000, + "cache_write_price": 12500000 + }, { "provider": "azure", "model": "claude-opus-4-1", @@ -514,10 +506,10 @@ { "provider": "azure", "model": "gpt-5.6-luna", - "input_price": 1000000, - "output_price": 6000000, - "cache_read_price": 100000, - "cache_write_price": 1250000 + "input_price": 200000, + "output_price": 1200000, + "cache_read_price": 20000, + "cache_write_price": 250000 }, { "provider": "azure", @@ -525,15 +517,15 @@ "input_price": 5000000, "output_price": 30000000, "cache_read_price": 500000, - "cache_write_price": null + "cache_write_price": 6250000 }, { "provider": "azure", "model": "gpt-5.6-terra", - "input_price": 2500000, - "output_price": 15000000, - "cache_read_price": 250000, - "cache_write_price": 3125000 + "input_price": 2000000, + "output_price": 12000000, + "cache_read_price": 200000, + "cache_write_price": 2500000 }, { "provider": "azure", @@ -615,6 +607,14 @@ "cache_read_price": null, "cache_write_price": null }, + { + "provider": "azure", + "model": "kimi-k2.7-code", + "input_price": 950000, + "output_price": 4000000, + "cache_read_price": 190000, + "cache_write_price": null + }, { "provider": "azure", "model": "llama-3.3-70b-instruct", @@ -1135,6 +1135,30 @@ "cache_read_price": 200000, "cache_write_price": 2500000 }, + { + "provider": "bedrock", + "model": "global.openai.gpt-5.6-luna", + "input_price": 220000, + "output_price": 1320000, + "cache_read_price": 22000, + "cache_write_price": 275000 + }, + { + "provider": "bedrock", + "model": "global.openai.gpt-5.6-sol", + "input_price": 5500000, + "output_price": 33000000, + "cache_read_price": 550000, + "cache_write_price": 6875000 + }, + { + "provider": "bedrock", + "model": "global.openai.gpt-5.6-terra", + "input_price": 2200000, + "output_price": 13200000, + "cache_read_price": 220000, + "cache_write_price": 2750000 + }, { "provider": "bedrock", "model": "google.gemma-3-12b-it", @@ -1429,7 +1453,7 @@ "input_price": 5500000, "output_price": 33000000, "cache_read_price": 550000, - "cache_write_price": 6880000 + "cache_write_price": 6875000 }, { "provider": "bedrock", @@ -1815,6 +1839,14 @@ "cache_read_price": 150000, "cache_write_price": null }, + { + "provider": "copilot", + "model": "gemini-3.7-flash", + "input_price": 750000, + "output_price": 3750000, + "cache_read_price": 75000, + "cache_write_price": null + }, { "provider": "copilot", "model": "gpt-4.1", @@ -1919,6 +1951,14 @@ "cache_read_price": 500000, "cache_write_price": null }, + { + "provider": "copilot", + "model": "grok-4.6", + "input_price": 2000000, + "output_price": 6000000, + "cache_read_price": 500000, + "cache_write_price": null + }, { "provider": "copilot", "model": "kimi-k2.7-code", @@ -1927,6 +1967,14 @@ "cache_read_price": 190000, "cache_write_price": null }, + { + "provider": "copilot", + "model": "kimi-k3", + "input_price": 3000000, + "output_price": 15000000, + "cache_read_price": 300000, + "cache_write_price": null + }, { "provider": "copilot", "model": "mai-code-1-flash-picker", @@ -1935,6 +1983,14 @@ "cache_read_price": 75000, "cache_write_price": null }, + { + "provider": "copilot", + "model": "mai-code-1.1-flash", + "input_price": 200000, + "output_price": 1200000, + "cache_read_price": 20000, + "cache_write_price": null + }, { "provider": "google", "model": "deep-research-max-preview-04-2026", @@ -1951,22 +2007,6 @@ "cache_read_price": 200000, "cache_write_price": null }, - { - "provider": "google", - "model": "gemini-2.0-flash", - "input_price": 100000, - "output_price": 400000, - "cache_read_price": 25000, - "cache_write_price": null - }, - { - "provider": "google", - "model": "gemini-2.0-flash-lite", - "input_price": 75000, - "output_price": 300000, - "cache_read_price": null, - "cache_write_price": null - }, { "provider": "google", "model": "gemini-2.5-computer-use-preview-10-2025", @@ -2047,14 +2087,6 @@ "cache_read_price": null, "cache_write_price": null }, - { - "provider": "google", - "model": "gemini-3-pro-preview", - "input_price": 2000000, - "output_price": 12000000, - "cache_read_price": 200000, - "cache_write_price": null - }, { "provider": "google", "model": "gemini-3.1-flash-image", @@ -2159,6 +2191,14 @@ "cache_read_price": 150000, "cache_write_price": null }, + { + "provider": "google", + "model": "gemini-3.7-flash", + "input_price": 750000, + "output_price": 3750000, + "cache_read_price": 75000, + "cache_write_price": null + }, { "provider": "google", "model": "gemini-embedding-001", @@ -2839,6 +2879,22 @@ "cache_read_price": null, "cache_write_price": null }, + { + "provider": "openrouter", + "model": "bytedance-seed/seed-2-1-turbo", + "input_price": 500000, + "output_price": 2500000, + "cache_read_price": null, + "cache_write_price": null + }, + { + "provider": "openrouter", + "model": "bytedance-seed/seed-2.0-code", + "input_price": 500000, + "output_price": 3000000, + "cache_read_price": null, + "cache_write_price": null + }, { "provider": "openrouter", "model": "bytedance-seed/seed-2.0-lite", @@ -2972,7 +3028,7 @@ "model": "deepseek/deepseek-v3.1-terminus", "input_price": 270000, "output_price": 1000000, - "cache_read_price": 135000, + "cache_read_price": null, "cache_write_price": null }, { @@ -2994,6 +3050,14 @@ { "provider": "openrouter", "model": "deepseek/deepseek-v4-flash", + "input_price": 79800, + "output_price": 159600, + "cache_read_price": 15960, + "cache_write_price": null + }, + { + "provider": "openrouter", + "model": "deepseek/deepseek-v4-flash-0731", "input_price": 140000, "output_price": 280000, "cache_read_price": 28000, @@ -3001,18 +3065,26 @@ }, { "provider": "openrouter", - "model": "deepseek/deepseek-v4-flash-0731", - "input_price": 90000, - "output_price": 180000, - "cache_read_price": 18000, + "model": "deepseek/deepseek-v4-pro", + "input_price": 660000, + "output_price": 1980000, + "cache_read_price": 22000, "cache_write_price": null }, { "provider": "openrouter", - "model": "deepseek/deepseek-v4-pro", - "input_price": 435000, - "output_price": 870000, - "cache_read_price": 3625, + "model": "deepseek/deepseek-v4-pro-0813", + "input_price": 660000, + "output_price": 1980000, + "cache_read_price": 22000, + "cache_write_price": null + }, + { + "provider": "openrouter", + "model": "dots-studio/dots-3-note-preview:free", + "input_price": 0, + "output_price": 0, + "cache_read_price": null, "cache_write_price": null }, { @@ -3162,10 +3234,18 @@ { "provider": "openrouter", "model": "google/gemini-3.6-flash", - "input_price": 1500000, - "output_price": 7500000, - "cache_read_price": 150000, - "cache_write_price": 83333 + "input_price": 750000, + "output_price": 3750000, + "cache_read_price": 75000, + "cache_write_price": 41667 + }, + { + "provider": "openrouter", + "model": "google/gemini-3.7-flash", + "input_price": 375000, + "output_price": 1875000, + "cache_read_price": 37500, + "cache_write_price": 20833 }, { "provider": "openrouter", @@ -3258,8 +3338,8 @@ { "provider": "openrouter", "model": "gryphe/mythomax-l2-13b", - "input_price": 80000, - "output_price": 110000, + "input_price": 60000, + "output_price": 60000, "cache_read_price": null, "cache_write_price": null }, @@ -3305,10 +3385,10 @@ }, { "provider": "openrouter", - "model": "inclusionai/ling-3.0-flash:free", - "input_price": 0, - "output_price": 0, - "cache_read_price": null, + "model": "inclusionai/ling-3.0-flash", + "input_price": 21000, + "output_price": 63000, + "cache_read_price": 4200, "cache_write_price": null }, { @@ -3343,6 +3423,14 @@ "cache_read_price": 150000, "cache_write_price": null }, + { + "provider": "openrouter", + "model": "liquid/lfm-2.5-2.6b:free", + "input_price": 0, + "output_price": 0, + "cache_read_price": null, + "cache_write_price": null + }, { "provider": "openrouter", "model": "mancer/weaver", @@ -3423,6 +3511,14 @@ "cache_read_price": null, "cache_write_price": null }, + { + "provider": "openrouter", + "model": "meta/muse-glimmer-30b", + "input_price": 350000, + "output_price": 1500000, + "cache_read_price": 40000, + "cache_write_price": null + }, { "provider": "openrouter", "model": "meta/muse-spark-1.1", @@ -3431,6 +3527,14 @@ "cache_read_price": 150000, "cache_write_price": null }, + { + "provider": "openrouter", + "model": "meta/muse-spark-1.2", + "input_price": 1250000, + "output_price": 4250000, + "cache_read_price": 150000, + "cache_write_price": null + }, { "provider": "openrouter", "model": "microsoft/phi-4", @@ -3490,7 +3594,7 @@ { "provider": "openrouter", "model": "minimax/minimax-m2.5", - "input_price": 150000, + "input_price": 220000, "output_price": 900000, "cache_read_price": 50000, "cache_write_price": null @@ -3498,9 +3602,9 @@ { "provider": "openrouter", "model": "minimax/minimax-m2.7", - "input_price": 250000, - "output_price": 1000000, - "cache_read_price": 50000, + "input_price": 300000, + "output_price": 1200000, + "cache_read_price": 60000, "cache_write_price": null }, { @@ -3634,8 +3738,8 @@ { "provider": "openrouter", "model": "mistralai/mistral-small-3.2-24b-instruct", - "input_price": 75000, - "output_price": 200000, + "input_price": 93750, + "output_price": 250000, "cache_read_price": null, "cache_write_price": null }, @@ -3690,15 +3794,15 @@ { "provider": "openrouter", "model": "moonshotai/kimi-k2.6", - "input_price": 589000, - "output_price": 2480000, - "cache_read_price": 99200, + "input_price": 560500, + "output_price": 2360000, + "cache_read_price": 94400, "cache_write_price": null }, { "provider": "openrouter", "model": "moonshotai/kimi-k2.7-code", - "input_price": 730000, + "input_price": 710000, "output_price": 3500000, "cache_read_price": 150000, "cache_write_price": null @@ -3839,6 +3943,22 @@ "cache_read_price": null, "cache_write_price": null }, + { + "provider": "openrouter", + "model": "nvidia/nemotron-3.5-lightning", + "input_price": 80000, + "output_price": 200000, + "cache_read_price": 40000, + "cache_write_price": null + }, + { + "provider": "openrouter", + "model": "nvidia/nemotron-3.5-lightning:free", + "input_price": 0, + "output_price": 0, + "cache_read_price": null, + "cache_write_price": null + }, { "provider": "openrouter", "model": "nvidia/nemotron-nano-12b-v2-vl:free", @@ -4095,14 +4215,6 @@ "cache_read_price": null, "cache_write_price": null }, - { - "provider": "openrouter", - "model": "openai/gpt-5.3-chat", - "input_price": 1750000, - "output_price": 14000000, - "cache_read_price": 175000, - "cache_write_price": null - }, { "provider": "openrouter", "model": "openai/gpt-5.3-codex", @@ -4170,50 +4282,50 @@ { "provider": "openrouter", "model": "openai/gpt-5.6-luna", - "input_price": 100000, - "output_price": 600000, - "cache_read_price": 10000, - "cache_write_price": 125000 + "input_price": 200000, + "output_price": 1200000, + "cache_read_price": 20000, + "cache_write_price": 250000 }, { "provider": "openrouter", "model": "openai/gpt-5.6-luna-pro", - "input_price": 100000, - "output_price": 600000, - "cache_read_price": 10000, - "cache_write_price": 125000 + "input_price": 200000, + "output_price": 1200000, + "cache_read_price": 20000, + "cache_write_price": 250000 }, { "provider": "openrouter", "model": "openai/gpt-5.6-sol", - "input_price": 5000000, - "output_price": 30000000, - "cache_read_price": 500000, - "cache_write_price": 6250000 + "input_price": 2500000, + "output_price": 15000000, + "cache_read_price": 250000, + "cache_write_price": 3125000 }, { "provider": "openrouter", "model": "openai/gpt-5.6-sol-pro", - "input_price": 5000000, - "output_price": 30000000, - "cache_read_price": 500000, - "cache_write_price": 6250000 + "input_price": 2500000, + "output_price": 15000000, + "cache_read_price": 250000, + "cache_write_price": 3125000 }, { "provider": "openrouter", "model": "openai/gpt-5.6-terra", - "input_price": 1000000, - "output_price": 6000000, - "cache_read_price": 100000, - "cache_write_price": 1250000 + "input_price": 2000000, + "output_price": 12000000, + "cache_read_price": 200000, + "cache_write_price": 2500000 }, { "provider": "openrouter", "model": "openai/gpt-5.6-terra-pro", - "input_price": 1000000, - "output_price": 6000000, - "cache_read_price": 100000, - "cache_write_price": 1250000 + "input_price": 2000000, + "output_price": 12000000, + "cache_read_price": 200000, + "cache_write_price": 2500000 }, { "provider": "openrouter", @@ -4242,9 +4354,9 @@ { "provider": "openrouter", "model": "openai/gpt-oss-120b", - "input_price": 37000, + "input_price": 30000, "output_price": 170000, - "cache_read_price": null, + "cache_read_price": 30000, "cache_write_price": null }, { @@ -4466,24 +4578,24 @@ { "provider": "openrouter", "model": "qwen/qwen-plus-2025-07-28:thinking", - "input_price": 400000, - "output_price": 1200000, + "input_price": 260000, + "output_price": 780000, "cache_read_price": null, - "cache_write_price": 500000 + "cache_write_price": null }, { "provider": "openrouter", "model": "qwen/qwen2.5-vl-72b-instruct", - "input_price": 250000, - "output_price": 750000, - "cache_read_price": null, + "input_price": 800000, + "output_price": 1000000, + "cache_read_price": 400000, "cache_write_price": null }, { "provider": "openrouter", "model": "qwen/qwen3-14b", - "input_price": 227500, - "output_price": 910000, + "input_price": 120000, + "output_price": 240000, "cache_read_price": null, "cache_write_price": null }, @@ -4498,8 +4610,8 @@ { "provider": "openrouter", "model": "qwen/qwen3-235b-a22b-2507", - "input_price": 149500, - "output_price": 598000, + "input_price": 90000, + "output_price": 550000, "cache_read_price": null, "cache_write_price": null }, @@ -4514,8 +4626,8 @@ { "provider": "openrouter", "model": "qwen/qwen3-30b-a3b", - "input_price": 120000, - "output_price": 500000, + "input_price": 130000, + "output_price": 520000, "cache_read_price": null, "cache_write_price": null }, @@ -4563,7 +4675,7 @@ "provider": "openrouter", "model": "qwen/qwen3-coder-30b-a3b-instruct", "input_price": 70000, - "output_price": 270000, + "output_price": 280000, "cache_read_price": null, "cache_write_price": null }, @@ -4610,9 +4722,9 @@ { "provider": "openrouter", "model": "qwen/qwen3-next-80b-a3b-instruct", - "input_price": 90000, + "input_price": 100000, "output_price": 1100000, - "cache_read_price": null, + "cache_read_price": 70000, "cache_write_price": null }, { @@ -4634,16 +4746,16 @@ { "provider": "openrouter", "model": "qwen/qwen3-vl-235b-a22b-thinking", - "input_price": 980000, - "output_price": 3950000, + "input_price": 400000, + "output_price": 4000000, "cache_read_price": null, "cache_write_price": null }, { "provider": "openrouter", "model": "qwen/qwen3-vl-30b-a3b-instruct", - "input_price": 150000, - "output_price": 600000, + "input_price": 130000, + "output_price": 520000, "cache_read_price": null, "cache_write_price": null }, @@ -4682,8 +4794,8 @@ { "provider": "openrouter", "model": "qwen/qwen3.5-122b-a10b", - "input_price": 260000, - "output_price": 2080000, + "input_price": 290000, + "output_price": 2400000, "cache_read_price": null, "cache_write_price": null }, @@ -4698,9 +4810,9 @@ { "provider": "openrouter", "model": "qwen/qwen3.5-35b-a3b", - "input_price": 140000, - "output_price": 1000000, - "cache_read_price": null, + "input_price": 225000, + "output_price": 1800000, + "cache_read_price": 225000, "cache_write_price": null }, { @@ -4756,7 +4868,7 @@ "model": "qwen/qwen3.6-35b-a3b", "input_price": 140000, "output_price": 1000000, - "cache_read_price": null, + "cache_read_price": 50000, "cache_write_price": null }, { @@ -4807,6 +4919,22 @@ "cache_read_price": 64000, "cache_write_price": 400000 }, + { + "provider": "openrouter", + "model": "qwen/qwen3.8-2.4t-a95b", + "input_price": 2000000, + "output_price": 6000000, + "cache_read_price": 250000, + "cache_write_price": null + }, + { + "provider": "openrouter", + "model": "qwen/qwen3.8-27b", + "input_price": 450000, + "output_price": 3200000, + "cache_read_price": 50000, + "cache_write_price": null + }, { "provider": "openrouter", "model": "qwen/qwen3.8-max", @@ -4855,6 +4983,14 @@ "cache_read_price": 500000, "cache_write_price": null }, + { + "provider": "openrouter", + "model": "sakana/sakana-namazu", + "input_price": 950000, + "output_price": 4000000, + "cache_read_price": 150000, + "cache_write_price": null + }, { "provider": "openrouter", "model": "sao10k/l3-lunaris-8b", @@ -4914,9 +5050,9 @@ { "provider": "openrouter", "model": "tencent/hy3-preview", - "input_price": 63000, - "output_price": 210000, - "cache_read_price": 21000, + "input_price": 180000, + "output_price": 600000, + "cache_read_price": 60000, "cache_write_price": null }, { @@ -4954,15 +5090,15 @@ { "provider": "openrouter", "model": "thinkingmachines/inkling", - "input_price": 1000000, + "input_price": 950000, "output_price": 4050000, - "cache_read_price": 170000, + "cache_read_price": 160000, "cache_write_price": null }, { "provider": "openrouter", "model": "thinkingmachines/inkling-small", - "input_price": 500000, + "input_price": 450000, "output_price": 1200000, "cache_read_price": 100000, "cache_write_price": null @@ -4983,6 +5119,14 @@ "cache_read_price": 15000, "cache_write_price": null }, + { + "provider": "openrouter", + "model": "upstage/solar-pro4", + "input_price": 30000, + "output_price": 120000, + "cache_read_price": 6000, + "cache_write_price": null + }, { "provider": "openrouter", "model": "writer/palmyra-x5", @@ -5023,6 +5167,14 @@ "cache_read_price": 300000, "cache_write_price": null }, + { + "provider": "openrouter", + "model": "x-ai/grok-4.6", + "input_price": 2000000, + "output_price": 6000000, + "cache_read_price": 500000, + "cache_write_price": null + }, { "provider": "openrouter", "model": "x-ai/grok-build-0.1", @@ -5106,9 +5258,9 @@ { "provider": "openrouter", "model": "z-ai/glm-5", - "input_price": 950000, - "output_price": 2550000, - "cache_read_price": 200000, + "input_price": 600000, + "output_price": 1920000, + "cache_read_price": 120000, "cache_write_price": null }, { @@ -5130,9 +5282,17 @@ { "provider": "openrouter", "model": "z-ai/glm-5.2", - "input_price": 760000, - "output_price": 2420000, - "cache_read_price": 140000, + "input_price": 490000, + "output_price": 1540000, + "cache_read_price": 91000, + "cache_write_price": null + }, + { + "provider": "openrouter", + "model": "z-ai/glm-5.2:free", + "input_price": 0, + "output_price": 0, + "cache_read_price": null, "cache_write_price": null }, { @@ -5178,18 +5338,18 @@ { "provider": "openrouter", "model": "~deepseek/deepseek-v4-flash-latest", - "input_price": 90000, - "output_price": 180000, - "cache_read_price": 18000, + "input_price": 78596, + "output_price": 157192, + "cache_read_price": 15719, "cache_write_price": null }, { "provider": "openrouter", "model": "~google/gemini-flash-latest", - "input_price": 1500000, - "output_price": 7500000, - "cache_read_price": 150000, - "cache_write_price": 83333 + "input_price": 375000, + "output_price": 1875000, + "cache_read_price": 37500, + "cache_write_price": 20833 }, { "provider": "openrouter", @@ -5202,18 +5362,18 @@ { "provider": "openrouter", "model": "~moonshotai/kimi-latest", - "input_price": 2900000, - "output_price": 14000000, + "input_price": 2600000, + "output_price": 13000000, "cache_read_price": 290000, "cache_write_price": null }, { "provider": "openrouter", "model": "~openai/gpt-latest", - "input_price": 5000000, - "output_price": 30000000, - "cache_read_price": 500000, - "cache_write_price": 6250000 + "input_price": 2500000, + "output_price": 15000000, + "cache_read_price": 250000, + "cache_write_price": 3125000 }, { "provider": "openrouter", @@ -5228,7 +5388,7 @@ "model": "~x-ai/grok-latest", "input_price": 2000000, "output_price": 6000000, - "cache_read_price": 300000, + "cache_read_price": 500000, "cache_write_price": null }, { @@ -5431,6 +5591,14 @@ "cache_read_price": 80000, "cache_write_price": 500000 }, + { + "provider": "vercel", + "model": "alibaba/qwen3.8-2.4t-a95b", + "input_price": 2000000, + "output_price": 6000000, + "cache_read_price": 200000, + "cache_write_price": null + }, { "provider": "vercel", "model": "alibaba/qwen3.8-max", @@ -5503,14 +5671,6 @@ "cache_read_price": 1500000, "cache_write_price": 18750000 }, - { - "provider": "vercel", - "model": "anthropic/claude-opus-4.1", - "input_price": 15000000, - "output_price": 75000000, - "cache_read_price": 1500000, - "cache_write_price": 18750000 - }, { "provider": "vercel", "model": "anthropic/claude-opus-4.5", @@ -5690,9 +5850,9 @@ { "provider": "vercel", "model": "deepseek/deepseek-v4-flash", - "input_price": 200000, - "output_price": 400000, - "cache_read_price": 40000, + "input_price": 130000, + "output_price": 260000, + "cache_read_price": 28000, "cache_write_price": null }, { @@ -5706,9 +5866,17 @@ { "provider": "vercel", "model": "deepseek/deepseek-v4-pro", - "input_price": 435000, - "output_price": 870000, - "cache_read_price": 3600, + "input_price": 1740000, + "output_price": 3480000, + "cache_read_price": 140000, + "cache_write_price": null + }, + { + "provider": "vercel", + "model": "deepseek/deepseek-v4-pro-0813", + "input_price": 1320000, + "output_price": 3960000, + "cache_read_price": 132000, "cache_write_price": null }, { @@ -5823,6 +5991,14 @@ "cache_read_price": 150000, "cache_write_price": null }, + { + "provider": "vercel", + "model": "google/gemini-3.7-flash", + "input_price": 750000, + "output_price": 3750000, + "cache_read_price": 75000, + "cache_write_price": null + }, { "provider": "vercel", "model": "google/gemini-omni-flash-preview", @@ -5865,10 +6041,10 @@ }, { "provider": "vercel", - "model": "inclusionai/ling-3.0-flash-free", - "input_price": 0, - "output_price": 0, - "cache_read_price": null, + "model": "inclusionai/ling-3.0-flash", + "input_price": 60000, + "output_price": 180000, + "cache_read_price": 12000, "cache_write_price": null }, { @@ -5951,6 +6127,14 @@ "cache_read_price": null, "cache_write_price": null }, + { + "provider": "vercel", + "model": "meta/muse-glimmer-30b", + "input_price": 350000, + "output_price": 1500000, + "cache_read_price": 40000, + "cache_write_price": null + }, { "provider": "vercel", "model": "meta/muse-spark-1.1", @@ -5959,6 +6143,22 @@ "cache_read_price": 150000, "cache_write_price": null }, + { + "provider": "vercel", + "model": "meta/muse-spark-1.2", + "input_price": 1250000, + "output_price": 4250000, + "cache_read_price": 150000, + "cache_write_price": null + }, + { + "provider": "vercel", + "model": "meta/muse-spark-1.2-contributor", + "input_price": 100000, + "output_price": 200000, + "cache_read_price": 2000, + "cache_write_price": null + }, { "provider": "vercel", "model": "minimax/minimax-m2", @@ -6399,14 +6599,6 @@ "cache_read_price": 30000, "cache_write_price": null }, - { - "provider": "vercel", - "model": "openai/gpt-5.1-instant", - "input_price": 1250000, - "output_price": 10000000, - "cache_read_price": 130000, - "cache_write_price": null - }, { "provider": "vercel", "model": "openai/gpt-5.1-thinking", @@ -6439,14 +6631,6 @@ "cache_read_price": null, "cache_write_price": null }, - { - "provider": "vercel", - "model": "openai/gpt-5.3-chat", - "input_price": 1750000, - "output_price": 14000000, - "cache_read_price": 175000, - "cache_write_price": null - }, { "provider": "vercel", "model": "openai/gpt-5.3-codex", @@ -6514,10 +6698,10 @@ { "provider": "vercel", "model": "openai/gpt-5.6-sol", - "input_price": 5000000, - "output_price": 30000000, - "cache_read_price": 500000, - "cache_write_price": 6250000 + "input_price": 2500000, + "output_price": 15000000, + "cache_read_price": 250000, + "cache_write_price": 3125000 }, { "provider": "vercel", @@ -6687,6 +6871,14 @@ "cache_read_price": 500000, "cache_write_price": null }, + { + "provider": "vercel", + "model": "sakana/namazu", + "input_price": 950000, + "output_price": 4000000, + "cache_read_price": 150000, + "cache_write_price": null + }, { "provider": "vercel", "model": "stepfun/step-3.5-flash", @@ -6807,6 +6999,14 @@ "cache_read_price": 300000, "cache_write_price": null }, + { + "provider": "vercel", + "model": "xai/grok-4.6", + "input_price": 2000000, + "output_price": 6000000, + "cache_read_price": 500000, + "cache_write_price": null + }, { "provider": "vercel", "model": "xai/grok-build-0.1", diff --git a/scripts/aibridgepricesdiff/main.go b/scripts/aibridgepricesdiff/main.go new file mode 100644 index 00000000000..95d45f94e12 --- /dev/null +++ b/scripts/aibridgepricesdiff/main.go @@ -0,0 +1,197 @@ +// aibridgepricesdiff renders a human-readable Markdown summary of the +// difference between two AI Gateway price seed files (the prices.json produced +// by aibridgepricesgen). +// +// The price refresh workflow uses it to fill the pull request body, so +// a reviewer sees at a glance which models appeared, which disappeared, and +// which repriced. Exact figures are deliberately left to the pull request +// diff, which is the source of truth. +// +// Usage: +// +// aibridgepricesdiff -old -new +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io" + "os" + "sort" + "strings" + + "golang.org/x/xerrors" +) + +// priceRow mirrors the seed file schema written by aibridgepricesgen. Pointer +// fields preserve the distinction between "not populated by upstream" (null) +// and "explicitly zero" (0). +type priceRow struct { + Provider string `json:"provider"` + Model string `json:"model"` + InputPrice *int64 `json:"input_price"` + OutputPrice *int64 `json:"output_price"` + CacheReadPrice *int64 `json:"cache_read_price"` + CacheWritePrice *int64 `json:"cache_write_price"` +} + +// modelKey identifies a model across the two snapshots. +type modelKey struct { + provider string + model string +} + +func (r priceRow) key() modelKey { + return modelKey{provider: r.Provider, model: r.Model} +} + +func (k modelKey) String() string { + return k.provider + "/" + k.model +} + +func less(a, b modelKey) bool { + if a.provider != b.provider { + return a.provider < b.provider + } + return a.model < b.model +} + +// diff is the full comparison between two snapshots +type diff struct { + added []modelKey + removed []modelKey + changed []modelKey +} + +func (d diff) empty() bool { + return len(d.added) == 0 && len(d.removed) == 0 && len(d.changed) == 0 +} + +func main() { + oldPath := flag.String("old", "", "path to the previous prices.json (required)") + newPath := flag.String("new", "", "path to the refreshed prices.json (required)") + flag.Parse() + if err := run(*oldPath, *newPath, os.Stdout); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "aibridgepricesdiff: %v\n", err) + os.Exit(1) + } +} + +func run(oldPath, newPath string, w io.Writer) error { + if oldPath == "" || newPath == "" { + return xerrors.New("-old and -new are both required") + } + oldRows, err := readRows(oldPath) + if err != nil { + return xerrors.Errorf("read %s: %w", oldPath, err) + } + newRows, err := readRows(newPath) + if err != nil { + return xerrors.Errorf("read %s: %w", newPath, err) + } + _, err = io.WriteString(w, render(compare(oldRows, newRows))) + return err +} + +func readRows(path string) ([]priceRow, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + var rows []priceRow + if err := json.Unmarshal(data, &rows); err != nil { + return nil, xerrors.Errorf("parse: %w", err) + } + return rows, nil +} + +// compare classifies every model as added, removed, or changed. +func compare(oldRows, newRows []priceRow) diff { + oldByKey := make(map[modelKey]priceRow, len(oldRows)) + for _, r := range oldRows { + oldByKey[r.key()] = r + } + + var d diff + seen := make(map[modelKey]struct{}, len(newRows)) + for _, r := range newRows { + seen[r.key()] = struct{}{} + prev, ok := oldByKey[r.key()] + switch { + case !ok: + d.added = append(d.added, r.key()) + case !samePrices(prev, r): + d.changed = append(d.changed, r.key()) + } + } + for _, r := range oldRows { + if _, ok := seen[r.key()]; !ok { + d.removed = append(d.removed, r.key()) + } + } + + for _, keys := range [][]modelKey{d.added, d.removed, d.changed} { + sort.Slice(keys, func(i, j int) bool { return less(keys[i], keys[j]) }) + } + return d +} + +// samePrices reports whether two rows for the same model carry identical +// prices. A price moving to or from null counts as a change. +func samePrices(a, b priceRow) bool { + return equalPrice(a.InputPrice, b.InputPrice) && + equalPrice(a.OutputPrice, b.OutputPrice) && + equalPrice(a.CacheReadPrice, b.CacheReadPrice) && + equalPrice(a.CacheWritePrice, b.CacheWritePrice) +} + +func equalPrice(a, b *int64) bool { + if a == nil || b == nil { + return a == nil && b == nil + } + return *a == *b +} + +// render writes the Markdown summary: counts, then the models in each +// category. Empty categories are omitted. +func render(d diff) string { + var b strings.Builder + write := func(format string, args ...any) { + _, _ = fmt.Fprintf(&b, format, args...) + } + + write("## Price book changes\n\n") + if d.empty() { + write("No price changes.\n") + return b.String() + } + + write("%s added, %s removed, %s changed.\n", + plural(len(d.added), "model"), + plural(len(d.removed), "model"), + plural(len(d.changed), "model"), + ) + + renderList := func(heading string, keys []modelKey) { + if len(keys) == 0 { + return + } + write("\n### %s\n\n", heading) + for _, k := range keys { + write("- %s\n", k) + } + } + renderList("Added", d.added) + renderList("Removed", d.removed) + renderList("Changed", d.changed) + + return b.String() +} + +func plural(n int, noun string) string { + if n == 1 { + return fmt.Sprintf("%d %s", n, noun) + } + return fmt.Sprintf("%d %ss", n, noun) +} diff --git a/scripts/aibridgepricesdiff/main_test.go b/scripts/aibridgepricesdiff/main_test.go new file mode 100644 index 00000000000..b79e187c640 --- /dev/null +++ b/scripts/aibridgepricesdiff/main_test.go @@ -0,0 +1,279 @@ +package main + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCompare(t *testing.T) { + t.Parallel() + + cases := []struct { + name string + old []priceRow + new []priceRow + wantAdded []string + wantRemoved []string + wantChanged []string + }{ + { + name: "identical", + old: []priceRow{row("anthropic", "claude", 1, 2)}, + new: []priceRow{row("anthropic", "claude", 1, 2)}, + }, + { + name: "added", + old: []priceRow{ + row("anthropic", "claude", 1, 2), + }, + new: []priceRow{ + row("anthropic", "claude", 1, 2), + row("openai", "gpt", 3, 4), + }, + wantAdded: []string{"openai/gpt"}, + }, + { + name: "removed", + old: []priceRow{ + row("anthropic", "claude", 1, 2), + row("openai", "gpt", 3, 4), + }, + new: []priceRow{ + row("anthropic", "claude", 1, 2), + }, + wantRemoved: []string{"openai/gpt"}, + }, + { + name: "changed", + old: []priceRow{row("anthropic", "claude", 1, 2)}, + new: []priceRow{row("anthropic", "claude", 1, 5)}, + wantChanged: []string{"anthropic/claude"}, + }, + { + // Every price field participates, not just input and output. + name: "cache price changed", + old: []priceRow{{ + Provider: "anthropic", + Model: "claude", + CacheReadPrice: int64Ptr(1), + }}, + new: []priceRow{{ + Provider: "anthropic", + Model: "claude", + CacheReadPrice: int64Ptr(2), + }}, + wantChanged: []string{"anthropic/claude"}, + }, + { + // A model whose price becomes null is a change, not a removal. + name: "price unset", + old: []priceRow{ + row("anthropic", "claude", 1, 2), + }, + new: []priceRow{{ + Provider: "anthropic", + Model: "claude", + InputPrice: int64Ptr(1), + }}, + wantChanged: []string{"anthropic/claude"}, + }, + { + // Zero is a real price, distinct from an absent one. + name: "zero is not null", + old: []priceRow{{ + Provider: "openai", + Model: "gpt", + InputPrice: int64Ptr(0), + }}, + new: []priceRow{{ + Provider: "openai", + Model: "gpt", + }}, + wantChanged: []string{"openai/gpt"}, + }, + { + // Same model identifier under two providers must not collide. + name: "same model different providers", + old: []priceRow{ + row("anthropic", "shared", 1, 2), + row("openai", "shared", 1, 2), + }, + new: []priceRow{ + row("anthropic", "shared", 1, 2), + row("openai", "shared", 9, 2), + }, + wantChanged: []string{"openai/shared"}, + }, + { + name: "added removed and changed together", + old: []priceRow{ + row("anthropic", "old-model", 1, 2), + row("openai", "gpt", 3, 4), + }, + new: []priceRow{ + row("anthropic", "new-model", 5, 6), + row("openai", "gpt", 3, 7), + }, + wantAdded: []string{"anthropic/new-model"}, + wantRemoved: []string{"anthropic/old-model"}, + wantChanged: []string{"openai/gpt"}, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + got := compare(tc.old, tc.new) + require.Equal(t, tc.wantAdded, names(got.added)) + require.Equal(t, tc.wantRemoved, names(got.removed)) + require.Equal(t, tc.wantChanged, names(got.changed)) + }) + } +} + +func TestCompareSortsDeterministically(t *testing.T) { + t.Parallel() + + newRows := []priceRow{ + row("openai", "b", 1, 1), + row("anthropic", "z", 1, 1), + row("anthropic", "a", 1, 1), + } + + got := compare(nil, newRows) + require.Equal(t, []string{"anthropic/a", "anthropic/z", "openai/b"}, names(got.added)) +} + +func TestRender(t *testing.T) { + t.Parallel() + + t.Run("no changes", func(t *testing.T) { + t.Parallel() + + out := render(compare( + []priceRow{row("anthropic", "claude", 1, 2)}, + []priceRow{row("anthropic", "claude", 1, 2)}, + )) + require.Equal(t, `## Price book changes + +No price changes. +`, out) + }) + + t.Run("full summary", func(t *testing.T) { + t.Parallel() + + out := render(compare( + []priceRow{ + row("anthropic", "gone", 1, 2), + row("openai", "gpt", 1, 2), + }, + []priceRow{ + row("anthropic", "fresh", 3, 4), + row("openai", "gpt", 2, 3), + }, + )) + + // Asserted whole rather than by substring so section order, spacing, + // and the absence of stray content are covered too. + require.Equal(t, `## Price book changes + +1 model added, 1 model removed, 1 model changed. + +### Added + +- anthropic/fresh + +### Removed + +- anthropic/gone + +### Changed + +- openai/gpt +`, out) + }) +} + +func TestRun(t *testing.T) { + t.Parallel() + + t.Run("missing flags", func(t *testing.T) { + t.Parallel() + + var out strings.Builder + require.ErrorContains(t, run("", "", &out), "-old and -new are both required") + }) + + t.Run("reads files", func(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + oldPath := filepath.Join(dir, "old.json") + newPath := filepath.Join(dir, "new.json") + writeFile(t, oldPath, `[{"provider":"openai","model":"gpt","input_price":1000000,"output_price":2000000,"cache_read_price":null,"cache_write_price":null}]`) + writeFile(t, newPath, `[{"provider":"openai","model":"gpt","input_price":1500000,"output_price":2000000,"cache_read_price":null,"cache_write_price":null}]`) + + var out strings.Builder + require.NoError(t, run(oldPath, newPath, &out)) + require.Contains(t, out.String(), "### Changed\n\n- openai/gpt\n") + }) + + t.Run("invalid json", func(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + path := filepath.Join(dir, "bad.json") + writeFile(t, path, "{") + + var out strings.Builder + err := run(path, path, &out) + // The path is part of the message so a failed refresh names the file + // that could not be read. + require.ErrorContains(t, err, path) + require.ErrorContains(t, err, "parse:") + }) + + t.Run("missing file", func(t *testing.T) { + t.Parallel() + + path := filepath.Join(t.TempDir(), "absent.json") + + var out strings.Builder + err := run(path, path, &out) + require.ErrorIs(t, err, os.ErrNotExist) + require.ErrorContains(t, err, path) + }) +} + +func row(provider, model string, input, output int64) priceRow { + return priceRow{ + Provider: provider, + Model: model, + InputPrice: int64Ptr(input), + OutputPrice: int64Ptr(output), + } +} + +func names(keys []modelKey) []string { + if len(keys) == 0 { + return nil + } + out := make([]string, 0, len(keys)) + for _, k := range keys { + out = append(out, k.String()) + } + return out +} + +func writeFile(t *testing.T, path, content string) { + t.Helper() + require.NoError(t, os.WriteFile(path, []byte(content), 0o600)) +} + +func int64Ptr(v int64) *int64 { return &v } diff --git a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json index 5ae75467a28..d851ef2feca 100644 --- a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json +++ b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json @@ -200,7 +200,8 @@ "maxOutputTokens": 128000, "inputCost": 5, "outputCost": 30, - "cacheReadCost": 0.5 + "cacheReadCost": 0.5, + "cacheWriteCost": 6.25 }, { "provider": "azure", @@ -209,10 +210,10 @@ "aliases": [], "contextLimit": 1050000, "maxOutputTokens": 128000, - "inputCost": 2.5, - "outputCost": 15, - "cacheReadCost": 0.25, - "cacheWriteCost": 3.125 + "inputCost": 2, + "outputCost": 12, + "cacheReadCost": 0.2, + "cacheWriteCost": 2.5 }, { "provider": "azure", @@ -221,10 +222,10 @@ "aliases": [], "contextLimit": 1050000, "maxOutputTokens": 128000, - "inputCost": 1, - "outputCost": 6, - "cacheReadCost": 0.1, - "cacheWriteCost": 1.25 + "inputCost": 0.2, + "outputCost": 1.2, + "cacheReadCost": 0.02, + "cacheWriteCost": 0.25 }, { "provider": "azure", @@ -464,10 +465,10 @@ "aliases": [], "contextLimit": 1050000, "maxOutputTokens": 128000, - "inputCost": 5, - "outputCost": 30, - "cacheReadCost": 0.5, - "cacheWriteCost": 6.25 + "inputCost": 2.5, + "outputCost": 15, + "cacheReadCost": 0.25, + "cacheWriteCost": 3.125 }, { "provider": "openrouter", @@ -476,10 +477,10 @@ "aliases": [], "contextLimit": 1050000, "maxOutputTokens": 128000, - "inputCost": 1, - "outputCost": 6, - "cacheReadCost": 0.1, - "cacheWriteCost": 1.25 + "inputCost": 2, + "outputCost": 12, + "cacheReadCost": 0.2, + "cacheWriteCost": 2.5 }, { "provider": "openrouter", @@ -488,10 +489,10 @@ "aliases": [], "contextLimit": 1050000, "maxOutputTokens": 128000, - "inputCost": 0.1, - "outputCost": 0.6, - "cacheReadCost": 0.01, - "cacheWriteCost": 0.125 + "inputCost": 0.2, + "outputCost": 1.2, + "cacheReadCost": 0.02, + "cacheWriteCost": 0.25 }, { "provider": "openrouter", @@ -500,8 +501,9 @@ "aliases": [], "contextLimit": 131072, "maxOutputTokens": 131072, - "inputCost": 0.037, - "outputCost": 0.17 + "inputCost": 0.03, + "outputCost": 0.17, + "cacheReadCost": 0.03 }, { "provider": "openrouter", @@ -520,10 +522,10 @@ "displayName": "GLM-5.2", "aliases": [], "contextLimit": 1048576, - "maxOutputTokens": 262144, - "inputCost": 0.76, - "outputCost": 2.42, - "cacheReadCost": 0.14 + "maxOutputTokens": 131072, + "inputCost": 0.49, + "outputCost": 1.54, + "cacheReadCost": 0.091 }, { "provider": "openrouter", @@ -603,10 +605,10 @@ "displayName": "DeepSeek V4 Flash", "aliases": [], "contextLimit": 1048576, - "maxOutputTokens": 393216, - "inputCost": 0.14, - "outputCost": 0.28, - "cacheReadCost": 0.028 + "maxOutputTokens": 384000, + "inputCost": 0.0798, + "outputCost": 0.1596, + "cacheReadCost": 0.01596 }, { "provider": "openrouter", @@ -615,9 +617,9 @@ "aliases": [], "contextLimit": 1048576, "maxOutputTokens": 384000, - "inputCost": 0.435, - "outputCost": 0.87, - "cacheReadCost": 0.003625 + "inputCost": 0.66, + "outputCost": 1.98, + "cacheReadCost": 0.022 }, { "provider": "openrouter", @@ -651,10 +653,10 @@ "aliases": [], "contextLimit": 1050000, "maxOutputTokens": 128000, - "inputCost": 5, - "outputCost": 30, - "cacheReadCost": 0.5, - "cacheWriteCost": 6.25 + "inputCost": 2.5, + "outputCost": 15, + "cacheReadCost": 0.25, + "cacheWriteCost": 3.125 }, { "provider": "vercel", @@ -791,20 +793,20 @@ "aliases": [], "contextLimit": 1000000, "maxOutputTokens": 384000, - "inputCost": 0.2, - "outputCost": 0.4, - "cacheReadCost": 0.04 + "inputCost": 0.13, + "outputCost": 0.26, + "cacheReadCost": 0.028 }, { "provider": "vercel", "modelIdentifier": "deepseek/deepseek-v4-pro", "displayName": "DeepSeek V4 Pro", "aliases": [], - "contextLimit": 1000000, - "maxOutputTokens": 384000, - "inputCost": 0.435, - "outputCost": 0.87, - "cacheReadCost": 0.0036 + "contextLimit": 1048600, + "maxOutputTokens": 1048600, + "inputCost": 1.74, + "outputCost": 3.48, + "cacheReadCost": 0.14 } ] }