Skip to content

fix(memory): synchronous OpenViking prefetch with current turn's query - #33260

Closed
per1970 wants to merge 1 commit into
NousResearch:mainfrom
per1970:fix/openviking-synchronous-prefetch
Closed

fix(memory): synchronous OpenViking prefetch with current turn's query#33260
per1970 wants to merge 1 commit into
NousResearch:mainfrom
per1970:fix/openviking-synchronous-prefetch

Conversation

@per1970

@per1970 per1970 commented May 27, 2026

Copy link
Copy Markdown

Problem

The OpenViking memory provider's prefetch() method ignores its query parameter entirely.
It returns background-thread results from queue_prefetch() which fired at the end of
the previous turn
— meaning automatic memory context is always about the wrong topic.

Turn N (user asks about AWS):
  Agent responds → background thread searches OV for "AWS" → stores in cache

Turn N+1 (user asks about CSS):
  Agent starts → pulls cache → injects results about "AWS"
  The query "CSS" is literally ignored

First turn of every session gets zero context. Every subsequent turn gets stale context
from the previous topic. The model can manually call viking_search to compensate,
but the automatic recall that fires on every turn is working against it.

This was identified and analyzed in detail by @hammerhoundai in
volcengine/OpenViking#2253,
which documents the full comparison against Claude Code's synchronous recall approach.

Evidence from source

The prefetch() method in plugins/memory/openviking/__init__.py:

def prefetch(self, query: str, ...) -> str:
    # The query parameter? Completely ignored.
    # Just returns whatever the background thread found.
    with self._prefetch_lock:
        result = self._prefetch_result
    return f"## OpenViking Context\n{result}"

queue_prefetch() fires at end of turn N with query "AWS", stores in _prefetch_result.
prefetch() at start of turn N+1 with query "CSS" returns the "AWS" result. The API
signature takes a query parameter but the OpenViking provider throws it away.

Fix

prefetch() now does a synchronous POST /api/v1/search/find with the current
turn's query parameter — matching how Claude Code's plugin works. The stale
background-thread cache is drained (discarded).

