feat(observability): export live per-neuron:model capacity metrics (#137 T1+T2) #153

Merged
grenade merged 4 commits from feat/137-capacity-metrics-t1 into main 2026-07-09 14:51:47 +00:00
Owner

Closes the #137 observability gap (T1 + T2). Every capacity signal cortex already had in hand but discarded is now on the Prometheus surface, plus the throughput and load-shedding numbers.

What lands

T1 — live load + GPU headroom (previously polled for routing, then dropped)

  • cortex_model_{in_flight,queue_depth,max_in_flight,max_queue_depth}{node,model} — saturation = in_flight / max_in_flight
  • cortex_device_{vram_used_mb,vram_free_mb,utilization_pct,temp_c}{node,device}

T2 — throughput + backpressure

  • cortex_model_tok_s_{prefill,decode}{node,model} — live tokens/sec EMA, folded in at every serving finish site (single-GPU worker, TP task, all four batch-engine finish_slot sites), so decode tok/s is honest under real concurrency, not just batch-1
  • cortex_model_rejections_total{node,model,reason} — queue_full / wait_timeout / per_principal, the load-shedding signal
  • non-streaming Anthropic path now emits the same token + tok/s metrics as the streaming proxy (#6)

Notes

  • Pure observation — no change to admission/batching behaviour.
  • All ModelLoad additions are #[serde(default)] for rolling-deploy back-compat (a pre-#137 neuron simply reports 0 / "unknown").
  • Four validation jobs green through the CUDA type-check (validates the TP-gated tok/s paths).
  • T3 (helexa-bench concurrency sweep + knee report + bench-UI endpoint) is a separate branch — it's a distinct subsystem and doesn't block this deploy.

🤖 Generated with Claude Code

Closes the #137 observability gap (T1 + T2). Every capacity signal cortex already had in hand but discarded is now on the Prometheus surface, plus the throughput and load-shedding numbers. ## What lands **T1 — live load + GPU headroom** (previously polled for routing, then dropped) - `cortex_model_{in_flight,queue_depth,max_in_flight,max_queue_depth}{node,model}` — saturation = in_flight / max_in_flight - `cortex_device_{vram_used_mb,vram_free_mb,utilization_pct,temp_c}{node,device}` **T2 — throughput + backpressure** - `cortex_model_tok_s_{prefill,decode}{node,model}` — live tokens/sec EMA, folded in at every serving finish site (single-GPU worker, TP task, all four batch-engine finish_slot sites), so decode tok/s is honest under real concurrency, not just batch-1 - `cortex_model_rejections_total{node,model,reason}` — queue_full / wait_timeout / per_principal, the load-shedding signal - non-streaming Anthropic path now emits the same token + tok/s metrics as the streaming proxy (#6) ## Notes - Pure observation — no change to admission/batching behaviour. - All `ModelLoad` additions are `#[serde(default)]` for rolling-deploy back-compat (a pre-#137 neuron simply reports 0 / "unknown"). - Four validation jobs green through the CUDA type-check (validates the TP-gated tok/s paths). - T3 (helexa-bench concurrency sweep + knee report + bench-UI endpoint) is a separate branch — it's a distinct subsystem and doesn't block this deploy. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
grenade added 4 commits 2026-07-09 14:51:36 +00:00
feat(observability): export live per-neuron:model load + device health (#137 T1)
Some checks failed
CI / CUDA type-check (push) Waiting to run
CI / Format (push) Successful in 7s
CI / Clippy (push) Successful in 2m38s
CI / Test (push) Successful in 6m12s
CI / Build cortex SRPM (push) Has been cancelled
CI / Build neuron SRPM (push) Has been cancelled
CI / Publish cortex to COPR (push) Has been cancelled
CI / Publish neuron to COPR (push) Has been cancelled
CI / Bump version in source (push) Has been cancelled
ddf1a8d7f5
Tier 1 of #137: publish the capacity signals cortex already polls from
neuron /health but discarded. Pure plumbing — no new polling, no change
to admission/batching behaviour.

neuron:
- AdmissionController gains max_in_flight()/max_queue_depth() getters and
  LoadedHandle::capacity(); load_snapshot() now advertises the ceiling
  alongside the live in_flight/queue_depth on /health.

cortex-core:
- ModelLoad carries max_in_flight/max_queue_depth (#[serde(default)] for
  rolling-deploy back-compat; 0 = pre-#137 neuron = "unknown").

cortex-gateway:
- poll_health exports gauges instead of dropping the data:
  cortex_model_{in_flight,queue_depth,max_in_flight,max_queue_depth}
  {node,model}, and cortex_device_{vram_used_mb,vram_free_mb,
  utilization_pct,temp_c}{node,device} (h.devices was previously discarded).
- saturation is now graphable as in_flight / max_in_flight.

Tests: ModelLoad back-compat parse; capacity gauges rendered from a
health poll with node/model/device labels and values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GaW6F2j3zsQ6yABKPsXXe
feat(observability): per-model admission rejection counters (#137 T2)
All checks were successful
CI / CUDA type-check (push) Successful in 12m13s
CI / Format (push) Successful in 8s
CI / Clippy (push) Successful in 2m21s
CI / Test (push) Successful in 5m51s
CI / Build cortex SRPM (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
62a5ef66f1
Adds the load-shedding signal. AdmissionController tallies rejections by
reason (queue_full / wait_timeout / per_principal) with lock-free atomics,
counted since the model loaded. Surfaced on /health via ModelLoad and
exported by cortex as cortex_model_rejections_total{node,model,reason}
(counter via .absolute — a model reload resets to 0, read as a normal
Prometheus counter reset).

This is the definitive "at capacity / shedding" metric: a rising
wait_timeout/queue_full rate means the neuron:model is turning work away.

Tests: admission unit test asserts a refused request tallies under
queue_full only; the cortex export test asserts the counter renders with
the reason label and value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GaW6F2j3zsQ6yABKPsXXe
feat(observability): live per-model tokens/sec (prefill + decode) (#137 T2)
All checks were successful
CI / Format (push) Successful in 9s
CI / Clippy (push) Successful in 2m19s
CI / Test (push) Successful in 6m12s
CI / CUDA type-check (push) Successful in 12m36s
CI / Build cortex SRPM (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
9a3d89304d
The headline capacity number: live decode throughput per neuron:model.

Evolves context_limit::PrefillRateEma into ThroughputEma — a dual
prefill+decode tokens/sec EMA (type alias keeps the ~15 prefill call
sites, incl. cuda-gated, unchanged). Decode is folded in at every finish
site the model already routes through: the single-GPU device-worker path,
the TP orchestration task, and all four batch-engine finish_slot sites
(engine derives the model's rate object from the active backend/session,
so Single and TP both record into the same tracker /health reads). The
pure-CPU arch.forward fallback has no rate object and is left untracked
(not a served-throughput number).

Surfaced on /health via ModelLoad.{tok_s_prefill,tok_s_decode} and
exported by cortex as gauges cortex_model_tok_s_{prefill,decode}{node,model}.

Tests: ThroughputEma decode EMA tracks independently of prefill and
ignores degenerate samples; cortex export test asserts both tok/s gauges
render with the polled values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GaW6F2j3zsQ6yABKPsXXe
feat(observability): emit token + tok/s metrics on non-streaming Anthropic path (#137 T2/#6)
All checks were successful
CI / Format (push) Successful in 9s
CI / Clippy (push) Successful in 2m17s
CI / Test (push) Successful in 6m7s
CI / CUDA type-check (push) Successful in 12m38s
CI / Build cortex SRPM (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
CI / Format (pull_request) Successful in 7s
CI / Clippy (pull_request) Successful in 2m50s
CI / Test (pull_request) Successful in 9m27s
CI / CUDA type-check (pull_request) Successful in 12m39s
CI / Build cortex SRPM (pull_request) Has been skipped
CI / Publish cortex to COPR (pull_request) Has been skipped
CI / Build neuron SRPM (pull_request) Has been skipped
CI / Publish neuron to COPR (pull_request) Has been skipped
CI / Bump version in source (pull_request) Has been skipped
d6e98e03b8
The /v1/messages non-streaming branch buffered the full response and
emitted only spend metering — none of the per-model cortex_prompt_tokens_total
/ cortex_completion_tokens_total / cortex_tokens_per_second the streaming
proxy emits. Closes that gap: usage is scanned once from the raw body
(deduping the prior metering-only scan) and drives both the per-model
metrics (tok/s over the full request duration, mirroring the streaming
path's non-stream fallback) and metering settle.

Test: a non-streaming Anthropic request now renders the token + tok/s
metric families.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GaW6F2j3zsQ6yABKPsXXe
grenade merged commit b2b4545151 into main 2026-07-09 14:51:47 +00:00
grenade deleted branch feat/137-capacity-metrics-t1 2026-07-09 14:51:47 +00:00
Sign in to join this conversation.