refactor(hermes_cli): observability subscriber/send_config/__init__ — projection table, inline safe-observe, tighter docs
This commit is contained in:
@@ -12,7 +12,10 @@ def observe_lifecycle(hook_name: str, **kwargs: Any) -> None:
|
||||
"""Dispatch a Hermes lifecycle event to built-in observability features."""
|
||||
from . import relay_shared_metrics
|
||||
|
||||
_safe_observe(relay_shared_metrics.observe_lifecycle, hook_name, kwargs)
|
||||
try:
|
||||
relay_shared_metrics.observe_lifecycle(hook_name, **kwargs)
|
||||
except Exception:
|
||||
logger.warning("Built-in observability hook failed: %s", hook_name, exc_info=True)
|
||||
|
||||
|
||||
def handles_hook(hook_name: str) -> bool:
|
||||
@@ -20,12 +23,3 @@ def handles_hook(hook_name: str) -> bool:
|
||||
from . import relay_shared_metrics
|
||||
|
||||
return relay_shared_metrics.handles_hook(hook_name)
|
||||
|
||||
|
||||
def _safe_observe(callback: Any, hook_name: str, kwargs: dict[str, Any]) -> None:
|
||||
try:
|
||||
callback(hook_name, **kwargs)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Built-in observability hook failed: %s", hook_name, exc_info=True
|
||||
)
|
||||
|
||||
@@ -8,14 +8,10 @@ from urllib.parse import urlparse
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
#: Production ingest endpoint. Overridable through config only.
|
||||
#:
|
||||
#: Deliberately NOT overridable by an environment variable: AGENTS.md reserves
|
||||
#: HERMES_* env vars for secrets, and a behavioural override here would be a
|
||||
#: consent hazard — a user who agreed to send metrics to Nous could have them
|
||||
#: silently redirected to any host by an inherited variable, with nothing
|
||||
#: visible in their config to show it. Tests and the staging E2E write this
|
||||
#: key into a throwaway profile instead.
|
||||
#: Production ingest endpoint. Overridable through config only — deliberately NOT by an
|
||||
#: environment variable: AGENTS.md reserves HERMES_* for secrets, and an inherited variable
|
||||
#: could silently redirect consented metrics to any host with nothing visible in config.
|
||||
#: Tests and the staging E2E write this key into a throwaway profile instead.
|
||||
DEFAULT_ENDPOINT = "https://telemetry.nousresearch.com/v1/telemetry"
|
||||
|
||||
_LOCAL_HOSTS = frozenset({"localhost", "127.0.0.1", "::1", "[::1]"})
|
||||
@@ -38,28 +34,20 @@ class SendConfig:
|
||||
|
||||
|
||||
def _endpoint_is_safe(endpoint: str) -> bool:
|
||||
"""Reject plaintext destinations unless they are loopback.
|
||||
|
||||
Telemetry must not leave a machine in clear text because of a typo in a config file. Loopback
|
||||
stays allowed so tests can use a local HTTP server.
|
||||
"""
|
||||
"""Reject plaintext destinations unless they are loopback (so tests can use local HTTP)."""
|
||||
try:
|
||||
parsed = urlparse(endpoint)
|
||||
except ValueError:
|
||||
return False
|
||||
if parsed.scheme == "https":
|
||||
return True
|
||||
if parsed.scheme == "http":
|
||||
return (parsed.hostname or "") in _LOCAL_HOSTS
|
||||
return False
|
||||
return parsed.scheme == "http" and (parsed.hostname or "") in _LOCAL_HOSTS
|
||||
|
||||
|
||||
def resolve_send_config(config: dict | None) -> SendConfig:
|
||||
"""Resolve transmission settings from config plus the environment.
|
||||
"""Resolve transmission settings from config (endpoint: config > production default).
|
||||
|
||||
Endpoint precedence: config > production default.
|
||||
|
||||
``send`` is returned as False whenever transmission cannot legitimately happen, so callers never
|
||||
``send`` is False whenever transmission cannot legitimately happen, so callers never
|
||||
have to re-check the combination.
|
||||
"""
|
||||
global _warned_send_without_collection
|
||||
@@ -74,8 +62,7 @@ def resolve_send_config(config: dict | None) -> SendConfig:
|
||||
send_requested = shared.get("send") is True
|
||||
|
||||
if send_requested and not enabled:
|
||||
# Loud, not silent: the user believes telemetry is being sent, and it
|
||||
# never will be. Error level, once per process.
|
||||
# Loud, not silent: the user believes telemetry is being sent, and it never will be.
|
||||
if not _warned_send_without_collection:
|
||||
_warned_send_without_collection = True
|
||||
logger.error(
|
||||
|
||||
@@ -26,6 +26,20 @@ from .shared_metrics_contract import (
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Contract projections in match order; each yields (metric_name, dimensions) or None.
|
||||
_COUNTERS = (
|
||||
client_active_counter,
|
||||
lambda event: _named(MODEL_ROUTE_METRIC, model_call_dimensions(event)),
|
||||
lambda event: _named(TOOL_CALL_METRIC, tool_call_dimensions(event)),
|
||||
task_counter,
|
||||
tool_approval_counter,
|
||||
skill_counter,
|
||||
)
|
||||
|
||||
|
||||
def _named(metric_name: str, dimensions: dict | None) -> tuple[str, dict] | None:
|
||||
return None if dimensions is None else (metric_name, dimensions)
|
||||
|
||||
|
||||
class SharedMetricsSubscriber:
|
||||
"""Persist validated Hermes counters from Relay lifecycle events."""
|
||||
@@ -56,17 +70,7 @@ class SharedMetricsSubscriber:
|
||||
@staticmethod
|
||||
def _classify(event: Any) -> tuple[str, dict] | None:
|
||||
"""Return ``(metric_name, dimensions)`` for the first matching contract, else None."""
|
||||
metric = client_active_counter(event)
|
||||
if metric is not None:
|
||||
return metric
|
||||
for metric_name, project in (
|
||||
(MODEL_ROUTE_METRIC, model_call_dimensions),
|
||||
(TOOL_CALL_METRIC, tool_call_dimensions),
|
||||
):
|
||||
dimensions = project(event)
|
||||
if dimensions is not None:
|
||||
return metric_name, dimensions
|
||||
return task_counter(event) or tool_approval_counter(event) or skill_counter(event)
|
||||
return next((m for m in (project(event) for project in _COUNTERS) if m is not None), None)
|
||||
|
||||
def __call__(self, event: Any) -> None:
|
||||
if self._runtime_id is not None:
|
||||
@@ -87,14 +91,8 @@ class SharedMetricsSubscriber:
|
||||
if metric_name == CLIENT_ACTIVE_METRIC:
|
||||
self.store.record_client_active(self._client_resource)
|
||||
else:
|
||||
self.store.record_counter(
|
||||
metric_name,
|
||||
dimensions,
|
||||
self._client_resource,
|
||||
)
|
||||
self.store.record_counter(metric_name, dimensions, self._client_resource)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Unable to persist the Hermes shared metric: %s",
|
||||
metric_name,
|
||||
exc_info=True,
|
||||
"Unable to persist the Hermes shared metric: %s", metric_name, exc_info=True
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user