Three changes:

  1. Uses the actual queryprefetch(query="CSS") searches for "CSS", not "AWS"
  2. Score threshold — minimum 0.35 (matches Claude Code's plugin), filters noise
  3. Clean early return — returns "" when no client configured

queue_prefetch() is unchanged — it still fires background searches at end of turn
for pre-warming, but prefetch() no longer consumes those results.

How to test

  1. Start Hermes with OpenViking memory provider configured
  2. Turn 1: ask a question about topic A — note that first turn gets no context (expected)
  3. Turn 2: ask about topic B — verify ## OpenViking Context contains results about B, not A
  4. Turn 3: ask about topic C — verify context follows C, not B

Tested on

  • WSL2 (Ubuntu 26.04), Hermes v0.14.0, OpenViking v0.3.21
  • Python 3.11.15, httpx
  • Verified: syntax check passes, plugin loads without errors

Related

prefetch() now calls /api/v1/search/find synchronously with the
actual query parameter instead of returning the background-thread
cache from the previous turn. The stale prefetch was injecting
context about the wrong topic on every turn after the first.

Changes:
- prefetch() does synchronous search with query (previously ignored)
- Drains stale background cache (discards previous turn's results)
- Adds 0.35 minimum score threshold to filter noise
- Early return '' when no client configured

Background queue_prefetch() is unchanged — it still fires at end of
turn for pre-warming, but prefetch() no longer consumes its output.

Ref: volcengine/OpenViking#2253
@per1970

per1970 commented May 27, 2026

Copy link
Copy Markdown
Author

Non-coder disclaimer: I am not a coder. This fix was generated by Muninn (my Hermes AI agent) based on the documented analysis at volcengine/OpenViking#2253. I reviewed the change, applied the patch, tested it on a clean clone, and verified the fix works before filing. Happy to make any changes maintainers request.

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins tool/memory Memory tool and memory providers labels May 27, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related: #32444 (open, broader OpenViking reliability PR) also adds current-query fallback for recall/prefetch. Also related to #5838 (sync_recall feature) and closed #8474 (earlier attempt at same fix).

@wgd753

wgd753 commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Re-tested the top_k vs limit field on OV /api/v1/search/find endpoint:

top_k=1  → 10 results (R:8 M:2)
top_k=2  → 10 results (R:8 M:2)  
top_k=5  → 10 results (R:8 M:2)
top_k=20 → 10 results (R:8 M:2)

limit=2  → 2 results  ✅
limit=5  → 5 results  ✅

top_k has zero effect on this endpoint — always returns all results regardless of the value. limit correctly caps.

Suggestion: switch from top_k to limit to avoid uncontrolled context bloat.

wgd753 added a commit to wgd753/hermes-agent-pr that referenced this pull request Jun 22, 2026
- Replace stale prefetch_result read with synchronous search/find call
- Use "limit" (correct OV field) instead of "top_k" (ignored by endpoint)
- Clean query with _derive_openviking_user_text to strip skill scaffolding
- Send tenant headers (X-OpenViking-Account/User) proactively when configured,
  avoiding retry round-trip on every tenant-scoped API call
- Add "ROOT requests to tenant-scoped APIs" error pattern to retry guard
- Update 6 test assertions for new tenant header behavior (134/134 pass)
- Use _PREFETCH_MIN_SCORE named constant for noise filtering
- Do NOT pass timeout to _VikingClient.post() to avoid silent TypeError

Closes: NousResearch#33260
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Merged via #51898. Your synchronous-prefetch diagnosis was the original root-cause identification for the stale-context problem, and your fix direction (use the current query in prefetch()) is exactly what shipped. Thanks for the detailed analysis.

kshitijk4poor added a commit that referenced this pull request Jun 24, 2026
Salvage of PR #48927 by @ehz0ah, which consolidates OpenViking recall
work from #41706 (@huangxun375-stack), #33260, #49975, and #32444.

Replaces stale background post-turn prefetch warming with synchronous
current-query recall. The old queue_prefetch warmed the PREVIOUS user
message while turn-start recall consumed the CURRENT one, so injected
context was always about the wrong topic.

Changes:
- prefetch() now does session-aware /api/v1/search/search with the
  current query, falls back to /api/v1/search/find on failure
- Contract-safe payloads: limit, score_threshold, context_type,
  session_id — no top_k, no search-body mode, no target_uri
- L2 content reads for items with level=2 or empty abstracts, capped
  at full_read_limit (default 2)
- Local ranking (score + query-token overlap + leaf boost), dedup,
  score threshold, and injected-char budget
- queue_prefetch() is now a no-op (background warming removed)
- Additive batched viking_read: uris param accepts up to 3 URIs
- Per-request timeout support on _VikingClient.get/post/delete
- Removes stale _prefetch_result/_prefetch_thread/_prefetch_generation
  state and _invalidate_prefetch_state()
- Strengthened system_prompt_block guidance

Salvage follow-up fixes:
- Expose all 8 recall config knobs in get_config_schema() (PR #48927
  had removed them; #41706 correctly exposed them). Env vars remain
  as internal mechanism but are now visible in setup wizard.
- Lower default timeout 8s→4s, request_timeout 6s→3s, full_read_limit
  3→2 to reduce per-turn blocking latency.

Co-authored-by: Hao Zhe <haozhe4547@gmail.com>
Co-authored-by: Eurekaxun <eurekaxun@163.com>
pai-scaffolde pushed a commit to pai-scaffolde/hermes-agent that referenced this pull request Jun 28, 2026
Salvage of PR NousResearch#48927 by @ehz0ah, which consolidates OpenViking recall
work from NousResearch#41706 (@huangxun375-stack), NousResearch#33260, NousResearch#49975, and NousResearch#32444.

Replaces stale background post-turn prefetch warming with synchronous
current-query recall. The old queue_prefetch warmed the PREVIOUS user
message while turn-start recall consumed the CURRENT one, so injected
context was always about the wrong topic.

Changes:
- prefetch() now does session-aware /api/v1/search/search with the
  current query, falls back to /api/v1/search/find on failure
- Contract-safe payloads: limit, score_threshold, context_type,
  session_id — no top_k, no search-body mode, no target_uri
- L2 content reads for items with level=2 or empty abstracts, capped
  at full_read_limit (default 2)
- Local ranking (score + query-token overlap + leaf boost), dedup,
  score threshold, and injected-char budget
- queue_prefetch() is now a no-op (background warming removed)
- Additive batched viking_read: uris param accepts up to 3 URIs
- Per-request timeout support on _VikingClient.get/post/delete
- Removes stale _prefetch_result/_prefetch_thread/_prefetch_generation
  state and _invalidate_prefetch_state()
- Strengthened system_prompt_block guidance

Salvage follow-up fixes:
- Expose all 8 recall config knobs in get_config_schema() (PR NousResearch#48927
  had removed them; NousResearch#41706 correctly exposed them). Env vars remain
  as internal mechanism but are now visible in setup wizard.
- Lower default timeout 8s→4s, request_timeout 6s→3s, full_read_limit
  3→2 to reduce per-turn blocking latency.

Co-authored-by: Hao Zhe <haozhe4547@gmail.com>
Co-authored-by: Eurekaxun <eurekaxun@163.com>
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
Salvage of PR NousResearch#48927 by @ehz0ah, which consolidates OpenViking recall
work from NousResearch#41706 (@huangxun375-stack), NousResearch#33260, NousResearch#49975, and NousResearch#32444.

Replaces stale background post-turn prefetch warming with synchronous
current-query recall. The old queue_prefetch warmed the PREVIOUS user
message while turn-start recall consumed the CURRENT one, so injected
context was always about the wrong topic.

Changes:
- prefetch() now does session-aware /api/v1/search/search with the
  current query, falls back to /api/v1/search/find on failure
- Contract-safe payloads: limit, score_threshold, context_type,
  session_id — no top_k, no search-body mode, no target_uri
- L2 content reads for items with level=2 or empty abstracts, capped
  at full_read_limit (default 2)
- Local ranking (score + query-token overlap + leaf boost), dedup,
  score threshold, and injected-char budget
- queue_prefetch() is now a no-op (background warming removed)
- Additive batched viking_read: uris param accepts up to 3 URIs
- Per-request timeout support on _VikingClient.get/post/delete
- Removes stale _prefetch_result/_prefetch_thread/_prefetch_generation
  state and _invalidate_prefetch_state()
- Strengthened system_prompt_block guidance

Salvage follow-up fixes:
- Expose all 8 recall config knobs in get_config_schema() (PR NousResearch#48927
  had removed them; NousResearch#41706 correctly exposed them). Env vars remain
  as internal mechanism but are now visible in setup wizard.
- Lower default timeout 8s→4s, request_timeout 6s→3s, full_read_limit
  3→2 to reduce per-turn blocking latency.

Co-authored-by: Hao Zhe <haozhe4547@gmail.com>
Co-authored-by: Eurekaxun <eurekaxun@163.com>
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
Salvage of PR NousResearch#48927 by @ehz0ah, which consolidates OpenViking recall
work from NousResearch#41706 (@huangxun375-stack), NousResearch#33260, NousResearch#49975, and NousResearch#32444.

Replaces stale background post-turn prefetch warming with synchronous
current-query recall. The old queue_prefetch warmed the PREVIOUS user
message while turn-start recall consumed the CURRENT one, so injected
context was always about the wrong topic.

Changes:
- prefetch() now does session-aware /api/v1/search/search with the
  current query, falls back to /api/v1/search/find on failure
- Contract-safe payloads: limit, score_threshold, context_type,
  session_id — no top_k, no search-body mode, no target_uri
- L2 content reads for items with level=2 or empty abstracts, capped
  at full_read_limit (default 2)
- Local ranking (score + query-token overlap + leaf boost), dedup,
  score threshold, and injected-char budget
- queue_prefetch() is now a no-op (background warming removed)
- Additive batched viking_read: uris param accepts up to 3 URIs
- Per-request timeout support on _VikingClient.get/post/delete
- Removes stale _prefetch_result/_prefetch_thread/_prefetch_generation
  state and _invalidate_prefetch_state()
- Strengthened system_prompt_block guidance

Salvage follow-up fixes:
- Expose all 8 recall config knobs in get_config_schema() (PR NousResearch#48927
  had removed them; NousResearch#41706 correctly exposed them). Env vars remain
  as internal mechanism but are now visible in setup wizard.
- Lower default timeout 8s→4s, request_timeout 6s→3s, full_read_limit
  3→2 to reduce per-turn blocking latency.

Co-authored-by: Hao Zhe <haozhe4547@gmail.com>
Co-authored-by: Eurekaxun <eurekaxun@163.com>
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
Salvage of PR NousResearch#48927 by @ehz0ah, which consolidates OpenViking recall
work from NousResearch#41706 (@huangxun375-stack), NousResearch#33260, NousResearch#49975, and NousResearch#32444.

Replaces stale background post-turn prefetch warming with synchronous
current-query recall. The old queue_prefetch warmed the PREVIOUS user
message while turn-start recall consumed the CURRENT one, so injected
context was always about the wrong topic.

Changes:
- prefetch() now does session-aware /api/v1/search/search with the
  current query, falls back to /api/v1/search/find on failure
- Contract-safe payloads: limit, score_threshold, context_type,
  session_id — no top_k, no search-body mode, no target_uri
- L2 content reads for items with level=2 or empty abstracts, capped
  at full_read_limit (default 2)
- Local ranking (score + query-token overlap + leaf boost), dedup,
  score threshold, and injected-char budget
- queue_prefetch() is now a no-op (background warming removed)
- Additive batched viking_read: uris param accepts up to 3 URIs
- Per-request timeout support on _VikingClient.get/post/delete
- Removes stale _prefetch_result/_prefetch_thread/_prefetch_generation
  state and _invalidate_prefetch_state()
- Strengthened system_prompt_block guidance

Salvage follow-up fixes:
- Expose all 8 recall config knobs in get_config_schema() (PR NousResearch#48927
  had removed them; NousResearch#41706 correctly exposed them). Env vars remain
  as internal mechanism but are now visible in setup wizard.
- Lower default timeout 8s→4s, request_timeout 6s→3s, full_read_limit
  3→2 to reduce per-turn blocking latency.

Co-authored-by: Hao Zhe <haozhe4547@gmail.com>
Co-authored-by: Eurekaxun <eurekaxun@163.com>
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
Salvage of PR NousResearch#48927 by @ehz0ah, which consolidates OpenViking recall
work from NousResearch#41706 (@huangxun375-stack), NousResearch#33260, NousResearch#49975, and NousResearch#32444.

Replaces stale background post-turn prefetch warming with synchronous
current-query recall. The old queue_prefetch warmed the PREVIOUS user
message while turn-start recall consumed the CURRENT one, so injected
context was always about the wrong topic.

Changes:
- prefetch() now does session-aware /api/v1/search/search with the
  current query, falls back to /api/v1/search/find on failure
- Contract-safe payloads: limit, score_threshold, context_type,
  session_id — no top_k, no search-body mode, no target_uri
- L2 content reads for items with level=2 or empty abstracts, capped
  at full_read_limit (default 2)
- Local ranking (score + query-token overlap + leaf boost), dedup,
  score threshold, and injected-char budget
- queue_prefetch() is now a no-op (background warming removed)
- Additive batched viking_read: uris param accepts up to 3 URIs
- Per-request timeout support on _VikingClient.get/post/delete
- Removes stale _prefetch_result/_prefetch_thread/_prefetch_generation
  state and _invalidate_prefetch_state()
- Strengthened system_prompt_block guidance

Salvage follow-up fixes:
- Expose all 8 recall config knobs in get_config_schema() (PR NousResearch#48927
  had removed them; NousResearch#41706 correctly exposed them). Env vars remain
  as internal mechanism but are now visible in setup wizard.
- Lower default timeout 8s→4s, request_timeout 6s→3s, full_read_limit
  3→2 to reduce per-turn blocking latency.

Co-authored-by: Hao Zhe <haozhe4547@gmail.com>
Co-authored-by: Eurekaxun <eurekaxun@163.com>
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
Salvage of PR NousResearch#48927 by @ehz0ah, which consolidates OpenViking recall
work from NousResearch#41706 (@huangxun375-stack), NousResearch#33260, NousResearch#49975, and NousResearch#32444.

Replaces stale background post-turn prefetch warming with synchronous
current-query recall. The old queue_prefetch warmed the PREVIOUS user
message while turn-start recall consumed the CURRENT one, so injected
context was always about the wrong topic.

Changes:
- prefetch() now does session-aware /api/v1/search/search with the
  current query, falls back to /api/v1/search/find on failure
- Contract-safe payloads: limit, score_threshold, context_type,
  session_id — no top_k, no search-body mode, no target_uri
- L2 content reads for items with level=2 or empty abstracts, capped
  at full_read_limit (default 2)
- Local ranking (score + query-token overlap + leaf boost), dedup,
  score threshold, and injected-char budget
- queue_prefetch() is now a no-op (background warming removed)
- Additive batched viking_read: uris param accepts up to 3 URIs
- Per-request timeout support on _VikingClient.get/post/delete
- Removes stale _prefetch_result/_prefetch_thread/_prefetch_generation
  state and _invalidate_prefetch_state()
- Strengthened system_prompt_block guidance

Salvage follow-up fixes:
- Expose all 8 recall config knobs in get_config_schema() (PR NousResearch#48927
  had removed them; NousResearch#41706 correctly exposed them). Env vars remain
  as internal mechanism but are now visible in setup wizard.
- Lower default timeout 8s→4s, request_timeout 6s→3s, full_read_limit
  3→2 to reduce per-turn blocking latency.

Co-authored-by: Hao Zhe <haozhe4547@gmail.com>
Co-authored-by: Eurekaxun <eurekaxun@163.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants