From 403c82b6b65bc3354bbd06fa52d56d8b2a577cbc Mon Sep 17 00:00:00 2001 From: tekgnosis-net Date: Tue, 21 Apr 2026 00:32:03 +1000 Subject: [PATCH] feat(hindsight): add configurable HINDSIGHT_TIMEOUT env var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Hindsight Cloud API can take 30-40 seconds per request. The hardcoded 30s timeout was too aggressive and caused frequent timeout errors. This patch: 1. Adds HINDSIGHT_TIMEOUT environment variable (default: 120s) 2. Adds timeout to the config schema for setup wizard visibility 3. Uses the configurable timeout in both _run_sync() and client creation 4. Reads from config.json or env var, falling back to 120s default This makes the timeout upgrade-proof — users can set it via env var or config without patching source code. Signed-off-by: Kumar --- plugins/memory/hindsight/__init__.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/memory/hindsight/__init__.py b/plugins/memory/hindsight/__init__.py index b46d3e6ed..9bc728078 100644 --- a/plugins/memory/hindsight/__init__.py +++ b/plugins/memory/hindsight/__init__.py @@ -3,6 +3,8 @@ Long-term memory with knowledge graph, entity resolution, and multi-strategy retrieval. Supports cloud (API key) and local modes. +Configurable timeout via HINDSIGHT_TIMEOUT env var or config.json. + Original PR #1811 by benfrank241, adapted to MemoryProvider ABC. Config via environment variables: @@ -11,6 +13,7 @@ Config via environment variables: HINDSIGHT_BUDGET — recall budget: low/mid/high (default: mid) HINDSIGHT_API_URL — API endpoint HINDSIGHT_MODE — cloud or local (default: cloud) + HINDSIGHT_TIMEOUT — API request timeout in seconds (default: 120) HINDSIGHT_RETAIN_TAGS — comma-separated tags attached to retained memories HINDSIGHT_RETAIN_SOURCE — metadata source value attached to retained memories HINDSIGHT_RETAIN_USER_PREFIX — label used before user turns in retained transcripts @@ -41,6 +44,7 @@ logger = logging.getLogger(__name__) _DEFAULT_API_URL = "https://api.hindsight.vectorize.io" _DEFAULT_LOCAL_URL = "http://localhost:8888" _MIN_CLIENT_VERSION = "0.4.22" +_DEFAULT_TIMEOUT = 120 # seconds — cloud API can take 30-40s per request _VALID_BUDGETS = {"low", "mid", "high"} _PROVIDER_DEFAULT_MODELS = { "openai": "gpt-4o-mini", @@ -98,7 +102,7 @@ def _get_loop() -> asyncio.AbstractEventLoop: return _loop -def _run_sync(coro, timeout: float = 120.0): +def _run_sync(coro, timeout: float = _DEFAULT_TIMEOUT): """Schedule *coro* on the shared loop and block until done.""" loop = _get_loop() future = asyncio.run_coroutine_threadsafe(coro, loop) @@ -347,6 +351,7 @@ class HindsightMemoryProvider(MemoryProvider): self._agent_identity = "" self._turn_index = 0 self._client = None + self._timeout = _DEFAULT_TIMEOUT self._prefetch_result = "" self._prefetch_lock = threading.Lock() self._prefetch_thread = None @@ -532,6 +537,11 @@ class HindsightMemoryProvider(MemoryProvider): # Step 4: Save everything provider_config["bank_id"] = "hermes" provider_config["recall_budget"] = "mid" + # Read existing timeout from config if present, otherwise use default + existing_timeout = self._config.get("timeout") if self._config else None + timeout_val = existing_timeout if existing_timeout else _DEFAULT_TIMEOUT + provider_config["timeout"] = timeout_val + env_writes["HINDSIGHT_TIMEOUT"] = str(timeout_val) config["memory"]["provider"] = "hindsight" save_config(config) @@ -618,6 +628,7 @@ class HindsightMemoryProvider(MemoryProvider): {"key": "recall_max_tokens", "description": "Maximum tokens for recall results", "default": 4096}, {"key": "recall_max_input_chars", "description": "Maximum input query length for auto-recall", "default": 800}, {"key": "recall_prompt_preamble", "description": "Custom preamble for recalled memories in context"}, + {"key": "timeout", "description": "API request timeout in seconds", "default": _DEFAULT_TIMEOUT}, ] def _get_client(self): @@ -648,11 +659,12 @@ class HindsightMemoryProvider(MemoryProvider): self._client = HindsightEmbedded(**kwargs) else: from hindsight_client import Hindsight - kwargs = {"base_url": self._api_url, "timeout": 30.0} + timeout = self._timeout or _DEFAULT_TIMEOUT + kwargs = {"base_url": self._api_url, "timeout": float(timeout)} if self._api_key: kwargs["api_key"] = self._api_key - logger.debug("Creating Hindsight cloud client (url=%s, has_key=%s)", - self._api_url, bool(self._api_key)) + logger.debug("Creating Hindsight cloud client (url=%s, has_key=%s, timeout=%s)", + self._api_url, bool(self._api_key), kwargs["timeout"]) self._client = Hindsight(**kwargs) return self._client @@ -699,6 +711,8 @@ class HindsightMemoryProvider(MemoryProvider): self._turn_index = 0 self._session_turns = [] self._mode = self._config.get("mode", "cloud") + # Read timeout from config or env var, fall back to default + self._timeout = self._config.get("timeout") or int(os.environ.get("HINDSIGHT_TIMEOUT", str(_DEFAULT_TIMEOUT))) # "local" is a legacy alias for "local_embedded" if self._mode == "local": self._mode = "local_embedded"