import os import time import requests from itertools import cycle # Try to load .env (local dev only, ignored in production) try: from dotenv import load_dotenv load_dotenv() except ImportError: pass # Streamlit-safe logging def log_message(msg, level="info"): try: import streamlit as st if level == "error": st.error(msg) elif level == "warning": st.warning(msg) else: st.info(msg) except ImportError: print(f"[{level.upper()}] {msg}") APP_TITLE = "PlanMate" APP_TAGLINE = "AI Powered smart trip planner" THEME = { "bg": "#fcfcfc", "text": "#383838", "label": "#153d15", "border": "#153d15", } CURRENCY = "PKR" UNITS = "metric" LANGUAGE = "en" # API Base URLs AMADEUS_BASE = "https://test.api.amadeus.com" OPENWEATHER_BASE = "https://api.openweathermap.org" OPENTRIPMAP_BASE = "https://api.opentripmap.com/0.1/en" # ---------- Hugging Face Secrets Configuration ---------- def get_secret(key, default=None): """ Get secret from environment first (production), fallback to .env for local dev. """ value = os.getenv(key) if value is None and default is not None: return default elif value is None: log_message(f"Missing required secret: {key}", level="error") raise RuntimeError(f"Missing required secret: {key}") return value def get_env(key: str) -> str: val = os.getenv(key) if not val: raise RuntimeError(f"Missing required environment variable: {key}") return val # ---------- Gemini Key Rotation ---------- gemini_keys = os.getenv("GEMINI_API_KEYS", "").split(",") gemini_keys = [k.strip() for k in gemini_keys if k.strip()] if not gemini_keys: raise RuntimeError("No GEMINI_API_KEYS found in environment!") gemini_cycle = cycle(gemini_keys) _current_gemini_key = next(gemini_cycle) def get_gemini_key(): """Get the current Gemini API key""" return _current_gemini_key def rotate_gemini_key(): """Rotate to the next Gemini API key (when quota exceeded)""" global _current_gemini_key _current_gemini_key = next(gemini_cycle) log_message(f"Rotated to new Gemini API key: {_current_gemini_key}", level="warning") return _current_gemini_key # ---------- Gemini Helper Function ---------- def make_gemini_request(prompt, model="gemini-2.0-flash-lite", max_retries=3): """ Make a Gemini API request with automatic key rotation on quota errors. """ import google.generativeai as genai for attempt in range(max_retries): try: # Configure SDK with current key genai.configure(api_key=get_gemini_key()) # Create the model model_instance = genai.GenerativeModel(model) # Generate response response = model_instance.generate_content(prompt) return response.text # or response.candidates, depending on usage except Exception as e: error_message = str(e) # Case 1: Quota exceeded (429) if "429" in error_message or "quota" in error_message.lower(): rotate_gemini_key() time.sleep(2) # short delay before retry continue # Case 2: Network/HTTP error elif isinstance(e, requests.exceptions.RequestException): log_message(f"Network issue: {e}. Retrying...", level="warning") time.sleep(2) continue # Any other error → stop immediately raise e raise RuntimeError("Gemini request failed after rotating all keys") # ---------- Other API Keys ---------- def get_amadeus_credentials(): return get_secret("AMADEUS_CLIENT_ID"), get_secret("AMADEUS_CLIENT_SECRET") def get_openweather_key(): return get_secret("OPENWEATHER_API_KEY") def get_opentripmap_key(): return get_secret("OPENTRIPMAP_API_KEY")