diff --git a/docker-compose.yml b/docker-compose.yml index 4be1b75..4cc7605 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,6 +77,7 @@ services: PLANNER_ENABLED: ${PLANNER_ENABLED:-0} # Lock STT to Korean (skip Whisper auto-detect). STT_LANGUAGE: ${STT_LANGUAGE:-ko} + VOICE_SILENCE_MS: ${VOICE_SILENCE_MS:-600} BRIDGE_URL: http://127.0.0.1:8765 depends_on: - ollama diff --git a/src/jarvis/reply/engine.py b/src/jarvis/reply/engine.py index 4960846..9cc3db3 100644 --- a/src/jarvis/reply/engine.py +++ b/src/jarvis/reply/engine.py @@ -1027,6 +1027,19 @@ def run_reply_engine(db: "Database", cfg, tts: Optional[Any], "planning", ) + # Conversational fast-path signal: did the router pick any tool that needs + # EXTERNAL DATA? Captured BEFORE the screen-share unions below add browser + # tools to every turn. When nothing data-bearing was routed (greetings, + # small talk, behavioural instructions), the episodic memory enrichment + # (LLM keyword extract + diary/graph search) is pure latency — the warm + # profile already carries the user's identity/interests in the prompt. Used + # at the needs_memory gate to skip enrichment for those turns. + _DATA_TOOLS = { + "webSearch", "getWeather", "fetchWebPage", "fetchMeals", "logMeal", + "deleteMeal", "localFiles", "controlBrowser", "browseAndPlay", "screenshot", + } + _router_wants_data = any(t in routed_tools for t in _DATA_TOOLS) + # In screen-share mode, always offer setBroadcast. "Turn the broadcast # on/off" is language-agnostic intent the embedding/keyword router won't # reliably surface for a non-English utterance (e.g. "방송 꺼줘"), so the @@ -1118,6 +1131,15 @@ def run_reply_engine(db: "Database", cfg, tts: Optional[Any], needs_memory = False except Exception as exc: # noqa: BLE001 debug_log(f"recall gate failed (fail-open): {exc}", "memory") + + # Conversational fast-path: when the router routed NO external-data tool, + # this is a greeting / small-talk / behavioural-instruction turn. Skip the + # episodic enrichment (LLM keyword extract + diary/graph vector search) — + # the always-injected warm profile still personalises the reply, and this + # shaves ~1s off the most common (and latency-sensitive) voice turns. + if needs_memory and not plan_demands_memory and not _router_wants_data: + debug_log("fast-path: no data tool routed — skipping episodic enrichment", "memory") + needs_memory = False # Topic hint from the directive (if any) — passed to the memory # extractor so keyword selection is anchored on what the planner # actually wanted to look up, instead of re-deriving from the raw