refactor(search): address PR #750 review feedback
- Cap all_results to limit*2 after sort in the per-doc_types branch of nc_semantic_search to bound over-verification (was unbounded N-types). - Switch BatchVerifier from (client, doc_ids, user_id) to (client, results, semaphore). Verifiers now read file paths and deck board/stack ids from SearchResult.metadata instead of doing fresh Qdrant scrolls — eliminates one duplicate round-trip per file/deck-card verification. - Bound per-id verification concurrency with a shared anyio.Semaphore (default 20, matching server/semantic.py context-expansion convention). Prevents httpx pool exhaustion / rate limiting on large search pages. - Propagate stack_id from Qdrant payload to SearchResult.metadata in both bm25_hybrid.py and semantic.py (board_id was already propagated). - Drop now-unused _resolve_file_path / _resolve_deck_metadata helpers. - Drop redundant int(d) in requested predicate from _verify_news_items. - Rewrite eviction comment to be honest about inline (not background) execution and the resulting latency coupling. - ADR-019 status: Proposed -> Accepted. - Add news property to NextcloudClientProtocol. - Widen SearchResult.id and SemanticSearchResult.id to int | str to match BatchVerifier signature and document support for future string-id types. - Flip openWorldHint to True on nc_semantic_search_answer (it calls into Nextcloud via nc_semantic_search). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
d90e793d19
commit
7784ec02d7
@@ -142,8 +142,13 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
)
|
||||
all_results.extend(unverified_results)
|
||||
|
||||
# Sort combined results by score
|
||||
# Sort combined results by score, then cap to `limit * 2` to
|
||||
# match the cross-app branch's over-fetch budget. Without this
|
||||
# cap, N requested doc_types × `limit * 2` results would all
|
||||
# flow into verification, multiplying the Nextcloud round-trip
|
||||
# cost by N.
|
||||
all_results.sort(key=lambda r: r.score, reverse=True)
|
||||
all_results = all_results[: limit * 2]
|
||||
|
||||
# ADR-019: Verify-on-read. The vector index is a recall layer;
|
||||
# Nextcloud is the source of truth for access. Filter out ghost
|
||||
@@ -300,7 +305,7 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
title="Search with AI-Generated Answer",
|
||||
annotations=ToolAnnotations(
|
||||
readOnlyHint=True, # Search doesn't modify data
|
||||
openWorldHint=False, # Searches only indexed Nextcloud data
|
||||
openWorldHint=True, # Calls into Nextcloud via nc_semantic_search
|
||||
),
|
||||
)
|
||||
@require_scopes("semantic.read")
|
||||
@@ -432,7 +437,7 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
async with semaphore:
|
||||
if result.doc_type == "note":
|
||||
try:
|
||||
note = await client.notes.get_note(result.id)
|
||||
note = await client.notes.get_note(int(result.id))
|
||||
content = note.get("content", "")
|
||||
accessible_results[index] = result
|
||||
full_contents[index] = content
|
||||
|
||||
Reference in New Issue
Block a user