From b74facd119493a2b77cb169065c6bfa40baea937 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:16:42 -0700 Subject: [PATCH] fix: handle YAML null values in session reset policy + configurable API timeout (#1194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: Home Assistant event filtering now closed by default Previously, when no watch_domains or watch_entities were configured, ALL state_changed events passed through to the agent, causing users to be flooded with notifications for every HA entity change. Now events are dropped by default unless the user explicitly configures: - watch_domains: list of domains to monitor (e.g. climate, light) - watch_entities: list of specific entity IDs to monitor - watch_all: true (new option — opt-in to receive all events) A warning is logged at connect time if no filters are configured, guiding users to set up their HA platform config. All 49 gateway HA tests + 52 HA tool tests pass. * docs: update Home Assistant integration documentation - homeassistant.md: Fix event filtering docs to reflect closed-by-default behavior. Add watch_all option. Replace Python dict config example with YAML. Fix defaults table (was incorrectly showing 'all'). Add required configuration warning admonition. - environment-variables.md: Add HASS_TOKEN and HASS_URL to Messaging section. - messaging/index.md: Add Home Assistant to description, architecture diagram, platform toolsets table, and Next Steps links. * fix(terminal): strip provider env vars from background and PTY subprocesses Extends the env var blocklist from #1157 to also cover the two remaining leaky paths in process_registry.py: - spawn_local() PTY path (line 156) - spawn_local() background Popen path (line 197) Both were still using raw os.environ, leaking provider vars to background processes and interactive PTY sessions. Now uses the same dynamic _HERMES_PROVIDER_ENV_BLOCKLIST from local.py. Explicit env_vars passed to spawn_local() still override the blocklist, matching the existing behavior for callers that intentionally need these. Gap identified by PR #1004 (@PeterFile). * feat(delegate): add observability metadata to subagent results Enrich delegate_task results with metadata from the child AIAgent: - model: which model the child used - exit_reason: completed | interrupted | max_iterations - tokens.input / tokens.output: token counts - tool_trace: per-tool-call trace with byte sizes and ok/error status Tool trace uses tool_call_id matching to correctly pair parallel tool calls with their results, with a fallback for messages without IDs. Cherry-picked from PR #872 by @omerkaz, with fixes: - Fixed parallel tool call trace pairing (was always updating last entry) - Removed redundant 'iterations' field (identical to existing 'api_calls') - Added test for parallel tool call trace correctness Co-authored-by: omerkaz * feat(stt): add free local whisper transcription via faster-whisper Replace OpenAI-only STT with a dual-provider system mirroring the TTS architecture (Edge TTS free / ElevenLabs paid): STT: faster-whisper local (free, default) / OpenAI Whisper API (paid) Changes: - tools/transcription_tools.py: Full rewrite with provider dispatch, config loading, local faster-whisper backend, and OpenAI API backend. Auto-downloads model (~150MB for 'base') on first voice message. Singleton model instance reused across calls. - pyproject.toml: Add faster-whisper>=1.0.0 as core dependency - hermes_cli/config.py: Expand stt config to match TTS pattern with provider selection and per-provider model settings - agent/context_compressor.py: Fix .strip() crash when LLM returns non-string content (dict from llama.cpp, None). Fixes #1100 partially. - tests/: 23 new tests for STT providers + 2 for compressor fix - docs/: Updated Voice & TTS page with STT provider table, model sizes, config examples, and fallback behavior Fallback behavior: - Local not installed → OpenAI API (if key set) - OpenAI key not set → local whisper (if installed) - Neither → graceful error message to user Co-authored-by: Jah-yee * fix: handle YAML null values in session reset policy + configurable API timeout Two fixes from PR #888 by @Jah-yee: 1. SessionResetPolicy.from_dict() — data.get('at_hour', 4) returns None when the YAML key exists with a null value. Now explicitly checks for None and falls back to defaults. Zero remains a valid value. 2. API timeout — hardcoded 900s is now configurable via HERMES_API_TIMEOUT env var. Useful for slow local models (llama.cpp) that need longer. Co-authored-by: Jah-yee --------- Co-authored-by: omerkaz Co-authored-by: Jah-yee --- gateway/config.py | 7 +++++-- run_agent.py | 2 +- website/docs/reference/environment-variables.md | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/gateway/config.py b/gateway/config.py index d325abcde..ec7d2b5cc 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -83,10 +83,13 @@ class SessionResetPolicy: @classmethod def from_dict(cls, data: Dict[str, Any]) -> "SessionResetPolicy": + # Handle both missing keys and explicit null values (YAML null → None) + at_hour = data.get("at_hour") + idle_minutes = data.get("idle_minutes") return cls( mode=data.get("mode", "both"), - at_hour=data.get("at_hour", 4), - idle_minutes=data.get("idle_minutes", 1440), + at_hour=at_hour if at_hour is not None else 4, + idle_minutes=idle_minutes if idle_minutes is not None else 1440, ) diff --git a/run_agent.py b/run_agent.py index eafb590c1..5a69a3870 100644 --- a/run_agent.py +++ b/run_agent.py @@ -2729,7 +2729,7 @@ class AIAgent: "model": self.model, "messages": api_messages, "tools": self.tools if self.tools else None, - "timeout": 900.0, + "timeout": float(os.getenv("HERMES_API_TIMEOUT", 900.0)), } if self.max_tokens is not None: diff --git a/website/docs/reference/environment-variables.md b/website/docs/reference/environment-variables.md index 6dd8bb050..6cecf2ac6 100644 --- a/website/docs/reference/environment-variables.md +++ b/website/docs/reference/environment-variables.md @@ -131,6 +131,7 @@ All variables go in `~/.hermes/.env`. You can also set them with `hermes config | `HERMES_HUMAN_DELAY_MIN_MS` | Custom delay range minimum (ms) | | `HERMES_HUMAN_DELAY_MAX_MS` | Custom delay range maximum (ms) | | `HERMES_QUIET` | Suppress non-essential output (`true`/`false`) | +| `HERMES_API_TIMEOUT` | LLM API call timeout in seconds (default: `900`) | | `HERMES_EXEC_ASK` | Enable execution approval prompts in gateway mode (`true`/`false`) | ## Session Settings