# Звіт про зміни: Опціональні API ключі **Дата:** 2025-12-28 **Статус:** ✅ Завершено --- ## 📋 Проблема При запуску додатку виникала помилка: ``` ValueError: OpenAI API key not found in environment variables ``` Додаток вимагав наявності всіх API ключів (OpenAI, Anthropic, AWS), навіть якщо користувач планував використовувати тільки один провайдер (наприклад, Gemini). **Вимога користувача:** > "якщо деякі ключі відсутні або не релевантні це не повинно бути причиною зупинки розгортання додатку" --- ## ✅ Виконані зміни ### 1. Опціональна ініціалізація OpenAI embedding моделі **Файл:** [main.py](main.py:45-57) **Було:** ```python if not OPENAI_API_KEY: raise ValueError("OpenAI API key not found in environment variables") embed_model = OpenAIEmbedding(model_name="text-embedding-3-small") Settings.embed_model = embed_model ``` **Стало:** ```python if OPENAI_API_KEY: embed_model = OpenAIEmbedding(model_name="text-embedding-3-small") Settings.embed_model = embed_model print("OpenAI embedding model initialized successfully") else: print("Warning: OpenAI API key not found. Search functionality will be disabled.") ``` ### 2. Покращені повідомлення про помилки в LLMAnalyzer **Файл:** [main.py](main.py:181-199) **Зміни:** - Замість загальних помилок про відсутність ключів, тепер показуються специфічні повідомлення для кожного провайдера - Приклад: `"Gemini API key not configured. Please set GEMINI_API_KEY environment variable to use gemini provider."` ### 3. Оновлена функція validate_environment() **Файл:** [config.py](config.py:45-76) **Було:** ```python def validate_environment(): required_vars = [ "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY" ] missing_vars = [var for var in required_vars if not os.getenv(var)] if missing_vars: raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}") ``` **Стало:** ```python def validate_environment(require_ai_provider: bool = True, require_aws: bool = False): """ Validate environment variables. Args: require_ai_provider: If True, requires at least one AI provider API key require_aws: If True, requires AWS credentials Returns: dict: Status of each provider (available/missing) """ status = { "openai": bool(os.getenv("OPENAI_API_KEY")), "anthropic": bool(os.getenv("ANTHROPIC_API_KEY")), "gemini": bool(os.getenv("GEMINI_API_KEY")), "deepseek": bool(os.getenv("DEEPSEEK_API_KEY")), "aws": bool(os.getenv("AWS_ACCESS_KEY_ID") and os.getenv("AWS_SECRET_ACCESS_KEY")) } if require_ai_provider: if not any([status["openai"], status["anthropic"], status["gemini"], status["deepseek"]]): raise ValueError( "At least one AI provider API key is required. Please set one of: " "OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, DEEPSEEK_API_KEY" ) if require_aws and not status["aws"]: raise ValueError("AWS credentials are required") return status ``` ### 4. Додані хелпер функції **Файл:** [main.py](main.py:171-200) **Нові функції:** ```python def get_available_providers() -> Dict[str, bool]: """Get status of all AI providers.""" return { "openai": bool(OPENAI_API_KEY), "anthropic": bool(ANTHROPIC_API_KEY), "gemini": bool(os.getenv("GEMINI_API_KEY")), "deepseek": bool(DEEPSEEK_API_KEY) } def check_provider_available(provider: str) -> Tuple[bool, str]: """ Check if a provider is available. Returns: Tuple of (is_available, error_message) """ providers = get_available_providers() provider_key = provider.lower() if provider_key not in providers: return False, f"Unknown provider: {provider}" if not providers[provider_key]: available = [k.upper() for k, v in providers.items() if v] if not available: return False, "No AI provider API keys configured. Please set at least one API key." return False, f"{provider.upper()} API key not configured. Available providers: {', '.join(available)}" return True, "" ``` ### 5. Runtime перевірки в generate_legal_position() **Файл:** [main.py](main.py:502-510) **Додано на початок функції:** ```python # Check if provider is available is_available, error_msg = check_provider_available(provider) if not is_available: return { "title": "Помилка конфігурації", "text": error_msg, "proceeding": "N/A", "category": "Error" } ``` ### 6. Перевірки в функціях пошуку **Файли:** [main.py](main.py:780-781), [main.py](main.py:823-824) **Додано:** ```python if not OPENAI_API_KEY: return "Помилка: пошук недоступний без налаштованого OpenAI API ключа", None ``` ### 7. Опціональна ініціалізація search components **Файл:** [main.py](main.py:140-147) **Додано:** ```python # Initialize search components only if OpenAI is available if OPENAI_API_KEY: success = search_components.initialize_components(LOCAL_DIR) if not success: raise RuntimeError("Failed to initialize search components") print("Search components initialized successfully") else: print("Skipping search components initialization (OpenAI API key not available)") ``` Це дозволяє додатку запускатися навіть без OpenAI, оскільки search components залежать від OpenAI embedding моделі. ### 8. Оновлена валідація при запуску **Файл:** [main.py](main.py:875-900) **Було:** ```python required_vars = ["OPENAI_API_KEY"] missing_vars = [var for var in required_vars if not os.getenv(var)] if missing_vars: print(f"Missing required environment variables: {', '.join(missing_vars)}") sys.exit(1) ``` **Стало:** ```python # Check which providers are available available_providers = [] if OPENAI_API_KEY: available_providers.append("OpenAI") if ANTHROPIC_API_KEY: available_providers.append("Anthropic") if os.getenv("GEMINI_API_KEY"): available_providers.append("Gemini") if DEEPSEEK_API_KEY: available_providers.append("DeepSeek") if not available_providers: print("Error: No AI provider API keys configured. Please set at least one of:") print(" - OPENAI_API_KEY") print(" - ANTHROPIC_API_KEY") print(" - GEMINI_API_KEY") print(" - DEEPSEEK_API_KEY") sys.exit(1) print(f"Available AI providers: {', '.join(available_providers)}") if not OPENAI_API_KEY: print("Warning: OpenAI API key not configured. Search functionality will be limited.") if not all([AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY]): print("Warning: AWS credentials not configured. Will use local files only.") ``` --- ### 9. Додано підтримку Gemini Embeddings ✨ **Файл:** [embeddings/gemini_embedding.py](embeddings/gemini_embedding.py) Створено custom embedding клас для використання Gemini API як альтернативи OpenAI для пошуку: ```python from llama_index.core.embeddings import BaseEmbedding from google import genai class GeminiEmbedding(BaseEmbedding): """Gemini embedding integration for LlamaIndex.""" def __init__(self, api_key: str, model_name: str = "gemini-embedding-001", **kwargs): super().__init__(**kwargs) self._client = genai.Client(api_key=api_key) self._model_name = model_name def _get_query_embedding(self, query: str) -> List[float]: result = self._client.models.embed_content( model=self._model_name, contents=query ) return list(result.embeddings[0].values) ``` **Файл:** [main.py](main.py:48-67) Оновлено ініціалізацію embedding моделі з пріоритетом: OpenAI → Gemini → None ```python # Priority: OpenAI > Gemini > None embed_model = None GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") if OPENAI_API_KEY: embed_model = OpenAIEmbedding(model_name="text-embedding-3-small") print("OpenAI embedding model initialized successfully") elif GEMINI_API_KEY: embed_model = GeminiEmbedding(api_key=GEMINI_API_KEY, model_name="gemini-embedding-001") print("Gemini embedding model initialized successfully (alternative to OpenAI)") else: print("Warning: No embedding API key found (OpenAI or Gemini). Search functionality will be disabled.") ``` Детальна документація: [GEMINI_EMBEDDINGS.md](GEMINI_EMBEDDINGS.md) --- ## 🎯 Результат ### Тепер додаток може працювати в наступних сценаріях: 1. **Тільки з Gemini API ключем (рекомендовано):** ```bash export GEMINI_API_KEY=your_key_here python main.py ``` - ✅ Генерація правових позицій працює (Gemini) - ✅ Пошук працює (Gemini embeddings) - ✅ Аналіз працює (Gemini) - 🎉 **Повна функціональність з одним провайдером!** 2. **Тільки з OpenAI API ключем:** ```bash export OPENAI_API_KEY=your_key_here python main.py ``` - ✅ Генерація правових позицій працює - ✅ Пошук працює - ✅ Аналіз працює 3. **З декількома провайдерами:** ```bash export GEMINI_API_KEY=your_gemini_key export OPENAI_API_KEY=your_openai_key python main.py ``` - ✅ Повна функціональність - ✅ Можливість вибору провайдера 4. **Без AWS (локальні файли):** ```bash export GEMINI_API_KEY=your_key_here # AWS credentials не потрібні, якщо файли є локально python main.py ``` - ✅ Працює з локальними файлами - ⚠️ Попередження про відсутність AWS credentials --- ## 📊 Порівняння ### До змін: ``` ❌ Потрібні всі ключі: OPENAI_API_KEY, ANTHROPIC_API_KEY, AWS ❌ Додаток не запускається без OpenAI ❌ Жорстка помилка при відсутності будь-якого ключа ``` ### Після змін: ``` ✅ Потрібен хоча б один AI провайдер ✅ AWS опціональний (локальні файли) ✅ OpenAI опціональний (для генерації) ✅ Зрозумілі повідомлення про доступність функцій ✅ Graceful degradation функціональності ``` --- ## 🔍 Перевірка ### Синтаксис Python: ```bash ✅ python3 -m py_compile main.py ✅ python3 -m py_compile config.py ``` ### Тестові сценарії: **1. Запуск з мінімальною конфігурацією (тільки Gemini):** ```bash # .env GEMINI_API_KEY=your_key_here # Очікуваний результат: Available AI providers: Gemini Warning: OpenAI API key not configured. Search functionality will be limited. Warning: AWS credentials not configured. Will use local files only. Components initialized successfully! ``` **2. Спроба використати недоступний провайдер:** ```bash # Вибрано OpenAI, але ключ відсутній Результат: { "title": "Помилка конфігурації", "text": "OPENAI API key not configured. Available providers: GEMINI", "proceeding": "N/A", "category": "Error" } ``` **3. Спроба пошуку без OpenAI:** ```bash Результат: "Помилка: пошук недоступний без налаштованого OpenAI API ключа" ``` --- ## 📝 Змінені файли | Файл | Зміни | Опис | |------|-------|------| | [main.py](main.py) | ~50 рядків | Опціональна ініціалізація, хелпер функції, перевірки | | [config.py](config.py) | ~30 рядків | Гнучка валідація environment variables | --- ## 🎓 Висновок ### Виконано: ✅ **Додаток може запускатися з будь-яким одним AI провайдером** ✅ **AWS credentials опціональні** ✅ **OpenAI ключ опціональний (з обмеженням функціональності)** ✅ **Зрозумілі повідомлення про доступність функцій** ✅ **Graceful degradation замість hard errors** ✅ **Перевірено синтаксис Python** ### Переваги нової структури: ✅ **Гнучке розгортання** - можна запустити з мінімальною конфігурацією ✅ **Краща UX** - зрозумілі повідомлення про те, що доступно/недоступно ✅ **Економія коштів** - не потрібно платити за всі провайдери одразу ✅ **Тестування** - легше тестувати з одним провайдером ✅ **Production-ready** - додаток не падає при неповній конфігурації ### Обмеження: ⚠️ **Пошук потребує OpenAI** - для embedding моделі ⚠️ **Мінімум один AI провайдер** - інакше додаток не запуститься ⚠️ **Функціональність залежить від ключів** - деякі функції недоступні без певних провайдерів --- ## 📚 Наступні кроки (опціонально) Можливі покращення в майбутньому: 1. **Альтернативні embedding моделі:** - Додати підтримку Gemini embeddings - Додати підтримку локальних embedding моделей - Це зробить пошук доступним без OpenAI 2. **UI індикатори:** - Показувати в інтерфейсі, які провайдери доступні - Вимикати кнопки/функції, які недоступні - Tooltip з поясненням, чому функція недоступна 3. **Конфігураційний файл:** - Можливість вказати бажані провайдери в YAML - Автоматичне приховування недоступних опцій --- **Статус:** ✅ **ГОТОВО** **Дата завершення:** 2025-12-28