Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1195de3
feat: automate weekly AI model price book refresh
evgeniy-scherbina Aug 13, 2026
76b0a4b
refactor(scripts/aibridgepricesdiff): list models only in the summary
evgeniy-scherbina Aug 13, 2026
131b1c1
refactor(scripts/aibridgepricesdiff): count changed models, not price…
evgeniy-scherbina Aug 13, 2026
2eb4944
refactor(scripts/aibridgepricesdiff): compare models instead of price…
evgeniy-scherbina Aug 16, 2026
34ea737
refactor: minor changes in comments
evgeniy-scherbina Aug 16, 2026
6a2d79d
test(scripts/aibridgepricesdiff): split multi-model fixtures onto the…
evgeniy-scherbina Aug 16, 2026
29aa769
test(scripts/aibridgepricesdiff): one field and one row per line in f…
evgeniy-scherbina Aug 16, 2026
9f5d1df
refactor: minor changes
evgeniy-scherbina Aug 16, 2026
f553306
test(scripts/aibridgepricesdiff): assert the rendered document and er…
evgeniy-scherbina Aug 16, 2026
9d17c89
refactor: minor changes
evgeniy-scherbina Aug 17, 2026
e756f5b
fix(.github/workflows): attribute refresh commits to github-actions[bot]
evgeniy-scherbina Aug 17, 2026
4b198c1
docs: say AI Gateway in prose, keep aibridge identifiers
evgeniy-scherbina Aug 17, 2026
a9cc639
chore(CODEOWNERS): own only the generated price artifacts
evgeniy-scherbina Aug 17, 2026
b32746f
refactor(.github/workflows): name the refresh workflow aigateway
evgeniy-scherbina Aug 17, 2026
f06344e
refactor(.github/workflows): rename the webhook secret to AIGATEWAY_P…
evgeniy-scherbina Aug 17, 2026
ff7cf11
TEMPORARY: run the price refresh on push to this branch
evgeniy-scherbina Aug 17, 2026
8ee6bd9
fix(.github/workflows): send a real newline in the Slack alert
evgeniy-scherbina Aug 17, 2026
b890e7d
fix(.github/workflows): fail when the Slack webhook is missing
evgeniy-scherbina Aug 17, 2026
c8f49c4
refactor: minor changes
evgeniy-scherbina Aug 17, 2026
5ae4a94
fix(.github/workflows): manage the refresh PR through the REST API
evgeniy-scherbina Aug 17, 2026
6aa0cac
chore: refresh AI model price book
github-actions[bot] Aug 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions .github/workflows/aigateway-prices-refresh.yaml
Original file line number Diff line number Diff line change
@@ -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"
7 changes: 7 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading
Loading