From ca1309adff3d302c46226449be24ea6aa5e3f0fa Mon Sep 17 00:00:00 2001 From: Floke Date: Sat, 3 Jan 2026 09:33:33 +0000 Subject: [PATCH] fix(config): Ensure API keys are loaded by orchestrator - Reverts docker-compose.yml to use volume mount for gemini_api_key.txt, due to format constraints. - Restores and refines Config.load_api_keys() in config.py. - **Crucially, calls Config.load_api_keys() at the start of gtm_architect_orchestrator.py to ensure keys are loaded.** - Adjusts _get_gemini_api_key in helpers.py to prioritize keys from Config.API_KEYS. - This definitively addresses the 'API Key missing' error by guaranteeing key initialization. --- config.py | 23 ++++++++++++++++++++++- docker-compose.yml | 9 +++++---- gtm_architect_orchestrator.py | 5 +++++ helpers.py | 25 ++++++++++++------------- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/config.py b/config.py index b1ab04bd..a1375be5 100644 --- a/config.py +++ b/config.py @@ -482,9 +482,30 @@ class Config: "NetCologne" ] - # --- API Schlüssel Speicherung (werden jetzt via Env Var geladen) --- + # --- API Schlüssel Speicherung (werden in main() geladen) --- API_KEYS = {} + @classmethod + def load_api_keys(cls): + """Laedt API-Schluessel aus den definierten Dateien.""" + logger = logging.getLogger(__name__) + logger.info("Lade API-Schluessel...") + cls.API_KEYS['openai'] = cls._load_key_from_file(API_KEY_FILE) + cls.API_KEYS['serpapi'] = cls._load_key_from_file(SERP_API_KEY_FILE) + cls.API_KEYS['genderize'] = cls._load_key_from_file(GENDERIZE_API_KEY_FILE) + + if cls.API_KEYS.get('openai'): + # Hier nehmen wir an, dass 'openai' für Gemini verwendet wird (Legacy) + # Falls in helpers.py direkt auf 'gemini' zugegriffen wird, müsste das hier auch gesetzt werden. + logger.info("Gemini API Key (via 'openai' slot) erfolgreich geladen.") + else: + logger.warning("Gemini API Key konnte nicht geladen werden. KI-Funktionen sind deaktiviert.") + + if not cls.API_KEYS.get('serpapi'): + logger.warning("SerpAPI Key konnte nicht geladen werden. Suchfunktionen sind deaktiviert.") + if not cls.API_KEYS.get('genderize'): + logger.warning("Genderize API Key konnte nicht geladen werden. Geschlechtserkennung ist eingeschraenkt.") + @staticmethod def _load_key_from_file(filepath): """Hilfsfunktion zum Laden eines Schluessels aus einer Datei.""" diff --git a/docker-compose.yml b/docker-compose.yml index 97206dda..56b89a21 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -101,8 +101,9 @@ services: - ./gtm_db_manager.py:/app/gtm_db_manager.py - ./gtm_projects.db:/app/gtm_projects.db - ./Log_from_docker:/app/Log_from_docker - environment: + # Mount API keys + - ./gemini_api_key.txt:/app/gemini_api_key.txt + - ./serpapikey.txt:/app/serpapikey.txt + environment: - PYTHONUNBUFFERED=1 - - DB_PATH=/app/gtm_projects.db - env_file: - - ./gemini_api_key.txt \ No newline at end of file + - DB_PATH=/app/gtm_projects.db \ No newline at end of file diff --git a/gtm_architect_orchestrator.py b/gtm_architect_orchestrator.py index 5513e784..5c03f84b 100644 --- a/gtm_architect_orchestrator.py +++ b/gtm_architect_orchestrator.py @@ -15,6 +15,7 @@ import gtm_db_manager as db_manager sys.path.append(os.path.dirname(os.path.abspath(__file__))) from helpers import call_gemini_flash, scrape_website_details +from config import Config, BASE_DIR # Import Config and BASE_DIR LOG_DIR = "Log_from_docker" if not os.path.exists(LOG_DIR): @@ -33,6 +34,10 @@ logging.basicConfig( ] ) logging.info(f"GTM Architect Orchestrator v{{ORCHESTRATOR_VERSION}} ({{run_timestamp}}) starting...") + +# !!! CRITICAL FIX: Load API keys at the very beginning !!! +# This ensures Config.API_KEYS is populated before any AI functions are called. +Config.load_api_keys() def log_and_save(project_id, step_name, data_type, content): logging.info(f"Project {project_id} - Step: {step_name} - Type: {data_type}") filename = f"{run_timestamp}_{step_name}_{data_type}.txt" diff --git a/helpers.py b/helpers.py index bdb824b9..08ee9fb2 100644 --- a/helpers.py +++ b/helpers.py @@ -294,31 +294,30 @@ def get_email_address(firstname, lastname, website): def _get_gemini_api_key(): """ - Retrieves Gemini API Key, prioritizing environment variables as the most robust method. + Retrieves Gemini API Key, prioritizing Config.API_KEYS after it has been loaded. """ logger = logging.getLogger(__name__) logging.info("Attempting to retrieve Gemini API Key...") - # Primary Method: Environment Variable (most robust for Docker) + # Primary Method: From Config.API_KEYS (expected to be loaded by orchestrator) + api_key = Config.API_KEYS.get('gemini') or Config.API_KEYS.get('openai') # Check both slots + if api_key: + logging.info("Successfully loaded API key from Config.API_KEYS.") + return api_key + + # Fallback 1: Environment Variable GEMINI_API_KEY api_key = os.environ.get("GEMINI_API_KEY") if api_key: - logging.info("Successfully loaded API key from GEMINI_API_KEY environment variable.") + logging.warning("Loaded API key from GEMINI_API_KEY environment variable (Config.API_KEYS was empty).") return api_key - # Fallback 1: Legacy Environment Variable + # Fallback 2: Legacy Environment Variable OPENAI_API_KEY api_key = os.environ.get("OPENAI_API_KEY") if api_key: - logging.warning("Loaded API key from legacy OPENAI_API_KEY environment variable.") - return api_key - - # Fallback 2: File-based (less reliable with volume mounts) - logging.warning("Could not find API key in environment variables. Falling back to file-based method.") - api_key = Config.API_KEYS.get('openai') # Legacy slot in config - if api_key: - logging.info("Successfully loaded API key from config file.") + logging.warning("Loaded API key from legacy OPENAI_API_KEY environment variable (Config.API_KEYS was empty).") return api_key - logger.error("CRITICAL: No API Key found in environment variables or config file.") + logger.error("CRITICAL: No API Key found in Config.API_KEYS or environment variables.") raise ValueError("API Key missing.") @retry_on_failure