|
25 | 25 | By Lyon :) Legal Ne? |
26 | 26 | """ |
27 | 27 |
|
28 | | -import json |
29 | 28 | import hashlib |
| 29 | +import json |
30 | 30 | import re |
31 | | -import time |
32 | 31 | import threading |
33 | | -import numpy as np |
34 | | -from pathlib import Path |
35 | | -from typing import List, Dict, Optional, Any, Tuple |
36 | | -from datetime import datetime |
| 32 | +import time |
37 | 33 | from collections import OrderedDict |
| 34 | +from datetime import datetime |
| 35 | +from pathlib import Path |
| 36 | +from typing import Any, Dict, List, Optional, Tuple |
38 | 37 |
|
39 | 38 | # ChromaDB |
40 | 39 | import chromadb |
|
43 | 42 | from fastembed import TextEmbedding |
44 | 43 | from fastembed.rerank.cross_encoder import TextCrossEncoder |
45 | 44 |
|
| 45 | +# FastMCP |
| 46 | +from mcp.server.fastmcp import FastMCP |
| 47 | + |
46 | 48 | # BM25 for keyword search (hybrid search) |
47 | 49 | from rank_bm25 import BM25Okapi |
| 50 | +from watchdog.events import FileSystemEventHandler |
48 | 51 |
|
49 | 52 | # File watcher for auto-reindex |
50 | 53 | from watchdog.observers import Observer |
51 | | -from watchdog.events import FileSystemEventHandler |
52 | | - |
53 | | -# FastMCP |
54 | | -from mcp.server.fastmcp import FastMCP |
55 | 54 |
|
56 | 55 | # Local imports |
57 | 56 | from .config import config |
58 | | -from .ingestion import DocumentParser, Document, parse_documents |
59 | | - |
| 57 | +from .ingestion import Document, DocumentParser |
60 | 58 |
|
61 | 59 | # ============================================================================= |
62 | 60 | # QUERY CACHE |
@@ -144,7 +142,7 @@ def __init__(self, model: str = None): |
144 | 142 | self._dim = config.embedding_dim |
145 | 143 | print(f"[INFO] Loading embedding model: {self.model_name} ({self._dim}D)...") |
146 | 144 | self._model = TextEmbedding(model_name=self.model_name) |
147 | | - print(f"[INFO] Embedding model loaded successfully") |
| 145 | + print("[INFO] Embedding model loaded successfully") |
148 | 146 |
|
149 | 147 | def __call__(self, input: List[str]) -> List[List[float]]: |
150 | 148 | """ |
@@ -206,7 +204,7 @@ def _ensure_model(self): |
206 | 204 | if self._model is None: |
207 | 205 | print(f"[INFO] Loading reranker model: {self.model_name}...") |
208 | 206 | self._model = TextCrossEncoder(model_name=self.model_name) |
209 | | - print(f"[INFO] Reranker model loaded successfully") |
| 207 | + print("[INFO] Reranker model loaded successfully") |
210 | 208 |
|
211 | 209 | def rerank( |
212 | 210 | self, |
@@ -466,7 +464,7 @@ def _check_dimension_mismatch(self) -> bool: |
466 | 464 | error_msg = str(e).lower() |
467 | 465 | if "dimension" in error_msg: |
468 | 466 | print(f"[MIGRATION] Embedding dimension mismatch detected: {e}") |
469 | | - print(f"[MIGRATION] Nuclear rebuild required.") |
| 467 | + print("[MIGRATION] Nuclear rebuild required.") |
470 | 468 | return True |
471 | 469 | # Other error — don't trigger rebuild |
472 | 470 | print(f"[WARN] Dimension check query failed (non-dimension error): {e}") |
@@ -803,7 +801,7 @@ def query( |
803 | 801 | where_filter = {"category": routed_category} |
804 | 802 |
|
805 | 803 | # Parallel Semantic + BM25 search (threaded for latency reduction) |
806 | | - from concurrent.futures import ThreadPoolExecutor, as_completed |
| 804 | + from concurrent.futures import ThreadPoolExecutor |
807 | 805 |
|
808 | 806 | semantic_results = {} |
809 | 807 | bm25_results = {} |
@@ -985,7 +983,6 @@ def _expand_with_adjacent_chunks(self, results: List[Dict], window: int = 1) -> |
985 | 983 | return results |
986 | 984 |
|
987 | 985 | for result in results: |
988 | | - doc_id_chunk = result.get("content", "") |
989 | 986 | source = result.get("source", "") |
990 | 987 | chunk_idx = result.get("chunk_index", 0) |
991 | 988 |
|
|
0 commit comments