Skip to content

Price book contract: negotiated rates, Azure OpenAI deployment aliases, effective dates - #5959

Merged
vivekchand merged 4 commits into
mainfrom
feat/price-book-azure-aliases-5936
Sep 14, 2026
Merged

vivekchand merged 4 commits into
mainfrom
feat/price-book-azure-aliases-5936

Conversation

@vivekchand

@vivekchand vivekchand commented Sep 14, 2026 •

Copy link
Copy Markdown
Owner

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_usage extension call defined here. It is not built yet.
Dependency #5934 / #5951 (Cost Optimizer) is conceptual only; this branch is off main and 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, schema clawmetry.price_book/1).
    • Entries name a model exactly, by prefix or by a validated pattern.
    • Each entry has effective_from / effective_to, and either rates per 1M tokens or discount_pct off the published rate.
    • Aliases map an Azure deployment, optionally scoped to its resource host, to a model.
    • Validation is per entry: malformed entries, and pairs claiming the same model/channel/region over overlapping dates, are rejected with reasons; the rest of the book stays usable.
    • Precedence is fixed: channel-scoped, then region-scoped, then exact (covers dated snapshots), then prefix, then pattern, then the longer name. A tie is reported ambiguous and no rate is chosen.
    • Effective intervals are half-open; a record with no observed time never matches a dated rate.
    • Versions:
      • Each version id is content-addressed (pb1-<sha256[:20]>) and written once under ~/.clawmetry/pricing_versions/, never rewritten.
      • A version file that no longer hashes to its name is refused.
      • Resolving against an old book_version returns the rate that version held.
    • The list basis is labelled list, default (provider stand-in) or unknown. An unknown model is unknown with no dollar figure, never the $1/$3 default.
    • A token count that is not a non-negative finite number (string, 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_input per source (OpenAI, Azure OpenAI, Meta: inside; Anthropic incl. Bedrock: additional).
    • The valuation record contract: 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.
    • valuations forwards to a valuation engine through extensions.call("pricing.value_usage").
    • No build contains that engine yet, so with none registered it returns 501 valuation_engine_unavailable and points to /api/pricing/resolve. It does not return an upgrade prompt, because no plan includes the engine.
    • Records from an engine that fail the contract are withheld and listed, never passed through.
  • providers_pricing.py: rate_basis() (the old _get_rates returns identical numbers), parse_azure_openai_url(), azure-openai priced 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.com under /openai/) captured as llm_call with deployment and endpoint_host; no cost estimated when the response names no model.
  • entitlements.py: new Pro feature key price_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:

  • Azure OpenAI calls that were uncounted now produce llm_call events.
  • Their cost_usd is estimated from OpenAI's published rates.
  • Azure audio deployments (whisper, tts) appear as model calls with zero tokens.

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.

  • Bedrock us.anthropic.claude-sonnet-4-5-20250929-v1:0 at 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's list.reason says so.
  • Azure deployment prod-chat aliased to gpt-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).
  • Effective boundary: 2026-06-30T23:59:59.999Z hits the H1 entry, 2026-07-01T00:00:00Z (and 02: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).
  • Review fixes, red before green: the 23 new or changed tests fail on the previous head cac1941206 and pass after.
    • Malformed token counts ("abc", Infinity, -Infinity, NaN, 1e400, -5, true, [], {}, "12") are sent to both POST endpoints.
    • Odd values are sent in every field.
    • valuations with no engine must return 501 with no upgrade wording.
  • Real dashboard app (detect_config() under a scratch HOME):
    • Malformed token requests: resolve returns 200 (cost_usd: null, field named) and valuations returns 501 valuation_engine_unavailable. The previous head returned Flask's 500 page for both.
    • GET /api/pricing/book returns 200, and a cross-origin POST gets 403.
  • Revert → red → restore → green (original change):
    1. providers_pricing.py + interceptor.py from origin/main: 22 of the original 31 fail.
    2. effective_to made inclusive: test_effective_date_boundary_is_half_open fails.
    3. Tie resolved by picking the first entry: test_equal_precedence_is_ambiguous_and_chooses_nothing fails.
    4. Valuation contract check removed from the route: test_valuations_without_their_basis_are_withheld fails.
  • Other checks:
    • check_ac_coverage.py --check: OK, 120/190.
    • gen_module_map.py --check: OK.
    • Ruff on the new files shows only the same two E402s (sys.path insert before imports) that the test file already had; CI's ruff step checks dashboard.py only.

Decisions for the reviewer

  • Tier: price_book sits in PRO_ONLY_FEATURES next to cost_optimizer. Move it to ENTERPRISE_FEATURES if negotiated-rate pricing should be Enterprise-only; nothing else changes.
  • JSON, not YAML: the issue proposed pricing.yaml; JSON avoids a parser dependency (ADR-004 in the blueprint).

Remaining (not in this PR; #5936 stays open)

  • Contract valuation, restatement and reconciliation: clawmetry-pro#252, against the pricing.value_usage contract 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.
  • Immutable usage facts stored separately from valuations (billing channel, region, currency, rate version, effective interval), as the 2026-09-13 refinement asks: not built. Resolution is computed per request; nothing is persisted as a fact or ledger row.
  • Settings UI for the book: deliberately not shipped, so no screen appears that changes no figure. Follows the valuation engine.
  • Usage tab figures carrying priced_from on every cost: Cost figures: label vendor-reported vs contract vs estimated everywhere; split actual spend from API-equivalent #5937 (provenance surfaces).
  • Daemon-side resolution of stored events (Azure deployment names from runtime adapters, gateway usage from LiteLLM integration: proxy callback / OTel ingestion + spend-log import #5940): not wired; only the opt-in interceptor records Azure deployments today.
  • Currency conversion of non-USD entries: out of scope; entries record their currency.
  • Validation refinements (non-blocking):
    • A provider-scoped and an unscoped entry for the same model and channel are both accepted, then reported ambiguous at resolve time. That is safe, but they could be flagged at load.
    • GET /api/pricing/book lists 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

@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

Copy link
Copy Markdown
Owner Author

Test plan & review notes

Repo: vivekchand/clawmetry

What changed

  • New clawmetry/price_book.py: versioned price-book contract for negotiated rates, Azure OpenAI deployment aliases, and effective date intervals — loaded from ~/.clawmetry/pricing.json
  • New routes/pricing.py: three @gate("price_book") endpoints (GET /api/pricing/book, POST /api/pricing/resolve, POST /api/pricing/valuations)
  • providers_pricing.py: adds rate_basis(), parse_azure_openai_url(), cache_tokens_included_in_input(); prices Azure OpenAI against OpenAI's published table
  • interceptor.py: captures Azure OpenAI calls (*.openai.azure.com, etc.) as llm_call with deployment and endpoint_host
  • entitlements.py: adds price_book Pro feature key (Grace-allowed)

⚠️ Merge conflict — GitHub reports this branch as dirty. It was opened at the same time as several other PRs (#5952, #5953, #5954, etc.) that may have landed on main and touched overlapping files (entitlements.py, dashboard.py). Please rebase locally:

git fetch origin main
git rebase origin/main
# resolve any conflicts in entitlements.py / providers_pricing.py, then:
git push --force-with-lease

Smoke 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-py39

Manual spot-checks

  • GET /api/pricing/book — should return the book schema/version
  • POST /api/pricing/resolve with a known Azure deployment → verify deployment alias resolves to model, rate labelled list
  • POST /api/pricing/valuations with no Pro engine → expect 402 upgrade_required (not a 500)
  • With CLAWMETRY_ENFORCE=1 and no price_book entitlement → GET /api/pricing/book returns 402/403

Things to verify in the diff

  • Tie-breaking: equal-precedence entries must return ambiguous with no rate chosen (covered by test, but worth a manual probe with two overlapping entries)
  • Content-addressed versions: write a book, modify ~/.clawmetry/pricing_versions/<pb1-sha>, confirm it's refused on load
  • An unknown model returns unknown basis with no dollar figure — not the $1/$3 default

Issue link


Generated by Claude Code

@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

Comment thread clawmetry/price_book.py Fixed
Comment thread clawmetry/price_book.py Fixed
Comment thread routes/pricing.py Fixed
@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Visual diff

Comparing d4b9647f7635 (head) against the PR base branch.

4 of 70 comparison(s) flagged (>1% pixel diff).

View Before After Diff
desktop overview ⚠️ before after diff · 1.87%
desktop flow before after diff · 0.00%
desktop brain before after diff · 0.00%
desktop usage before after diff · 0.00%
desktop crons before after diff · 0.00%
desktop memory before after diff · 0.02%
desktop security before after diff · 0.02%
desktop subagents before after diff · 0.00%
desktop transcripts before after diff · 0.00%
desktop logs before after diff · 0.00%
desktop skills before after diff · 0.01%
desktop models before after diff · 0.00%
desktop approvals before after diff · 0.02%
desktop alerts before after diff · 0.00%
desktop notifications before after diff · 0.00%
desktop limits before after diff · 0.00%
desktop history before after diff · 0.00%
desktop channels before after diff · 0.00%
desktop harness before after diff · 0.00%
desktop inventory before after diff · 0.00%
desktop nemoclaw before after diff · 0.27%
desktop guard before after diff · 0.00%
desktop signals before after diff · 0.00%
desktop policy before after diff · 0.00%
desktop selfevolve before after diff · 0.00%
desktop swimlane before after diff · 0.00%
desktop tool-catalog before after diff · 0.00%
desktop tracing before after diff · 0.00%
desktop turn-anatomy before after diff · 0.02%
desktop version-impact before after diff · 0.00%
desktop context-economics before after diff · 0.00%
desktop agents before after diff · 0.00%
desktop evals before after diff · 0.00%
desktop bench before after diff · 0.00%
desktop trail before after diff · 0.00%
mobile overview before after diff · 0.00%
mobile flow before after diff · 0.00%
mobile brain before after diff · 0.03%
mobile usage before after diff · 0.00%
mobile crons before after diff · 0.00%
mobile memory before after diff · 0.03%
mobile security before after diff · 0.04%
mobile subagents before after diff · 0.00%
mobile transcripts before after diff · 0.00%
mobile logs before after diff · 0.00%
mobile skills before after diff · 0.01%
mobile models before after diff · 0.00%
mobile approvals before after diff · 0.00%
mobile alerts before after diff · 0.00%
mobile notifications before after diff · 0.00%
mobile limits before after diff · 0.00%
mobile history before after diff · 0.00%
mobile channels before after diff · 0.00%
mobile harness ⚠️ before after diff · 100.00%
mobile inventory before after diff · 0.00%
mobile nemoclaw ⚠️ before after diff · 100.00%
mobile guard before after diff · 0.00%
mobile signals before after diff · 0.00%
mobile policy before after diff · 0.00%
mobile selfevolve before after diff · 0.00%
mobile swimlane before after diff · 0.00%
mobile tool-catalog before after diff · 0.00%
mobile tracing before after diff · 0.00%
mobile turn-anatomy ⚠️ before after diff · 100.00%
mobile version-impact before after diff · 0.00%
mobile context-economics before after diff · 0.00%
mobile agents before after diff · 0.00%
mobile evals before after diff · 0.00%
mobile bench before after diff · 0.00%
mobile trail before after diff · 0.00%

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.

@vivekchand
vivekchand force-pushed the feat/price-book-azure-aliases-5936 branch from 59a1add to 359d53b Compare September 14, 2026 01:19
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@vivekchand
vivekchand force-pushed the feat/price-book-azure-aliases-5936 branch from 608ed69 to cac1941 Compare September 14, 2026 02:48
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@vivekchand

Copy link
Copy Markdown
Owner Author

Coordinator review

Reviewed head cac1941206. It is rebased on current main (41475e80b9); GitHub reports MERGEABLE/CLEAN, and every check passes except OpenSSF Scorecard, which was skipped. The Factory URL is given in full, the regression guards are in tests/test_price_book.py and CI runs it, no check was weakened, and cloud route-policy PR #2437 is merged. The split between OSS (contract, aliases, dates) and Pro (valuation) matches the 2026-09-13 refinements on #5936 and clawmetry-pro#252, and no competing price book exists. The cloud relay only forwards allow-listed query types, so it cannot reach /api/pricing/*.

Blocking (fix before merge)

  1. Malformed token counts crash the resolve and valuations endpoints with a 500 error. price_book._list_basis calls int(fact.get("input_tokens") or 0) and does the same for the other token fields. Only the any(...) pre-check looks at the type. Tested against routes/pricing.py on this head:

    • {"facts":[{"model":"gpt-4o","input_tokens":5,"output_tokens":"abc"}]} → ValueError, and Flask returns its 500 HTML page. /api/pricing/resolve and /api/pricing/valuations both fail.
    • {"facts":[{"model":"gpt-4o","input_tokens":Infinity}]}: Python's JSON decoder accepts this. It raises OverflowError: cannot convert float infinity to integer and returns 500.

    resolve_usage is documented as "Never raises", and CLAUDE.md says "never crash on bad input". Fix: coerce each token field with a guard that treats non-finite, non-numeric or negative values as missing, or reject that record with a stated reason. Add a test for these inputs.

  2. The valuations endpoint and the CHANGELOG describe a paid capability that does not exist yet. clawmetry-pro#252 is still open and has no engine. Today POST /api/pricing/valuations returns 402 upgrade_required for every user, with required_tier set and the hint "Contract valuation and restatement come with clawmetry-pro". The CHANGELOG also says "contract valuation and restatement come with clawmetry-pro". The problems:

    • A Pro user who calls it is told to upgrade to a plan they already have.
    • A free user is told that paying unlocks something no build contains.
    • Public repo docs, which drift-bot uses as its source of truth for landing claims, would carry that claim.

    Fix, either way works:

    • (a) Keep the route, but when no pricing.value_usage handler is registered, return a truthful status that is not an upgrade prompt, e.g. 501 {"error":"valuation_engine_unavailable", "message":"Contract valuation is not available in this build yet. Rate selection is at /api/pricing/resolve."}. Reword the CHANGELOG to say the endpoint defines the contract a future engine answers.
    • (b) Leave the valuations route out until pro#252 ships.

    In both cases, update test_valuations_without_an_engine_is_the_upgrade_body to match.

Non-blocking (worth doing, not merge-gating)

  • List basis for Bedrock. us. / eu. regional inference-profile ids get Anthropic's direct rate labelled list with no caveat, and the PR body calls $4.62 the "Bedrock list" price. For Claude Sonnet 4.5 and later, AWS charges a premium on regional (non-global) endpoints, so this can understate the Bedrock list price. Please verify it. At minimum, add a reason caveat like the one Azure already gets, and say "Anthropic published rate" rather than "Bedrock list" in the PR body.
  • "This PR does not change any existing Usage number" is not quite true. With CLAWMETRY_INTERCEPT=1, Azure OpenAI calls now produce llm_call events with a cost_usd taken from OpenAI rates. Before, they were not captured at all, so interceptor users will see Usage totals go up, and those figures carry no basis label. This is an improvement, but the PR body and CHANGELOG should say it. Separately, /openai/deployments/<whisper|tts>/audio/... calls are now recorded as zero-token llm_call events.
  • List the unpersisted usage facts as their own remaining item. The refinement's "persist immutable usage facts separately from valuations (billing channel, region, currency, rate version, effective interval)" is not implemented. It only appears indirectly as "stored events are not resolved on the daemon side". Name it explicitly so Pricing: custom price book for negotiated rates + Azure OpenAI deployment aliases #5936 is not closed by this PR. The body correctly uses "Refs", not "Closes".
  • Provider scope does not break ties. An entry scoped to provider and an unscoped entry for the same model and channel are both accepted, and at resolve time they are reported as ambiguous. This is safe, since no rate is chosen, but provider could be added to _entry_rank, or the pair flagged at validation.
  • The book endpoint can omit the current version. GET /api/pricing/book lists versions, but a version is only recorded on resolve/valuations, so right after an edit the list may not contain the current version.
  • Acceptance check "hand-calculated examples match the ledger". These are list-basis figures; there is no ledger or contract amount yet. Mark it partial, not met.
  • Founder decisions are unchanged: Pro versus Enterprise for price_book, and JSON instead of YAML.

Verdict: fix. Two blocking items remain, both small.

@8090-software-factory

Copy link
Copy Markdown

⚠️ Drift Bot (ClawMetry): 4 potential drift finding(s)

1. Blueprint: Price Book: negotiated rates, deployment aliases and effective dates

File: clawmetry/providers_pricing.py

The blueprint specifies parse_azure_openai_url(), rate_basis(), and cache_tokens_included_in_input() functions in providers_pricing.py, but these functions are not implemented. The interceptor.py file imports and calls parse_azure_openai_url() which causes import/runtime failures.

2. Blueprint: Price Book: negotiated rates, deployment aliases and effective dates

File: clawmetry/interceptor.py:189

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.

3. Requirement: Price Book: negotiated rates, deployment aliases and effective dates

File: clawmetry/providers_pricing.py

The requirement specifies that providers_pricing.py should implement parse_azure_openai_url() to recognize Azure OpenAI endpoints and support deployment alias resolution (AC-OBS-CEA-024.5), but this function is not present in the implementation.

4. Blueprint: Price Book: negotiated rates, deployment aliases and effective dates

File: dashboard.py

The blueprint specifies that routes/pricing.py provides the three gated API endpoints (GET /api/pricing/book, POST /api/pricing/resolve, POST /api/pricing/valuations). The routes/pricing.py file exists and implements these endpoints correctly, but the blueprint is not imported or registered with the Flask app in dashboard.py, so the endpoints are not accessible.

Comment thread clawmetry/interceptor.py
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Drift Bot (ClawMetry) — Blueprint: Price Book: negotiated rates, deployment aliases and effective dates

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.

@vivekchand

Copy link
Copy Markdown
Owner Author

Review fixes pushed on 6736c24630, rebased on current main (658342f238).

Blocking 1: malformed token counts returned 500.

  • Fix: _list_basis checks each token field. null or absent counts as 0. A string, Infinity, NaN, negative, boolean, list or object makes that record's cost_usd null and list.reason names the field. Pricing only the valid fields would undercount, so nothing is priced.
  • Guard: test_malformed_token_counts_never_crash_either_endpoint sends 10 raw-JSON values to both endpoints, 20 cases in all. test_odd_values_in_every_field_never_crash_the_api puts 9 odd values into each of 14 fields, plus odd book_version / restate_against. test_malformed_token_counts_price_nothing_rather_than_an_undercount covers the undercount.
  • Red before green: all of these failed on cac1941206.
  • Real app: through detect_config() under a scratch HOME, resolve now returns 200 and valuations 501 where both returned Flask's 500 page.

Blocking 2: valuations sold a paid capability that no build contains.

  • Fix: with no pricing.value_usage handler the route returns 501 valuation_engine_unavailable ("Contract valuation is not available in this build yet. Which rate applies to each record is available at /api/pricing/resolve."). There is no upgrade body and no required_tier.
  • Docs: the CHANGELOG now says the endpoint defines the contract a future engine will answer. The blueprint composition bullet and ADR-001 were updated to match.
  • Guard: test_valuations_without_an_engine_says_unavailable_not_upgrade asserts no upgrade wording anywhere in the body.

Non-blocking items:

  • Addressed:
    • Bedrock: the Bedrock list basis now carries a caveat that Bedrock's own list can differ, for example on regional inference profiles, and the test asserts it. The PR body calls $4.62 "Anthropic's published rate".
    • Interceptor totals: the CHANGELOG and PR body now say interceptor users' totals rise, because Azure calls are counted at OpenAI rates and whisper/tts deployments appear as zero-token calls.
    • Remaining section: "Immutable usage facts stored separately from valuations" is now listed there. The Bedrock/Azure contract-amount and hand-calculated-ledger criteria are marked partial.
  • Left as they are, and noted in the body:
    • A provider-scoped plus an unscoped entry is still reported ambiguous at resolve time rather than flagged at load.
    • GET /book lists only versions that were used; the requirement says versions are recorded when first used to resolve.

Local checks: tests/test_price_book.py 53 passed; AC gate OK at 120/190; module map in sync.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

@vivekchand

Copy link
Copy Markdown
Owner Author

Drift Bot on 6736c24630: all 4 findings are a truncation misread. Drift Bot reads only the head of each file, and the code it says is missing sits below that point. Evidence from git show 831c79594b:<path> (same code as 6736c24630 plus one empty commit):

Findings 1-3: "parse_azure_openai_url, rate_basis, cache_tokens_included_in_input not implemented in providers_pricing.py" and "import will fail". All three are defined there:

  • clawmetry/providers_pricing.py is 685 lines.
    • line 302: def cache_tokens_included_in_input(provider: str):
    • line 314: def parse_azure_openai_url(url: str):
    • line 340: def rate_basis(provider: str, model: str) -> tuple[float, float, str]:
  • clawmetry/interceptor.py:189 imports parse_azure_openai_url, and the import resolves. tests/test_price_book.py calls all three functions directly (test_azure_url_parser, test_rate_basis_keeps_the_published_rates, test_cache_convention_is_stated_per_source, test_interceptor_captures_azure_openai_with_its_deployment), and all 53 tests pass.

Finding 4: "bp_pricing not imported or registered in dashboard.py". Both are there:

  • dashboard.py is 13,840 lines.
    • line 161: from routes.pricing import bp_pricing
    • line 6100: app.register_blueprint(bp_pricing)
  • Verified in the real app: after detect_config() under a scratch HOME, the URL map lists /api/pricing/book, /api/pricing/resolve and /api/pricing/valuations. They return 200 / 200 / 501, and a cross-origin POST gets 403.

This PR changed neither of those definition sites since Drift Bot's six earlier "no drift" passes on this branch. I pushed empty commit 831c79594b so Drift Bot runs again. No check was skipped or weakened.

@vivekchand

Copy link
Copy Markdown
Owner Author

Ready to merge (not merged; left for the founder).

Merge-after dependencies: none. The branch is rebased on current main.

Companion PRs:

  • vivekchand/clawmetry-cloud#2437: cloud_route_policy entries marking /api/pricing/book, /api/pricing/resolve and /api/pricing/valuations cloud-disabled, since_version 0.12.877. Already merged. It takes effect with the first cloud pin at or after the OSS release that carries this PR.
  • vivekchand/clawmetry-pro#252 (issue): the valuation engine that will answer pricing.value_usage. It is not a blocker; until it exists valuations returns 501 valuation_engine_unavailable.

Post-merge / post-release verification:

  1. Cloud boots. After the [RELEASE] that carries this PR, confirm the auto-pin bump PR in clawmetry-cloud passes the route-policy audit (0 errors), and that https://app.clawmetry.com/api/pricing/resolve is refused as cloud-disabled rather than served.
  2. Local routes behave. Run pip install -U clawmetry in a scratch venv with a scratch HOME, then clawmetry --port <free>, and check:
    • curl -s localhost:<port>/api/pricing/book returns 200 with present: false.
    • curl -s -XPOST -H 'Content-Type: application/json' -d '{"facts":[{"model":"gpt-4o","observed_at":"2026-03-01","input_tokens":5,"output_tokens":"abc"}]}' localhost:<port>/api/pricing/resolve returns 200, with list.cost_usd null and a reason naming output_tokens.
    • The same body sent to /api/pricing/valuations returns 501 valuation_engine_unavailable, with no upgrade prompt.
  3. Book selection works. Write ~/.clawmetry/pricing.json with one Bedrock entry (channel: aws-bedrock) and one Azure alias, then resolve us.anthropic.claude-sonnet-4-5-20250929-v1:0 and an https://<res>.openai.azure.com/openai/deployments/<name>/... URL. Each should return contract.status: matched with its entry id, effective interval and book_version. Edit a rate, resolve with the old book_version, and confirm the old rate comes back.
  4. Interceptor. With CLAWMETRY_INTERCEPT=1, an Azure OpenAI call is recorded as an llm_call with deployment and endpoint_host.

Founder decisions still open:

  • Should price_book stay Pro or move to Enterprise?
  • Is the JSON book acceptable instead of the YAML the issue proposed?

@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@vivekchand
vivekchand force-pushed the feat/price-book-azure-aliases-5936 branch from f53fddd to b3f8f0d Compare September 14, 2026 12:31
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

1 similar comment
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift 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
@vivekchand
vivekchand force-pushed the feat/price-book-azure-aliases-5936 branch from 2f91831 to afb8efc Compare September 14, 2026 13:25
@vivekchand

Copy link
Copy Markdown
Owner Author

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.

  • Conflicts: resolved against Cost basis labels: published-rate usage value, subscription value and metered usage kept apart #5975 (cost basis labels) and Cost Optimizer: bounded wait, labelled figures, advice from recorded usage (#5934) #5951 (Cost Optimizer).
  • `docs/acceptance_criteria.json`: the `AC-OBS-CEA-024` block now appears once. Earlier merges had duplicated it, and the `AC-GOV-FWM` block too. `check_ac_coverage --check` passes at 156/226. The parallel tip `2f918319bf` had the same dedupe; my commit has the same content plus the two changes below.
  • Integrated with Cost basis labels: published-rate usage value, subscription value and metered usage kept apart #5975: every cost figure this surface returns carries the shared `cost_basis` label. `resolve`'s `list` figure is `published_rate`, or `unknown` when no amount was computed. Accepted valuations go through `cost_basis.label`, so a contract figure cannot say "contract rate" without its `rate_version`. The guard is `test_figures_carry_the_shared_cost_basis_vocabulary`, and the withheld-valuation test was updated.
  • CHANGELOG: this PR's entry is removed; the release adds it.
  • Local checks: 142 passed across `test_price_book`, `test_providers_pricing`, `test_pricing_accuracy`, `test_cost_basis_labels`, both optimizer honesty tests and the module-map drift test. The module map is in sync.
  • Cloud policy: the paths and methods in vivekchand/clawmetry-cloud#2437 still match: `GET /api/pricing/book`, `POST /api/pricing/resolve`, `POST /api/pricing/valuations`, endpoints `pricing.api_pricing_*`.

@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
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
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@8090-software-factory

Copy link
Copy Markdown

✅ Drift Bot (ClawMetry): no drift detected

Drift Bot analyzed the changed files against this project's blueprints and requirements and found no drift.

github-actions Bot pushed a commit that referenced this pull request Sep 14, 2026
@vivekchand
vivekchand merged commit 60c0966 into main Sep 14, 2026
52 checks passed
vivekchand added a commit that referenced this pull request Sep 14, 2026
…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
vivekchand added a commit that referenced this pull request Sep 14, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants