{"repo":"NousResearch/hermes-agent","public_id":"3404","display_label":"Auxiliary and vision LLM timeout configuration issues","summary":"These issues and PRs concern default HTTP timeouts (often 30s) being too short for auxiliary LLM calls, vision analysis, and context compression when using slow or local model endpoints. The work spans bug reports, fixes, and feature requests for making these timeouts configurable and auto-adjusted for local inference.","subsystem":"agent","counts":{"members":3,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":1},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":3597,"merged_at":"2026-03-28T21:35:28+00:00"},"resolution":{"state":"unresolved","by":null},"freshness":{"state":"stale","last_verified_at":"2026-03-28T21:35:28+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.6666666666666666},"fix_edges":[{"issue_number":3404,"pr_number":3597,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false,"pr_merged_at":"2026-03-28T21:35:28+00:00"}],"members":[{"kind":"issue","number":3404,"title":"[Feature]: Configurable timeouts for auxiliary call_llm and context compression","url":null,"state":"closed"},{"kind":"pr","number":3406,"title":"feat(agent): configurable timeouts for auxiliary LLM calls and context compression","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#3597","number":3597}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":3597,"title":"feat(agent): configurable timeouts for auxiliary LLM calls via config.yaml (salvage #3406)","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/auxiliary_client.py","agent/context_compressor.py","agent/title_generator.py","hermes_cli/config.py"],"fix_hunks":[{"file":"agent/auxiliary_client.py","hunks":["@@ -1458,6 +1458,29 @@ def _resolve_task_provider_model(\n     return \"auto\", resolved_model, None, None\n \n \n+_DEFAULT_AUX_TIMEOUT = 30.0\n+\n+\n+def _get_task_timeout(task: str, default: float = _DEFAULT_AUX_TIMEOUT) -> float:\n+    \"\"\"Read timeout from auxiliary.{task}.timeout in config, falling back to *default*.\"\"\"\n+    if not task:\n+        return default\n+    try:\n+        from hermes_cli.config import load_config\n+        config = load_config()\n+    except ImportError:\n+        return default\n+    aux = config.get(\"auxiliary\", {}) if isinstance(config, dict) else {}\n+    task_config = aux.get(task, {}) if isinstance(aux, dict) else {}\n+    raw = task_config.get(\"timeout\")\n+    if raw is not None:\n+        try:\n+            return float(raw)\n+        except (ValueError, TypeError):\n+            pass\n+    return default\n+\n+\n def _build_call_kwargs(\n     provider: str,\n     model: str,","@@ -1515,7 +1538,7 @@ def call_llm(\n     temperature: float = None,\n     max_tokens: int = None,\n     tools: list = None,\n-    timeout: float = 30.0,\n+    timeout: float = None,\n     extra_body: dict = None,\n ) -> Any:\n     \"\"\"Centralized synchronous LLM call.","@@ -1533,7 +1556,7 @@ def call_llm(\n         temperature: Sampling temperature (None = provider default).\n         max_tokens: Max output tokens (handles max_tokens vs max_completion_tokens).\n         tools: Tool definitions (for function calling).\n-        timeout: Request timeout in seconds.\n+        timeout: Request timeout in seconds (None = read from auxiliary.{task}.timeout config).\n         extra_body: Additional request body fields.\n \n     Returns:","@@ -1598,10 +1621,12 @@ def call_llm(\n                 f\"No LLM provider configured for task={task} provider={resolved_provider}. \"\n                 f\"Run: hermes setup\")\n \n+    effective_timeout = timeout if timeout is not None else _get_task_timeout(task)\n+\n     kwargs = _build_call_kwargs(\n         resolved_provider, final_model, messages,\n         temperature=temperature, max_tokens=[redacted:secret-assignment]\n-        tools=tools, timeout=timeout, extra_body=extra_body,\n+        tools=tools, timeout=effective_timeout, extra_body=extra_body,\n         base_url=resolved_base_url)\n \n     # Handle max_tokens vs max_completion_tokens retry","@@ -1683,7 +1708,7 @@ async def async_call_llm(\n     temperature: float = None,\n     max_tokens: int = None,\n     tools: list = None,\n-    timeout: float = 30.0,\n+    timeout: float = None,\n     extra_body: dict = None,\n ) -> Any:\n     \"\"\"Centralized asynchronous LLM call.","@@ -1744,10 +1769,12 @@ async def async_call_llm(\n                 f\"No LLM provider configured for task={task} provider={resolved_provider}. \"\n                 f\"Run: hermes setup\")\n \n+    effective_timeout = timeout if timeout is not None else _get_task_timeout(task)\n+\n     kwargs = _build_call_kwargs(\n         resolved_provider, final_model, messages,\n         temperature=temperature, max_tokens=[redacted:secret-assignment]\n-        tools=tools, timeout=timeout, extra_body=extra_body,\n+        tools=tools, timeout=effective_timeout, extra_body=extra_body,\n         base_url=resolved_base_url)\n \n     try:"]},{"file":"agent/context_compressor.py","hunks":["@@ -347,7 +347,7 @@ def _generate_summary(self, turns_to_summarize: List[Dict[str, Any]]) -> Optiona\n                 \"messages\": [{\"role\": \"user\", \"content\": prompt}],\n                 \"temperature\": 0.3,\n                 \"max_tokens\": [redacted:secret-assignment] * 2,\n-                \"timeout\": 45.0,\n+                # timeout resolved from auxiliary.compression.timeout config by call_llm\n             }\n             if self.summary_model:\n                 call_kwargs[\"model\"] = self.summary_model"]},{"file":"agent/title_generator.py","hunks":["@@ -19,7 +19,7 @@\n )\n \n \n-def generate_title(user_message: str, assistant_response: str, timeout: float = 15.0) -> Optional[str]:\n+def generate_title(user_message: str, assistant_response: str, timeout: float = 30.0) -> Optional[str]:\n     \"\"\"Generate a session title from the first exchange.\n \n     Uses the auxiliary LLM client (cheapest/fastest available model)."]},{"file":"hermes_cli/config.py","hunks":["@@ -227,42 +227,49 @@ def ensure_hermes_home():\n             \"model\": \"\",\n             \"base_url\": \"\",\n             \"api_key\": \"\",\n+            \"timeout\": 30,         # seconds — increase for slow local models\n         },\n         \"compression\": {\n             \"provider\": \"auto\",\n             \"model\": \"\",\n             \"base_url\": \"\",\n             \"api_key\": \"\",\n+            \"timeout\": 120,        # seconds — compression summarises large contexts; increase for local models\n         },\n         \"session_search\": {\n             \"provider\": \"auto\",\n             \"model\": \"\",\n             \"base_url\": \"\",\n             \"api_key\": \"\",\n+            \"timeout\": 30,\n         },\n         \"skills_hub\": {\n             \"provider\": \"auto\",\n             \"model\": \"\",\n             \"base_url\": \"\",\n             \"api_key\": \"\",\n+            \"timeout\": 30,\n         },\n         \"approval\": {\n             \"provider\": \"auto\",\n             \"model\": \"\",           # fast/cheap model recommended (e.g. gemini-flash, haiku)\n             \"base_url\": \"\",\n             \"api_key\": \"\",\n+            \"timeout\": 30,\n         },\n         \"mcp\": {\n             \"provider\": \"auto\",\n             \"model\": \"\",\n             \"base_url\": \"\",\n             \"api_key\": \"\",\n+            \"timeout\": 30,\n         },\n         \"flush_memories\": {\n             \"provider\": \"auto\",\n             \"model\": \"\",\n             \"base_url\": \"\",\n             \"api_key\": \"\",\n+            \"timeout\": 30,\n         },\n     },\n     \n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}