Improve suggested notes: limit 8, threshold 0.45, show relevance scores
- Raise similarity threshold 0.30 → 0.45: only genuinely relevant notes shown; loosely-related notes no longer pad the sidebar - Increase max suggested notes 3 → 8 (zero added compute — threshold is the real gate; the embedding call is fixed regardless of limit) - semantic_search_notes now returns list[tuple[float, Note]] instead of list[Note] so scores propagate through context_meta to the frontend - Keyword fallback notes carry score=null (no cosine similarity available) - ChatView sidebar shows % badge on each suggested note: green ≥75%, amber 60–74%, muted <60% Hovering reveals the raw score in a tooltip Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Minimum cosine similarity to include a note in context results.
|
||||
# nomic-embed-text produces unit-normalized vectors, so range is [-1, 1].
|
||||
_SIMILARITY_THRESHOLD = 0.30
|
||||
# 0.45 keeps only genuinely relevant notes; lower values like 0.30 let in
|
||||
# loosely-related results that pad the sidebar without adding real value.
|
||||
_SIMILARITY_THRESHOLD = 0.45
|
||||
|
||||
|
||||
async def get_embedding(text: str, model: str | None = None) -> list[float]:
|
||||
@@ -75,10 +77,12 @@ async def semantic_search_notes(
|
||||
user_id: int,
|
||||
query: str,
|
||||
exclude_ids: set[int] | None = None,
|
||||
limit: int = 3,
|
||||
) -> list[Note]:
|
||||
"""Return up to *limit* notes most relevant to *query* using cosine similarity.
|
||||
limit: int = 8,
|
||||
) -> list[tuple[float, Note]]:
|
||||
"""Return up to *limit* (score, note) pairs most relevant to *query*.
|
||||
|
||||
Scores are cosine similarities in [-1, 1]; only notes at or above
|
||||
_SIMILARITY_THRESHOLD are returned, sorted highest-first.
|
||||
Returns an empty list if the embedding model is unavailable or on any error.
|
||||
"""
|
||||
try:
|
||||
@@ -114,7 +118,7 @@ async def semantic_search_notes(
|
||||
scored.append((sim, note))
|
||||
|
||||
scored.sort(key=lambda x: x[0], reverse=True)
|
||||
return [note for _, note in scored[:limit]]
|
||||
return scored[:limit]
|
||||
|
||||
|
||||
async def backfill_note_embeddings() -> None:
|
||||
|
||||
Reference in New Issue
Block a user