99import requests
1010
1111from collections import deque
12- from typing import Any , Callable , Dict , List , Optional , Tuple
12+ from typing import Any , Callable , Dict , List , Optional , Tuple , TYPE_CHECKING
1313import hashlib
1414import json as _json
1515from rq import Queue , job , exceptions
1616from sqlalchemy import not_
1717
18- from application .utils .external_project_parsers .base_parser import BaseParser
19- from application .utils .external_project_parsers .parsers import master_spreadsheet_parser
2018from application import create_app # type: ignore
2119from application .config import CMDConfig
2220from application .database import db
2624from application .utils import spreadsheet as sheet_utils
2725from application .utils import redis
2826from application .utils import db_backend
29- from alive_progress import alive_bar
30- from application .prompt_client import prompt_client as prompt_client
3127from application .utils import gap_analysis
3228from application .utils import cres_csv_export
3329
30+ if TYPE_CHECKING :
31+ from application .prompt_client import prompt_client as prompt_client
32+
3433logging .basicConfig ()
3534logger = logging .getLogger (__name__ )
3635logger .setLevel (logging .INFO )
@@ -469,6 +468,8 @@ def _standard_structure_fingerprint(resource_name: str) -> str:
469468 return hashlib .sha256 (payload .encode ("utf-8" )).hexdigest ()
470469
471470 conn = redis .connect ()
471+ from application .prompt_client import prompt_client as prompt_client
472+
472473 ph = prompt_client .PromptHandler (database = collection )
473474 importing_name = standard_entries [0 ].name
474475 effective_calculate_gap_analysis = (
@@ -541,7 +542,7 @@ def _standard_structure_fingerprint(resource_name: str) -> str:
541542def parse_standards_from_spreadsheeet (
542543 cre_file : List [Dict [str , Any ]],
543544 cache_location : str ,
544- prompt_handler : prompt_client .PromptHandler ,
545+ prompt_handler : " prompt_client.PromptHandler" ,
545546) -> None :
546547 """given a csv with standards, build a list of standards in the db"""
547548 if not cre_file :
@@ -568,6 +569,10 @@ def parse_standards_from_spreadsheeet(
568569 from application .utils import import_pipeline
569570
570571 collection = db_connect (cache_location )
572+ from application .utils .external_project_parsers .parsers import (
573+ master_spreadsheet_parser ,
574+ )
575+
571576 parse_result = master_spreadsheet_parser .MasterSpreadsheetParser .parse_rows (
572577 cre_file
573578 )
@@ -682,6 +687,8 @@ def download_gap_analysis_from_upstream(cache: str) -> None:
682687 pairs = [(sa , sb ) for sa in standards for sb in standards if sa != sb ]
683688
684689 if os .environ .get ("BENCHMARK_MODE" ) == "1" :
690+ from alive_progress import alive_bar
691+
685692 with alive_bar (len (pairs ), title = "Fetching upstream Gap Analysis" ) as bar :
686693 for sa , sb in pairs :
687694 res = requests .get (
@@ -846,11 +853,15 @@ def backfill_gap_analysis_only(
846853 jobs .append (j )
847854
848855 if jobs :
856+ from alive_progress import alive_bar
857+
849858 with alive_bar (
850859 len (jobs ), title = f"GA batch { i // batch_size + 1 } "
851860 ) as bar :
852861 redis .wait_for_jobs (jobs , bar )
853862 else :
863+ from alive_progress import alive_bar
864+
854865 with alive_bar (len (batch ), title = f"GA batch { i // batch_size + 1 } " ) as bar :
855866 for sa , sb in batch :
856867 _compute_pair_direct (collection , sa , sb )
@@ -901,6 +912,8 @@ def run(args: argparse.Namespace) -> None: # pragma: no cover
901912 cache .delete_nodes (args .delete_resource )
902913
903914 # individual resource importing
915+ from application .utils .external_project_parsers .base_parser import BaseParser
916+
904917 if args .zap_in :
905918 from application .utils .external_project_parsers .parsers import zap_alerts_parser
906919
@@ -1014,6 +1027,8 @@ def run(args: argparse.Namespace) -> None: # pragma: no cover
10141027
10151028
10161029def ai_client_init (database : db .Node_collection ):
1030+ from application .prompt_client import prompt_client as prompt_client
1031+
10171032 return prompt_client .PromptHandler (database = database )
10181033
10191034
@@ -1053,6 +1068,8 @@ def prepare_for_review(cache: str) -> Tuple[str, str]:
10531068
10541069
10551070def generate_embeddings (db_url : str ) -> None :
1071+ from application .prompt_client import prompt_client as prompt_client
1072+
10561073 database = db_connect (path = db_url )
10571074 prompt_client .PromptHandler (database , load_all_embeddings = True )
10581075
@@ -1109,20 +1126,20 @@ def run_librarian(
11091126 "land W8); running in dry-run mode"
11101127 )
11111128
1129+ from application .prompt_client import prompt_client as prompt_client
1130+
11121131 cfg = load_config ()
11131132 database = db_connect (path = cache_file )
11141133 ph = prompt_client .PromptHandler (database = database )
11151134
11161135 backend = RetrieverBackend (cfg .retriever_backend )
11171136 if backend is RetrieverBackend .pgvector :
1118- dialect = database .session .connection ().dialect .name
1119- if dialect != "postgresql" :
1120- logger .warning (
1121- "CRE_LIBRARIAN_RETRIEVER_BACKEND=pgvector selected on a %r "
1122- "database, but the pgvector backend needs Postgres with the "
1123- "embedding_vec column (lands W8) and will fail at retrieve() "
1124- "time here. Set the backend to in_memory until then." ,
1125- dialect ,
1137+ # Hard-fail: do not silently fall back to in_memory / sklearn CSV paths.
1138+ from application .database .pgvector_utils import fail_pgvector_unavailable
1139+
1140+ if not database .can_use_pgvector_similarity ():
1141+ fail_pgvector_unavailable (
1142+ context = "CRE_LIBRARIAN_RETRIEVER_BACKEND=pgvector"
11261143 )
11271144 # The CRE ids present in the hub are exactly the known ids the explicit
11281145 # resolver may auto-link to (W2 seeded this from the golden set; here it is
@@ -1136,13 +1153,16 @@ def run_librarian(
11361153 if backend is RetrieverBackend .in_memory
11371154 else None
11381155 )
1156+ pg_connection = None
1157+ if backend is RetrieverBackend .pgvector :
1158+ pg_connection = database .session .connection ()
11391159 retriever = build_retriever (
11401160 backend ,
11411161 embed_fn = ph .get_text_embeddings ,
11421162 top_k = cfg .top_k_retrieval ,
11431163 threshold = cfg .link_threshold ,
11441164 pool = pool ,
1145- connection = database . session . connection () ,
1165+ connection = pg_connection ,
11461166 )
11471167
11481168 # C.2 reranker: reads each (section, candidate-CRE) pair together and
@@ -1207,6 +1227,8 @@ def run_librarian(
12071227
12081228def regenerate_embeddings (db_url : str ) -> None :
12091229 """Wipe all embedding rows, then rebuild (CRE + every node type) like ``--generate_embeddings``."""
1230+ from application .prompt_client import prompt_client as prompt_client
1231+
12101232 database = db_connect (path = db_url )
12111233 removed = database .delete_all_embeddings ()
12121234 logger .info ("Removed %s embedding rows; rebuilding embeddings" , removed )
0 commit comments