Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/issues/memory-recall-hot-path-keyword-recall/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Plan

## Implementation

- Add small pure helpers in the memory presenter for recall keywordization and soft timeout handling.
- Extend the agent-memory repository search contract with a keyword match mode, defaulting to current all-term behavior.
- Add a corpus-aware recall keyword stats query to the repository contract. It counts active recallable rows per candidate term and excludes persona, working, archived, conflicted, and superseded rows.
- Implement corpus-aware term stats as one aggregate SQL statement per recall: `COUNT(*)` plus `SUM(CASE WHEN content LIKE ? ESCAPE '\\' THEN 1 ELSE 0 END)` for bounded candidate terms.
- Guard warm query embedding with one tracked in-flight entry per `agentId + providerId + modelId`. Skip vector recall while a fresh entry is active; replace stale entries after 30 seconds and let late promises clear only their own map entry.
- Add `recordAccessBatch` to the repository contract, SQLite implementation, and fake repository.
- Route public agent-facing recall and injection through dynamically selected keywordized OR matching; leave management search and internal neighbor lookups on precise all-term matching.
- Keep keyword candidate extraction pure and stopword-free. Extract ASCII/code and CJK candidates into one position-ordered pool before applying the candidate cap, then rank terms by low corpus hit count, term length, and original position; emit the selected terms back in original query order.
- Add settings-panel hints for missing embedding/extraction model configuration.
- Archive memory SDD #19 and #20 with explicit rejection notes and update the memory README.

## Compatibility

- Existing callers of `repository.search(agentId, query, limit)` continue to work because search mode defaults to all-term matching.
- Existing public contracts and persisted memory rows are unchanged.
- Late query embedding promises are intentionally not aborted; their result is ignored after timeout. The in-flight guard is a tracked rate gate, not a provider-level hard concurrency cap, so stale replacement may leave old provider calls running until they settle.
- If no extracted term hits the active memory corpus, the keyword branch returns no rows; vector recall still uses the original query when available.

## Test Strategy

- Main presenter tests cover timeout degradation, corpus-aware English and CJK recall, management search precision, no-hit keyword behavior, and batch access updates.
- SQLite table tests cover OR search mode, aggregate corpus term stats filtering, and batch access persistence.
- Presenter tests cover query-embedding in-flight suppression and stale replacement.
- Pure recall keyword tests cover extraction/ranking edge cases without presenter setup, including mixed CJK/ASCII candidate caps.
- Renderer tests cover new missing-model hints.
- Final validation runs through mise.
46 changes: 46 additions & 0 deletions docs/issues/memory-recall-hot-path-keyword-recall/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Memory Recall Hot Path and Keyword Recall

## User Need

Memory recall must not add unbounded first-token latency, and FTS-only recall must remain useful for normal chat messages. Today a warm vector recall waits for the query embedding in the pre-stream memory injection path, and keyword recall uses the full user message with all terms required to match.

## Goal

- Add a bounded soft timeout to the hot-path query embedding call so a slow provider degrades the current turn to FTS-only recall.
- Use a recall-specific keyword query for agent-facing recall and memory injection so long English or CJK messages can still match relevant memories without requiring the whole message to match.
- Keep management search precise and unchanged by default.
- Archive rejected Wave 2 recall experiments #19 and #20 while preserving their decision history.

## Acceptance Criteria

- Warm vector recall returns from FTS-only when query embedding exceeds the soft timeout, without clearing vector readiness, starting reindex, or blocking on the late embedding result.
- Non-timeout vector failures keep the existing degrade-to-FTS behavior.
- Agent-facing recall and injection keyword search use a bounded keywordized query and OR-style matching.
- Recall keyword selection is corpus-aware: query terms are extracted without a static stopword list, terms with no active corpus hits are dropped, and high-frequency terms are filtered when better lower-frequency terms exist.
- Mixed ASCII/code/CJK query term extraction preserves original query order before applying the candidate cap, so earlier CJK terms cannot be starved by later ASCII/code tokens.
- Corpus-aware term stats are collected with one bounded aggregate query per recall, not one query per candidate term.
- At most one tracked warm query-embedding entry per agent/model is active; later turns skip vector recall while that entry is fresh, and stale entries older than 30 seconds can be replaced without aborting old provider requests.
- `memory.search` management search keeps the existing all-term semantics.
- Access counter updates happen in one repository call for a recalled result set.
- Settings explain the degraded FTS-only state when memory is enabled without an embedding model and warn when extraction falls back to the chat model.
- #21 remains unchanged.

## Constraints

- No DB schema migration.
- No public route, IPC, or tool schema changes.
- No new external dependencies.
- No query embedding abort requirement; late provider requests are ignored when the caller has already degraded.
- Validation commands must run through `mise exec -- pnpm ...`.

## Non-Goals

- Do not implement #19 query expansion or #20 reranker.
- Do not implement #21 policy port or deduplicate `deriveRecall`.
- Do not maintain a static recall stopword list.
- Do not add DuckDB vacuum/orphan vector cleanup.
- Do not auto-select models or change memory defaults.

## Open Questions

None.
19 changes: 19 additions & 0 deletions docs/issues/memory-recall-hot-path-keyword-recall/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Tasks

- [x] Archive #19/#20 and update memory README.
- [x] Add repository search mode and batch access update.
- [x] Add recall keywordizer and query embedding soft timeout.
- [x] Wire agent-facing recall/injection to keywordized OR matching while keeping management search precise.
- [x] Add settings missing-model hints.
- [x] Add/update focused tests.
- [x] Run focused and final validation with `mise exec -- pnpm ...`.
- [x] Replace the static recall stopword list with corpus-aware term selection.
- [x] Add repository term stats support and tests.
- [x] Re-run focused and final validation with `mise exec -- pnpm ...`.
- [x] Change term stats to one aggregate SQL query.
- [x] Add query embedding in-flight suppression with stale replacement.
- [x] Add pure recall keyword tests and in-flight presenter tests.
- [x] Re-run focused, native-sqlite, renderer, and final validation with `mise exec -- pnpm ...`.
- [x] Fix mixed ASCII/code/CJK keyword candidate cap ordering.
- [x] Clarify query embedding in-flight guard semantics.
- [x] Add recall keyword edge test and rerun focused validation.
Loading