POC: wire search page to semantic KNN search (sc-45806) - #3589
Open
nsantacruz wants to merge 8 commits into
Open
POC: wire search page to semantic KNN search (sc-45806)#3589nsantacruz wants to merge 8 commits into
nsantacruz wants to merge 8 commits into
Conversation
Disables Elasticsearch keyword search on the /search page and instead renders results from the semantic KNN search pipeline: up to 40 direct semantic matches followed by up to 10 link-origin matches, each tagged with a chip. Filter sidebar and sort dropdown are hidden since they aren't useful for this few, non-facetable results. Sheet search is unaffected (KNN index only covers texts). /api/knn-search is bearer-token gated for external/tool callers, so this adds a public same-origin proxy (api/search-wrapper/semantic) that reuses the same core search logic (KnnSearch.run_search) without shipping the shared secret to browser JS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
📊 Code Quality Score: 31/100
Was this score accurate? 👍 Yes · 👎 No Scored by GitVelocity · How are scores calculated? |
SemanticSearchQuerier set isQueryRunning but left the previous query's hits in state, so SearchResultList kept rendering the old results alongside the loading spinner until the new response landed -- every search appeared to be one query behind. Reset hits on requery, matching ElasticSearchQuerier's existing behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… API Adds /api/natural-language-search (and its same-origin wrapper), which elaborates a user query with an LLM (model configurable via NATURAL_LANGUAGE_SEARCH_MODEL, default claude-sonnet-5) into a verbose English query and a Hebrew translation, then runs both through semantic search in parallel threads -- 40 results per leg, no per-leg linked-ref lookup. The two chunk sets are unioned by ref, and linked refs are computed once over the unioned set. SemanticSearchQuerier.jsx now points at the new endpoint. Also refactors semantic_search/linked_refs.py so the first hop reads the linked_refs already stored on each chunk instead of querying Mongo's links collection -- zero extra DB calls for the depth used in production, and a pgvector-backed lookup (instead of Mongo LinkSet) for any deeper hops. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two bugs were causing every /api/natural-language-search request to fail
with a 502: ChatAnthropic was called with temperature=0, which
claude-sonnet-5 rejects outright ("temperature is deprecated for this
model"), and _get_llm() only checked Django settings for
ANTHROPIC_API_KEY, ignoring the environment variable convention used
elsewhere in the codebase (sefaria/helper/linker/disambiguator.py).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… relevance scoring and summaries Converts the natural-language-search pipeline (query expansion -> parallel EN/HE semantic search -> link expansion) into a polled Celery task with per-phase progress. Adds an LLM relevance-scoring pass (1-5, keep 3-5, stably sorted) and a per-result LLM relevance summary, merges linked-ref matches into the main results list annotated with their source (english/hebrew/both/linked), and surfaces the expanded EN/HE queries to the client as soon as they're available. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The task pod (helm-chart/sefaria/templates/rollout/task.yaml) already had pgvector and Anthropic secrets but never got the Gemini secret, so any in-task call to the embedding API (query embedding for semantic search) failed with "GEMINI_API_KEY is not configured" even though the web pod had it. Mirrors web.yaml's existing envFrom entry via the shared sefaria.secrets.geminiApiKey helper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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
/searchpage (text search only) and instead renders results from the semantic KNN search pipeline.Semantic Match/Related Link).Why a new backend endpoint?
/api/knn-searchis gated by a sharedAuthorization: Bearer <SEMANTIC_SEARCH_API_TOKEN>secret intended for server-to-server/tool callers. Calling it directly from browser JS would ship that secret to every visitor. Instead:KnnSearch.post()was split into a thin auth/dispatch wrapper and a reusableKnnSearch.run_search(body)classmethod (api/views.py).semantic_search_wrapper_api(reader/views.py, routed atPOST /api/search-wrapper/semantic) callsrun_searchdirectly server-side — no token required, same trust model as the existingsearch_wrapper_api.Frontend
SemanticSearchQuerier.jsx(sibling toElasticSearchQuerier.jsx) posts to the new endpoint, tags each hitresultOrigin: 'semantic'|'link', and feedsSearchPage.SearchResultList.jsxgets a newtype === "semantic"render branch (avoids the!!result._source.versionfilter, which would silently drop every link-origin result).SearchTextResult.jsxrenders the origin chip and falls back to a version-less href for link-origin rows (which lackversion/lang).SearchPage.jsxhides the sort/filter controls whentype === "semantic".ReaderPanel.jsxmountsSemanticSearchQuerierfor text search, keepsElasticSearchQuerierfor sheet search.Test plan
webpackbuild succeeds across all bundles./api/search-wrapper/semanticvia Django test client — correctly calls Gemini to embed the query and reaches the pgvector query (failed only on local Postgres not running in this sandbox).docker-compose up, open/search?q=..., confirm no filter sidebar/sort dropdown, semantic results first with chips, link results below with chips, click-through navigates correctly.🤖 Generated with Claude Code