Price book contract: negotiated rates, Azure OpenAI deployment aliases, effective dates - #5959
Conversation
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Test plan & review notesRepo: vivekchand/clawmetry What changed
git fetch origin main
git rebase origin/main
# resolve any conflicts in entitlements.py / providers_pricing.py, then:
git push --force-with-leaseSmoke commands pytest tests/test_price_book.py -q # 31 tests
pytest tests/test_providers_pricing.py tests/test_pricing_accuracy.py -q
pytest tests/test_entitlements_catalogue.py -q
python3 scripts/check_ac_coverage.py --check
python3 scripts/gen_module_map.py --check
make lint-daemon-allowlist lint-py39Manual spot-checks
Things to verify in the diff
Issue link
Generated by Claude Code |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Visual diffComparing 4 of 70 comparison(s) flagged (>1% pixel diff).
Folder: d4b9647f7635. Full PNGs also attached as a workflow artefact. Generated by visual-diff bot. Pixel diffs >1% flagged; eyeball the table before merging. This check is non-blocking — fail = bot bug, not a code problem. |
59a1add to
359d53b
Compare
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
608ed69 to
cac1941
Compare
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Coordinator reviewReviewed head Blocking (fix before merge)
Non-blocking (worth doing, not merge-gating)
Verdict: fix. Two blocking items remain, both small. |
cac1941 to
6736c24
Compare
|
| return True | ||
| # Azure OpenAI lives on a customer-named resource host (#5936), so it | ||
| # cannot be a fixed pattern; the parser also requires the /openai/ path. | ||
| from clawmetry.providers_pricing import parse_azure_openai_url |
There was a problem hiding this comment.
The code imports parse_azure_openai_url from clawmetry.providers_pricing at line 189, but this function is not defined in providers_pricing.py. This will cause an ImportError or AttributeError at runtime.
|
Review fixes pushed on Blocking 1: malformed token counts returned 500.
Blocking 2:
Non-blocking items:
Local checks: |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
Drift Bot on Findings 1-3: "
Finding 4: "
This PR changed neither of those definition sites since Drift Bot's six earlier "no drift" passes on this branch. I pushed empty commit |
|
Ready to merge (not merged; left for the founder). Merge-after dependencies: none. The branch is rebased on current Companion PRs:
Post-merge / post-release verification:
Founder decisions still open:
|
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
f53fddd to
b3f8f0d
Compare
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
1 similar comment
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
…s, effective dates A local price book (~/.clawmetry/pricing.json, schema clawmetry.price_book/1) with exact/prefix/pattern entries, effective_from/effective_to, rates or a discount off the published rate, and Azure deployment aliases scoped to their resource. POST /api/pricing/resolve says which entry applies to each usage record and why; GET /api/pricing/book shows the validated book, rejected entries and recorded content-addressed versions; POST /api/pricing/valuations defines the contract a future engine answers (501 until one exists). The interceptor now captures Azure OpenAI calls with their deployment and host. Integrated with the cost basis labels (#5975): every figure this surface returns carries the shared cost_basis vocabulary. A list figure is published_rate, no amount is unknown, and a contract valuation goes through cost_basis.label, so it cannot claim "contract rate" without its rate version. Rebased onto current main as one commit. acceptance_criteria.json carries the AC-OBS-CEA-024 block once (earlier merges had duplicated it and the AC-GOV-FWM block). The CHANGELOG entry is left to the release. Refs #5936. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9
2f91831 to
afb8efc
Compare
|
Rebased onto current `main` as a single commit, `afb8efc3f3`. Please don't push merge commits onto this branch; rebase on `afb8efc3f3` if you need to change something.
|
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Resolves conflicts in docs/MODULE_MAP.md (keep branch count: 258 modules, 83 blueprints) and docs/acceptance_criteria.json (keep both sets: CEA-024 price-book criteria and OTG-001 trace/Guard criteria from main). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B5EycwbWKDtGZtoAHVdDSA
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017cF1pNfkt6jLiKc8qF9Zro
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
…ed Azure calls priced once Follow-up to the rebase onto the price book contract (#5959): - Gateway spend now also states priced_from "vendor_reported", and its financial basis comes from price_book.financial_basis() instead of a second copy of the mapping, so the gateway card and a price book valuation cannot disagree. basis stays measured, cost_basis published_rate, and the top-level cost_source stays "gateway_reported". - #5959 taught the interceptor to capture Azure OpenAI. Loaded inside a LiteLLM proxy, it would price the proxy's upstream Azure call a third time, next to the agent's own cost and the gateway record. Calls made from a LiteLLM proxy process are now recorded without a cost and marked via_gateway "gateway:litellm". A direct Azure call from an ordinary app is priced exactly as #5959 ships it. - docs/MODULE_MAP.md regenerated (the committed map was one module behind). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9
Publishes #5953 (OTLP spans reach Guard and are redacted), #5959 (price book for negotiated rates and Azure OpenAI aliases) and #5970 (signed self-hosted server image), with their CHANGELOG entries. Claude-Session: https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Refs #5936
Factory requirement (REQ-OBS-CEA-024): https://factory.8090.ai/project/b415065f-ab2f-4f53-8864-0c009fd098cb/requirements/76e32850-c505-44a3-8ead-48a1bf350808
Blueprint: Price Book: negotiated rates, deployment aliases and effective dates (39b2db07-561a-4bb7-8006-7e162599d2dd)
Companion cloud PR (route policy, already merged): https://github.com/vivekchand/clawmetry-cloud/pull/2437
Valuation engine: vivekchand/clawmetry-pro#252 is meant to answer the
pricing.value_usageextension call defined here. It is not built yet.Dependency #5934 / #5951 (Cost Optimizer) is conceptual only; this branch is off
mainand does not touch the optimizer.Why
Organisations on cloud contracts rarely pay list price, and ClawMetry had no way to be told what they pay: prices were a constant in
providers_pricing.py. Azure OpenAI calls were not recognised as model calls at all, because the endpoint is a customer-named resource and the deployment name says nothing about the model behind it.What (split per the 2026-09-13 refinement: contract, aliases and effective dates in OSS; valuation in Pro)
clawmetry/price_book.py(new): the book file contract (~/.clawmetry/pricing.json, schemaclawmetry.price_book/1).effective_from/effective_to, and eitherratesper 1M tokens ordiscount_pctoff the published rate.ambiguousand no rate is chosen.pb1-<sha256[:20]>) and written once under~/.clawmetry/pricing_versions/, never rewritten.book_versionreturns the rate that version held.list,default(provider stand-in) orunknown. An unknown model isunknownwith no dollar figure, never the $1/$3 default.Infinity,NaN, negative, boolean) prices nothing for that record and names the field. It never causes a server error, and the valid fields are not priced alone, which would undercount.cache_tokens_in_inputper source (OpenAI, Azure OpenAI, Meta: inside; Anthropic incl. Bedrock: additional).priced_from,amount,currency,rate_version, effective interval,restated(+restatement_of,original_rate_version).routes/pricing.py(new,@gate("price_book")on all three,Cache-Control: no-store):GET /api/pricing/book,POST /api/pricing/resolve,POST /api/pricing/valuations.valuationsforwards to a valuation engine throughextensions.call("pricing.value_usage").valuation_engine_unavailableand points to/api/pricing/resolve. It does not return an upgrade prompt, because no plan includes the engine.providers_pricing.py:rate_basis()(the old_get_ratesreturns identical numbers),parse_azure_openai_url(),azure-openaipriced from OpenAI's published table (labelled as such),cache_tokens_included_in_input().interceptor.py: Azure OpenAI (*.openai.azure.com,*.cognitiveservices.azure.com,*.services.ai.azure.comunder/openai/) captured asllm_callwithdeploymentandendpoint_host; no cost estimated when the response names no model.entitlements.py: new Pro feature keyprice_book(+ label, docs/ENTITLEMENTS.md). Grace mode allows it.Behaviour change for interceptor users
This PR changes no existing Usage-tab computation. With
CLAWMETRY_INTERCEPT=1, though, totals rise:llm_callevents.cost_usdis estimated from OpenAI's published rates.The CHANGELOG entry for the release that carries this PR will say so.
Hand-calculated list-basis examples (asserted in
tests/test_price_book.py)These are published-rate figures, not contract amounts. Contract amounts need the valuation engine.
us.anthropic.claude-sonnet-4-5-20250929-v1:0at Anthropic's published rate: 1,000,000 in x $3 + 100,000 out x $15 + 400,000 cache read x $0.30 = $4.62 (cache additional to input). Bedrock's own list can differ, for example on regional inference profiles; the resolution'slist.reasonsays so.prod-chataliased togpt-4o, at OpenAI's published rate: 600,000 uncached x $2.50 + 400,000 cached x $1.25 + 100,000 out x $10 = $3.00 (cache inside input).2026-06-30T23:59:59.999Zhits the H1 entry,2026-07-01T00:00:00Z(and02:00+02:00, and epoch 1782864000) hits H2.Verification
pytest tests/test_price_book.py: 53 passed (in the CI "Published provider pricing and cache accounting" step).cac1941206and pass after."abc",Infinity,-Infinity,NaN,1e400,-5,true,[],{},"12") are sent to both POST endpoints.valuationswith no engine must return 501 with no upgrade wording.detect_config()under a scratch HOME):resolvereturns 200 (cost_usd: null, field named) andvaluationsreturns 501valuation_engine_unavailable. The previous head returned Flask's 500 page for both.GET /api/pricing/bookreturns 200, and a cross-origin POST gets 403.providers_pricing.py+interceptor.pyfromorigin/main: 22 of the original 31 fail.effective_tomade inclusive:test_effective_date_boundary_is_half_openfails.test_equal_precedence_is_ambiguous_and_chooses_nothingfails.test_valuations_without_their_basis_are_withheldfails.check_ac_coverage.py --check: OK, 120/190.gen_module_map.py --check: OK.sys.pathinsert before imports) that the test file already had; CI's ruff step checksdashboard.pyonly.Decisions for the reviewer
price_booksits inPRO_ONLY_FEATURESnext tocost_optimizer. Move it toENTERPRISE_FEATURESif negotiated-rate pricing should be Enterprise-only; nothing else changes.pricing.yaml; JSON avoids a parser dependency (ADR-004 in the blueprint).Remaining (not in this PR; #5936 stays open)
pricing.value_usagecontract here. Until it ships there is no contract dollar amount. "A Bedrock session and an Azure deployment session both price from the book" is proven only at the selection level (which entry, interval, version), and the hand-calculated examples are list-basis only, so both criteria are partial.priced_fromon every cost: Cost figures: label vendor-reported vs contract vs estimated everywhere; split actual spend from API-equivalent #5937 (provenance surfaces).GET /api/pricing/booklists versions recorded by resolve/valuations, so the current version appears only after its first use (per the requirement's version-store note).🤖 Generated with Claude Code
https://claude.ai/code/session_01Jm9d7s4fN55hN3YzQo75o9