refactor(gateway/slash): unify context-breakdown transcript load; tidy module headers and import order
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
"""Gateway slash-command handlers for GatewayRunner.
|
||||
|
||||
The in-session slash commands (/model, /reset, /usage, ...) dispatched from ``_handle_message``,
|
||||
lifted out of ``gateway/run.py`` into a mixin so every ``self._handle_*_command`` reference keeps
|
||||
working via the MRO. run.py helpers (``_hermes_home``, ``_load_gateway_config``, ...) are imported
|
||||
lazily inside handler bodies — a deferred ``from gateway.run import ...`` avoids the import cycle.
|
||||
The in-session slash commands dispatched from ``_handle_message``, lifted out of ``gateway/run.py``
|
||||
into a mixin so every ``self._handle_*_command`` reference keeps working via the MRO. Cohesive
|
||||
clusters live in sibling mixins this class inherits: ``slash_commands_model`` (/model, /reasoning,
|
||||
/fast, ...), ``slash_commands_session`` (/new, /resume, /branch, /compress, ...),
|
||||
``slash_commands_status`` (/status, /context, /usage, ...) and ``slash_commands_goals`` (/goal,
|
||||
/loop, /heartbeat, ...). This module keeps the shared helpers plus the remaining one-off commands.
|
||||
run.py helpers (``_hermes_home``, ``_load_gateway_config``, ...) are imported lazily inside handler
|
||||
bodies — a deferred ``from gateway.run import ...`` avoids the import cycle.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -24,25 +28,16 @@ from typing import Optional, Union
|
||||
from agent.i18n import t
|
||||
from gateway.config import HomeChannel, Platform, PlatformConfig, persist_home_channel
|
||||
from gateway.platforms.base import EphemeralReply, MessageEvent
|
||||
from gateway.session import (
|
||||
AsyncSessionStore,
|
||||
)
|
||||
from hermes_cli.config import atomic_config_write, cfg_get
|
||||
from utils import (
|
||||
atomic_json_write,
|
||||
is_truthy_value,
|
||||
)
|
||||
|
||||
from gateway.slash_commands_model import ( # noqa: F401 — _model_switch_skew_guard re-exported for callers/tests
|
||||
from gateway.session import AsyncSessionStore
|
||||
from gateway.slash_commands_goals import GatewayGoalCommandsMixin
|
||||
from gateway.slash_commands_model import ( # noqa: F401 — _model_switch_skew_guard re-exported for tests
|
||||
GatewayModelCommandsMixin,
|
||||
_model_switch_skew_guard,
|
||||
)
|
||||
|
||||
from gateway.slash_commands_session import GatewaySessionCommandsMixin
|
||||
|
||||
from gateway.slash_commands_status import GatewayStatusCommandsMixin
|
||||
|
||||
from gateway.slash_commands_goals import GatewayGoalCommandsMixin
|
||||
from hermes_cli.config import atomic_config_write, cfg_get
|
||||
from utils import atomic_json_write, is_truthy_value
|
||||
|
||||
logger = logging.getLogger("gateway.run")
|
||||
|
||||
@@ -190,8 +185,13 @@ def _home_thread_from_source(source) -> Optional[str]:
|
||||
return str(thread_id)
|
||||
|
||||
|
||||
class GatewaySlashCommandsMixin(GatewayModelCommandsMixin, GatewaySessionCommandsMixin, GatewayStatusCommandsMixin, GatewayGoalCommandsMixin):
|
||||
"""In-session slash-command handlers for GatewayRunner."""
|
||||
class GatewaySlashCommandsMixin(
|
||||
GatewayModelCommandsMixin,
|
||||
GatewaySessionCommandsMixin,
|
||||
GatewayStatusCommandsMixin,
|
||||
GatewayGoalCommandsMixin,
|
||||
):
|
||||
"""In-session slash-command handlers for GatewayRunner (plus the helpers the sibling mixins share)."""
|
||||
|
||||
async_session_store: AsyncSessionStore
|
||||
|
||||
@@ -320,7 +320,6 @@ class GatewaySlashCommandsMixin(GatewayModelCommandsMixin, GatewaySessionCommand
|
||||
adapter = self.adapters.get(platform) if getattr(self, "adapters", None) else None
|
||||
return getattr(adapter, "typed_command_prefix", "/") if adapter is not None else "/"
|
||||
|
||||
|
||||
async def _handle_profile_command(self, event: MessageEvent) -> str:
|
||||
"""Handle /profile — show the profile serving this source and its home.
|
||||
|
||||
@@ -523,7 +522,6 @@ class GatewaySlashCommandsMixin(GatewayModelCommandsMixin, GatewaySessionCommand
|
||||
await asyncio.to_thread(_sub)
|
||||
return True
|
||||
|
||||
|
||||
async def _handle_stop_command(self, event: MessageEvent) -> Union[str, EphemeralReply]:
|
||||
"""Handle /stop command - interrupt a running agent.
|
||||
|
||||
@@ -791,7 +789,6 @@ class GatewaySlashCommandsMixin(GatewayModelCommandsMixin, GatewaySessionCommand
|
||||
)
|
||||
return self._telegramized_command_reply(event, reply.text)
|
||||
|
||||
|
||||
async def _handle_set_home_command(self, event: MessageEvent) -> str:
|
||||
"""Handle /sethome command -- set the current chat as the platform's home channel."""
|
||||
from gateway.run import _home_target_env_var, _home_thread_env_var
|
||||
@@ -1183,7 +1180,6 @@ class GatewaySlashCommandsMixin(GatewayModelCommandsMixin, GatewaySessionCommand
|
||||
|
||||
return t("gateway.btw.started", preview=preview)
|
||||
|
||||
|
||||
async def _handle_memory_command(self, event: MessageEvent) -> str:
|
||||
"""Handle /memory — review pending memory writes + toggle the approval gate.
|
||||
|
||||
@@ -1247,7 +1243,6 @@ class GatewaySlashCommandsMixin(GatewayModelCommandsMixin, GatewaySessionCommand
|
||||
f"~/.hermes/pending/skills/{pending_id}.json)")
|
||||
return out
|
||||
|
||||
|
||||
async def _handle_approvals_command(self, event: MessageEvent) -> str:
|
||||
"""Show or persist the profile-wide dangerous-command approval mode."""
|
||||
from gateway.slash_access import policy_for_source
|
||||
@@ -1441,7 +1436,6 @@ class GatewaySlashCommandsMixin(GatewayModelCommandsMixin, GatewaySessionCommand
|
||||
example = t("gateway.footer.example_line", preview=preview)
|
||||
return t("gateway.footer.saved", state=state, example=example)
|
||||
|
||||
|
||||
async def _handle_reload_mcp_command(self, event: MessageEvent) -> Optional[str]:
|
||||
"""Handle /reload-mcp — reconnect MCP servers and rebuild the cached agent.
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import ...``) inside the bodies to avoid the import cycle.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from agent.i18n import t
|
||||
from gateway.platforms.base import MessageEvent, MessageType
|
||||
|
||||
|
||||
@@ -9,10 +9,11 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
|
||||
from agent.i18n import t
|
||||
from gateway.platforms.base import MessageEvent
|
||||
from hermes_cli.config import atomic_config_write, clear_model_endpoint_credentials
|
||||
from typing import Optional
|
||||
from utils import base_url_host_matches
|
||||
|
||||
# Log-record parity with gateway/run.py and the origin module.
|
||||
|
||||
@@ -13,12 +13,13 @@ import contextlib
|
||||
import dataclasses
|
||||
import os
|
||||
import shlex
|
||||
from typing import Optional, Union
|
||||
|
||||
from agent.i18n import t
|
||||
from agent.turn_context import extract_api_content_sidecar
|
||||
from gateway.config import Platform
|
||||
from gateway.platforms.base import EphemeralReply, MessageEvent, MessageType
|
||||
from gateway.session import SessionSource, build_session_key, is_shared_multi_user_session
|
||||
from typing import Optional, Union
|
||||
|
||||
# Log-record parity with gateway/run.py and the origin module.
|
||||
logger = logging.getLogger("gateway.run")
|
||||
|
||||
@@ -13,11 +13,12 @@ import hashlib
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from agent.account_usage import fetch_account_usage, render_account_usage_lines
|
||||
from agent.i18n import t
|
||||
from gateway.config import Platform
|
||||
from gateway.platforms.base import MessageEvent
|
||||
from typing import Any
|
||||
|
||||
# Log-record parity with gateway/run.py and the origin module.
|
||||
logger = logging.getLogger("gateway.run")
|
||||
@@ -610,20 +611,9 @@ class GatewayStatusCommandsMixin:
|
||||
Estimated (chars/4), same engine as /usage. Runs in a thread; returns [] and never raises.
|
||||
"""
|
||||
try:
|
||||
from agent.context_breakdown import (
|
||||
compute_context_details,
|
||||
compute_session_context_breakdown,
|
||||
render_context_breakdown_lines,
|
||||
)
|
||||
from agent.context_breakdown import compute_context_details, render_context_breakdown_lines
|
||||
|
||||
history: list[dict] = []
|
||||
try:
|
||||
entry = self.session_store.get_or_create_session(source)
|
||||
history = self.session_store.load_transcript(entry.session_id) or []
|
||||
except Exception:
|
||||
history = []
|
||||
|
||||
payload = compute_session_context_breakdown(agent, history)
|
||||
payload = self._session_context_breakdown(agent, source)
|
||||
if not (payload.get("categories") or []):
|
||||
return []
|
||||
|
||||
@@ -638,22 +628,25 @@ class GatewayStatusCommandsMixin:
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def _session_context_breakdown(self, agent, source) -> dict:
|
||||
"""Per-category context estimate (chars/4) for *agent* over the session transcript (sync)."""
|
||||
from agent.context_breakdown import compute_session_context_breakdown
|
||||
|
||||
history: list[dict] = []
|
||||
try:
|
||||
entry = self.session_store.get_or_create_session(source)
|
||||
history = self.session_store.load_transcript(entry.session_id) or []
|
||||
except Exception:
|
||||
history = []
|
||||
return compute_session_context_breakdown(agent, history)
|
||||
|
||||
def _context_breakdown_lines(self, agent, source) -> list[str]:
|
||||
"""Render the per-category context breakdown for /usage.
|
||||
|
||||
Estimated (chars/4). Returns [] and never raises so /usage stays robust.
|
||||
"""
|
||||
try:
|
||||
from agent.context_breakdown import compute_session_context_breakdown
|
||||
|
||||
history: list[dict] = []
|
||||
try:
|
||||
entry = self.session_store.get_or_create_session(source)
|
||||
history = self.session_store.load_transcript(entry.session_id) or []
|
||||
except Exception:
|
||||
history = []
|
||||
|
||||
payload = compute_session_context_breakdown(agent, history)
|
||||
payload = self._session_context_breakdown(agent, source)
|
||||
categories = payload.get("categories") or []
|
||||
if not categories:
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user