Skip to content

fix(agent): sync logging session context on compaction id rotation - #41726

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-8e3e1b3d
Jun 8, 2026
Merged

teknium1 merged 2 commits into
mainfrom
hermes/hermes-8e3e1b3d

Conversation

@teknium1

@teknium1 teknium1 commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

After a mid-turn context compaction, log lines now carry the new session id instead of the stale pre-rotation one — closing the log-correlation gap from #34089.

When compress_context rotates agent.session_id, it already moves the gateway/tools session context (gateway.session_context.set_current_session_id → ContextVar + HERMES_SESSION_ID env). But the [session_id] tag on log lines comes from a separate mechanism — hermes_logging._session_context (a threading.local read by the global LogRecord factory, pinned once per turn in the conversation loop). The rotation block never updated it, so post-compaction log lines in the same turn kept the old id while the message body / session DB / gateway state carried the new one. That is exactly the "started session=X, done session=Y" ambiguity in the #34089 evidence.

Changes

  • agent/conversation_compression.py: after the rotation updates the gateway/tools context, also call hermes_logging.set_session_context(agent.session_id), guarded separately so a logging failure can never regress the routing update.
  • tests/agent/test_compression_logging_session_context.py: regression test asserting the logging context follows the rotation.

Validation

Before After
Log [session_id] tag after mid-turn compaction stale parent id rotated child id
tests/agent/test_compression_logging_session_context.py 1 passed

Salvaged from #39994 by @JimStenstrom; original commit cherry-picked with authorship preserved. AUTHOR_MAP entry added.

Infographic

Session-id stability across compaction

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Jun 8, 2026
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-8e3e1b3d vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10074 on HEAD, 10073 on base (🆕 +1)

🆕 New issues (1):

Rule Count
unresolved-attribute 1
First entries
tests/agent/test_compression_logging_session_context.py:52: [unresolved-attribute] unresolved-attribute: Unresolved attribute `context_compressor` on type `AIAgent`

✅ Fixed issues: none

Unchanged: 5225 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1
teknium1 force-pushed the hermes/hermes-8e3e1b3d branch from 811fc1a to cae83f6 Compare June 8, 2026 05:10
JimStenstrom and others added 2 commits June 7, 2026 22:21
When context compaction rotates agent.session_id, it updates the gateway/tools
session context (set_current_session_id -> HERMES_SESSION_ID env + ContextVar)
but never updates the separate logging session context. The [session_id] tag on
log lines comes from hermes_logging._session_context (set once per turn in
conversation_loop.py), so post-compaction log lines in the same turn carry the
STALE old id while the message/DB/gateway state carry the new one — breaking log
correlation exactly at the compaction boundary.

Call hermes_logging.set_session_context(agent.session_id) alongside the existing
set_current_session_id, guarded so a logging failure can't regress the routing
update. Logs-only; no runtime or caching impact.

Refs #34089
@teknium1
teknium1 force-pushed the hermes/hermes-8e3e1b3d branch from cae83f6 to 1bd5dff Compare June 8, 2026 05:21
@teknium1
teknium1 merged commit 39c4ac3 into main Jun 8, 2026
23 checks passed
@teknium1
teknium1 deleted the hermes/hermes-8e3e1b3d branch June 8, 2026 05:30
fabiosiqueira added a commit to fabiosiqueira/hermes-engine that referenced this pull request Aug 17, 2026
`interruptible_streaming_api_call` runs the provider call on a worker
thread — `_call` for the OpenAI/Anthropic path, `_bedrock_call` for
Bedrock Converse. The `[session]` tag every log line carries comes from a
`threading.local` in `hermes_logging`, and `_context_thread_target`
carries the caller's ContextVars across the boundary but cannot carry a
thread-local. Both workers therefore start unbound and everything they
log is formatted without a session tag.

The cost lands where it hurts most: each worker is where its own stream
failure is logged, so in a process serving concurrent sessions the one
line that carries the cause is the one line that names no session —

    INFO agent.chat_completion_helpers: Streaming failed before delivery:
    'dict' object has no attribute 'model_dump'

Bind the agent's session id onto both workers. Sourced from
`agent.session_id` rather than inherited from the calling thread: the
agent is the ground truth, and a caller that reached streaming without
going through `turn_context` is itself unbound. Best effort — a
session-less agent leaves the thread unbound instead of raising inside
the worker.

The tests drive the real `interruptible_streaming_api_call` on both
paths and assert on a record emitted from inside each worker, with the
calling thread deliberately left unbound; placement is the defect, so
exercising the helper alone would not catch a worker that was never
bound.

Same class of gap as NousResearch#41726, which synced the session context on
compaction id rotation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants