Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
163 changes: 163 additions & 0 deletions .github/workflows/aibridge-prices-refresh.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Refreshes the AI Bridge 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: aibridge-prices-refresh

on:
schedule:
# 09:00 UTC every Monday, so the PR is waiting when EU and US start the
# week and there is room to review well before the release freeze.
- cron: "0 9 * * 1"
workflow_dispatch: # allows manual runs for testing

permissions: {}

concurrency:
group: aibridge-prices-refresh

env:
REFRESH_BRANCH: bot/aibridge-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"

# Catalog generation formats its output through scripts/biome_format.sh,
# which runs `pnpm exec biome` from site/. Without the install, that
# script logs a warning and exits 0, committing an unformatted file.
- 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
echo "Price book already matches upstream; nothing to propose."
echo "changed=false" >> "$GITHUB_OUTPUT"
else
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 [aibridge-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:
# The default GITHUB_TOKEN cannot trigger workflow runs, which would
# leave the PR without CI signal on a file that feeds cost
# calculation. cdrci is the machine user already used for
# bot-authored PRs in release.yaml.
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 "cdrci"
git config user.email "cdrci@coder.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}"

pr_number="$(gh pr list --head "${REFRESH_BRANCH}" --base main --state open --json number --jq '.[0].number // empty')"
if [ -n "${pr_number}" ]; then
gh pr edit "${pr_number}" --title "${PR_TITLE}" --body-file "${RUNNER_TEMP}/body.md"
echo "Updated existing PR #${pr_number}."
else
gh pr create \
--base main \
--head "${REFRESH_BRANCH}" \
--title "${PR_TITLE}" \
--body-file "${RUNNER_TEMP}/body.md"
fi

- name: Send Slack notification on failure
if: failure()
env:
SLACK_WEBHOOK: ${{ secrets.AIBRIDGE_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 "::warning::AIBRIDGE_PRICES_SLACK_WEBHOOK is not set; skipping notification."
exit 0
fi
text=":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: ${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"
9 changes: 9 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,12 @@ 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 AI model price book and frontend model catalog, plus the
# generators behind them. Both artifacts are refreshed from live models.dev
# data by the aibridge-prices-refresh workflow and carry customer-visible
# cost numbers, so every refresh needs a review from someone who owns them.
coderd/aibridge/prices/data/prices.json @evgeniy-scherbina
scripts/aibridgepricesgen/ @evgeniy-scherbina
scripts/aibridgepricesdiff/ @evgeniy-scherbina
site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/ @evgeniy-scherbina
Loading
Loading