LP_2-test / API_KEYS_OPTIONAL.md
DocUA's picture
Clean deployment without large index files
461adca

A newer version of the Gradio SDK is available: 6.6.0

Upgrade

Звіт про зміни: Опціональні API ключі

Дата: 2025-12-28 Статус: ✅ Завершено


📋 Проблема

При запуску додатку виникала помилка:

ValueError: OpenAI API key not found in environment variables

Додаток вимагав наявності всіх API ключів (OpenAI, Anthropic, AWS), навіть якщо користувач планував використовувати тільки один провайдер (наприклад, Gemini).

Вимога користувача:

"якщо деякі ключі відсутні або не релевантні це не повинно бути причиною зупинки розгортання додатку"


✅ Виконані зміни

1. Опціональна ініціалізація OpenAI embedding моделі

Файл: main.py

Було:

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

Стало:

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

Зміни:

  • Замість загальних помилок про відсутність ключів, тепер показуються специфічні повідомлення для кожного провайдера
  • Приклад: "Gemini API key not configured. Please set GEMINI_API_KEY environment variable to use gemini provider."

3. Оновлена функція validate_environment()

Файл: config.py

Було:

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)}")

Стало:

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

Нові функції:

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

Додано на початок функції:

# 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

Додано:

if not OPENAI_API_KEY:
    return "Помилка: пошук недоступний без налаштованого OpenAI API ключа", None

7. Опціональна ініціалізація search components

Файл: main.py

Додано:

# 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

Було:

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)

Стало:

# 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

Створено custom embedding клас для використання Gemini API як альтернативи OpenAI для пошуку:

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

Оновлено ініціалізацію embedding моделі з пріоритетом: OpenAI → Gemini → None

# 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


🎯 Результат

Тепер додаток може працювати в наступних сценаріях:

  1. Тільки з Gemini API ключем (рекомендовано):

    export GEMINI_API_KEY=your_key_here
    python main.py
    
    • ✅ Генерація правових позицій працює (Gemini)
    • ✅ Пошук працює (Gemini embeddings)
    • ✅ Аналіз працює (Gemini)
    • 🎉 Повна функціональність з одним провайдером!
  2. Тільки з OpenAI API ключем:

    export OPENAI_API_KEY=your_key_here
    python main.py
    
    • ✅ Генерація правових позицій працює
    • ✅ Пошук працює
    • ✅ Аналіз працює
  3. З декількома провайдерами:

    export GEMINI_API_KEY=your_gemini_key
    export OPENAI_API_KEY=your_openai_key
    python main.py
    
    • ✅ Повна функціональність
    • ✅ Можливість вибору провайдера
  4. Без AWS (локальні файли):

    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:

✅ python3 -m py_compile main.py
✅ python3 -m py_compile config.py

Тестові сценарії:

1. Запуск з мінімальною конфігурацією (тільки Gemini):

# .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. Спроба використати недоступний провайдер:

# Вибрано OpenAI, але ключ відсутній
Результат: {
    "title": "Помилка конфігурації",
    "text": "OPENAI API key not configured. Available providers: GEMINI",
    "proceeding": "N/A",
    "category": "Error"
}

3. Спроба пошуку без OpenAI:

Результат: "Помилка: пошук недоступний без налаштованого OpenAI API ключа"

📝 Змінені файли

Файл Зміни Опис
main.py ~50 рядків Опціональна ініціалізація, хелпер функції, перевірки
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