amitbhatt6075 commited on
Commit
a78ff79
Β·
1 Parent(s): fb11997

fix: Final definitive fix for model initialization race condition

Browse files
Files changed (2) hide show
  1. api/main.py +85 -34
  2. core/creative_chat.py +5 -27
api/main.py CHANGED
@@ -27,10 +27,13 @@ from core.strategist import AIStrategist
27
  from core.predictor import rank_influencers_by_match
28
  from core.utils import get_supabase_client
29
  from core.anomaly_detector import find_anomalies
30
- from core.matcher import load_embedding_model, rank_documents_by_similarity
31
  from core.utils import get_supabase_client, extract_colors_from_url
32
  from core.document_parser import parse_pdf_from_url
33
- from core.creative_chat import CreativeDirector
 
 
 
34
 
35
  try:
36
  from core.rag.store import VectorStore
@@ -48,7 +51,7 @@ MODEL_REPO = "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF"
48
  MODEL_FILENAME = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
49
  MODEL_SAVE_DIRECTORY = os.path.join(os.environ.get("WRITABLE_DIR", "/data"), "llm_model")
50
  LLAMA_MODEL_PATH = os.path.join(MODEL_SAVE_DIRECTORY, MODEL_FILENAME)
51
- EMBEDDING_MODEL_PATH = os.path.join(ROOT_DIR, 'embedding_model') # This path is correct
52
  DB_PATH = os.path.join(os.environ.get("WRITABLE_DIR", "/tmp"), "vector_db_persistent")
53
 
54
  _llm_instance: Optional[Llama] = None
@@ -70,18 +73,9 @@ _performance_scorer = None
70
  def to_snake(name: str) -> str:
71
  return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
72
 
73
- class ChatMessage(BaseModel):
74
- role: str # "user" or "assistant"
75
- content: str
76
-
77
- class CreativeChatRequest(BaseModel):
78
- message: str
79
- history: List[ChatMessage]
80
- task_context: str
81
-
82
- class FinalizeScriptRequest(BaseModel):
83
- history: List[ChatMessage]
84
- task_context: str
85
 
86
  class FinalScriptResponse(BaseModel):
87
  hook: str
@@ -498,37 +492,94 @@ app = FastAPI(title="Reachify AI Service (Deploy-Ready)", version="11.0.0")
498
 
499
  @app.on_event("startup")
500
  def startup_event():
501
- global _llm_instance, _creative_director, _ai_strategist, _support_agent
 
 
 
 
502
 
 
503
  print("--- πŸš€ AI Service Starting Up... ---")
504
-
505
- # STEP 1: DOWNLOAD AND LOAD THE LLM
506
  try:
507
  os.makedirs(MODEL_SAVE_DIRECTORY, exist_ok=True)
508
  if not os.path.exists(LLAMA_MODEL_PATH):
509
- print(f" - Downloading LLM: {MODEL_FILENAME}...")
510
- hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILENAME, local_dir=MODEL_SAVE_DIRECTORY)
511
- print(" - βœ… Download complete.")
 
 
 
 
 
 
 
512
 
513
- print(" - Loading LLM into memory...")
514
  _llm_instance = Llama(model_path=LLAMA_MODEL_PATH, n_gpu_layers=0, n_ctx=2048, verbose=False)
515
- print(" - βœ… LLM Loaded.")
516
 
517
  except Exception as e:
518
- print(f" - ❌ FATAL ERROR: Could not load LLM. Error: {e}")
519
- return
 
 
520
 
