From 5e67277049dec9489909e795d247b4a678d7fb2c Mon Sep 17 00:00:00 2001 From: Chris Coutinho Date: Sat, 15 Nov 2025 00:00:40 +0100 Subject: [PATCH] docs: Add architecture diagrams and viz pane UI to ADR-012 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhances ADR-012 with detailed architecture visualization and UI mockup for the vector visualization pane. Added sections: - Architecture diagram showing MCP tool and viz pane integration - Data flow diagrams for both MCP requests and viz pane interactions - Detailed UI mockup with ASCII art showing: * Search configuration controls * Algorithm selector with weight sliders * Interactive 2D scatter plot (Plotly.js) * Results panel with scores * Performance comparison table - Technology stack details (htmx, Alpine.js, Plotly.js, Tailwind CSS) The diagrams illustrate how the viz pane and MCP tool share the same search algorithm implementations from search/algorithms.py, ensuring consistency between user testing interface and programmatic API. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../ADR-012-unified-multi-algorithm-search.md | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/docs/ADR-012-unified-multi-algorithm-search.md b/docs/ADR-012-unified-multi-algorithm-search.md index a788a6a..1fc3738 100644 --- a/docs/ADR-012-unified-multi-algorithm-search.md +++ b/docs/ADR-012-unified-multi-algorithm-search.md @@ -43,6 +43,105 @@ Additionally, users need a **testing interface** (viz pane) to: We will implement a **unified multi-algorithm search architecture** with the following components: +### Architecture Diagram + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ MCP Client / User Browser β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ MCP Tool Call β”‚ β”‚ Viz Pane (Browser UI) β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ nc_semantic_search( β”‚ β”‚ - Algorithm selector dropdown β”‚ β”‚ +β”‚ β”‚ query="kubernetes", β”‚ β”‚ - Weight adjustment sliders β”‚ β”‚ +β”‚ β”‚ algorithm="hybrid", β”‚ β”‚ - Interactive 2D scatter plot β”‚ β”‚ +β”‚ β”‚ semantic_weight=0.5, β”‚ β”‚ - Side-by-side comparison β”‚ β”‚ +β”‚ β”‚ keyword_weight=0.3, β”‚ β”‚ - Real-time search testing β”‚ β”‚ +β”‚ β”‚ fuzzy_weight=0.2 β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ ) β”‚ β”‚ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ + β”‚ MCP Protocol β”‚ HTTPS (htmx) + β”‚ β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ MCP Server (/app endpoint) β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Unified Search Interface (server/semantic.py) β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ @mcp.tool() nc_semantic_search(algorithm, weights...) β”‚ β”‚ +β”‚ β”‚ β”œβ”€ Validate parameters (weights sum ≀1.0) β”‚ β”‚ +β”‚ β”‚ β”œβ”€ Dispatch to algorithm selector β”‚ β”‚ +β”‚ β”‚ └─ Return ranked SearchResponse β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Algorithm Dispatcher (search/algorithms.py) β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ if algorithm == "semantic": β†’ semantic.py β”‚ β”‚ +β”‚ β”‚ if algorithm == "keyword": β†’ keyword.py β”‚ β”‚ +β”‚ β”‚ if algorithm == "fuzzy": β†’ fuzzy.py β”‚ β”‚ +β”‚ β”‚ if algorithm == "hybrid": β†’ hybrid.py (RRF fusion) β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ semantic.py β”‚ β”‚ keyword.py β”‚ β”‚ fuzzy.py β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β€’ Query Qdrant β”‚ β”‚ β€’ Token matching β”‚ β”‚ β€’ Char overlap β”‚ β”‚ +β”‚ β”‚ β€’ Cosine dist β”‚ β”‚ β€’ Title weight β”‚ β”‚ β€’ 70% threshold β”‚ β”‚ +β”‚ β”‚ β€’ Score β‰₯0.7 β”‚ β”‚ β€’ ADR-001 logic β”‚ β”‚ β€’ Simple impl β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ hybrid.py (Reciprocal Rank Fusion) β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ 1. Run algorithms in parallel (semantic, keyword, fuzzy) β”‚ β”‚ +β”‚ β”‚ 2. Collect ranked results from each β”‚ β”‚ +β”‚ β”‚ 3. Apply RRF formula: score = weight / (k + rank) β”‚ β”‚ +β”‚ β”‚ 4. Combine scores across algorithms β”‚ β”‚ +β”‚ β”‚ 5. Re-rank by combined score β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Qdrant Vector DB β”‚ β”‚ Nextcloud APIs β”‚ + β”‚ β”‚ β”‚ β”‚ + β”‚ β€’ Vector search β”‚ β”‚ β€’ Access verificationβ”‚ + β”‚ β€’ user_id filter β”‚ β”‚ β€’ Full metadata fetchβ”‚ + β”‚ β€’ Score threshold β”‚ β”‚ β€’ Permission checks β”‚ + β”‚ β€’ 768-dim embeddingsβ”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Data Flow + +#### MCP Tool Request +``` +1. Client calls nc_semantic_search(query, algorithm="hybrid", weights...) +2. Server validates parameters (weights sum ≀1.0) +3. Dispatcher routes to hybrid.py +4. Hybrid search runs semantic, keyword, fuzzy in parallel +5. RRF combines results with weighted scores +6. Access verification via Nextcloud API +7. Return ranked SearchResponse to client +``` + +#### Viz Pane Request +``` +1. User navigates to /app (Vector Visualization tab) +2. Browser loads vector-viz fragment via htmx +3. User adjusts algorithm selector and weight sliders +4. JavaScript calls same search/algorithms.py backend +5. PCA reduces vectors to 2D for visualization +6. Plotly.js renders interactive scatter plot +7. Matching results highlighted, non-matches grayed out +``` + ### 1. Core Search Algorithms Four search algorithms will be available: @@ -148,6 +247,98 @@ Update viz pane (`nextcloud_mcp_server/auth/userinfo_routes.py`) to: - Compare results across algorithms - Visualize result distribution in 2D space +#### Viz Pane UI Components + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Vector Visualization [Status] β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Search Configuration β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Query: [_______________________________________________] [Search]β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Algorithm: [Hybrid β–Ό] [Semantic] [Keyword] [Fuzzy] β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Weights (Hybrid Mode): β”‚ β”‚ +β”‚ β”‚ Semantic: [========50========] 0.5 β”‚ β”‚ +β”‚ β”‚ Keyword: [======30====== ] 0.3 β”‚ β”‚ +β”‚ β”‚ Fuzzy: [====20==== ] 0.2 β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Document Types: β˜‘ Notes β˜‘ Files β˜‘ Calendar β˜‘ Contacts β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Vector Space Visualization (PCA 2D Projection) β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β–² β”‚ β”‚ +β”‚ β”‚ PC2 β”‚ ● ● ● πŸ”΅ Matching results (full opacity) β”‚ β”‚ +β”‚ β”‚ β”‚ ● ● ● βšͺ Non-matching results (40% opacity) β”‚ β”‚ +β”‚ β”‚ β”‚ πŸ”΅ ● ● β”‚ β”‚ +β”‚ β”‚ β”‚ ● πŸ”΅ ● Hover: Show document title + excerpt β”‚ β”‚ +β”‚ β”‚ β”‚ ● ● πŸ”΅ ● Click: Open document in Nextcloud β”‚ β”‚ +β”‚ β”‚ β”€β”€β”€β”€β”Όβ”€β”€β—β”€πŸ”΅β”€β”€β—β”€β—β”€β”€β”€β”€β–Ί PC1 β”‚ β”‚ +β”‚ β”‚ β”‚ ● ● ● β”‚ β”‚ +β”‚ β”‚ β”‚ πŸ”΅ ● ● Explained Variance: β”‚ β”‚ +β”‚ β”‚ β”‚ ● ● ● PC1: 23.4% | PC2: 18.7% β”‚ β”‚ +β”‚ β”‚ β”‚ ● ● β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Search Results (12 matching documents) β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ πŸ”΅ Kubernetes Setup Guide Score: 0.87 β”‚ β”‚ +β”‚ β”‚ "...configure kubectl to connect to cluster..." β”‚ β”‚ +β”‚ β”‚ [Open in Nextcloud] β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ πŸ”΅ Container Orchestration Notes Score: 0.82 β”‚ β”‚ +β”‚ β”‚ "...deployment strategies for kubernetes..." β”‚ β”‚ +β”‚ β”‚ [Open in Nextcloud] β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ πŸ”΅ K8s Troubleshooting Score: 0.79 β”‚ β”‚ +β”‚ β”‚ "...common kuberntes errors and solutions..." β”‚ β”‚ +β”‚ β”‚ [Open in Nextcloud] β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ [Show More Results...] β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Algorithm Performance Comparison β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ Algorithm β”‚ Results β”‚ Avg Score β”‚ Time (ms) β”‚ Precision β”‚ β”‚ +β”‚ β”‚ ─────────────┼─────────┼───────────┼───────────┼─────────── β”‚ β”‚ +β”‚ β”‚ Semantic β”‚ 45 β”‚ 0.78 β”‚ 145ms β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–‘ 0.82 β”‚ β”‚ +β”‚ β”‚ Keyword β”‚ 23 β”‚ 0.91 β”‚ 42ms β”‚ β–ˆβ–ˆβ–ˆβ–‘β–‘ 0.67 β”‚ β”‚ +β”‚ β”‚ Fuzzy β”‚ 67 β”‚ 0.72 β”‚ 89ms β”‚ β–ˆβ–ˆβ–‘β–‘β–‘ 0.45 β”‚ β”‚ +β”‚ β”‚ Hybrid (RRF) β”‚ 52 β”‚ 0.84 β”‚ 198ms β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 0.89 β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**Key UI Features**: + +1. **Search Input**: Real-time query testing with instant visualization +2. **Algorithm Selector**: Dropdown + quick-select buttons +3. **Weight Sliders**: Visual adjustment with live preview (hybrid mode only) +4. **Document Type Filters**: Checkboxes for notes, files, calendar, contacts +5. **2D Scatter Plot**: Interactive Plotly.js visualization + - Blue dots = matching documents (full opacity) + - Gray dots = non-matching documents (40% opacity) + - Hover = show title + excerpt tooltip + - Click = open document in Nextcloud + - Zoom/pan controls for exploration +6. **Results Panel**: Ranked list with scores and excerpts +7. **Performance Table**: Compare algorithm speed and accuracy +8. **Explained Variance**: Show how much information PCA preserves + +**Technology Stack**: +- **Frontend**: htmx for dynamic loading, Alpine.js for reactivity +- **Visualization**: Plotly.js for interactive scatter plots +- **Styling**: Tailwind CSS (consistent with existing /app UI) +- **Backend**: Shared `search/algorithms.py` implementation + ### 5. Reciprocal Rank Fusion (RRF) for Hybrid Search Following ADR-003's design: