feat(honcho): make latency-adding paths configurable

queryRewrite (default off) gates the latest-message rewrite so the
extra auxiliary LLM call is opt-in. firstTurnBaseWait and
firstTurnDialecticWait expose the turn-1 bounded waits in seconds
(0 disables). All three resolve host-block-first like every other
field. Also pins per-host timeout resolution with tests.
This commit is contained in:
Erosika 2026-07-16 12:59:50 -04:00 committed by Teknium
parent e7fb51d5ac
commit e8957babf4
5 changed files with 113 additions and 2 deletions

View file

@ -256,6 +256,7 @@ class HonchoMemoryProvider(MemoryProvider):
# B5: Cost-awareness turn counting and cadence
self._turn_count = 0
self._query_rewrite_enabled = False
self._injection_frequency = "every-turn" # or "first-turn"
self._context_cadence = 1 # minimum turns between context API calls
self._dialectic_cadence = 1 # backwards-compat fallback; wizard writes 2 on new configs
@ -361,6 +362,9 @@ class HonchoMemoryProvider(MemoryProvider):
self._injection_frequency = cfg.injection_frequency
self._context_cadence = cfg.context_cadence
self._dialectic_cadence = cfg.dialectic_cadence
self._query_rewrite_enabled = cfg.query_rewrite
self._FIRST_TURN_BASE_TIMEOUT = cfg.first_turn_base_wait
self._FIRST_TURN_DIALECTIC_CAP = cfg.first_turn_dialectic_wait
self._dialectic_depth = max(1, min(cfg.dialectic_depth, 3))
self._dialectic_depth_levels = cfg.dialectic_depth_levels
self._reasoning_heuristic = cfg.reasoning_heuristic
@ -510,7 +514,7 @@ class HonchoMemoryProvider(MemoryProvider):
except Exception as e:
logger.debug("Honcho context prewarm failed: %s", e)
if self._query_rewriter is None:
if self._query_rewriter is None or not self._query_rewrite_enabled:
_prewarm_query = (
"Summarize what you know about this user. "
"Focus on preferences, current projects, and working style."
@ -1225,7 +1229,7 @@ class HonchoMemoryProvider(MemoryProvider):
is_cold = not self._base_context_cache
results: list[str] = []
rewritten_query = ""
if use_query_rewrite and self._query_rewriter:
if use_query_rewrite and self._query_rewrite_enabled and self._query_rewriter:
try:
rewritten_query = self._query_rewriter(query).strip()
except Exception as exc:

View file

@ -145,6 +145,17 @@ def _parse_int_config(host_val, root_val, default: int) -> int:
return default
def _parse_float_config(host_val, root_val, default: float) -> float:
"""Parse a float config: host wins, then root, then default. Clamped ≥ 0."""
for val in (host_val, root_val):
if val is not None:
try:
return max(0.0, float(val))
except (ValueError, TypeError):
pass
return default
def _parse_string_map(host_obj: dict, root_obj: dict, key: str) -> dict[str, str]:
"""Parse a string-to-string map with host-level whole-map override."""
source = host_obj[key] if key in host_obj else root_obj.get(key)
@ -368,6 +379,14 @@ class HonchoClientConfig:
context_cadence: int = 1
# Minimum turns between dialectic prefetch fires (supplement layer cadence)
dialectic_cadence: int = 1
# Rewrite the latest user message into a retrieval query before dialectic.
# Off by default: adds one auxiliary LLM call per dialectic fire
# (model/timeout under auxiliary.memory_query_rewrite in config.yaml).
query_rewrite: bool = False
# Bounded synchronous waits on turn 1, in seconds. 0 disables the wait
# entirely (fully async first turn; context surfaces on later turns).
first_turn_base_wait: float = 3.0
first_turn_dialectic_wait: float = 2.0
# Observation mode: legacy string shorthand ("directional" or "unified").
# Kept for backward compat; granular per-peer booleans below are preferred.
observation_mode: str = "directional"
@ -627,6 +646,21 @@ class HonchoClientConfig:
raw.get("dialecticCadence"),
default=1,
),
query_rewrite=_resolve_bool(
host_block.get("queryRewrite"),
raw.get("queryRewrite"),
default=False,
),
first_turn_base_wait=_parse_float_config(
host_block.get("firstTurnBaseWait"),
raw.get("firstTurnBaseWait"),
default=3.0,
),
first_turn_dialectic_wait=_parse_float_config(
host_block.get("firstTurnDialecticWait"),
raw.get("firstTurnDialecticWait"),
default=2.0,
),
# Migration guard: existing configs without an explicit
# observationMode keep the old "unified" default so users
# aren't silently switched to full bidirectional observation.