| # Звіт про зміни: Опціональні 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 | |