Phase 28: P3 — Pydantic Settings, dependency-injector IoC, tenacity retry

- config.py: dataclasses → pydantic-settings BaseSettings (flat AppConfig,
  env vars auto-loaded from .env, type-safe validation)
- api_client.py: HTTPAPIClient takes AppConfig directly (APIConfig removed);
  tenacity retry on 5 methods (reset/ingest/list/delete/feedback) —
  retries on 5xx + TransportError, 3 attempts, exponential backoff 1-8s
- container.py: manual DI → dependency_injector DeclarativeContainer with
  providers.Singleton; Container() needs no args
- app.py: container.X → container.X() calls, remove AppConfig import
- requirements.txt: add pydantic-settings, tenacity, dependency-injector

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sal
2026-06-02 05:59:23 +09:00
parent 148211e236
commit 974bab7cd8
6 changed files with 67 additions and 70 deletions
+8 -34
View File
@@ -1,41 +1,15 @@
"""수동 DI 컨테이너."""
from dependency_injector import containers, providers
from api_client import HTTPAPIClient
from config import AppConfig
from services import ChatService, DocumentService, TTSService
class Container:
def __init__(self, config: AppConfig):
self._config = config
self._api_client: HTTPAPIClient | None = None
self._chat_service: ChatService | None = None
self._document_service: DocumentService | None = None
self._tts_service: TTSService | None = None
class Container(containers.DeclarativeContainer):
config = providers.Singleton(AppConfig)
@property
def config(self) -> AppConfig:
return self._config
api_client = providers.Singleton(HTTPAPIClient, config=config)
@property
def api_client(self) -> HTTPAPIClient:
if self._api_client is None:
self._api_client = HTTPAPIClient(self._config.api)
return self._api_client
@property
def chat_service(self) -> ChatService:
if self._chat_service is None:
self._chat_service = ChatService(self.api_client)
return self._chat_service
@property
def document_service(self) -> DocumentService:
if self._document_service is None:
self._document_service = DocumentService(self.api_client)
return self._document_service
@property
def tts_service(self) -> TTSService:
if self._tts_service is None:
self._tts_service = TTSService(self._config)
return self._tts_service
chat_service = providers.Singleton(ChatService, api_client=api_client)
document_service = providers.Singleton(DocumentService, api_client=api_client)
tts_service = providers.Singleton(TTSService, config=config)