diff --git a/CLAUDE.md b/CLAUDE.md index 194a9cb..73b5ceb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,13 +15,17 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - **Use Python 3.10+ union syntax**: `str | None` instead of `Optional[str]` - **Use lowercase generics**: `dict[str, Any]` instead of `Dict[str, Any]` - **Type all function signatures** - Parameters and return types -- **No explicit type checker configured** - Ruff handles linting only +- **Type checker**: `ty` is configured for static type checking + ```bash + uv run ty check -- nextcloud_mcp_server + ``` ### Code Quality -- **Run ruff before committing**: +- **Run ruff and ty before committing**: ```bash uv run ruff check uv run ruff format + uv run ty check -- nextcloud_mcp_server ``` - **Ruff configuration** in pyproject.toml (extends select: ["I"] for import sorting) diff --git a/nextcloud_mcp_server/auth/viz_routes.py b/nextcloud_mcp_server/auth/viz_routes.py index 20eaf1c..337c472 100644 --- a/nextcloud_mcp_server/auth/viz_routes.py +++ b/nextcloud_mcp_server/auth/viz_routes.py @@ -19,9 +19,7 @@ from starlette.responses import HTMLResponse, JSONResponse from nextcloud_mcp_server.config import get_settings from nextcloud_mcp_server.search import ( - FuzzySearchAlgorithm, - HybridSearchAlgorithm, - KeywordSearchAlgorithm, + BM25HybridSearchAlgorithm, SemanticSearchAlgorithm, ) from nextcloud_mcp_server.vector.pca import PCA @@ -208,10 +206,8 @@ async def vector_visualization_html(request: Request) -> HTMLResponse:
@@ -259,25 +255,13 @@ async def vector_visualization_html(request: Request) -> HTMLResponse: - -
- - -
- - - -
-
- - - -
-
- - - -
+ +
+

+ BM25 Hybrid Search: Uses Qdrant's native Reciprocal Rank Fusion (RRF) + to automatically combine dense semantic vectors with sparse BM25 keyword vectors. + No manual weight tuning required. +

@@ -364,12 +348,9 @@ async def vector_visualization_search(request: Request) -> JSONResponse: # Parse query parameters query = request.query_params.get("query", "") - algorithm = request.query_params.get("algorithm", "hybrid") + algorithm = request.query_params.get("algorithm", "bm25_hybrid") limit = int(request.query_params.get("limit", "50")) - score_threshold = float(request.query_params.get("score_threshold", "0.7")) - semantic_weight = float(request.query_params.get("semantic_weight", "0.5")) - keyword_weight = float(request.query_params.get("keyword_weight", "0.3")) - fuzzy_weight = float(request.query_params.get("fuzzy_weight", "0.2")) + score_threshold = float(request.query_params.get("score_threshold", "0.0")) # Parse doc_types (comma-separated list, None = all types) doc_types_param = request.query_params.get("doc_types", "") @@ -433,16 +414,8 @@ async def vector_visualization_search(request: Request) -> JSONResponse: # Create search algorithm if algorithm == "semantic": search_algo = SemanticSearchAlgorithm(score_threshold=score_threshold) - elif algorithm == "keyword": - search_algo = KeywordSearchAlgorithm() - elif algorithm == "fuzzy": - search_algo = FuzzySearchAlgorithm() - elif algorithm == "hybrid": - search_algo = HybridSearchAlgorithm( - semantic_weight=semantic_weight, - keyword_weight=keyword_weight, - fuzzy_weight=fuzzy_weight, - ) + elif algorithm == "bm25_hybrid": + search_algo = BM25HybridSearchAlgorithm(score_threshold=score_threshold) else: return JSONResponse( {"success": False, "error": f"Unknown algorithm: {algorithm}"},