Spaces:
Sleeping
Sleeping
| 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") | |