Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
186 commits
Select commit Hold shift + click to select a range
bcb230b
feat: report per-invocation token usage from Hermes
mnajafian-nv Oct 15, 2026
02c2fc6
feat: report per-invocation token usage from Pi
mnajafian-nv Oct 15, 2026
e5eae71
fix: leave Pi usage unknown after an aborted call
mnajafian-nv Oct 15, 2026
b0dd2f7
fix: bound Hermes usage counters and state what they cover
mnajafian-nv Oct 15, 2026
b9bf5d4
fix: leave Hermes usage unknown when a provider response omits it
mnajafian-nv Oct 16, 2026
fd68928
fix: leave Pi usage unknown for unreported or retried calls
mnajafian-nv Oct 16, 2026
f734edf
fix: leave Pi usage unknown after a split-turn compaction
mnajafian-nv Oct 16, 2026
734519a
Merge upstream main into feat/hermes-pi-invocation-tokens
mnajafian-nv Oct 19, 2026
a1d62ce
fix: leave omitted Hermes and Pi token counts unknown
mnajafian-nv Oct 19, 2026
0bc8fcf
fix: check cache fields and the Hermes call site for partial usage
mnajafian-nv Oct 19, 2026
4491ef9
fix: leave Hermes usage unknown after a truncated or refused reply
mnajafian-nv Oct 19, 2026
de7fe86
docs: explain the Pi Bedrock input rule
mnajafian-nv Oct 19, 2026
9d6bc21
fix: count every Deep Agents model call in token usage
mnajafian-nv Oct 19, 2026
99ac9df
fix: leave Hermes usage unknown after an invalid response
mnajafian-nv Oct 19, 2026
1f9af06
docs: describe the Hermes response handlers the adapter observes
mnajafian-nv Oct 19, 2026
5de4904
feat: add what_ran provenance to run results
mnajafian-nv Oct 18, 2026
4f16bff
docs: describe RunResult.what_ran in the Python SDK guide
mnajafian-nv Oct 18, 2026
26f06b0
docs: regenerate API references for what_ran
mnajafian-nv Oct 18, 2026
638cb2e
feat: bound what_ran size and entry counts
mnajafian-nv Oct 19, 2026
6dea00a
docs: describe what_ran bounds in the Python SDK guide
mnajafian-nv Oct 19, 2026
e3a2876
docs: regenerate API references for what_ran bounds
mnajafian-nv Oct 19, 2026
cdc0636
docs: name NeMo Fabric in the Deep Agents usage limits
mnajafian-nv Oct 20, 2026
9be221e
fix: treat empty Hermes usage as a report with every count omitted
mnajafian-nv Oct 20, 2026
f47ca12
docs: exclude cache warm-ups from the Pi usage list
mnajafian-nv Oct 20, 2026
e2e1c42
fix: count Hermes usage from session counters and skipped responses
mnajafian-nv Oct 20, 2026
718c42a
fix: keep Pi usage known through rate limits and split compactions
mnajafian-nv Oct 20, 2026
f076c10
test: cover what_ran on a realistic config
mnajafian-nv Oct 20, 2026
d64832d
test: cover what_ran for each bundled descriptor and at the entry caps
mnajafian-nv Oct 20, 2026
4656ff6
fix: leave Hermes counts above the usage bound unknown
mnajafian-nv Oct 20, 2026
75c29a3
fix: apply the Pi rejected-request rule only to built-in protocols
mnajafian-nv Oct 20, 2026
744906c
docs: describe the corrected Hermes and Pi usage rules
mnajafian-nv Oct 20, 2026
cdea991
fix: count the Hermes iteration-limit summary response
mnajafian-nv Oct 20, 2026
d012300
fix: leave Hermes usage unknown when context compression runs
mnajafian-nv Oct 20, 2026
9ea4ff8
fix: count a failed Pi call as 0 only after an HTTP error status
mnajafian-nv Oct 20, 2026
17e0735
docs: describe the Hermes summary and compression and Pi rejection rules
mnajafian-nv Oct 20, 2026
e28bab5
fix: keep Pi cache reads known when input is unknown
mnajafian-nv Oct 20, 2026
4eecbda
fix: count a failed Pi attempt as 0 only after a rate limit
mnajafian-nv Oct 20, 2026
54b5332
fix: keep Hermes cached input known when input is unknown
mnajafian-nv Oct 20, 2026
8eba8b5
test: loosen the catalog count and rename the what_ran size test
mnajafian-nv Oct 20, 2026
f298d46
fix: skip the Hermes model title call in Fabric runs
mnajafian-nv Oct 20, 2026
ea9dbca
test: cover the Hermes model-title setting in the existing config tests
mnajafian-nv Oct 20, 2026
72ccbb2
fix: account for failed Hermes iteration-limit summaries
mnajafian-nv Oct 20, 2026
5fe8400
docs: describe the review fixes to Hermes and Pi usage
mnajafian-nv Oct 20, 2026
07ecc56
feat: read adapter report keys into what_ran
mnajafian-nv Oct 20, 2026
4a7425b
feat: report adapter and harness versions from six adapters
mnajafian-nv Oct 20, 2026
c76d9fb
feat: report the model that served each Claude Code invocation
mnajafian-nv Oct 20, 2026
68b4d0f
docs: describe adapter-reported versions in what_ran
mnajafian-nv Oct 20, 2026
81044a6
docs: regenerate API references for adapter report keys
mnajafian-nv Oct 20, 2026
c5e9f5e
fix: leave a Pi Codex call unknown after a failed WebSocket attempt
mnajafian-nv Oct 20, 2026
ce14531
fix: unregister the Hermes summary observer when the runtime stops
mnajafian-nv Oct 20, 2026
779f894
docs: describe the Pi Codex WebSocket fallback rule
mnajafian-nv Oct 20, 2026
57b64f3
feat: report the package version from the other Python adapters
mnajafian-nv Oct 20, 2026
a6a7290
feat: report the package version from the TypeScript adapters
mnajafian-nv Oct 20, 2026
b852eac
docs: note that every bundled adapter reports its package version
mnajafian-nv Oct 20, 2026
98ba43b
fix: declare the versions report in the Harbor SWE-bench descriptors
mnajafian-nv Oct 20, 2026
2d43065
fix: report the Hermes version that hermes --version prints
mnajafian-nv Oct 20, 2026
a021cdf
docs: keep only the API reference pages this branch changes
mnajafian-nv Oct 20, 2026
12f8f9e
fix: let the version probe recover when its thread cannot start
mnajafian-nv Oct 20, 2026
bd48fc0
fix: report hard-coded harness names with the adapter_report method
mnajafian-nv Oct 20, 2026
bc4d30f
feat: report Cline's served model as not attributed
mnajafian-nv Oct 20, 2026
ea23194
docs: narrow when an adapter fact reads key_not_declared
mnajafian-nv Oct 20, 2026
fbddfd8
fix: read no_source for Hermes only when its version call is missing
mnajafian-nv Oct 20, 2026
0dca1d2
test: require descriptor copies to match their bundled source byte fo…
mnajafian-nv Oct 20, 2026
14ff8c6
docs: document the served-model argument of the TypeScript report hel…
mnajafian-nv Oct 20, 2026
4531f8a
fix: keep plan MCP servers ahead of harness entries under the caps
mnajafian-nv Oct 20, 2026
b78d66e
docs: state the Pi Codex retry and mid-stream usage rules exactly
mnajafian-nv Oct 20, 2026
c9d6fad
docs: name the state together with each what_ran reason code
mnajafian-nv Oct 20, 2026
b35c3ff
test: read expected versions in the provenance tests from package met…
mnajafian-nv Oct 20, 2026
b047245
test: read the Hermes adapter version from package metadata
mnajafian-nv Oct 20, 2026
d35a1e9
test: fix the common package check and the Windows probe timing
mnajafian-nv Oct 20, 2026
723326d
fix: count Hermes auxiliary calls in invocation usage
mnajafian-nv Oct 20, 2026
069a8c5
fix: read Hermes Anthropic auxiliary usage before conversion
mnajafian-nv Oct 20, 2026
753e9ea
fix: leave Hermes usage unknown for unaccounted auxiliary requests
mnajafian-nv Oct 20, 2026
d856b40
fix: skip Hermes' background review in Fabric runs
mnajafian-nv Oct 20, 2026
a26f8ba
docs: describe the skipped Hermes background review
mnajafian-nv Oct 20, 2026
2889355
test: write the background review test's files under tmp_path
mnajafian-nv Oct 20, 2026
d5ecdff
fix: keep Hermes auxiliary responses unresolved until recorded
mnajafian-nv Oct 20, 2026
3bb134d
fix: leave Hermes usage unknown when a background review starts
mnajafian-nv Oct 20, 2026
a4a46c6
fix: fail Hermes start when the background review cannot be skipped
mnajafian-nv Oct 20, 2026
dd2d542
fix: count Hermes auxiliary responses from envelope gateways
mnajafian-nv Oct 20, 2026
a0f06ea
test: assert Hermes usage stays known when the review is skipped
mnajafian-nv Oct 20, 2026
57a9874
fix: order TypeScript report roles by code point like core
mnajafian-nv Oct 20, 2026
9af928b
docs: say when an undeclared report key fails the run
mnajafian-nv Oct 20, 2026
f213551
fix: leave Hermes usage unknown for hidden auxiliary attempts
mnajafian-nv Oct 20, 2026
8be9fab
fix: leave Hermes usage unknown for an invocation that delegates
mnajafian-nv Oct 20, 2026
70bf925
fix: leave a Pi count unknown when its sum is not a safe integer
mnajafian-nv Oct 20, 2026
c9e6881
fix: leave a Pi prompt unknown after any cache warm-up attempt
mnajafian-nv Oct 20, 2026
94ce714
fix: keep Hermes Bedrock and Gemini omitted counts unknown
mnajafian-nv Oct 20, 2026
f43462a
docs: describe Hermes delegation, native transport and Pi warm-up rules
mnajafian-nv Oct 20, 2026
3023e80
fix: add a report's harness server drops to core's servers marker
mnajafian-nv Oct 20, 2026
29cf0fd
test: show an older core accepts descriptors that declare the version…
mnajafian-nv Oct 20, 2026
5696458
test: read the string length vectors from one shared fixture
mnajafian-nv Oct 20, 2026
1bc76ec
Merge the #419 delegation and native-transport changes
mnajafian-nv Oct 20, 2026
1affc92
fix: align Hermes auxiliary usage with delegation and native transports
mnajafian-nv Oct 20, 2026
64feb12
test: check versions report validation under three run_result schemas
mnajafian-nv Oct 20, 2026
e16b42b
docs: explain the descriptor-copy migration for the versions report
mnajafian-nv Oct 20, 2026
49c33a6
fix: account for Pi warm-up attempts when a prompt throws
mnajafian-nv Oct 20, 2026
d690432
docs: state what the Hermes adapter checks at startup
mnajafian-nv Oct 20, 2026
883dc08
fix: record late Hermes Anthropic auxiliary usage without raising
mnajafian-nv Oct 20, 2026
2e32242
docs: narrow the Hermes auxiliary retry boundary
mnajafian-nv Oct 20, 2026
53a211c
Merge the #419 Pi warm-up and startup-check fixes
mnajafian-nv Oct 20, 2026
2f34572
docs: state what the Hermes auxiliary startup checks cover
mnajafian-nv Oct 20, 2026
4585ac7
fix: report Deep Agents cached input tokens
mnajafian-nv Oct 20, 2026
1ab2640
fix: count only the report's harness-only server drops in core's marker
mnajafian-nv Oct 20, 2026
7501f47
docs: define report server drops against the full capability plan
mnajafian-nv Oct 20, 2026
fee4c92
fix: report zero Deep Agents usage when no model call ran
mnajafian-nv Oct 20, 2026
2ee128d
fix: report unknown Deep Agents usage with an Agent Protocol subagent
mnajafian-nv Oct 20, 2026
06de4c0
docs: say a failed Deep Agents call may have been billed
mnajafian-nv Oct 20, 2026
e535051
style: format the Deep Agents remote subagent check
mnajafian-nv Oct 20, 2026
cb42bfe
test: keep zero observed Deep Agents calls unknown with a remote suba…
mnajafian-nv Oct 20, 2026
ce74415
fix: preserve empty OpenShell environment arguments
mnajafian-nv Oct 22, 2026
790f369
fix: avoid premature Relay gateway startup failures
mnajafian-nv Oct 22, 2026
5db4917
docs: use the NeMo Fabric product name
mnajafian-nv Oct 22, 2026
4bea319
feat: report complete and observed model usage
mnajafian-nv Oct 22, 2026
1802df2
feat: add core and plan facts to run results
mnajafian-nv Oct 22, 2026
be32c6e
docs: use the full product name in Relay guidance
mnajafian-nv Oct 22, 2026
563baac
docs: use the full product name in Relay guidance
mnajafian-nv Oct 22, 2026
f2bf311
feat: report harness versions in run results
mnajafian-nv Oct 22, 2026
cbb0699
fix: validate recovered usage and optional pricing rates
mnajafian-nv Oct 22, 2026
559ce0a
fix: preserve fractional cost precision across usage reducers
mnajafian-nv Oct 22, 2026
0f6e0de
chore: combine usage and provenance prerequisites
mnajafian-nv Oct 22, 2026
2ae6b9e
test: respect NOOA Python support in provenance cases
mnajafian-nv Oct 22, 2026
554ed79
test: carry NOOA provenance compatibility into prerequisites
mnajafian-nv Oct 22, 2026
e20c939
feat: report Hermes model usage per invocation
mnajafian-nv Oct 22, 2026
a6318d4
chore: check packaged usage schema declarations
mnajafian-nv Oct 22, 2026
630820f
fix: return detached applied pricing rates in Python
mnajafian-nv Oct 22, 2026
83d5663
docs: document execution facts in the Python result reference
mnajafian-nv Oct 22, 2026
0061a06
docs: carry the corrected Python result reference
mnajafian-nv Oct 22, 2026
f96d200
docs: clarify collector pricing scope
mnajafian-nv Oct 23, 2026
93b00ec
fix: expose omitted optional SDK usage attributes
mnajafian-nv Oct 23, 2026
101bce6
fix: reject Relay exit during successful health probe
mnajafian-nv Oct 23, 2026
2014cbe
fix: exclude unknown keys from request usage
mnajafian-nv Oct 23, 2026
616b824
chore: incorporate Relay readiness prerequisite
mnajafian-nv Oct 23, 2026
dc2b018
chore: incorporate Relay readiness prerequisite
mnajafian-nv Oct 23, 2026
c038b36
fix: validate reported MCP transport facts
mnajafian-nv Oct 23, 2026
13ffdb1
chore: incorporate provenance prerequisite
mnajafian-nv Oct 23, 2026
67bd068
fix: preserve Hermes failure and auxiliary evidence
mnajafian-nv Oct 23, 2026
8355989
fix: avoid repeating a timed-out Hermes version probe
mnajafian-nv Oct 23, 2026
218a5e2
chore: merge shared accounting fixes for Hermes usage
mnajafian-nv Oct 23, 2026
437acb9
chore: merge provenance fixes for Hermes usage
mnajafian-nv Oct 23, 2026
11f72a4
fix: enforce Relay gateway startup deadline
mnajafian-nv Oct 23, 2026
350bbbd
chore: incorporate updated provenance prerequisite
mnajafian-nv Oct 23, 2026
0801d56
chore: incorporate updated provenance prerequisite
mnajafian-nv Oct 23, 2026
e3a2355
fix: classify empty usage coverage as unverified
mnajafian-nv Oct 23, 2026
c7b9589
chore: incorporate current main into OpenShell launcher fix
mnajafian-nv Oct 23, 2026
8cc5c23
chore: incorporate updated OpenShell prerequisite
mnajafian-nv Oct 23, 2026
a8f6fd8
test: account for unverified empty usage coverage
mnajafian-nv Oct 23, 2026
75d579a
chore: incorporate updated Relay prerequisite
mnajafian-nv Oct 23, 2026
e203f3d
chore: incorporate updated Relay prerequisite
mnajafian-nv Oct 23, 2026
58aed7d
chore: incorporate updated provenance prerequisite
mnajafian-nv Oct 23, 2026
802bfb6
Merge latest shared accounting into Hermes usage
mnajafian-nv Oct 23, 2026
74bde21
Merge latest provenance into Hermes usage
mnajafian-nv Oct 23, 2026
83d8ada
docs: keep adapter usage guidance within current contract
mnajafian-nv Oct 23, 2026
600b1b3
Merge corrected accounting guidance into Hermes usage
mnajafian-nv Oct 23, 2026
ebcfe97
test: run OpenShell launcher checks with system Bash
mnajafian-nv Oct 23, 2026
4286931
chore: include system Bash test prerequisite
mnajafian-nv Oct 23, 2026
83b5e76
fix: reject Relay readiness after the startup deadline
mnajafian-nv Oct 23, 2026
5702ead
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
5390e81
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
a6d81df
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
d042115
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
2149bd1
fix: bound Pi Relay readiness checks
mnajafian-nv Oct 23, 2026
ba4567b
test: support alternative model schemas in catalog provenance checks
mnajafian-nv Oct 25, 2026
46936e2
chore: align provenance declarations with the Strands adapter
mnajafian-nv Oct 25, 2026
bf0bcc5
chore: integrate current provenance declarations
mnajafian-nv Oct 25, 2026
a1d5527
fix: reject positive usage with exhaustive empty models
mnajafian-nv Oct 26, 2026
f9b8f6a
chore: integrate shared accounting with execution provenance
mnajafian-nv Oct 26, 2026
c6b3b30
chore: integrate shared accounting with adapter provenance
mnajafian-nv Oct 26, 2026
4c0dbc0
docs: qualify normalized model usage availability
mnajafian-nv Oct 26, 2026
d7ec72b
chore: sync shared usage validation
mnajafian-nv Oct 26, 2026
3356f5a
chore: merge main into shared usage accounting
mnajafian-nv Oct 26, 2026
d7db56c
chore: merge main into execution facts
mnajafian-nv Oct 26, 2026
a2d2dbd
chore: merge main into adapter version reports
mnajafian-nv Oct 26, 2026
0586894
chore: merge main into Hermes usage accounting
mnajafian-nv Oct 26, 2026
b30b122
fix: enforce usage accounting parity across bindings
mnajafian-nv Oct 26, 2026
87568be
fix: enforce usage accounting parity across bindings
mnajafian-nv Oct 26, 2026
ff89776
fix: preserve runtime version and Claude turn attribution
mnajafian-nv Oct 26, 2026
dc87ca2
chore: merge main into adapter version reports
mnajafian-nv Oct 26, 2026
be41b8d
chore: merge latest contract parity into version reports
mnajafian-nv Oct 26, 2026
4524f55
fix: preserve adapter health checks in version wrapper
mnajafian-nv Oct 27, 2026
4e4ac3e
chore: reconcile usage accounting with current main
mnajafian-nv Oct 27, 2026
52203e0
Merge shared usage accounting into run facts
mnajafian-nv Oct 27, 2026
fd002e7
Merge updated run facts contract into version reports
mnajafian-nv Oct 27, 2026
f7d7b5e
feat: report the Strands adapter package version
mnajafian-nv Oct 27, 2026
2bfd533
docs: qualify when normalized model usage is present
mnajafian-nv Oct 27, 2026
9a93e8d
chore: merge shared run evidence into Hermes usage
mnajafian-nv Oct 27, 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
6 changes: 5 additions & 1 deletion .agents/skills/contribute-adapter/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ Keep descriptor claims, implementation, focused tests, public documentation,
catalog entries, and packaged metadata synchronized. Start with the narrowest
truthful capability set.

