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
30 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
4cca010
chore(CODEOWNERS): add @ssncferreira and @johnstcn to the price artif…
evgeniy-scherbina Aug 17, 2026
64a3b55
chore(.github/workflows): refresh AI Gateway prices on Thursdays
evgeniy-scherbina Aug 18, 2026
5284552
test(scripts/aibridgepricesdiff): cover a newly populated price
evgeniy-scherbina Aug 18, 2026
127ce34
test(scripts/aibridgepricesdiff): cover a missing price becoming zero
evgeniy-scherbina Aug 18, 2026
16d0d65
feat(scripts/aibridgepricesdiff): collapse model lists in refresh PRs
evgeniy-scherbina Aug 19, 2026
5697690
style(.github/workflows): keep PR review-note paragraphs on single lines
evgeniy-scherbina Aug 19, 2026
915c61b
feat(.github/workflows): send a weekly no-change Slack heartbeat
evgeniy-scherbina Aug 19, 2026
b279d2e
refactor(.github/workflows): consolidate Slack notifications
evgeniy-scherbina Aug 19, 2026
1d6c51a
refactor(.github/workflows): name the notification status after its c…
evgeniy-scherbina Aug 19, 2026
2de1651
chore: refresh AI model price book
github-actions[bot] Aug 19, 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
184 changes: 184 additions & 0 deletions .github/workflows/aigateway-prices-refresh.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# 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 Thursday. 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 Thursday, leaving three business days before Tuesday releases.
- cron: "0 9 * * 4"
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 [models.dev](https://models.dev) catalog. Both artifacts come from one 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 PR is never merged automatically. The summary above lists what moved; check 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
if: failure() || steps.detect.outputs.changed == 'false'
env:
JOB_STATUS: ${{ job.status }}
PRICE_BOOK_CHANGED: ${{ steps.detect.outputs.changed }}
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 notification could not be sent."
exit 1
fi

if [ "${JOB_STATUS}" = "failure" ]; then
# 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}")"
elif [ "${PRICE_BOOK_CHANGED}" = "false" ]; then
text=":white_check_mark: *AI Gateway price book refresh completed.* No generated changes were found. Run: ${RUN_URL}"
else
echo "::error::Unexpected notification state: status=${JOB_STATUS}, changed=${PRICE_BOOK_CHANGED}"
exit 1
fi

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 @ssncferreira @johnstcn
site/src/pages/AgentsPage/components/ChatModelAdminPanel/knownModels/knownModelsGenerated.json @evgeniy-scherbina @ssncferreira @johnstcn
Loading
Loading