-
Notifications
You must be signed in to change notification settings - Fork 1.5k
178 lines (156 loc) · 7.59 KB
/
Copy pathaigateway-prices-refresh.yaml
File metadata and controls
178 lines (156 loc) · 7.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# 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
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/modules/aiModels/knownModels/knownModelsGenerated.json
jobs:
refresh:
name: Refresh price book
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
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"