Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
177 commits
Select commit Hold shift + click to select a range
a5d4ab9
feat: add runtime resource snapshots to the Rust core
mnajafian-nv Oct 15, 2026
0e016fd
feat: expose runtime resource snapshots in the Python SDK
mnajafian-nv Oct 15, 2026
ce4f5f4
docs: document runtime resource snapshots
mnajafian-nv Oct 15, 2026
d5a09f9
docs: state which exited descendants count toward snapshot CPU time
mnajafian-nv Oct 15, 2026
85f1e5a
refactor: queue macOS snapshot children with their usage
mnajafian-nv Oct 16, 2026
c846c78
fix: check each macOS snapshot child is still the listed process
mnajafian-nv Oct 16, 2026
ba3404a
fix: report a snapshot as unavailable when a runtime process cannot b…
mnajafian-nv Oct 16, 2026
5cf94dc
perf: read only the runtime's own processes on Linux
mnajafian-nv Oct 16, 2026
9c54d0a
docs: regenerate API references for unavailable snapshot reads
mnajafian-nv Oct 16, 2026
888230b
fix: read macOS snapshots from each process's children
mnajafian-nv Oct 16, 2026
ced92e8
docs: explain unavailable and unsupported resource snapshots
mnajafian-nv Oct 16, 2026
8e52556
test: name the snapshot mapping test for both checks it makes
mnajafian-nv Oct 16, 2026
2e9cfed
docs: regenerate API references for resource snapshot docs
mnajafian-nv Oct 16, 2026
e46f641
docs: stop saying a later snapshot explains a missing result snapshot
mnajafian-nv Oct 16, 2026
f4cba23
docs: list a changing process tree and hidepid among snapshot limits
mnajafian-nv Oct 16, 2026
0371c19
docs: regenerate API references for resource snapshot error docs
mnajafian-nv Oct 16, 2026
e7665eb
refactor: move the RFC 3339 formatter into a shared time module
mnajafian-nv Oct 16, 2026
5e4c29d
feat: write an opt-in run record for each invocation
mnajafian-nv Oct 16, 2026
3d0b6ed
fix: check the parent's generation before counting a Linux child
mnajafian-nv Oct 16, 2026
b2e92f2
fix: make a Linux snapshot unavailable when /proc hides a descendant
mnajafian-nv Oct 16, 2026
5e39195
docs: state that a hidden descendant makes a snapshot unavailable
mnajafian-nv Oct 16, 2026
2f76f8c
docs: regenerate the Rust reference for the hidepid module doc
mnajafian-nv Oct 16, 2026
6655b13
test: cover run record write failures
mnajafian-nv Oct 16, 2026
f43809e
fix: start the run record before the adapter deadline
mnajafian-nv Oct 16, 2026
100394a
fix: keep run record model role and context key names whole
mnajafian-nv Oct 16, 2026
264e34b
docs: describe run recording as synchronous and best-effort
mnajafian-nv Oct 16, 2026
73ebc0a
feat: record why each invocation stopped
mnajafian-nv Oct 16, 2026
41670ff
test: drop impossible host exits from two stop rule tests
mnajafian-nv Oct 16, 2026
ff6141c
docs: say when a forced stop records the signal Fabric sent
mnajafian-nv Oct 16, 2026
59cee92
fix: read the end oom_kill count when the exchange ends
mnajafian-nv Oct 16, 2026
51077a4
test: pin no stop cause for a cancelled result with a timeout code
mnajafian-nv Oct 16, 2026
b13430a
docs: qualify forced stop exit status and the Harbor cause mapping
mnajafian-nv Oct 16, 2026
2fcc672
test: lower the exited-child CPU threshold to two clock ticks
mnajafian-nv Oct 16, 2026
a3ea77e
Merge runtime resource snapshots into the run record
mnajafian-nv Oct 16, 2026
5ef3593
feat: add sample lines and resource snapshots to run records
mnajafian-nv Oct 16, 2026
bc7da5c
fix: close a record's sampling window when its exchange ends
mnajafian-nv Oct 16, 2026
8e7eda7
test: block a sample read across the end line instead of sleeping
mnajafian-nv Oct 16, 2026
f7c6886
docs: say what a recent sample and a stale run record show
mnajafian-nv Oct 16, 2026
7e2be97
test: build the sampling window test helpers only where the test runs
mnajafian-nv Oct 16, 2026
24407bb
docs: name NeMo Fabric in the resource snapshot docs
mnajafian-nv Oct 16, 2026
778d8d8
docs: regenerate the references for the snapshot product name
mnajafian-nv Oct 16, 2026
395efa6
test: say the exited-child threshold is two ticks only on Linux
mnajafian-nv Oct 16, 2026
9d33297
Merge runtime resource snapshot review fixes into the run record samples
mnajafian-nv Oct 16, 2026
f18b0ed
chore: regenerate the run record line schema for the snapshot wording
mnajafian-nv Oct 16, 2026
ddff2da
docs: call end minus start CPU time an estimate
mnajafian-nv Oct 16, 2026
e1dd296
test: drop a sample read that finishes after the exchange ends
mnajafian-nv Oct 16, 2026
6bb233f
docs: tie the run record format's stability to nemo-fabric report
mnajafian-nv Oct 16, 2026
baf3566
test: bound the sample race test handshakes
mnajafian-nv Oct 16, 2026
c723c68
fix: require RFC 3339 UTC resource snapshot timestamps
mnajafian-nv Oct 16, 2026
6b10b68
docs: limit the record write failure event to returned results
mnajafian-nv Oct 16, 2026
5b0033b
fix: require the full RFC 3339 shape for snapshot timestamps
mnajafian-nv Oct 16, 2026
ac9f02c
docs: name the result fields a record write failure leaves unchanged
mnajafian-nv Oct 16, 2026
f83521b
fix: record a schema-invalid result extension as invalid_agent_run_re…
mnajafian-nv Oct 16, 2026
fd56894
docs: mention sample lines in the RecordConfig description
mnajafian-nv Oct 16, 2026
83a93bb
Merge upstream main into feat/runtime-resource-snapshots
mnajafian-nv Oct 16, 2026
bc75c39
docs: regenerate references after merging main
mnajafian-nv Oct 16, 2026
3b6e98e
Merge upstream main into feat/opt-in-run-record
mnajafian-nv Oct 16, 2026
c233cf4
test: reject the run record on the OpenShell provider
mnajafian-nv Oct 16, 2026
caa72f5
docs: regenerate references after merging main
mnajafian-nv Oct 16, 2026
ce56953
Merge feat/opt-in-run-record into feat/run-record-stop-cause-v2
mnajafian-nv Oct 16, 2026
cf72b69
Merge feat/run-record-stop-cause-v2 into feat/run-record-samples
mnajafian-nv Oct 16, 2026
ee3676e
Merge feat/runtime-resource-snapshots into feat/run-record-samples
mnajafian-nv Oct 16, 2026
874731d
docs: regenerate references after merging main
mnajafian-nv Oct 16, 2026
1619b1d
test: build race-test fixtures before the sampling window opens
mnajafian-nv Oct 18, 2026
9c89293
Merge upstream main into feat/runtime-resource-snapshots
mnajafian-nv Oct 18, 2026
e61e58b
Merge upstream main into feat/opt-in-run-record
mnajafian-nv Oct 18, 2026
ea303d6
Merge feat/opt-in-run-record into feat/run-record-stop-cause-v2
mnajafian-nv Oct 18, 2026
ab6bd98
Merge feat/runtime-resource-snapshots into feat/run-record-samples
mnajafian-nv Oct 18, 2026
3f6419e
Merge feat/run-record-stop-cause-v2 into feat/run-record-samples
mnajafian-nv Oct 18, 2026
84ad05d
Merge upstream main into feat/opt-in-run-record
mnajafian-nv Oct 19, 2026
cb9c537
Merge upstream main into feat/run-record-stop-cause-v2
mnajafian-nv Oct 19, 2026
1065395
Merge upstream main into feat/run-record-samples-v3
mnajafian-nv Oct 19, 2026
9d6bc21
fix: count every Deep Agents model call in token usage
mnajafian-nv Oct 19, 2026
cdc0636
docs: name NeMo Fabric in the Deep Agents usage limits
mnajafian-nv Oct 20, 2026
5660039
docs: clarify when run records get sample lines
mnajafian-nv Oct 20, 2026
6980d82
test: check warm-runtime invocation CPU is end minus start
mnajafian-nv Oct 20, 2026
a0902a1
test: check the end line keeps the exchange-end CPU time
mnajafian-nv Oct 20, 2026
4585ac7
fix: report Deep Agents cached input tokens
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
be32c6e
docs: use the full product name in Relay guidance
mnajafian-nv Oct 22, 2026
06369c4
feat: estimate model cost with versioned pricing
mnajafian-nv Oct 22, 2026
5f084b5
feat: save and inspect opt-in invocation records
mnajafian-nv Oct 22, 2026
cbb0699
fix: validate recovered usage and optional pricing rates
mnajafian-nv Oct 22, 2026
05dee0f
feat: record requests that return no invocation result
mnajafian-nv Oct 22, 2026
559ce0a
fix: preserve fractional cost precision across usage reducers
mnajafian-nv Oct 22, 2026
f3ece9c
feat: save invocation resource observations in run records
mnajafian-nv Oct 22, 2026
19a305f
fix: apply shared usage accounting corrections
mnajafian-nv Oct 22, 2026
87bce87
feat: compare task attempts with accounting coverage
mnajafian-nv Oct 22, 2026
b9844de
fix: apply shared usage accounting corrections
mnajafian-nv Oct 22, 2026
4bb1753
feat: record requests that return no invocation result
mnajafian-nv Oct 22, 2026
8a07728
feat: save invocation resource observations in run records
mnajafian-nv Oct 22, 2026
d3a40dc
feat: compare task attempts with accounting coverage
mnajafian-nv Oct 22, 2026
a6318d4
chore: check packaged usage schema declarations
mnajafian-nv Oct 22, 2026
d35ebb3
fix: qualify child model pricing by provider endpoint
mnajafian-nv Oct 22, 2026
c198472
fix: return detached applied pricing rates in Python
mnajafian-nv Oct 22, 2026
06e2396
fix: preserve frozen catalog pricing for OpenShell results
mnajafian-nv Oct 22, 2026
630820f
fix: return detached applied pricing rates in Python
mnajafian-nv Oct 22, 2026
5e18572
fix: inherit corrected pricing in saved run records
mnajafian-nv Oct 22, 2026
5c2f549
fix: inherit corrected pricing in request records
mnajafian-nv Oct 23, 2026
d9e226a
fix: inherit corrected pricing in resource records
mnajafian-nv Oct 23, 2026
8232c0e
fix: inherit corrected pricing in run comparisons
mnajafian-nv Oct 23, 2026
93b00ec
fix: expose omitted optional SDK usage attributes
mnajafian-nv Oct 23, 2026
f010d42
fix: keep optional SDK usage attributes nullable
mnajafian-nv Oct 23, 2026
78aec4e
fix: retain nullable usage attributes in saved results
mnajafian-nv Oct 23, 2026
8c4c507
fix: retain nullable usage attributes in request records
mnajafian-nv Oct 23, 2026
119e871
fix: retain nullable usage attributes in resource records
mnajafian-nv Oct 23, 2026
23ceba1
fix: retain nullable usage attributes in run comparisons
mnajafian-nv Oct 23, 2026
f5aa5be
docs: clarify run record crash durability
mnajafian-nv Oct 23, 2026
a4abe7a
fix: retain the first request recording error
mnajafian-nv Oct 23, 2026
1b1cba2
docs: inherit run record durability correction
mnajafian-nv Oct 23, 2026
28c5e01
fix: retain request diagnostics in comparisons
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
3fd1566
fix: skip execution-host pricing during inspection
mnajafian-nv Oct 23, 2026
6efff4d
docs: use full product name in pricing references
mnajafian-nv Oct 23, 2026
fe6f17b
chore: incorporate accounting prerequisite
mnajafian-nv Oct 23, 2026
aef62dd
test: clean up recorded invocation fixtures
mnajafian-nv Oct 23, 2026
e7c495b
test: require exact Python API reference coverage
mnajafian-nv Oct 23, 2026
94c5302
chore: incorporate pricing prerequisite
mnajafian-nv Oct 23, 2026
57a292f
chore: merge recording parent fixes for request evidence
mnajafian-nv Oct 23, 2026
1e25f4a
chore: merge recording parent fixes for resource evidence
mnajafian-nv Oct 23, 2026
fc265da
docs: name NeMo Fabric in attempt comparisons
mnajafian-nv Oct 23, 2026
917db4f
Merge request record updates into attempt comparisons
mnajafian-nv Oct 23, 2026
de8ba96
Merge resource record updates into attempt comparisons
mnajafian-nv Oct 23, 2026
a71c3fc
test: retain exchange timing in comparison fallback
mnajafian-nv Oct 23, 2026
4f7e551
fix: preserve lifecycle stage for recorded one-shot failures
mnajafian-nv Oct 23, 2026
304cacd
chore: incorporate recording parent lifecycle fix
mnajafian-nv Oct 23, 2026
94bfba6
chore: merge recording lifecycle fix for resource records
mnajafian-nv Oct 23, 2026
abd8199
chore: incorporate request-record parent fixes
mnajafian-nv Oct 23, 2026
9caea72
chore: incorporate resource-record parent fixes
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
793b4bb
chore: incorporate updated accounting prerequisite
mnajafian-nv Oct 23, 2026
46e7ccf
docs: clarify providerless pricing lookup order
mnajafian-nv Oct 23, 2026
05132d8
docs: regenerate pricing run-plan schema
mnajafian-nv Oct 23, 2026
85100cf
docs: clarify pricing references and usage summary guidance
mnajafian-nv Oct 23, 2026
d021e95
chore: refresh run recording on pricing parent
mnajafian-nv Oct 23, 2026
4663e40
fix: preserve one-shot startup error stages
mnajafian-nv Oct 23, 2026
83d8ada
docs: keep adapter usage guidance within current contract
mnajafian-nv Oct 23, 2026
34975c1
docs: inherit corrected usage guidance
mnajafian-nv Oct 23, 2026
e0d2e43
fix: preserve startup error stage with request records
mnajafian-nv Oct 23, 2026
7be9e5d
chore: inherit current recording prerequisite
mnajafian-nv Oct 23, 2026
2cdbab3
Merge corrected shared accounting guidance into pricing
mnajafian-nv Oct 23, 2026
b1fd4e3
chore: inherit final pricing prerequisite
mnajafian-nv Oct 23, 2026
17e3c34
chore: inherit final recording prerequisite
mnajafian-nv Oct 23, 2026
405353b
chore: inherit final recording prerequisite
mnajafian-nv Oct 23, 2026
2210295
chore: inherit current recording and pricing prerequisites
mnajafian-nv Oct 23, 2026
ebcfe97
test: run OpenShell launcher checks with system Bash
mnajafian-nv Oct 23, 2026
8db3c57
fix: preserve recorded startup stages in Python errors
mnajafian-nv Oct 23, 2026
5ccf26e
chore: inherit recorded startup-stage fix
mnajafian-nv Oct 23, 2026
5214b14
chore: inherit recorded startup-stage fix
mnajafian-nv Oct 23, 2026
4286931
chore: include system Bash test prerequisite
mnajafian-nv Oct 23, 2026
f54074b
chore: inherit recorded startup-stage fix
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
5a8589a
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
535adfd
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
1d395a2
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
beed43e
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
b1cca9e
chore: sync Relay startup prerequisite
mnajafian-nv Oct 23, 2026
56e59c7
test: allow zero coverage in resource sampling checks
mnajafian-nv Oct 24, 2026
6798d8a
test: integrate valid zero sampling coverage checks
mnajafian-nv Oct 25, 2026
c712208
chore: integrate the resource sampling test correction
mnajafian-nv Oct 25, 2026
b2d0828
chore: integrate the resource sampling test correction
mnajafian-nv Oct 25, 2026
ef34303
chore: integrate the resource sampling test correction
mnajafian-nv Oct 25, 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ libc = "0.2.186"
schemars = { version = "1", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# Standard digest for reproducible price-catalog snapshots; already in the lockfile.
sha2 = { version = "0.10", default-features = false }
thiserror = "2"
windows-sys = { version = "0.61.2", features = ["Win32_Foundation", "Win32_Security", "Win32_System_Diagnostics_ToolHelp", "Win32_System_JobObjects", "Win32_System_Threading"] }
pyo3 = "0.28.2"
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ NeMo Fabric includes adapters for the following harnesses and execution targets.

✅ Supported · — Not supported. Support may come through normalized configuration, native harness behavior, or a specific target. Refer to each adapter guide or the [complete compatibility matrix](adapters/README.md#configuration-compatibility) for details and limitations.

Opt in to local run evidence with `runtime.record.enabled` and an artifact root,
or use `nemo-fabric run --preset scripted --record --artifacts ./runs`.
Recording saves a bounded invocation record and the finalized full result.
Inspect a saved result with `python -m nemo_fabric.reporting <run-result.json>`;
add `--details` for resources and pricing evidence or `--json` for exact values.
See [run recording](docs/sdk/python.mdx#opt-in-run-recording) for boundaries and limitations.

## NVIDIA Ecosystem

NeMo Fabric is an execution layer for NVIDIA products that build, evaluate, deploy, and improve agents. The ecosystem includes shipped integrations with [NVIDIA NeMo Helix](https://github.com/NVIDIA-NeMo/nemo-helix), concrete integrations with [NVIDIA NemoClaw](https://github.com/NVIDIA/NemoClaw/) and [NVIDIA NeMo Gym](https://github.com/NVIDIA-NeMo/gym), and emerging rollout integrations such as [NVIDIA Polar](https://github.com/NVIDIA-NeMo/ProRL-Agent-Server). [Explore the NeMo Fabric ecosystem](docs/about-nemo-fabric/ecosystem.mdx).
Expand Down
121 changes: 121 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 @@ -521,6 +521,127 @@ 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:
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