For usage changes, verify invocation-local accounting through the adapter contract and consumer SDK, including warm-session deltas, unknown counters, cache semantics, and unsuccessful results. Keep generated schemas and language bindings in parity. Preserve the closed `fabric.adapter/v1alpha2` `AgentUsage` shape; carry detailed cache-write, reasoning, peak request, and per-model data in the descriptor-declared `usage.extensions["nemo_fabric.detailed_token_usage.v1"]` object until a new adapter contract revision negotiates those fields. Consumers treat any detailed field for an input or output dimension as selecting the detailed counters for that dimension, so require complete, internally consistent counters instead of relying on the top-level view as a fallback.
For usage changes, verify invocation-local accounting through the adapter contract and consumer SDK, including warm-session deltas, omitted fields, cache semantics and unsuccessful results. Keep generated schemas and language bindings in parity. Preserve the closed `fabric.adapter/v1alpha2` `AgentUsage` shape.

For a collector using shared accounting, declare `usage.extensions["nemo_fabric.usage_accounting.v1"]` in the descriptor and use Python `usage_from_observations` or TypeScript `usageFromObservations`. Keep source parsing, deduplication and invocation ownership in the collector; core normalizes counts and applies qualified pricing. An omitted count stays unknown and a reported zero stays zero. Freeze observations at return; late usage cannot move to a later invocation. An execution failure alone does not erase settled usage.

