Manage apikeys in settings

1. More pydantic management of api keys.
2. Clean up of variable declarations from docker compose files, used to block .env imports. Now should be managed ether by settings.py defaults or .env
This commit is contained in:
Pavel
2026-01-16 13:02:02 +03:00
parent 176133573d
commit 3ea137bfef
5 changed files with 55 additions and 58 deletions

View File

@@ -6,19 +6,6 @@ from typing import Dict, List, Optional
logger = logging.getLogger(__name__)
def _is_valid_api_key(key: Optional[str]) -> bool:
"""
Check if an API key is valid (not None, not empty, not string 'None').
Handles Pydantic loading 'None' from .env as string "None".
"""
if key is None:
return False
if not isinstance(key, str):
return False
key_stripped = key.strip().lower()
return key_stripped != "" and key_stripped != "none"
class ModelProvider(str, Enum):
OPENAI = "openai"
AZURE_OPENAI = "azure_openai"
@@ -101,29 +88,29 @@ class ModelRegistry:
if not settings.OPENAI_BASE_URL:
self._add_docsgpt_models(settings)
if (
_is_valid_api_key(settings.OPENAI_API_KEY)
or (settings.LLM_PROVIDER == "openai" and _is_valid_api_key(settings.API_KEY))
settings.OPENAI_API_KEY
or (settings.LLM_PROVIDER == "openai" and settings.API_KEY)
or settings.OPENAI_BASE_URL
):
self._add_openai_models(settings)
if settings.OPENAI_API_BASE or (
settings.LLM_PROVIDER == "azure_openai" and _is_valid_api_key(settings.API_KEY)
settings.LLM_PROVIDER == "azure_openai" and settings.API_KEY
):
self._add_azure_openai_models(settings)
if _is_valid_api_key(settings.ANTHROPIC_API_KEY) or (
settings.LLM_PROVIDER == "anthropic" and _is_valid_api_key(settings.API_KEY)
if settings.ANTHROPIC_API_KEY or (
settings.LLM_PROVIDER == "anthropic" and settings.API_KEY
):
self._add_anthropic_models(settings)
if _is_valid_api_key(settings.GOOGLE_API_KEY) or (
settings.LLM_PROVIDER == "google" and _is_valid_api_key(settings.API_KEY)
if settings.GOOGLE_API_KEY or (
settings.LLM_PROVIDER == "google" and settings.API_KEY
):
self._add_google_models(settings)
if _is_valid_api_key(settings.GROQ_API_KEY) or (
settings.LLM_PROVIDER == "groq" and _is_valid_api_key(settings.API_KEY)
if settings.GROQ_API_KEY or (
settings.LLM_PROVIDER == "groq" and settings.API_KEY
):
self._add_groq_models(settings)
if _is_valid_api_key(settings.HUGGINGFACE_API_KEY) or (
settings.LLM_PROVIDER == "huggingface" and _is_valid_api_key(settings.API_KEY)
if settings.HUGGINGFACE_API_KEY or (
settings.LLM_PROVIDER == "huggingface" and settings.API_KEY
):
self._add_huggingface_models(settings)
# Default model selection
@@ -140,7 +127,7 @@ class ModelRegistry:
self.default_model_id = settings.LLM_NAME
if not self.default_model_id:
if settings.LLM_PROVIDER and _is_valid_api_key(settings.API_KEY):
if settings.LLM_PROVIDER and settings.API_KEY:
for model_id, model in self.models.items():
if model.provider.value == settings.LLM_PROVIDER:
self.default_model_id = model_id
@@ -178,7 +165,7 @@ class ModelRegistry:
)
else:
# Standard OpenAI API usage - add standard models if API key is valid
if _is_valid_api_key(settings.OPENAI_API_KEY):
if settings.OPENAI_API_KEY:
for model in OPENAI_MODELS:
self.models[model.id] = model

View File

@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional
from application.core.model_settings import ModelRegistry, _is_valid_api_key
from application.core.model_settings import ModelRegistry
def get_api_key_for_provider(provider: str) -> Optional[str]:
@@ -19,11 +19,9 @@ def get_api_key_for_provider(provider: str) -> Optional[str]:
}
provider_key = provider_key_map.get(provider)
if provider_key and _is_valid_api_key(provider_key):
if provider_key:
return provider_key
if _is_valid_api_key(settings.API_KEY):
return settings.API_KEY
return None
return settings.API_KEY
def get_all_available_models() -> Dict[str, Dict[str, Any]]:

View File

@@ -2,6 +2,7 @@ import os
from pathlib import Path
from typing import Optional
from pydantic import field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
current_dir = os.path.dirname(
@@ -158,6 +159,36 @@ class Settings(BaseSettings):
COMPRESSION_PROMPT_VERSION: str = "v1.0" # Track prompt iterations
COMPRESSION_MAX_HISTORY_POINTS: int = 3 # Keep only last N compression points to prevent DB bloat
@field_validator(
"API_KEY",
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
"GOOGLE_API_KEY",
"GROQ_API_KEY",
"HUGGINGFACE_API_KEY",
"EMBEDDINGS_KEY",
"FALLBACK_LLM_API_KEY",
"QDRANT_API_KEY",
"ELEVENLABS_API_KEY",
"INTERNAL_KEY",
mode="before",
)
@classmethod
def normalize_api_key(cls, v: Optional[str]) -> Optional[str]:
"""
Normalize API keys: convert 'None', 'none', empty strings,
and whitespace-only strings to actual None.
Handles Pydantic loading 'None' from .env as string "None".
"""
if v is None:
return None
if not isinstance(v, str):
return v
stripped = v.strip()
if stripped == "" or stripped.lower() == "none":
return None
return stripped
# Project root is one level above application/
path = Path(__file__).parent.parent.parent.absolute()

View File

@@ -11,17 +11,13 @@ services:
backend:
build: ../application
env_file:
- ../.env
environment:
- API_KEY=$OPENAI_API_KEY
- EMBEDDINGS_KEY=$OPENAI_API_KEY
# Override URLs to use docker service names
- CELERY_BROKER_URL=redis://redis:6379/0
- CELERY_RESULT_BACKEND=redis://redis:6379/1
- MONGO_URI=mongodb://mongo:27017/docsgpt
- OPENAI_API_KEY=$OPENAI_API_KEY
- OPENAI_API_BASE=$OPENAI_API_BASE
- OPENAI_API_VERSION=$OPENAI_API_VERSION
- AZURE_DEPLOYMENT_NAME=$AZURE_DEPLOYMENT_NAME
- AZURE_EMBEDDINGS_DEPLOYMENT_NAME=$AZURE_EMBEDDINGS_DEPLOYMENT_NAME
ports:
- "7091:7091"
volumes:
@@ -35,18 +31,14 @@ services:
worker:
build: ../application
command: celery -A application.app.celery worker -l INFO
env_file:
- ../.env
environment:
- API_KEY=$OPENAI_API_KEY
- EMBEDDINGS_KEY=$OPENAI_API_KEY
# Override URLs to use docker service names
- CELERY_BROKER_URL=redis://redis:6379/0
- CELERY_RESULT_BACKEND=redis://redis:6379/1
- MONGO_URI=mongodb://mongo:27017/docsgpt
- API_URL=http://backend:7091
- OPENAI_API_KEY=$OPENAI_API_KEY
- OPENAI_API_BASE=$OPENAI_API_BASE
- OPENAI_API_VERSION=$OPENAI_API_VERSION
- AZURE_DEPLOYMENT_NAME=$AZURE_DEPLOYMENT_NAME
- AZURE_EMBEDDINGS_DEPLOYMENT_NAME=$AZURE_EMBEDDINGS_DEPLOYMENT_NAME
depends_on:
- redis
- mongo

View File

@@ -19,17 +19,11 @@ services:
env_file:
- ../.env
environment:
- API_KEY=$API_KEY
- EMBEDDINGS_KEY=$EMBEDDINGS_KEY
- EMBEDDINGS_BASE_URL=$EMBEDDINGS_BASE_URL
- LLM_PROVIDER=$LLM_PROVIDER
- LLM_NAME=$LLM_NAME
# Override URLs to use docker service names
- CELERY_BROKER_URL=redis://redis:6379/0
- CELERY_RESULT_BACKEND=redis://redis:6379/1
- MONGO_URI=mongodb://mongo:27017/docsgpt
- CACHE_REDIS_URL=redis://redis:6379/2
- OPENAI_BASE_URL=$OPENAI_BASE_URL
- INTERNAL_KEY=$INTERNAL_KEY
ports:
- "7091:7091"
volumes:
@@ -47,17 +41,12 @@ services:
env_file:
- ../.env
environment:
- API_KEY=$API_KEY
- EMBEDDINGS_KEY=$EMBEDDINGS_KEY
- EMBEDDINGS_BASE_URL=$EMBEDDINGS_BASE_URL
- LLM_PROVIDER=$LLM_PROVIDER
- LLM_NAME=$LLM_NAME
# Override URLs to use docker service names
- CELERY_BROKER_URL=redis://redis:6379/0
- CELERY_RESULT_BACKEND=redis://redis:6379/1
- MONGO_URI=mongodb://mongo:27017/docsgpt
- API_URL=http://backend:7091
- CACHE_REDIS_URL=redis://redis:6379/2
- INTERNAL_KEY=$INTERNAL_KEY
volumes:
- ../application/indexes:/app/indexes
- ../application/inputs:/app/inputs