@@ -41,8 +41,10 @@ def filter(self, record: logging.LogRecord) -> bool:
4141litellm .suppress_debug_info = True
4242from dotenv import load_dotenv
4343
44+ from openkb .agent .compiler import compile_long_doc
4445from openkb .config import DEFAULT_CONFIG , load_config , save_config , load_global_config , register_kb
4546from openkb .converter import _registry_path , convert_document
47+ from openkb .indexer import import_cloud_document
4648from openkb .locks import atomic_write_json , atomic_write_text , kb_ingest_lock , kb_read_lock
4749from openkb .log import append_log
4850from openkb .schema import AGENTS_MD , INDEX_SEED , PAGE_CONTENT_DIRS
@@ -386,6 +388,83 @@ def _add_single_file_locked(file_path: Path, kb_dir: Path) -> Literal["added", "
386388 return "added"
387389
388390
391+ def import_from_pageindex_cloud (
392+ doc_id : str , kb_dir : Path
393+ ) -> Literal ["added" , "skipped" , "failed" ]:
394+ """Import an existing PageIndex Cloud document into the KB by ``doc_id``.
395+
396+ Fetches structure + page content from the cloud (no local PDF), compiles
397+ concepts, and registers a raw-less ``pageindex_cloud`` entry. Idempotent:
398+ re-importing the same ``doc_id`` is skipped. The user's cloud corpus is
399+ never modified.
400+ """
401+ import hashlib
402+ from openkb .state import HashRegistry
403+
404+ logger = logging .getLogger (__name__ )
405+ openkb_dir = kb_dir / ".openkb"
406+ config = load_config (openkb_dir / "config.yaml" )
407+ _setup_llm_key (kb_dir )
408+ model : str = config .get ("model" , DEFAULT_CONFIG ["model" ])
409+
410+ path_key = f"pageindex-cloud:{ doc_id } "
411+ synthetic_hash = hashlib .sha256 (path_key .encode ("utf-8" )).hexdigest ()
412+
413+ registry = HashRegistry (openkb_dir / "hashes.json" )
414+ if registry .is_known (synthetic_hash ):
415+ click .echo (f" [SKIP] Already imported from PageIndex Cloud: { doc_id } " )
416+ return "skipped"
417+
418+ click .echo (f"Importing from PageIndex Cloud: { doc_id } " )
419+ try :
420+ import_result = import_cloud_document (doc_id , kb_dir , path_key )
421+ except Exception as exc :
422+ click .echo (f" [ERROR] Import failed: { exc } " )
423+ logger .debug ("Cloud import traceback:" , exc_info = True )
424+ return "failed"
425+
426+ doc_name = import_result .doc_name
427+ summary_path = kb_dir / "wiki" / "summaries" / f"{ doc_name } .md"
428+ click .echo (f" Compiling imported doc (doc_id={ doc_id } )..." )
429+ for attempt in range (2 ):
430+ try :
431+ asyncio .run (
432+ compile_long_doc (
433+ doc_name , summary_path , doc_id , kb_dir , model ,
434+ doc_description = import_result .description ,
435+ )
436+ )
437+ break
438+ except Exception as exc :
439+ if attempt == 0 :
440+ click .echo (" Retrying compilation in 2s..." )
441+ time .sleep (2 )
442+ else :
443+ click .echo (f" [ERROR] Compilation failed: { exc } " )
444+ logger .debug ("Compilation traceback:" , exc_info = True )
445+ return "failed"
446+
447+ # Register the raw-less cloud entry only after successful compilation.
448+ registry = HashRegistry (openkb_dir / "hashes.json" )
449+ meta = {
450+ "name" : import_result .name ,
451+ "doc_name" : doc_name ,
452+ "type" : "pageindex_cloud" ,
453+ "origin" : "cloud" ,
454+ "path" : path_key ,
455+ "source_path" : _registry_path (
456+ kb_dir / "wiki" / "sources" / f"{ doc_name } .json" , kb_dir
457+ ),
458+ "doc_id" : doc_id ,
459+ }
460+ registry .remove_by_doc_name (doc_name )
461+ registry .add (synthetic_hash , meta )
462+
463+ append_log (kb_dir / "wiki" , "ingest" , doc_name )
464+ click .echo (f" [OK] { doc_name } imported from PageIndex Cloud." )
465+ return "added"
466+
467+
389468# ---------------------------------------------------------------------------
390469# CLI
391470# ---------------------------------------------------------------------------
0 commit comments