Spaces:
Paused
Paused
| """ | |
| Session-scoped context variables for the Hermes gateway. | |
| Replaces the previous ``os.environ``-based session state | |
| (``HERMES_SESSION_PLATFORM``, ``HERMES_SESSION_CHAT_ID``, etc.) with | |
| Python's ``contextvars.ContextVar``. | |
| **Why this matters** | |
| The gateway processes messages concurrently via ``asyncio``. When two | |
| messages arrive at the same time the old code did: | |
| os.environ["HERMES_SESSION_THREAD_ID"] = str(context.source.thread_id) | |
| Because ``os.environ`` is *process-global*, Message A's value was | |
| silently overwritten by Message B before Message A's agent finished | |
| running. Background-task notifications and tool calls therefore routed | |
| to the wrong thread. | |
| ``contextvars.ContextVar`` values are *task-local*: each ``asyncio`` | |
| task (and any ``run_in_executor`` thread it spawns) gets its own copy, | |
| so concurrent messages never interfere. | |
| **Backward compatibility** | |
| The public helper ``get_session_env(name, default="")`` mirrors the old | |
| ``os.getenv("HERMES_SESSION_*", ...)`` calls. Existing tool code only | |
| needs to replace the import + call site: | |
| # before | |
| import os | |
| platform = os.getenv("HERMES_SESSION_PLATFORM", "") | |
| # after | |
| from gateway.session_context import get_session_env | |
| platform = get_session_env("HERMES_SESSION_PLATFORM", "") | |
| """ | |
| from contextvars import ContextVar | |
| # --------------------------------------------------------------------------- | |
| # Per-task session variables | |
| # --------------------------------------------------------------------------- | |
| _SESSION_PLATFORM: ContextVar[str] = ContextVar("HERMES_SESSION_PLATFORM", default="") | |
| _SESSION_CHAT_ID: ContextVar[str] = ContextVar("HERMES_SESSION_CHAT_ID", default="") | |
| _SESSION_CHAT_NAME: ContextVar[str] = ContextVar("HERMES_SESSION_CHAT_NAME", default="") | |
| _SESSION_THREAD_ID: ContextVar[str] = ContextVar("HERMES_SESSION_THREAD_ID", default="") | |
| _SESSION_USER_ID: ContextVar[str] = ContextVar("HERMES_SESSION_USER_ID", default="") | |
| _SESSION_USER_NAME: ContextVar[str] = ContextVar("HERMES_SESSION_USER_NAME", default="") | |
| _SESSION_KEY: ContextVar[str] = ContextVar("HERMES_SESSION_KEY", default="") | |
| _VAR_MAP = { | |
| "HERMES_SESSION_PLATFORM": _SESSION_PLATFORM, | |
| "HERMES_SESSION_CHAT_ID": _SESSION_CHAT_ID, | |
| "HERMES_SESSION_CHAT_NAME": _SESSION_CHAT_NAME, | |
| "HERMES_SESSION_THREAD_ID": _SESSION_THREAD_ID, | |
| "HERMES_SESSION_USER_ID": _SESSION_USER_ID, | |
| "HERMES_SESSION_USER_NAME": _SESSION_USER_NAME, | |
| "HERMES_SESSION_KEY": _SESSION_KEY, | |
| } | |
| def set_session_vars( | |
| platform: str = "", | |
| chat_id: str = "", | |
| chat_name: str = "", | |
| thread_id: str = "", | |
| user_id: str = "", | |
| user_name: str = "", | |
| session_key: str = "", | |
| ) -> list: | |
| """Set all session context variables and return reset tokens. | |
| Call ``clear_session_vars(tokens)`` in a ``finally`` block to restore | |
| the previous values when the handler exits. | |
| Returns a list of ``Token`` objects (one per variable) that can be | |
| passed to ``clear_session_vars``. | |
| """ | |
| tokens = [ | |
| _SESSION_PLATFORM.set(platform), | |
| _SESSION_CHAT_ID.set(chat_id), | |
| _SESSION_CHAT_NAME.set(chat_name), | |
| _SESSION_THREAD_ID.set(thread_id), | |
| _SESSION_USER_ID.set(user_id), | |
| _SESSION_USER_NAME.set(user_name), | |
| _SESSION_KEY.set(session_key), | |
| ] | |
| return tokens | |
| def clear_session_vars(tokens: list) -> None: | |
| """Restore session context variables to their pre-handler values.""" | |
| if not tokens: | |
| return | |
| vars_in_order = [ | |
| _SESSION_PLATFORM, | |
| _SESSION_CHAT_ID, | |
| _SESSION_CHAT_NAME, | |
| _SESSION_THREAD_ID, | |
| _SESSION_USER_ID, | |
| _SESSION_USER_NAME, | |
| _SESSION_KEY, | |
| ] | |
| for var, token in zip(vars_in_order, tokens): | |
| var.reset(token) | |
| def get_session_env(name: str, default: str = "") -> str: | |
| """Read a session context variable by its legacy ``HERMES_SESSION_*`` name. | |
| Drop-in replacement for ``os.getenv("HERMES_SESSION_*", default)``. | |
| Resolution order: | |
| 1. Context variable (set by the gateway for concurrency-safe access) | |
| 2. ``os.environ`` (used by CLI, cron scheduler, and tests) | |
| 3. *default* | |
| """ | |
| import os | |
| var = _VAR_MAP.get(name) | |
| if var is not None: | |
| value = var.get() | |
| if value: | |
| return value | |
| # Fall back to os.environ for CLI, cron, and test compatibility | |
| return os.getenv(name, default) | |