add-price-model
GitHub用于在genai-prices仓库中添加或更新LLM模型定价数据。涵盖多供应商价格搜集、OpenRouter探测、YAML编辑及PR构建,确保价格历史完整与数据准确发布。
Trigger Scenarios
Install
npx skills add pydantic/genai-prices --skill add-price-model -g -y
SKILL.md
Frontmatter
{
"name": "add-price-model",
"description": "Add a new LLM model (or provider) to genai-prices pricing data, or change the price of one that is already there. Use when asked to add\/update pricing for a model — e.g. \"add grok 4.5\", \"add the new Claude\", \"update openai o5 prices\", \"provider X cut its prices\". Covers sourcing prices, probing OpenRouter for undocumented dated snapshot IDs, editing the provider YAML, preserving price history across a rate change, building, verifying resolution, and opening the PR."
}
Add a model to genai-prices
Never hand-edit generated data. Edit the provider YAML in prices/providers/<provider>.yml, then
make build. The live published payload is prices/new_data/v2/data.json; prices/data.json and
prices/data_slim.json are frozen v1 snapshots that no build step writes any more — leave them alone.
0. Scope: every provider that hosts this model, not just the one you were named
A model rarely lives on one provider. The big vendors' flagships are resold across clouds and
aggregators, and each needs its own YAML entry — the same request "add the new Claude" means
anthropic.yml and aws.yml (Bedrock) and google.yml (Vertex) and openrouter.yml.
Adding only the direct-vendor entry is the most common miss (it's what happened for Opus 5 in #501,
requiring the follow-up #502). Before editing, list every provider that hosts the model and cover
them in one PR:
- Direct vendor —
anthropic.yml,openai.yml,google.yml(Gemini),x_ai.yml, etc. - Aggregators/gateways —
openrouter.yml(usually day-one; often exposes-fast/:betavariants). - Cloud resellers —
aws.yml(Bedrock:global.*+regional.*split, regional ~+10%),google.yml(Vertex Claude entries live here too, separate from Gemini), Azure (azure.ymlis Azure OpenAI only — Claude on Foundry is out of scope there; note it, don't force it).
Confirm hosting from each provider's docs/model list; don't assume. If a reseller genuinely hasn't shipped it yet, that's the only reason to defer a provider — say which one and why in the PR, and follow up when it lands (see "Provider rollout timing" below). "I was only asked about provider X" is not a reason to skip the others.
1. Branch
Contribute via a branch on origin (this repo is pydantic/genai-prices, no fork). Always base off
freshly-fetched upstream:
git fetch origin && git checkout -b <slug> origin/main
2. Source the prices (cite everything)
Get input / cached-input / output per-Mtok and context window from the provider's own docs first (authoritative). Vendor docs often omit the cache-read rate — cross-check OpenRouter's endpoint API, which exposes it:
https://openrouter.ai/api/v1/models/<provider>/<model>/endpoints
pricing.input_cache_read is per-token — ×1,000,000 for the per-Mtok value. Record every number's
source; put them in the PR body.
3. Probe OpenRouter for the dated snapshot ID (do this every time)
Providers ship dated snapshot IDs (e.g. grok-4.5-20260708) that aren't in their docs. A real minimal
request returns the resolved dated ID in the response model field — capture it so the YAML match
covers future dated snapshots.
Key lives in ~/ai-coding-tools/.env as OPENROUTER_API_KEY. Don't reference secret env vars in an
inline command (a hook blocks it and env-run rejects it) — put the request in a script that consumes
the var internally, then run it with env-run:
# scratchpad/or_probe.sh consumes $OPENROUTER_API_KEY internally
~/.claude/scripts/env-run ~/ai-coding-tools/.env -- bash scratchpad/or_probe.sh
curl -sS https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer ${OPENROUTER_API_KEY}" -H "Content-Type: application/json" \
-d '{"model":"<provider>/<model>","messages":[{"role":"user","content":"hi"}],"max_tokens":5}' \
| jq '{id, model, provider}'
The model field (e.g. x-ai/grok-4.5-20260708) reveals the snapshot. A regex: '^<model>-\d{8}$'
clause (plus a <provider>/-prefixed variant) makes it resolve without a separate entry.
4. Add the YAML entry
Match a sibling model's shape in prices/providers/<provider>.yml (ordering, match/or style,
fields). Include:
match.or: bare id,regex: '^<id>-\d{8}$',<provider>/-prefixed bare + dated,-latestcontext_windowprices_checked:today's date (check thecurrentDatesystem reminder)prices:input_mtok,cache_read_mtok(omit if the provider has none),output_mtok
Add a price_comments field when a value needs explanation/reference.
Those three keys cover the common case. The full vocabulary is derived from prices/units.yml — check
it when the model bills for anything else (reasoning or citation tokens, per-modality rates, 1h cache
writes, web searches, requests). Not every key is per-Mtok: _kcount is per 1,000, _mchars per
1M characters, _hours per 3,600s, _gpixels per 1e9, _kpages per 1,000. A per-Mtok figure under a
_kcount key is valid YAML and wrong by 1000×. Prices must also cover their ancestors — a model with
cache_write_1h_mtok needs cache_write_mtok too; make build will tell you which key is missing.
Do not add a new unit to prices/units.yml to make a model fit. That widens the published v2
schema and is a v3 change — see AGENTS.md § "Adding a unit". Open an issue instead.
Migrate family-level -latest aliases when the new model is the current flagship. Two kinds of
-latest alias coexist, and they behave differently:
- version-specific (
<id>-latest, e.g.grok-4.3-latest) — always stays on its own entry. - family-level / bare (
<provider>-latest, e.g.grok-latest) — means "the current flagship" and should point at whichever model is newest/best right now.
When adding a new flagship, move the family-level alias off the previous flagship onto the new
entry (add it here, delete it there). First verify which model the vendor's alias actually resolves
to — check the provider docs and, if you can, hit the API and read the response model field — then
match that. Don't assume; the aliasing scheme is provider-specific (some vendors have no bare-family
alias at all).
4b. Changing the price of a model that already exists
A provider changing its rates is not an edit to the existing prices: block. Overwriting those
values re-prices every request the library ever priced for that model, so a request from before the
change gets billed at the new rate. That is what happened to GPT-5.6 Luna and Terra in #531, and #535
had to undo it.
Add a dated entry instead. Convert prices: from a mapping to a list of conditional entries:
prices:
- prices: # the rates that were already there, unchanged and unconstrained
input_mtok: 1
output_mtok: 6
- constraint:
# https://developers.openai.com/api/docs/changelog
start_date: 2026-07-30
prices: # the new rates
input_mtok: 0.2
output_mtok: 1.2
- Put the entry with no
constraintfirst. Both engines scan the list backwards and take the first entry whose constraint is active, so an unconstrained entry placed last would always win. - Set
start_dateto the date the provider's new price took effect, not to today. Cite the changelog or announcement that states that date, in a YAML comment besidestart_date. - Set
prices_checkedto today. It records when you verified the rates, which is a different fact from when the rates changed. - Append one entry to a model that already uses a list. Leave the existing entries alone.
Overwrite in place in exactly one case: the old value was wrong when it was written. A correction has no history worth preserving. State which of the two cases you are in, in the PR body.
Verify both sides of the boundary:
uv run python -c "
from datetime import datetime, timezone
from genai_prices import calc_price, Usage
u = Usage(input_tokens=1_000_000)
for day in [(2026, 7, 29), (2026, 7, 30)]:
t = datetime(*day, tzinfo=timezone.utc)
r = calc_price(u, '<id>', provider_id='<provider_id>', genai_request_timestamp=t)
print(t.date(), '->', r.model_price.input_mtok, r.total_price)
"
Then pin both sides in tests/test_price_calc.py — one assertion the day before the change, one on
the day of. A test that only covers the current rate passes just as well against an overwritten
history, which is why #531 went green.
5. Build + verify resolution
Use make build, not just make build-prices. The installed genai_prices package (and the JS
package) read their bundled data (packages/python/genai_prices/data.py, packages/js/src/data.ts).
make build-prices writes only prices/new_data/v2/* and prices/providers/.schema.json — it does not
touch the bundled data, so a calc_price check run after it verifies stale package data and can
silently show the wrong result. make build runs build-prices + package-data + inject-providers.
make build # build-prices + package-data + inject-providers
Confirm the base id, the dated snapshot, the provider-prefixed dated id, and any -latest alias you
touched all resolve to the intended entry (include the previous flagship to prove its version-specific
-latest didn't move):
uv run python -c "
from genai_prices import calc_price, Usage
u = Usage(input_tokens=1000, output_tokens=1000)
for m in ['<id>', '<id>-<YYYYMMDD>', '<provider>/<id>-<YYYYMMDD>', '<provider>-latest', '<prev-id>-latest']:
r = calc_price(u, m, provider_id='<provider_id>')
print(m, '->', r.model.id, r.model.prices.input_mtok, r.model.prices.output_mtok)
"
6. Commit, push, PR
The pre-commit build hook regenerates ten paths, so the first git commit will abort after it
rewrites them; re-stage and commit again. Stage files explicitly — never git add -A (it leaks
local/scratch files) — and never --no-verify, since that hook is what keeps the published data in
sync with the YAML:
git add prices/providers/<provider>.yml \
prices/providers/.schema.json \
prices/new_data/v2/data.json prices/new_data/v2/data.schema.json \
prices/new_data/v2/data_slim.json prices/new_data/v2/data_slim.schema.json \
packages/python/genai_prices/data.py packages/python/genai_prices/data_units.py \
packages/js/src/data.ts packages/js/src/dataUnits.ts \
README.md
git commit -m "Add <Provider> <Model> pricing" # re-run once if hooks rewrite files
git push -u origin <slug>
gh pr create --base main --title "Add <Provider> <Model> pricing" --body "..."
A plain price addition usually only dirties a subset of these — git status after the aborted commit
tells you which. The schema and *_units files change only when prices/units.yml does, which a price
addition should not do.
Never force-push. PR body: pricing table, sources (provider docs + OpenRouter for cache rate), and
scope notes (e.g. single variant / no cache-write / any -latest alias you moved, each with its
one-line reason).
After pushing, don't go idle — poll until CI is green and every reviewer comment (cubic included) is
addressed or dismissed (see AGENTS.md). Unresolved review threads mean the PR isn't mergeable.
Provider rollout timing
This is the escape hatch for step 0, not a reason to default to a single-provider PR. Cover every provider that already hosts the model in the same PR. OpenRouter usually lists new models day-one. Only when a reseller (Bedrock / Vertex) genuinely hasn't shipped yet do you defer that provider — name it in the PR body and follow up in a later PR once it lands, rather than blocking the rest.
Version History
-
1f01992
Current 2026-08-19 23:50
新增要求覆盖所有托管该模型的供应商;明确v2数据构建流程,冻结v1快照文件;强调保留价格历史记录。
- ffbbb14 2026-07-25 05:34


