Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12378,9 +12378,19 @@ def handle_get(handler, parsed) -> bool:
# Inject the running version so the UI badge stays in sync with git tags
# without any manual release step.
try:
from api.updates import AGENT_VERSION, WEBUI_VERSION
from api.updates import (
AGENT_VERSION,
WEBUI_VERSION,
_detect_agent_version_from_gateway_health,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Importing a private symbol across module boundaries

_detect_agent_version_from_gateway_health is a module-private helper (leading underscore convention). Importing it directly into routes.py couples the route handler to an internal implementation detail of updates.py. If the function is later renamed, inlined, or replaced with a cached variant, routes.py will silently break at runtime (the surrounding except Exception: pass will swallow the ImportError). Consider promoting a thin public wrapper — e.g. get_live_agent_version() — in updates.py that routes.py can import by stable name.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

)
settings["webui_version"] = WEBUI_VERSION
settings["agent_version"] = AGENT_VERSION
# Prefer live gateway health detection so Docker gateway
# deployments always show the actual running Agent version
# even when the import-time AGENT_VERSION is stale (#6150).
settings["agent_version"] = (
_detect_agent_version_from_gateway_health(timeout=2.0)
or AGENT_VERSION
)
Comment on lines +12390 to +12393

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Blocking HTTP probe on every settings request

_detect_agent_version_from_gateway_health(timeout=2.0) is called synchronously on every GET /api/settings with no caching. The function probes two paths sequentially (/health, then /health/detailed), each with its own 2.0 s socket timeout. When the configured gateway is reachable but slow — or when HERMES_API_URL points at a host that accepts the TCP connection but never responds — both probes exhaust their full timeout before None is returned and the fallback kicks in. That ceiling is 4 seconds of wall-clock blocking per settings request, which fires on every page load. In non-Docker environments the default hostname (hermes-agent) normally fails with a fast DNS error, but any env where one of the four gateway vars is set to a temporarily-unreachable host will regress noticeably. Adding a short TTL cache (e.g. 30 s) around the probe result would cap the HTTP overhead to one round-trip per interval rather than one per UI refresh.

except Exception:
pass
# Channel-scoped display badge — SEPARATE from webui_version (which is
Expand Down
9 changes: 8 additions & 1 deletion api/updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,10 +507,17 @@ def _read_agent_source_version(agent_dir: Path) -> str | None:


def _gateway_health_base_url() -> str:
"""Return the configured/default Hermes Agent gateway base URL."""
"""Return the configured/default Hermes Agent gateway base URL.

Falls back through the same env-var chain used by agent_health.py
(#6150): GATEWAY_HEALTH_URL → HERMES_GATEWAY_HEALTH_URL →
HERMES_API_URL → HERMES_WEBUI_GATEWAY_BASE_URL → default.
"""
raw = (
os.environ.get('GATEWAY_HEALTH_URL')
or os.environ.get('HERMES_GATEWAY_HEALTH_URL')
or os.environ.get('HERMES_API_URL')
or os.environ.get('HERMES_WEBUI_GATEWAY_BASE_URL')
or 'http://hermes-agent:8642'
).strip()
if raw.endswith('/health/detailed'):
Expand Down
Loading