feat(status): restore model and context in gateway status

PROBLEM: The old public /status PR drifted out of the current Amy patch stack, leaving /status without the model/provider, context window, or explicit cumulative token label that Wolfram uses to monitor context pressure from chat.

SOLUTION: Re-port the feature onto the current gateway status handler. Prefer live/cached agent runtime metadata, fall back to SessionDB + SessionStore state between turns, add localized status model/context lines, and keep token totals explicitly labeled cumulative.

Verification: tests/gateway/test_status_command.py, tests/hermes_cli/test_commands.py
This commit is contained in:
Wolfram Ravenwolf
2026-06-15 07:46:34 -07:00
committed by Teknium
parent 5035fa9029
commit ead38107a2
19 changed files with 257 additions and 10 deletions
+119 -9
View File
@@ -394,20 +394,35 @@ class GatewaySlashCommandsMixin:
async def _handle_status_command(self, event: MessageEvent) -> str:
"""Handle /status command."""
from gateway.run import _AGENT_PENDING_SENTINEL, _load_gateway_config, _resolve_gateway_model
source = event.source
session_entry = self.session_store.get_or_create_session(source)
connected_platforms = [p.value for p in self.adapters.keys()]
# Check if there's an active agent
# Check if there's an active agent. Keep the sentinel distinct: a
# starting/pending run should not be treated as a fully usable agent for
# model/context display, but it still occupies the session slot.
session_key = session_entry.session_key
is_running = session_key in self._running_agents
agent = self._running_agents.get(session_key)
is_running = agent is not None and agent is not _AGENT_PENDING_SENTINEL
# Count pending /queue follow-ups (slot + overflow).
adapter = self.adapters.get(source.platform) if source else None
queue_depth = self._queue_depth(session_key, adapter=adapter)
def _clean_str(value: Any) -> str:
return value.strip() if isinstance(value, str) and value.strip() else ""
def _int_value(value: Any) -> int:
try:
return int(value)
except (TypeError, ValueError):
return 0
title = None
session_row: dict[str, Any] = {}
# Pull token totals from the SQLite session DB rather than the
# in-memory SessionStore. The agent's per-turn token deltas are
# persisted into sessions_db (run_agent.py), not into SessionEntry,
@@ -422,17 +437,106 @@ class GatewaySlashCommandsMixin:
title = None
try:
row = self._session_db.get_session(session_entry.session_id)
if row:
if isinstance(row, dict):
session_row = row
db_total_tokens = (
(row.get("input_tokens") or 0)
+ (row.get("output_tokens") or 0)
+ (row.get("cache_read_tokens") or 0)
+ (row.get("cache_write_tokens") or 0)
+ (row.get("reasoning_tokens") or 0)
_int_value(row.get("input_tokens"))
+ _int_value(row.get("output_tokens"))
+ _int_value(row.get("cache_read_tokens"))
+ _int_value(row.get("cache_write_tokens"))
+ _int_value(row.get("reasoning_tokens"))
)
except Exception:
db_total_tokens = 0
# Resolve model/context for cockpit-style status. Prefer the live or
# cached agent because it carries the actual runtime route and context
# compressor. Fall back to persisted SessionDB metadata plus the
# SessionStore's last_prompt_tokens so /status remains useful between
# turns without making billing/account calls.
status_agent = agent if is_running else None
if status_agent is None:
cache_lock = getattr(self, "_agent_cache_lock", None)
cache = getattr(self, "_agent_cache", None)
if cache_lock is not None and cache is not None:
try:
with cache_lock:
cached = cache.get(session_key)
if cached:
status_agent = cached[0]
except Exception:
status_agent = None
model_name = ""
provider_name = ""
base_url = ""
context_used = 0
context_total = 0
if status_agent is not None and status_agent is not _AGENT_PENDING_SENTINEL:
model_name = _clean_str(getattr(status_agent, "model", ""))
provider_name = _clean_str(getattr(status_agent, "provider", ""))
base_url = _clean_str(getattr(status_agent, "base_url", ""))
ctx = getattr(status_agent, "context_compressor", None)
if ctx is not None:
context_used = _int_value(getattr(ctx, "last_prompt_tokens", 0))
context_total = _int_value(getattr(ctx, "context_length", 0))
model_name = model_name or _clean_str(session_row.get("model"))
provider_name = provider_name or _clean_str(session_row.get("billing_provider"))
base_url = base_url or _clean_str(session_row.get("billing_base_url"))
context_used = context_used or _int_value(getattr(session_entry, "last_prompt_tokens", 0))
user_config: dict[str, Any] = {}
if not model_name or not provider_name or not context_total:
try:
user_config = _load_gateway_config()
except Exception:
user_config = {}
if not model_name:
model_name = _resolve_gateway_model(user_config)
if not provider_name:
model_cfg = user_config.get("model", {}) if isinstance(user_config, dict) else {}
if isinstance(model_cfg, dict):
provider_name = _clean_str(model_cfg.get("provider"))
if not context_total and model_name:
try:
from agent.model_metadata import get_model_context_length
model_cfg = user_config.get("model", {}) if isinstance(user_config, dict) else {}
configured_context = None
if isinstance(model_cfg, dict):
configured_context = model_cfg.get("context_length")
custom_providers = user_config.get("custom_providers") if isinstance(user_config, dict) else None
context_total = get_model_context_length(
model_name,
base_url=base_url,
api_key="",
config_context_length=configured_context if isinstance(configured_context, int) else None,
provider=provider_name,
custom_providers=custom_providers if isinstance(custom_providers, list) else None,
)
except Exception:
context_total = 0
model_line = ""
if model_name:
if provider_name:
model_line = t("gateway.status.model_provider", model=model_name, provider=provider_name)
else:
model_line = t("gateway.status.model", model=model_name)
context_line = ""
if context_total:
pct = min(100, round((context_used / context_total) * 100)) if context_total else 0
context_line = t(
"gateway.status.context",
used=f"{context_used:,}",
total=f"{context_total:,}",
pct=f"{pct}",
)
elif context_used:
context_line = t("gateway.status.context_used", used=f"{context_used:,}")
lines = [
t("gateway.status.header"),
"",
@@ -443,7 +547,13 @@ class GatewaySlashCommandsMixin:
lines.extend([
t("gateway.status.created", timestamp=session_entry.created_at.strftime('%Y-%m-%d %H:%M')),
t("gateway.status.last_activity", timestamp=session_entry.updated_at.strftime('%Y-%m-%d %H:%M')),
t("gateway.status.tokens", tokens=f"{db_total_tokens:,}"),
])
if model_line:
lines.append(model_line)
if context_line:
lines.append(context_line)
lines.extend([
t("gateway.status.tokens", tokens=f"{db_total_tokens:,} (cumulative)"),
t("gateway.status.agent_running", state=t("gateway.status.state_yes") if is_running else t("gateway.status.state_no")),
])
if queue_depth: