feat: FactStore-first memory_search for ticker-scoped queries (closes #159)#169
Open
luceinaltis wants to merge 2 commits into
Open
feat: FactStore-first memory_search for ticker-scoped queries (closes #159)#169luceinaltis wants to merge 2 commits into
luceinaltis wants to merge 2 commits into
Conversation
…159) Cross-session memory recall for ticker queries ("how's my AAPL thesis?") previously relied on BM25/vector overlap with Markdown summaries. This PR turns `memory_search` into a two-stage lookup: 1. If a FactStore and `intent.tickers` are available, query open theses (and findings, when the FactStore exposes them) first. 2. Fall back to the existing MemorySearcher free-text path otherwise. The structured path is deterministic, so ticker-scoped questions get direct recall instead of depending on LLM/keyword overlap. - `pipeline.memory_search`: new `fact_store` + `tickers` kwargs, two stages, structured results short-circuit when non-empty. Findings lookup gated on hasattr for forward-compat with #157. - `dispatcher.invoke_tool[s]`: forward `fact_store` to pipeline, passing `intent.tickers` through. - `QuickPathHandler`, `ComparisonHandler`, `StandardHandler`: thread the existing/new `fact_store` into invoke_tools. - `ConversationEngine`: wire `fact_store` into `ComparisonHandler`.
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
Closes #159.
pipeline.memory_search()previously passed the raw user query directly to BM25/vector search — it had no awareness ofIntent.tickersor theFactStore. Ticker-scoped questions like "how's my AAPL thesis going?" relied on keyword overlap with past Markdown summaries instead of a direct lookup against the structured thesis table.This PR turns
memory_searchinto the two-stage retrieval described in the issue:FactStoreis configured and the intent carries tickers, runget_open_theses(tickers)(andget_findings(ticker)when the store exposes it — gated onhasattrso we're forward-compatible with feat: extract and persist Findings from ToolResults (FactStore Phase 2) #157 / PR feat: extract and persist Findings from ToolResults (FactStore Phase 2) #166). If any structured records come back, return them directly as the sole result — no free-text search is performed.MemorySearcher.search(query)path. If there's no searcher either, return an emptyresultslist — same graceful-degradation behavior as before.What changed
qracer/tools/pipeline.pymemory_search(query, searcher=None, *, fact_store=None, tickers=None, **kwargs)— new kwargs; old keyword-only callers are still compatible._structured_lookup(fact_store, tickers)helper builds the theses/findings payload and swallows store-side exceptions so a brokenFactStorealways falls back to free-text search."source"field ("fact_store","memory_searcher", or"none") so analysts / downstream formatters can tell which path won.qracer/conversation/dispatcher.pyinvoke_tool/invoke_toolsaccept an optionalfact_storekwarg and forward it intopipeline.memory_searchalongsideintent.tickers.qracer/conversation/handlers.pyQuickPathHandler,StandardHandler: pass their existingself._fact_storeintoinvoke_tools.ComparisonHandler: gains an optionalfact_storeconstructor arg and threads it through the per-tickerinvoke_toolsfan-out.qracer/conversation/engine.pyfact_store=fact_storeinto bothComparisonHandlerconstructions (init +update_registrieshot-swap).Scope mapping (#159)
pipeline.memory_search()acceptsfact_store_structured_lookupruns before free-textthesesorfindingsnon-emptyMemorySearcher.search(query)otherwisefact_storethrough dispatcher → pipelineinvoke_tool/invoke_toolskwargsTest plan
uv run pytest tests/tools/test_pipeline.py tests/conversation/test_engine.py— 63 passed (5 newTestMemorySearchcases + updated dispatcher assertion + newtest_memory_search_forwards_fact_store_and_tickers).uv run pytestfull suite — 784 passed, 14 skipped.uv run ruff checkon changed files — clean.uv run pyrighton changed files — 0 errors.New tests cover
FactStoreshort-circuit the free-text search (source=fact_store, theses populated,resultsempty).MemorySearcher.search()(source=memory_searcher, searcher called exactly once).FactStore.get_open_thesesraises → fall back to free-text instead of bubbling.get_findings, findings are included in the payload.fact_store+intent.tickersintopipeline.memory_search.Manual verification