mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-16 20:50:18 +00:00
4fc51072a2
The streaming worker called meter().begin_session(stream_id) but never a paired end_session. A turn that produced zero output tokens (pre-flight cancel, or a setup exception before the first token) left a _SessionMeter in GlobalMeter._sessions forever: get_stats() only prunes sessions with first_token_ts > 0, so a zero-token session is never reclaimed, and each leaked entry inflates the `active` count sent over the SSE metering event. begin_session() and the metering ticker's .start() are now registered as the first statements inside the worker's outer `try`, and paired with an idempotent meter().end_session(stream_id, 0) plus a deterministic _metering_stop.set() in that try's outer `finally` (the same block that pops STREAMS/CANCEL_FLAGS) — so every exit path tears the session down. Deferring .start() until after put() is defined also closes a latent start-before-put ordering window in the ticker closure. The metering payload is unchanged; end_session only pops the session. Verification: tests/test_metering_session_lifecycle.py (leak reproduction, end_session reclaim, idempotency, begin->cancel-before-token->empty) plus a plain-assert run on python3.11 (conftest gates pytest to 3.11-3.13; system python is 3.14). py_compile clean. WARNING streaming-contract: adjusts the streaming worker's begin/end teardown ordering. Needs RFC/contract review against docs/rfcs/ (session-sse-contract-v1.md) before merge — draft PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>