"""Configuration du service, lue depuis l'environnement (`pydantic-settings`). Contrairement à `requireInternalWorker` côté `apps/api` (`apps/api/src/middlewares/require-internal-worker.ts`), qui tolère un `INTERNAL_WORKER_SECRET` absent (le worker LLM est un job de fond optionnel) et échoue "juste" requête par requête dans ce cas, ce service est une dépendance coeur : `INTENT_SERVICE_SECRET` absent doit empêcher `uvicorn` de démarrer du tout plutôt que de démarrer dans un état où chaque requête échouerait silencieusement en boucle — `Settings` n'a donc aucune valeur par défaut ni type optionnel pour ce champ, la validation Pydantic lève dès l'import de ce module si la variable manque. """ from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): # `env_file=".env"` : lu uniquement en dev natif (`cp .env.example .env`, # voir le README de ce service) — sans effet en Docker, où # docker-compose.yml passe les variables directement en `environment:` # et où aucun `.env` n'est copié dans l'image. Un `.env` absent n'est pas # une erreur ici (pydantic-settings ignore silencieusement un fichier # manquant) ; c'est bien `intent_service_secret` ci-dessous, sans valeur # par défaut, qui fait échouer le démarrage si la variable n'est # disponible par aucune des deux voies. # # `case_sensitive` par défaut (False) : `INTENT_SERVICE_SECRET` (la # convention majuscule utilisée partout ailleurs dans le repo, cf. # `docker-compose.yml`/`.env.example`) matche bien le champ # `intent_service_secret` ci-dessous. model_config = SettingsConfigDict(env_file=".env") # Secret partagé attendu sur le header `X-Intent-Service-Secret` de # chaque requête (sauf `GET /health`) — voir `security.py`. Doit matcher # `INTENT_SERVICE_SECRET` côté `apps/api/src/config/env.ts`. intent_service_secret: str # Pas de `port` ici : `uvicorn` prend son port en argument de ligne de # commande (`--port`, voir le Dockerfile et le README de ce service), # jamais lu depuis `Settings` — une variable d'env dupliquant ce que la # commande de démarrage fixe déjà explicitement n'aurait aucun lecteur. # Niveau du logging structuré (`logging_config.py`) — voir ce module pour # le format. `INFO` par défaut : c'est à ce niveau que `routes/process.py` # journalise chaque input/output du pipeline NLP, et que # `pipeline_registry.py` journalise l'entraînement au démarrage, pour # qu'un déploiement par défaut les voie sans configuration # supplémentaire (`docker logs`/Portainer). log_level: str = "INFO" settings = Settings()