Commit
Β·
a78ff79
1
Parent(s):
fb11997
fix: Final definitive fix for model initialization race condition
Browse files- api/main.py +85 -34
- 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
|
| 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')
|
| 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
|
| 74 |
-
|
| 75 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 510 |
-
hf_hub_download(
|
| 511 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
| 519 |
-
|
|
|
|
|
|
|
| 520 |
|
| 521 |
-
# STEP 2: INITIALIZE AI COMPONENTS
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 529 |
|
| 530 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 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 |
|