monitoring: scrape neuron /metrics, and give it a request counter #328
Open
opened 2026-09-22 20:07:08 +00:00 by grenade
·
1 comment
No Branch/Tag Specified
main
feat/397-replicas
feat/385-acestep-memory
fix/376-models-unhealthy-node
feat/music-surprise-genres
feat/web-decisions-route
fix/cold-load-serialise
fix/music-hide-stale-on-compose
feat/audio-disk-guard
feat/music-studio-ux
fix/cortex-glibc-floor
feat/music-history-sidebar
feat/audio-flac-mp3
feat/393-heartmula-serving
feat/391-heartmula-port
feat/387-acestep-rollout
feat/383-acestep-engine
feat/390-heartmula-fixtures
feat/381-acestep-fixtures
feat/402-music-ui
feat/386-audio-rates
feat/382-audio-job-api
feat/356-systemone-playground
fix/bench-skip-decision-models
feat/341-laya-residency
feat/341-laya-rollout
feat/337-laya-perf
feat/338-neuron-systemone
feat/335-laya-prompt-builder
feat/336-laya-engine
feat/339-laya-lang-detect
feat/338-systemone-surface
feat/334-laya-reference-fixtures
feat/96-mtp-probe
fix/324-qsa-visible-set
feat/310-ple-random-advice
feat/neuron-phase-timing
fix/qwen4-ple-conv-launch-bound
feat/318-per-layer-activation-hop
fix/318-release-host-memory
feat/318-per-model-max-in-flight
fix/318-logits-last-position
fix/318-cached-experts-residency
feat/318-host-source-tensors
feat/318-host-resident-experts
fix/322-cache-telemetry
feat/322-isq-cache
test/323-parity-long-prompt
fix/323-layer-type-alias
fix/323-qsa-scores-in-f32
feat/310-mmap-ngram-table
fix/quant-fallback-match-llamacpp
fix/qwen4-exp-expert-row-width
fix/qwen4-exp-load-reporting-and-pool-trim
fix/320-isq-reachable-and-log-levels
feat/qwen4-exp-quantised-experts
feat/qwen4-exp-expert-bank
feat/qwen4-exp-mtp-head
docs/mtp-wiring-settled
feat/qwen4-exp-snapshot
feat/rustingface-always
feat/qwen4-exp-harness
feat/qwen4-exp-qsa-oracle
feat/qwen4-exp-model
feat/qwen4-exp-decoder
feat/qwen4-exp-linear-attn
feat/qwen4-exp-moe
feat/qwen4-exp-full-attn
feat/qwen4-exp-qsa-indexer
feat/qwen4-exp-config
feat/qwen4-exp-qsa-selection
feat/qwen4-exp-ple-consumption
fix/seed-cutlass-anonymously
feat/qwen4-exp-ple-hashing
fix/seed-cutlass-in-cuda-ci
feat/hf-endpoint-env
feat/qwen4-exp-hyper-connections
feat/qwen4-exp-norm-primitives
fix/alias-test-temp-catalogue-collision
chore/retire-vestigial-reasoning-ladder
fix/context-limit-honours-kv-budget
feat/server-advertised-reasoning-ladder
feat/web-chat-collapsible-working
fix/router-sync-restarts-only-on-settings-change
feat/ci-owns-the-router-config
fix/infra-setup-ships-ratelimit-to-gateway
fix/nginx-preflight-checks-every-grant
fix/restore-max-in-flight-8
fix/retire-qwen36-27b
fix/nginx-sync-must-not-block-cortex-deploy
feat/nginx-search-alias-and-sync
fix/wire-surface-in-neuron-spans
fix/295-ignore-tiny-prefill-samples
fix/300-anthropic-streaming-thinking
fix/300-return-the-models-reasoning
feat/operator-repetition-knobs
fix/292-prefix-cache-budget
feat/stamp-preserve-thinking-state
fix/296-one-step-one-assistant-turn
feat/preserve-thinking-ab-gate
fix/client-capability-misattributions
fix/291-max-in-flight-and-config-only-ci
feat/290-reasoning-rungs-from-the-model
fix/284-gdn-state-f32
feat/bench-measurement-regimes
feat/285-nonstreaming-through-engine
fix/288-bench-authenticates
feat/283-operator-sampling-override
fix/286-clippy-all-targets
fix/replay-only-the-latest-reasoning
fix/developer-role-maps-to-system
fix/223-reasoning-effort-off
fix/fetch-the-models-own-prompt-format
fix/278-advertise-the-real-limits
fix/277-responses-reasoning-roundtrip
fix/272-sampling-gaps
fix/clippy-1.98-result-large-err
feat/sampling-defaults
fix/streaming-completion-log
fix/prometheus-histogram-buckets
fix/responses-reasoning-events
fix/stream-reasoning-content
fix/cortex-stream-read-timeout
fix/sse-keepalive-during-prefill
fix/anonymous-yields-to-authenticated
fix/upstream-test-fixture-collisions
fix/docs-directory-index
feat/angels-internal-vhost
v0.1.16
v0.1.15
v0.1.14
v0.1.12
v0.1.11
v0.1.10
v0.1.9
v0.1.8
v0.1.7
v0.1.6
v0.1.5
v0.1.4
v0.1.3
v0.1.2
v0.1.1
v0.1.0
Labels
Clear labels
out-of-scope
p1-now
p2-next
p3-later
Outside helexa's constraining principles: near-frontier models on consumer hardware, served transparently and predictably
Active pain or unblocking work — pick up first
Engine levers — start once P1 baselines exist
Dependent on earlier work, or demand-driven
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: helexa/helexa#328
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two gaps, found while trying to answer "how many requests has beast served in the last 15 minutes"
1. The neuron scrape job exists in the repo but is not applied.
asset/monitoring/prometheus-neuron.scrape.ymllanded with5458b285("feat(neuron): serve /metrics, with the decode phases as histograms") and carries its own rationale for why cortex's 10s-sampled gauges cannot answer where a request's time goes. But Prometheus on golgafrinchans has only ever scraped cortex:So every
neuron_*series — the decode-phase histograms the commit was written for — exists on each host and is collected by nobody. The file says it needs no new firewalld rule (13131 is already open to the control plane), so applying it is: append the job to/etc/prometheus/prometheus.yml,curl -X POST http://localhost:26559/-/reload. Worth doing through whatever manages that host's config rather than by hand, so it survives the next rebuild.2. neuron's
/metricshas no request counter.Today it publishes only:
neuron_prefill_seconds,neuron_decode_seconds(histograms)neuron_decode_phase_seconds{phase=forward|sample|emit|residual}neuron_completion_tokens_totalThere is no "requests received" or "responses sent". The histograms'
_countis a usable proxy for completed requests, but only for requests that reached decode, and it says nothing about requests that were rejected, refused for context length, or are still in flight.cortex_requests_total{node,model}counts only what cortex proxied — it misseshelexa-bench, direct probes, and anything else that talks to 13131. Today the only complete answer is counting lines in the journal:which is also a trap: a TP request logs its completion twice, so the naive grep double-counts.
Proposal
Add to neuron's exporter, labelled
{model, wire}(openai-chat/openai-responses/images) so the split that the journal shows is queryable:neuron_requests_total— admitted requests, incremented at the start of serving;neuron_responses_total{outcome}—stop/length/tool_calls/error/client_gone; the finish reason is already in the completion log line;neuron_rejections_total{reason}— matching the/healthcounters (queue_full,wait_timeout,per_principal,anon_yield,kv_timeout,kv_unservable) pluscontext_length_exceeded, which currently exists only as a WARN;neuron_in_flight/neuron_queue_depthgauges, mirroring/healthso the same numbers are queryable over time without cortex in the path.Then
sum(increase(neuron_requests_total{instance=~"beast.*"}[15m]))is the answer, and it covers every caller rather than only cortex-proxied ones.Acceptance
/metricson a serving neuron exposes the counters above, and they agree with the journal over the same window (the count that motivated this: 10 started / 10 completed / 0 rejected in 15 min, 5openai-chat+ 5openai-responses).neuron_requests_totalexactly once (the double-logging trap above).neuron_rejections_totaland notneuron_responses_total.upfor every reachable neuron.Related
tok_s_decodeon/healthcan report nonsense (18,191 tok/s observed 2026-09-22); anything built on the live decode EMA inherits that until it is fixed.🤖 Generated with Claude Code
https://claude.ai/code/session_01XViS3RVfGTRaRSopLN68MA
Two more observability gaps from the same session, both "the neuron knows this and never says it". Filing here rather than separately because they are the same surface as the counters above — what a request was, not just how many there were.
1. The resolved reasoning effort is never logged
apply_effort_kwarg(crates/neuron/src/harness/candle.rs:6348) readsextra.reasoning.effort, snaps it to a rung the model's template accepts, and putsreasoning_effortin the template kwargs. It logs only the interesting-but-rare cases: an INFO when the requested effort had to be mapped to a neighbouring rung, and a DEBUG when there is no comparable rung. The ordinary case — request askedmedium, model gotmedium— logs nothing, and neither does the request-start line, which carriestemperature,top_p,top_k,seedandmax_newbut not effort.So "what effort did this request actually run at?" cannot be answered from the journal. Answering it took reading four files across pi, pi-ai and neuron to prove the value survives each hop, then an A/B against the live host to prove the knob reaches the template:
(One sample each on a question with nothing to think about, so this shows the knob is wired, nothing more.)
It also matters because the template's own default is the strongest rung (
xhighforQwen/Qwen3.8-27B), so a caller that sends nothing silently gets the most expensive setting. Whether that happened is currently unanswerable after the fact.Proposal: add the resolved effort to the existing
TP chat_completion (stream): startingINFO line —effort=medium(oreffort=default:xhighwhen the caller sent none and the template's default applies). One field, no new line, and it makes the reasoning-token totals interpretable.2. A request's principal is not in the neuron journal
Identifying which client was sending a recurring workload meant grepping cortex's TRACE-level inbound bodies on the gateway host, because neuron's log lines carry
req_id,modelandwirebut nothing about who the caller is. cortex already stamps principal headers for fair-share admission (#54) and neuron already parses them —principalnever reaches the log.The body grep worked, but TRACE bodies are a blunt instrument: they log entire request payloads, including base64 image data, to the journal of the gateway.
Proposal: add
principal(account/key, already available at the admission boundary) to the request-start and request-done lines, and to theneuron_requests_totallabels proposed above if cardinality allows — a per-principal request count is exactly what "who is my second tenant" needs.Nonefor direct/unauthenticated requests, as today.🤖 Generated with Claude Code
https://claude.ai/code/session_01XViS3RVfGTRaRSopLN68MA