diff --git a/Backend/app/routes/ai.py b/Backend/app/routes/ai.py index a21a482..8427dce 100644 --- a/Backend/app/routes/ai.py +++ b/Backend/app/routes/ai.py @@ -11,22 +11,53 @@ # Initialize router router = APIRouter() -# Load environment variables for Supabase and Gemini -SUPABASE_URL = os.environ.get("SUPABASE_URL") -SUPABASE_KEY = os.environ.get("SUPABASE_KEY") -GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY") +# Cache for the lazily-created Supabase client so it is built only once. +_supabase_client: Client | None = None -# Validate required environment variables -if not all([SUPABASE_URL, SUPABASE_KEY, GEMINI_API_KEY]): - raise ValueError("Missing required environment variables: SUPABASE_URL, SUPABASE_KEY, GEMINI_API_KEY") -supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY) +def get_supabase_client() -> Client: + """ + Lazily create and return the Supabase client. + + Reading the environment variables and constructing the client is deferred + to request time so that importing this module never crashes the whole app + when the required variables are missing. If the configuration is absent we + raise an HTTPException so only the AI endpoints fail (with a clear 503), + not unrelated routes. + """ + global _supabase_client + if _supabase_client is not None: + return _supabase_client + + supabase_url = os.environ.get("SUPABASE_URL") + supabase_key = os.environ.get("SUPABASE_KEY") + if not supabase_url or not supabase_key: + raise HTTPException( + status_code=503, + detail="AI service is not configured: missing SUPABASE_URL and/or SUPABASE_KEY.", + ) + + _supabase_client = create_client(supabase_url, supabase_key) + return _supabase_client + + +def get_gemini_api_key() -> str: + """Return the Gemini API key, raising a clear 503 if it is not configured.""" + gemini_api_key = os.environ.get("GEMINI_API_KEY") + if not gemini_api_key: + raise HTTPException( + status_code=503, + detail="AI service is not configured: missing GEMINI_API_KEY.", + ) + return gemini_api_key + def fetch_from_gemini(): prompt = ( "List the top 6 trending content niches for creators and brands this week. For each, provide: name (the niche), insight (a short qualitative reason why it's trending), and global_activity (a number from 1 to 5, where 5 means very high global activity in this category, and 1 means low).Return as a JSON array of objects with keys: name, insight, global_activity." ) - url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent?key={GEMINI_API_KEY}" + gemini_api_key = get_gemini_api_key() + url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-lite:generateContent?key={gemini_api_key}" # Set up retry strategy retry_strategy = Retry( total=3, @@ -60,6 +91,7 @@ def trending_niches(): - Otherwise, fetch from Gemini, store in Supabase, and return the new data. - If Gemini fails, fallback to the most recent data available. """ + supabase = get_supabase_client() today = str(date.today()) # Check if today's data exists in Supabase result = supabase.table("trending_niches").select("*").eq("fetched_at", today).execute() @@ -75,6 +107,8 @@ def trending_niches(): "fetched_at": today }).execute() result = supabase.table("trending_niches").select("*").eq("fetched_at", today).execute() + except HTTPException: + raise except Exception as e: print("Gemini fetch failed:", e) # fallback: serve most recent data