refactor(search): address PR #750 round 6 review feedback
Closes out the remaining nits flagged in the round-6 review. Critical: - _verify_files contract comment now enumerates all None-return cases (404 + malformed PROPFIND XML) and documents the false-eviction trade-off; self-healing via re-indexing recovers - int(r.id) cast at the SemanticSearchResult boundary now raises a TypeError with explicit doc_type/value context instead of bubbling up as an opaque "Search failed: ..." McpError Design observations: - nc_semantic_search_answer docstring documents the per-note round-trip cost from the post-verification race guard - News verification latency hint added to configuration.md - SemanticSearchResponse exposes verified_count + dropped_count so short result pages on high-ghost-density indexes are distinguishable from genuine scarcity. verify_search_results now returns (kept, dropped_count); production caller and tests updated Minor: - Comment clarifies the .get() fallback in verify_search_results is defensive only (run_verifier always populates the entry) - Eviction task-group guard narrowed from except Exception to except RuntimeError (the only documented failure mode of TaskGroup.start_soon on a closed group) - Indexer logs a warning when a deck_card task is missing board_id/stack_id, surfacing data-quality issues at index time rather than at verification time - New unit test covers the news verifier's non-numeric-id fail-open path (one bad doc_id keeps the entire batch) 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
ffcca23a7b
commit
e8df6003c5
@@ -172,11 +172,12 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
eviction_task_group = getattr(
|
||||
ctx.request_context.lifespan_context, "eviction_task_group", None
|
||||
)
|
||||
verified_results = await verify_search_results(
|
||||
verified_results, dropped_count = await verify_search_results(
|
||||
client,
|
||||
all_results,
|
||||
eviction_task_group=eviction_task_group,
|
||||
)
|
||||
verified_count = len(verified_results)
|
||||
search_results = verified_results[:limit]
|
||||
|
||||
# Convert SearchResult objects to SemanticSearchResult for response.
|
||||
@@ -188,9 +189,24 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
# public API.
|
||||
results = []
|
||||
for r in search_results:
|
||||
try:
|
||||
narrowed_id = int(r.id)
|
||||
except (TypeError, ValueError) as e:
|
||||
# Re-raise with explicit context so the outer handler logs
|
||||
# something operators can act on (the generic "Search
|
||||
# failed: invalid literal for int()" is opaque).
|
||||
raise TypeError(
|
||||
f"SemanticSearchResult.id must be int-convertible, "
|
||||
f"got {r.id!r} (type={type(r.id).__name__}) for "
|
||||
f"doc_type={r.doc_type!r}. This indicates a doc_type "
|
||||
f"with non-numeric ids has been indexed but the "
|
||||
f"public response model has not been widened. Add "
|
||||
f"the doc_type to the SemanticSearchResult.id type "
|
||||
f"or convert at the verifier layer."
|
||||
) from e
|
||||
results.append(
|
||||
SemanticSearchResult(
|
||||
id=int(r.id),
|
||||
id=narrowed_id,
|
||||
doc_type=r.doc_type,
|
||||
title=r.title,
|
||||
category=r.metadata.get("category", "") if r.metadata else "",
|
||||
@@ -304,6 +320,8 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
query=query,
|
||||
total_found=len(results),
|
||||
search_method=f"bm25_hybrid_{fusion}",
|
||||
verified_count=verified_count,
|
||||
dropped_count=dropped_count,
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
@@ -382,6 +400,14 @@ def configure_semantic_tools(mcp: FastMCP):
|
||||
Note: Requires MCP client to support sampling. If sampling is unavailable,
|
||||
the tool gracefully degrades to returning documents with an explanation.
|
||||
The client may prompt the user to approve the sampling request.
|
||||
|
||||
Latency profile: For each note in the result page, this tool fetches
|
||||
the full note body via ``client.notes.get_note`` after upstream
|
||||
verify-on-read has already round-tripped to the same endpoint as a
|
||||
race guard (ADR-019). Expect one additional Nextcloud round-trip per
|
||||
note result; raising ``limit`` above the default of 5 amplifies this
|
||||
cost roughly linearly. File / news / deck results do not pay this
|
||||
cost — they reuse the verified excerpt.
|
||||
"""
|
||||
# 1. Retrieve relevant documents via existing semantic search
|
||||
search_response = await nc_semantic_search(
|
||||
|
||||
Reference in New Issue
Block a user