The shared helper also supplies the legacy `nemo_fabric.detailed_token_usage.v1` projection for older consumers. Keep its descriptor declaration and inclusion flags consistent. Detailed legacy counters select their dimension without falling back to top-level counters. Do not replace incomplete shared accounting with legacy fields, logs or Relay totals, and do not promote client estimates to provider-reported cost.

For `instructions.system`, keep `config.system_instruction_modes`, planning
behavior, direct adapter validation, and target-native composition synchronized.
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/ci_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ jobs:
npm run build --prefix adapters/typescript --workspace nemo-fabric-adapters-pi

- name: Run pytest
env:
FABRIC_REQUIRE_TYPESCRIPT_REDUCER_PARITY: "1"
run: |
set -euo pipefail
just --set no_uv true test-python
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ chrono = { version = "0.4.45", default-features = false, features = ["std"] }
# Authenticate local health messages with the endpoint secret.
getrandom = "0.4.3"
hmac = "0.12.1"
sha2 = "0.10.9"
# SHA-256 for health authentication and `what_ran` truncation fingerprints.
sha2 = { version = "0.10.9", default-features = false }

clap = { version = "4", default-features = false, features = ["derive", "std", "help", "usage", "error-context", "suggestions"] }
# Validate adapter-owned JSON Schema in Rust while preserving precise instance paths.
Expand Down
130 changes: 130 additions & 0 deletions adapter-contract/python/src/nemo_fabric_adapter_contract/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,136 @@ def _validate(self) -> None:
)


