Repository navigation
session_search recovers paraphrased multi-word queries via OR-relaxed retry on a zero-result miss - #88030
Merged
Conversation
૮ >ﻌ< ა ci reviewran on fcf9e52 — test(session-search): collapse OR-relaxed tests to three inv
|
Closed
teknium1
force-pushed
the
ironclaw-port/session-search-or-fallback
branch
from
September 15, 2026 01:50
d3f9d4e to
7712cf2
Compare
Port from nearai/ironclaw#7553 (Filter::FtsRanked): FTS5's implicit AND between terms means a paraphrased multi-word query misses a stored sentence that lacks even one of the words. When the exact-match search and the substring fallbacks all return zero rows, retry the same unicode61 FTS index with the terms OR-joined, ranked by bm25 so rows covering more terms surface first. Strictly additive: gated on a zero-result miss, so successful searches keep exact-match semantics and ordering. Queries with explicit OR/NOT, single-term queries, and CJK-routed queries are left untouched. Quoted phrases relax as whole units. Adapted for hermes-agent: implemented inside SessionSearchMixin's zero-result fallback chain (after the CJK-bigram/trigram substring retries) rather than as a separate filter variant, reusing the already- built SQL/params so all source/role/sort filters apply to the retry.
The discovery payload exposes hits under `results`, not `matches`, so the
loop over `result_rewind.get("matches", [])` never iterated and the
invariant (the rewound row never surfaces, even via the OR-relaxed retry)
was not being checked. Assert on the anchor message id and snippet of each
returned result instead.
…comment says sort applies The retry passes ``route`` through unchanged, so the caller's ``sort`` (newest/oldest) still drives ORDER BY; the comment claimed bm25 ranking unconditionally. Say what the code does rather than force rank order — a user who asked for newest-first should get newest-first from the relaxed hits too. Tests: the seven ``_or_relaxed_query`` helper cases become one parametrized test; one DB recovery test (exact untouched, paraphrase recovered, all partial rows, role_filter honoured) and one negative (explicit NOT not relaxed, true miss stays empty, CJK route never reaches the rewrite). Drops the upstream product name from module prose (credit stays in the PR body).
teknium1
force-pushed
the
ironclaw-port/session-search-or-fallback
branch
from
September 15, 2026 04:14
7712cf2 to
fcf9e52
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
session_searchnow recovers paraphrased multi-word queries: when the exact FTS5 match (and the substring fallbacks) return zero rows, the same query is retried with its terms OR-joined and bm25-ranked, so a fact saved as "Sarah prefers the standup meeting scheduled early on Thursday mornings" is found by "when does Sarah like her standup scheduled".Ported from nearai/ironclaw#7553 (
Filter::FtsRanked): FTS5's implicit AND requires every query term to appear, so a query worded even slightly differently from the stored sentence returns nothing.Changes
hermes_state_search.py: new_or_relaxed_query()helper (OR-joins searchable units, preserves quoted phrases as units, declines on explicitOR/NOTor single-term queries) + a zero-result-gated retry at the tail ofsearch_messages's fallback chain, reusing the already-built SQL so all source/role/sort filters apply.tests/state/test_search_or_relaxed_fallback.py: 13 tests — helper semantics + end-to-end paraphrased recall (verified to fail with the helper sabotaged out).tests/tools/test_session_search.py: one rewind-exclusion test tightened from "count == 0" to "the rewound row never surfaces" (the OR retry may legitimately surface other active rows sharing a common term).Adaptation notes
Filter::FtsRankedvariant at the storage layer and switches the memory-native search path onto it. Hermes' session search already has a layered fallback chain (unicode61 → CJK-bigram → trigram → LIKE), so the port slots in as one more strictly-additive zero-result retry rather than a new filter mode — exact-match semantics and ordering are untouched for queries that hit.OR/NOT) are never relaxed; CJK-routed queries keep their own substring semantics.Validation
search_messages("when does Sarah like her standup scheduled")[]standup NOT ThursdayInfographic