fix(memory): recall facts when an LLM search query contains FTS5 metasyntax - #43490
fix(memory): recall facts when an LLM search query contains FTS5 metasyntax#43490ly-wang19 wants to merge 1 commit into
Conversation
…syntax FactRetriever.search() — the LLM-facing memory `search` action — passes the raw query straight to an FTS5 MATCH in _fts_candidates(). LLM queries routinely contain FTS5 metasyntax (a `key:value` colon, a stray double-quote, a bare AND/OR/NEAR, or a parenthesis); MATCH raises sqlite3.OperationalError on those, and the candidate fetch swallowed the error and returned [] — so the agent recalled NO memories even when relevant facts existed. Retry the MATCH with each token quoted as a literal phrase (_fts5_safe_query) before giving up. The raw query is tried first so valid FTS5 (e.g. `term*` prefix queries) is preserved; only a rejected query falls back to the quoted retry, which degrades to a literal-phrase match instead of returning nothing. Adds tests for the colon/boolean/paren/NEAR metasyntax cases (the colon and dangling-operator cases fail without the retry) plus the helper.
|
Verification: LGTM ✅ The retry-with-literal-quotes approach is sound — each whitespace-delimited token gets wrapped in double quotes (with embedded quotes doubled per FTS5 phrase syntax), turning malformed queries into safe phrase matches.
Small note: the function splits on whitespace, so multi-word phrases with spaces inside quotes (e.g., |
austinpickett
left a comment
There was a problem hiding this comment.
✅ Approved
The bug: _fts_candidates in holographic/retrieval.py was swallowing FTS5 sqlite3.OperationalError and silently returning [] — so the agent received no memories even when relevant facts existed.
The fix: Replace the silent-return-empty fallback with a retry using _fts5_safe_query (same token-quoting approach as PR #43435 which fixes the same pattern in store.py). The inner try/except on the retry is correct — guarantees empty return only after two real attempts fail.
Note: PR #43435 applies the same pattern to store.py's search_facts — different file, no conflict, both are wanted.
Reviewed by Hermes Agent
|
Thanks for the focused regression analysis. This has already been implemented on current
Automated hermes-sweeper review. |
Summary
FactRetriever.search()is the path behind the LLM-facing memorysearchaction in the holographic memory plugin (_handle_fact_action→retriever.search). It passes the raw query straight to an FTS5MATCHin_fts_candidates():LLM-issued search queries routinely contain FTS5 metasyntax — a
key:valuecolon (project:hermes deadline), a stray double-quote, a bareAND/OR/NEAR, or a parenthesis. FTS5MATCHraisessqlite3.OperationalErroron those, so the fetch returned[]and the agent recalled no memories even when relevant facts existed — a silent correctness loss on entirely normal input.Fix
Before giving up, retry the
MATCHwith each token quoted as a literal phrase:The raw query is tried first, so valid FTS5 (e.g.
term*prefix queries) is preserved; only a rejected query falls back to the quoted retry, which degrades to a literal-phrase match instead of returning nothing.This applies the same try-raw-then-quote approach as #43435 (which fixes the crash in
MemoryStore.search_facts, the direct-API sibling). Here the symptom is silent empty results rather than a crash, because_fts_candidatesalready swallowed the error into[].Tests
New
tests/plugins/memory/test_holographic_retrieval_fts5.py(12 tests):AND/ paren /NEAR(/ stray-quote queries no longer return empty;test_colon_query_still_recalls_matching_factand the boolean-operator case fail without the retry (they return[]) and pass with it;term*prefix queries are unaffected (raw is tried first);_fts5_safe_queryunit coverage.