@@ -188,49 +188,68 @@ def index_long_document(
188188 if attempt == max_retries :
189189 raise RuntimeError (f"Failed to index { pdf_path .name } after { max_retries } attempts: { exc } " ) from exc
190190
191- # Fetch complete document (metadata + structure + text)
192- doc = col .get_document (doc_id , include_text = True )
193- indexed_doc_name : str = doc .get ("doc_name" , pdf_path .stem )
194- description : str = doc .get ("doc_description" , "" )
195- structure : list = doc .get ("structure" , [])
196-
197- # Debug: print doc keys and page_count to diagnose get_page_content range
198- logger .info ("Doc keys: %s" , list (doc .keys ()))
199- logger .info ("page_count from doc: %s" , doc .get ("page_count" , "NOT PRESENT" ))
200-
201- tree = {
202- "doc_name" : indexed_doc_name ,
203- "doc_description" : description ,
204- "structure" : structure ,
205- }
206-
207- # Write wiki/sources/ — per-page content
208- sources_dir = kb_dir / "wiki" / "sources"
209- sources_dir .mkdir (parents = True , exist_ok = True )
210- images_dir = sources_dir / "images" / source_name
211-
212- all_pages : list [dict [str , Any ]] = []
213- if pageindex_api_key :
214- # Cloud mode: fetch OCR'd markdown from PageIndex. get_page_content
215- # requires a page range, so pass "1-N".
216- page_count = _get_pdf_page_count (pdf_path )
217- try :
218- all_pages = _normalize_page_content (col .get_page_content (doc_id , f"1-{ page_count } " ))
219- except Exception as exc :
220- logger .warning ("Cloud get_page_content failed for %s: %s" , pdf_path .name , exc )
221-
222- if not all_pages :
191+ # The PageIndex blob for doc_id is now durably on disk. The add mutation no
192+ # longer eagerly snapshots .openkb/files — it registers the new blob via
193+ # snapshot.track_new() only on a successful return — so if any step below
194+ # fails, delete the document we just added. Otherwise the blob leaks as an
195+ # orphan that pageindex.db (rolled back by the snapshot) no longer refs and
196+ # no reaper reclaims.
197+ try :
198+ # Fetch complete document (metadata + structure + text)
199+ doc = col .get_document (doc_id , include_text = True )
200+ indexed_doc_name : str = doc .get ("doc_name" , pdf_path .stem )
201+ description : str = doc .get ("doc_description" , "" )
202+ structure : list = doc .get ("structure" , [])
203+
204+ # Debug: print doc keys and page_count to diagnose get_page_content range
205+ logger .info ("Doc keys: %s" , list (doc .keys ()))
206+ logger .info ("page_count from doc: %s" , doc .get ("page_count" , "NOT PRESENT" ))
207+
208+ tree = {
209+ "doc_name" : indexed_doc_name ,
210+ "doc_description" : description ,
211+ "structure" : structure ,
212+ }
213+
214+ # Write wiki/sources/ — per-page content
215+ sources_dir = kb_dir / "wiki" / "sources"
216+ sources_dir .mkdir (parents = True , exist_ok = True )
217+ images_dir = sources_dir / "images" / source_name
218+
219+ all_pages : list [dict [str , Any ]] = []
223220 if pageindex_api_key :
224- logger .warning ("Cloud returned no pages for %s; falling back to local pymupdf" , pdf_path .name )
225- all_pages = _normalize_page_content (_convert_pdf_to_pages (pdf_path , source_name , images_dir ))
226-
227- if not all_pages :
228- raise RuntimeError (f"No page content extracted for { pdf_path .name } " )
229-
230- _write_long_doc_artifacts (
231- tree , all_pages , source_name , doc_id , kb_dir , description = description
232- )
233- return IndexResult (doc_id = doc_id , description = description , tree = tree )
221+ # Cloud mode: fetch OCR'd markdown from PageIndex. get_page_content
222+ # requires a page range, so pass "1-N".
223+ page_count = _get_pdf_page_count (pdf_path )
224+ try :
225+ all_pages = _normalize_page_content (col .get_page_content (doc_id , f"1-{ page_count } " ))
226+ except Exception as exc :
227+ logger .warning ("Cloud get_page_content failed for %s: %s" , pdf_path .name , exc )
228+
229+ if not all_pages :
230+ if pageindex_api_key :
231+ logger .warning ("Cloud returned no pages for %s; falling back to local pymupdf" , pdf_path .name )
232+ all_pages = _normalize_page_content (_convert_pdf_to_pages (pdf_path , source_name , images_dir ))
233+
234+ if not all_pages :
235+ raise RuntimeError (f"No page content extracted for { pdf_path .name } " )
236+
237+ _write_long_doc_artifacts (
238+ tree , all_pages , source_name , doc_id , kb_dir , description = description
239+ )
240+ return IndexResult (doc_id = doc_id , description = description , tree = tree )
241+ except BaseException :
242+ # Best-effort: remove the blob this add created. A failure here (e.g. a
243+ # second interrupt) only means the blob may stay orphaned — the original
244+ # error still propagates so the caller (mutation coordinator) rolls back
245+ # everything else it snapshotted.
246+ try :
247+ col .delete_document (doc_id )
248+ except Exception :
249+ logger .warning (
250+ "PageIndex cleanup of %s failed after error; blob may be orphaned" , doc_id
251+ )
252+ raise
234253
235254
236255# PageIndex's get_page_content rejects a single page range covering more than
0 commit comments