Conversation
The end-to-end _build_api_kwargs parity test asserted the Nous Portal tags exactly equal the base two-tag list. With the per-session conversation tag, a real agent (which has a session_id) now emits a third `conversation=<session_id>` tag. Assert against nous_portal_tags(session_id=agent.session_id) so the check stays exact.
Update max-iteration summary assertions to include the agent session ID now attached to Nous Portal requests.
teknium1
added a commit
that referenced
this pull request
Jul 16, 2026
… calls Extends the conversation=<id> Portal tag (salvaged from PR #65183 by @J-SUPHA) from main-loop-only to every LLM call in a conversation: - agent/portal_tags.py: ContextVar-based conversation context. nous_portal_tags() falls back to the ambient id when no explicit session_id is passed, so every aux tag site (auxiliary_client, chat_completion_helpers summary path, web_tools) inherits the tag with zero per-call-site plumbing. Ambient id wins over explicit per-segment ids since it carries the lineage root. - hermes_state.py: SessionDB.get_conversation_root() — public wrapper over the lineage walk; returns the ROOT session id, so one user-facing conversation keeps a single conversation= value across context-compression rotation, and delegate subagent trees tag as their parent conversation. - run_agent.py: run_conversation() publishes the root id for the turn and resets it in finally. _conversation_root_id() resolves via _parent_session_id for subagents. - agent/moa_loop.py: MoA reference fan-out workers now run under propagate_context_to_thread so advisor slots attribute to the acting conversation (also fixes approval-callback propagation on that path). - agent/title_generator.py: bare title thread republishes the context from its session id (spawned after turn reset). Tests: ContextVar semantics, cross-context isolation, thread-hop propagation, lineage-root resolution incl. cycle guard.
Contributor
|
Merged via PR #65468 with your commits cherry-picked onto current main — your authorship is preserved in git history. Thanks for the clean, well-tested contribution! We extended your work in the same PR to cover the known limitations you called out in the description: a ContextVar-based ambient conversation context now propagates the |
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
… calls Extends the conversation=<id> Portal tag (salvaged from PR NousResearch#65183 by @J-SUPHA) from main-loop-only to every LLM call in a conversation: - agent/portal_tags.py: ContextVar-based conversation context. nous_portal_tags() falls back to the ambient id when no explicit session_id is passed, so every aux tag site (auxiliary_client, chat_completion_helpers summary path, web_tools) inherits the tag with zero per-call-site plumbing. Ambient id wins over explicit per-segment ids since it carries the lineage root. - hermes_state.py: SessionDB.get_conversation_root() — public wrapper over the lineage walk; returns the ROOT session id, so one user-facing conversation keeps a single conversation= value across context-compression rotation, and delegate subagent trees tag as their parent conversation. - run_agent.py: run_conversation() publishes the root id for the turn and resets it in finally. _conversation_root_id() resolves via _parent_session_id for subagents. - agent/moa_loop.py: MoA reference fan-out workers now run under propagate_context_to_thread so advisor slots attribute to the acting conversation (also fixes approval-callback propagation on that path). - agent/title_generator.py: bare title thread republishes the context from its session id (spawned after turn reset). Tests: ContextVar semantics, cross-context isolation, thread-hop propagation, lineage-root resolution incl. cycle guard.
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
… calls Extends the conversation=<id> Portal tag (salvaged from PR NousResearch#65183 by @J-SUPHA) from main-loop-only to every LLM call in a conversation: - agent/portal_tags.py: ContextVar-based conversation context. nous_portal_tags() falls back to the ambient id when no explicit session_id is passed, so every aux tag site (auxiliary_client, chat_completion_helpers summary path, web_tools) inherits the tag with zero per-call-site plumbing. Ambient id wins over explicit per-segment ids since it carries the lineage root. - hermes_state.py: SessionDB.get_conversation_root() — public wrapper over the lineage walk; returns the ROOT session id, so one user-facing conversation keeps a single conversation= value across context-compression rotation, and delegate subagent trees tag as their parent conversation. - run_agent.py: run_conversation() publishes the root id for the turn and resets it in finally. _conversation_root_id() resolves via _parent_session_id for subagents. - agent/moa_loop.py: MoA reference fan-out workers now run under propagate_context_to_thread so advisor slots attribute to the acting conversation (also fixes approval-callback propagation on that path). - agent/title_generator.py: bare title thread republishes the context from its session id (spawned after turn reset). Tests: ContextVar semantics, cross-context isolation, thread-hop propagation, lineage-root resolution incl. cycle guard.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds a per-session
conversation=<session_id>tag to Nous Portal inferencerequests so Portal-side usage can be attributed to a specific Hermes session.
Every Portal request already carries two product-attribution tags
(
product=hermes-agent,client=hermes-client-v<version>) built centrally inagent/portal_tags.py. The Nous provider profile already receives the agent'ssession_idbut discarded it. This PR threads that id into the tag list as anadditive, opt-in third tag, emitted only when a session id is present.
The approach keeps the single-source-of-truth pattern in
agent/portal_tags.py(no inlined literals) and is fully backward compatible:
nous_portal_tags()with no argument still returns the canonical two-tag list, so auxiliary/base
call sites are unchanged.
Related Issue
N/A (no tracking issue). Happy to open one if maintainers prefer.
Type of Change
Changes Made
agent/portal_tags.pyconversation_tag(session_id: str) -> strreturningconversation=<session_id>(mirrors the existinghermes_client_tag()factoring; documents that this tag is high-cardinality and opt-in).
nous_portal_tags(session_id: str | None = None)to append theconversation tag only when a truthy
session_idis provided. Defaultbehavior (no arg) is unchanged → still
["product=hermes-agent", "client=hermes-client-v<version>"].plugins/model-providers/nous/__init__.pyNousProfile.build_extra_body()now forwards itssession_idkwarg intothe tag list (alongside the existing
provider_preferenceshandling).tests/agent/test_portal_tags.pyand omission when it is
None/"".tests/providers/test_provider_profiles.pyTestNousProfile.test_tags_include_conversation_when_session_id.tests/run_agent/test_provider_parity.py_build_api_kwargsNous parity assertion to expectthe conversation tag when the agent has a
session_id.Scope / known limitations (intentional)
(compression, title generation, vision, web-extract, session search, kanban,
curator, etc.) build tags without a session id and remain untagged, so
per-conversation cost analytics built on this tag will undercount.
session_idrotates on context compression and/new, so a singleuser-facing conversation can emit multiple
conversation=values. The valueis the current session segment, not a stable end-to-end conversation id.
session_id(<timestamp>_<uuid>) is sent — never thegateway session key, which can embed PII (phone numbers / user ids).
How to Test
Point Hermes at the Nous Portal (
hermes model→ providernous, or setmodel.provider: nousin~/.hermes/config.yaml).Confirm the profile emits the tag when a session id is present:
Expected:
pytest tests/agent/test_portal_tags.py \ tests/providers/test_provider_profiles.py \ tests/run_agent/test_provider_parity.py::TestBuildApiKwargsNousPortal -qChecklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — docstrings inagent/portal_tags.pyupdated; no user-facing docs neededcli-config.yaml.exampleif I added/changed config keys — N/A (no new config keys)CONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/AScreenshots / Logs