class UsageDimension(StrEnum):
"""Raw dimensions in the declared usage-accounting extension."""

INPUT_TOKENS = "input_tokens"
CACHED_INPUT_TOKENS = "cached_input_tokens"
CACHE_WRITE_INPUT_TOKENS = "cache_write_input_tokens"
OUTPUT_TOKENS = "output_tokens"
REASONING_TOKENS = "reasoning_tokens"
TOTAL_TOKENS = "total_tokens"
PEAK_REQUEST_INPUT_TOKENS = "peak_request_input_tokens"
COST_USD = "cost_usd"


class GapReason(StrEnum):
"""Why complete accounting is unavailable."""

UNFINISHED_WORK = "unfinished_work"
MISSING_USAGE = "missing_usage"
SCOPE_UNVERIFIED = "scope_unverified"


@dataclass(slots=True, kw_only=True)
class ReportedUsage(ContractModel):
"""Raw provider/harness counts, retaining independent field presence."""

input_tokens: int | None = _optional()
cached_input_tokens: int | None = _optional()
cache_write_input_tokens: int | None = _optional()
input_tokens_include_cache: bool | None = _optional()
output_tokens: int | None = _optional()
reasoning_tokens: int | None = _optional()
output_tokens_include_reasoning: bool | None = _optional()
total_tokens: int | None = _optional()
peak_request_input_tokens: int | None = _optional()
cost_usd: float | None = _optional()

