@@ -88,6 +88,29 @@ def _resolve_data_asset(filename: str) -> Path:
8888 print (f"Failed to load FAISS/model: { e } " , flush = True )
8989 import traceback ; traceback .print_exc ()
9090
91+
92+ def _expected_vector_rows () -> Optional [int ]:
93+ try :
94+ con = sqlite3 .connect (DB_PATH )
95+ row = con .execute (
96+ "SELECT COUNT(*) FROM chunks WHERE source != 'gaokao' AND text IS NOT NULL AND text != ''"
97+ ).fetchone ()
98+ con .close ()
99+ return int (row [0 ]) if row else None
100+ except Exception :
101+ return None
102+
103+
104+ if faiss_index is not None :
105+ expected_rows = _expected_vector_rows ()
106+ if expected_rows is not None and faiss_index .ntotal != expected_rows :
107+ print (
108+ f"FAISS disabled: index vectors={ faiss_index .ntotal } , expected_rows={ expected_rows } . "
109+ "Rebuild textbook_chunks.index to re-enable dense retrieval." ,
110+ flush = True ,
111+ )
112+ faiss_index = None
113+
91114# ── Jieba custom dictionary ──────────────────────────────────────────
92115try :
93116 import jieba
@@ -226,6 +249,7 @@ def search(
226249 d = dict (r )
227250 # Add basic highlighting for the LIKE snippet
228251 d ['snippet' ] = d ['snippet' ].replace (clean_q , f"<mark>{ clean_q } </mark>" )
252+ d ["match_channel" ] = "exact"
229253 rows .append (d )
230254 existing_ids .add (d ['id' ])
231255
@@ -252,7 +276,9 @@ def search(
252276
253277 for r in fts_rows :
254278 if r ['id' ] not in existing_ids :
255- rows .append (dict (r ))
279+ d = dict (r )
280+ d ["match_channel" ] = "fts"
281+ rows .append (d )
256282 existing_ids .add (r ['id' ])
257283
258284 # 3. Sort by rank (exact matches get -100.0 so they appear first) and trim to limit
@@ -290,6 +316,7 @@ def search(
290316 "text" : text [:2000 ],
291317 "image_count" : img_count ,
292318 "source" : r ["source" ] or "mineru" ,
319+ "match_channel" : r .get ("match_channel" , "fts" ),
293320 "page_url" : page_url ,
294321 "page_num" : page_num ,
295322 "total_pages" : bm_info .get ("pages" , 0 ),
@@ -1631,7 +1658,7 @@ def health():
16311658def page_image (
16321659 book_key : str = Query (..., description = "book_key from search result" ),
16331660 page : int = Query (..., ge = 0 , description = "Page number (0-indexed)" ),
1634- context : int = Query (2 , ge = 0 , le = 5 , description = "Number of context pages before/after" ),
1661+ context : int = Query (4 , ge = 0 , le = 8 , description = "Number of context pages before/after" ),
16351662):
16361663 """Return R2 CDN URLs for a page and surrounding context pages."""
16371664 # Find the book in book_map
0 commit comments