521
- # STEP 2: INITIALIZE AI COMPONENTS that need the LLM
522
- print(" - Initializing AI components...")
523
- try:
524
- _creative_director = CreativeDirector(llm_instance=_llm_instance)
525
- _ai_strategist = AIStrategist(llm_instance=_llm_instance, store=None) # Assuming store is optional
526
- _support_agent = SupportAgent(llm_instance=_llm_instance, embedding_path=EMBEDDING_MODEL_PATH, db_path=DB_PATH)
527
- except Exception as e:
528
- print(f" - ❌ Error initializing AI agents: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
529
 
530
- print("\n--- βœ… AI Service startup sequence finished! ---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
531
 
 
 
532
  @app.get("/")
533
  def health_check():
534
  if _llm_instance:
 
27
  from core.predictor import rank_influencers_by_match
28
  from core.utils import get_supabase_client
29
  from core.anomaly_detector import find_anomalies
30
+ from core.matcher import rank_documents_by_similarity
31
  from core.utils import get_supabase_client, extract_colors_from_url
32
  from core.document_parser import parse_pdf_from_url
33
+ from core.creative_chat import CreativeDirector
34
+ from core.strategist import AIStrategist
35
+ from core.support_agent import SupportAgent
36
+ from core.matcher import load_embedding_model
37
 
38
  try:
39
  from core.rag.store import VectorStore
 
51
  MODEL_FILENAME = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
52
  MODEL_SAVE_DIRECTORY = os.path.join(os.environ.get("WRITABLE_DIR", "/data"), "llm_model")
53
  LLAMA_MODEL_PATH = os.path.join(MODEL_SAVE_DIRECTORY, MODEL_FILENAME)
54
+ EMBEDDING_MODEL_PATH = os.path.join(ROOT_DIR, 'embedding_model')
55
  DB_PATH = os.path.join(os.environ.get("WRITABLE_DIR", "/tmp"), "vector_db_persistent")
56
 
57
  _llm_instance: Optional[Llama] = None
 
73
  def to_snake(name: str) -> str:
74
  return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
75
 
76
+ class CreativeChatRequest(BaseModel): message: str; history: list; task_context: str
77
+ class FinalizeScriptRequest(BaseModel): history: list; task_context: str
78
+ class FinalScriptResponse(BaseModel): hook: str; script: str; visuals: List[str]; tools: List[str]
 
 
 
 
 
 
 
 
 
79
 
80
  class FinalScriptResponse(BaseModel):
81
  hook: str
 
492
 
493
  @app.on_event("startup")
494
  def startup_event():
495
+ # Make sure we can modify the global variables
496
+ global _llm_instance, _creative_director, _support_agent, _ai_strategist, _vector_store, \
497
+ _budget_predictor, _influencer_matcher, _performance_predictor, _payout_forecaster, \
498
+ _earnings_optimizer, _earnings_encoder, _likes_predictor, _comments_predictor, \
499
+ _revenue_forecaster, _performance_scorer
500
 
501
+ # --- STEP 1: DOWNLOAD AND LOAD THE LLM MODEL ---
502
  print("--- πŸš€ AI Service Starting Up... ---")
 
 
503
  try:
504
  os.makedirs(MODEL_SAVE_DIRECTORY, exist_ok=True)
505
  if not os.path.exists(LLAMA_MODEL_PATH):
506
+ print(f" - LLM model not found locally. Downloading '{MODEL_FILENAME}'...")
507
+ hf_hub_download(
508
+ repo_id=MODEL_REPO,
509
+ filename=MODEL_FILENAME,
510
+ local_dir=MODEL_SAVE_DIRECTORY,
511
+ local_dir_use_symlinks=False
512
+ )
513
+ print(" - βœ… Model downloaded successfully.")
514
+ else:
515
+ print(f" - LLM model found locally at {LLAMA_MODEL_PATH}. Skipping download.")
516
 
517
+ print(" - Loading Llama LLM into memory...")
518
  _llm_instance = Llama(model_path=LLAMA_MODEL_PATH, n_gpu_layers=0, n_ctx=2048, verbose=False)
519
+ print(" - βœ… LLM Loaded successfully.")
520
 
521
  except Exception as e:
522
+ print(f" - ❌ FATAL ERROR: Could not download or load the LLM model. LLM-dependent features will be disabled.")
523
+ traceback.print_exc()
524
+ _llm_instance = None # Ensure global variable is None on failure
525
+ # We don't return here, so that other non-LLM models can still load.
526
 
527
+ # --- STEP 2: INITIALIZE ALL AI COMPONENTS THAT NEED THE LLM ---
528
+ # This part only runs if the LLM was loaded successfully
529
+ if _llm_instance:
530
+ try:
531
+ print(" - Initializing AI components that depend on LLM...")
532
+
533
+ # Initialize CreativeDirector
534
+ _creative_director = CreativeDirector(llm_instance=_llm_instance)
535
+
536
+ # Initialize VectorStore (if it exists)
537
+ if VectorStore:
538
+ _vector_store = VectorStore()
539
+ print(" - RAG Engine Ready.")
540
+
541
+ # Initialize AIStrategist
542
+ _ai_strategist = AIStrategist(llm_instance=_llm_instance, store=_vector_store)
543
+
544
+ # Initialize SupportAgent
545
+ _support_agent = SupportAgent(llm_instance=_llm_instance, embedding_path=EMBEDDING_MODEL_PATH, db_path=DB_PATH)
546
+
547
+ print(" - βœ… Core AI components (Director, Strategist, Agent) are online.")
548
 
549
+ except Exception as e:
550
+ print(f" - ❌ FAILED to initialize core AI components: {e}")
551
+ traceback.print_exc()
552
+ else:
553
+ print(" - ⚠️ SKIPPING initialization of LLM-dependent components because LLM failed to load.")
554
+
555
+ # --- STEP 3: LOAD ALL OTHER MODELS (These don't depend on the LLM) ---
556
+ print(" - Loading ML models from joblib files...")
557
+ model_paths = {
558
+ 'budget': ('_budget_predictor', 'budget_predictor_v1.joblib'),
559
+ 'matcher': ('_influencer_matcher', 'influencer_matcher_v1.joblib'),
560
+ 'performance': ('_performance_predictor', 'performance_predictor_v1.joblib'),
561
+ 'payout': ('_payout_forecaster', 'payout_forecaster_v1.joblib'),
562
+ 'earnings': ('_earnings_optimizer', 'earnings_model.joblib'),
563
+ 'earnings_encoder': ('_earnings_encoder', 'earnings_encoder.joblib'),
564
+ 'likes_predictor': ('_likes_predictor', 'likes_predictor_v1.joblib'),
565
+ 'comments_predictor': ('_comments_predictor', 'comments_predictor_v1.joblib'),
566
+ 'revenue_forecaster': ('_revenue_forecaster', 'revenue_forecaster_v1.joblib'),
567
+ 'performance_scorer': ('_performance_scorer', 'performance_scorer_v1.joblib'),
568
+ }
569
+ for name, (var, file) in model_paths.items():
570
+ path = os.path.join(MODELS_DIR, file)
571
+ try:
572
+ globals()[var] = joblib.load(path)
573
+ print(f" - Loaded {name} model.")
574
+ except FileNotFoundError:
575
+ globals()[var] = None
576
+ print(f" - ⚠️ WARNING: Model '{name}' not found at {path}. Endpoint will be disabled.")
577
+
578
+ print(" - Initializing Text Embedding Model...")
579
+ load_embedding_model(EMBEDDING_MODEL_PATH)
580
 
581
+ print("\n--- βœ… AI Service startup sequence finished! ---")
582
+
583
  @app.get("/")
584
  def health_check():
585
  if _llm_instance:
core/creative_chat.py CHANGED
@@ -1,12 +1,9 @@
1
-
2
  import os
3
  import sys
4
  from llama_cpp import Llama
5
  import json
6
  import re
7
 
8
- # Path setup to import VectorStore from the parent directory
9
- # This logic is correct and remains the same
10
  current_dir = os.path.dirname(os.path.abspath(__file__))
11
  parent_dir = os.path.dirname(current_dir)
12
  sys.path.append(parent_dir)
@@ -14,30 +11,11 @@ sys.path.append(parent_dir)
14
  from core.rag.store import VectorStore
15
 
16
  class CreativeDirector:
17
- def __init__(self):
18
- """Initialize Model and Memory once to save time."""
19
-
20
- # βœ… THE FIX IS HERE: We now look for the model in the writable directory
21
- # where main.py downloads it, not in the read-only application directory.
22
- model_name = "tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf"
23
- # Hugging Face Spaces provides '/data' as a writable persistent directory.
24
- writable_dir = os.environ.get("WRITABLE_DIR", "/data")
25
- model_path = os.path.join(writable_dir, "llm_model", model_name)
26
- # =====================================================================
27
-
28
- if not os.path.exists(model_path):
29
- # This error will now correctly point to the writable directory
30
- raise FileNotFoundError(f"❌ Model not found at: {model_path}. Please check the download logic in main.py.")
31
-
32
- print("🧠 Loading AI Director (TinyLlama - SUPER FAST MODE)...")
33
-
34
- self.llm = Llama(
35
- model_path=model_path,
36
- n_ctx=512,
37
- n_batch=32,
38
- n_threads=4,
39
- verbose=False
40
- )
41
  self.memory = VectorStore(collection_name="creative_mind")
42
  print("βœ… AI Director is Online.")
43
 
 
 
1
  import os
2
  import sys
3
  from llama_cpp import Llama
4
  import json
5
  import re
6
 
 
 
7
  current_dir = os.path.dirname(os.path.abspath(__file__))
8
  parent_dir = os.path.dirname(current_dir)
9
  sys.path.append(parent_dir)
 
11
  from core.rag.store import VectorStore
12
 
13
  class CreativeDirector:
14
+ def __init__(self, llm_instance: Llama):
15
+ if not llm_instance:
16
+ raise ValueError("CreativeDirector received an invalid LLM instance.")
17
+ print("🧠 Initializing AI Director with pre-loaded LLM...")
18
+ self.llm = llm_instance
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  self.memory = VectorStore(collection_name="creative_mind")
20
  print("βœ… AI Director is Online.")
21