def _validate(self) -> None:
import math

for dimension in UsageDimension:
value = getattr(self, dimension.value)
if dimension is not UsageDimension.COST_USD:
_bounded_int(value, dimension.value, (1 << 64) - 1)
elif value is not None and (
not math.isfinite(value) or value < 0 or value * 1e9 >= 1 << 64
):
raise ContractValidationError(
"cost must fit finite nonnegative nano-USD"
)


@dataclass(slots=True, kw_only=True)
class UsageGap(ContractModel):
"""A nonempty set of incomplete raw dimensions."""

dimensions: list[UsageDimension]
reason: GapReason

def _validate(self) -> None:
if not self.dimensions or len(set(self.dimensions)) != len(self.dimensions):
raise ContractValidationError("gap dimensions must be nonempty and unique")


@dataclass(slots=True, kw_only=True)
class UsageRequest(ContractModel):
"""One deduplicated model-call snapshot, not an aggregate."""

usage: ReportedUsage
gap: GapReason | None = _optional()
tokens_aligned: bool = False


@dataclass(slots=True, kw_only=True)
class AccountedModel(ContractModel):
"""Complete and observed evidence for one literal model/provider identity."""

model: str
gaps: list[UsageGap]
provider: str | None = _optional()
reported: ReportedUsage = field(default_factory=ReportedUsage)
observed: ReportedUsage | None = _optional()

observed_tokens_aligned: bool = False
requests: list[UsageRequest] = _empty_list()
requests_exhaustive: bool = False

def _validate(self) -> None:
_nonblank(self.model, "model")
if self.requests_exhaustive and not self.requests:
raise ContractValidationError("exhaustive requests must be nonempty")
if self.provider is not None:
_nonblank(self.provider, "provider")
for gap in self.gaps:
if any(
getattr(self.reported, dimension.value) is not None
for dimension in gap.dimensions
):
raise ContractValidationError(
"complete value conflicts with explicit gap"
)


@dataclass(slots=True, kw_only=True)
class UsageAccounting(ContractModel):
"""Typed nemo_fabric.usage_accounting.v1 payload.

Declare its schema in extension_schemas.usage. This does not change the
closed AgentUsage contract. Per-model complete counts live in reported.
"""

models_exhaustive: bool
gaps: list[UsageGap]
observed: ReportedUsage | None = _optional()
models: list[AccountedModel] = _empty_list()

def _validate(self) -> None:
if self.models_exhaustive and not self.models and self.observed is not None:
for dimension in UsageDimension:
if dimension is UsageDimension.COST_USD:
continue
count = getattr(self.observed, dimension.value)
if count is not None and count > 0:
raise ContractValidationError(
"exhaustive empty model list conflicts with positive tokens"
)
identities = [(model.provider, model.model) for model in self.models]
if len(set(identities)) != len(identities):
raise ContractValidationError("duplicate model identity")


@dataclass(slots=True, kw_only=True)
class AgentRunResult(AgentContractBlock):
"""Southbound terminal result returned by an adapter target."""
Expand Down
Loading
Loading