from pydantic.types import PositiveInt from pydantic_settings import BaseSettings, SettingsConfigDict from reflector.schemas.platform import DAILY_PLATFORM, Platform from reflector.utils.string import NonEmptyString class Settings(BaseSettings): model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", extra="ignore", ) ROOT_PATH: str = "/" # WebRTC port range for ICE candidates (e.g. "50000-50100"). # When set, monkey-patches aioice to bind UDP sockets within this range, # allowing Docker port mapping instead of network_mode: host. WEBRTC_PORT_RANGE: str | None = None # Host IP or hostname to advertise in ICE candidates instead of the # container's internal IP. Use "host.docker.internal" in Docker with # extra_hosts, or a specific LAN IP. Resolved at connection time. WEBRTC_HOST: str | None = None # CORS UI_BASE_URL: str = "http://localhost:3000" CORS_ORIGIN: str = "*" CORS_ALLOW_CREDENTIALS: bool = False # Database DATABASE_URL: str = ( "postgresql+asyncpg://reflector:reflector@localhost:5432/reflector" ) # local data directory DATA_DIR: str = "./data" # Audio Chunking # backends: silero, frames AUDIO_CHUNKER_BACKEND: str = "frames" # HuggingFace token for gated models (pyannote diarization in --cpu mode) HF_TOKEN: str | None = None # Audio Transcription # backends: # - whisper: in-process model loading (no HTTP, runs in same process) # - modal: HTTP API client (works with Modal.com OR self-hosted gpu/self_hosted/) TRANSCRIPT_BACKEND: str = "whisper" # Whisper model sizes for local transcription # Options: "tiny", "base", "small", "medium", "large-v2" WHISPER_CHUNK_MODEL: str = "tiny" WHISPER_FILE_MODEL: str = "tiny" TRANSCRIPT_URL: str | None = None TRANSCRIPT_TIMEOUT: int = 90 TRANSCRIPT_FILE_TIMEOUT: int = ( 540 # Below Hatchet TIMEOUT_HEAVY (600) to avoid timeout race ) # Audio Transcription: modal backend TRANSCRIPT_MODAL_API_KEY: str | None = None # Audio transcription storage TRANSCRIPT_STORAGE_BACKEND: str | None = None # Storage configuration for AWS TRANSCRIPT_STORAGE_AWS_BUCKET_NAME: str = "reflector-bucket" TRANSCRIPT_STORAGE_AWS_REGION: str = "us-east-1" TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID: str | None = None TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY: str | None = None TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL: str | None = None # Platform-specific recording storage (follows {PREFIX}_STORAGE_AWS_{CREDENTIAL} pattern) # Whereby storage configuration WHEREBY_STORAGE_AWS_BUCKET_NAME: str | None = None WHEREBY_STORAGE_AWS_REGION: str | None = None WHEREBY_STORAGE_AWS_ACCESS_KEY_ID: str | None = None WHEREBY_STORAGE_AWS_SECRET_ACCESS_KEY: str | None = None # Daily.co storage configuration DAILYCO_STORAGE_AWS_BUCKET_NAME: str | None = None DAILYCO_STORAGE_AWS_REGION: str | None = None DAILYCO_STORAGE_AWS_ROLE_ARN: str | None = None # Worker credentials for reading/deleting from Daily's recording bucket DAILYCO_STORAGE_AWS_ACCESS_KEY_ID: str | None = None DAILYCO_STORAGE_AWS_SECRET_ACCESS_KEY: str | None = None # Translate into the target language TRANSLATION_BACKEND: str = "passthrough" TRANSLATE_URL: str | None = None TRANSLATE_TIMEOUT: int = 90 # Translation: modal backend TRANSLATE_MODAL_API_KEY: str | None = None # LLM LLM_MODEL: str = "microsoft/phi-4" LLM_URL: str | None = None LLM_API_KEY: str | None = None LLM_CONTEXT_WINDOW: int = 16000 LLM_REQUEST_TIMEOUT: float = 300.0 # HTTP request timeout for LLM calls (seconds) LLM_PARSE_MAX_RETRIES: int = ( 3 # Max retries for JSON/validation errors (total attempts = retries + 1) ) LLM_STRUCTURED_RESPONSE_TIMEOUT: int = ( 300 # Timeout in seconds for structured responses (5 minutes) ) # Diarization # backends: modal — HTTP API client, pyannote — in-process pyannote.audio DIARIZATION_ENABLED: bool = True DIARIZATION_BACKEND: str = "modal" DIARIZATION_URL: str | None = None DIARIZATION_FILE_TIMEOUT: int = 600 # Diarization: modal backend DIARIZATION_MODAL_API_KEY: str | None = None # Audio Padding # backends: # - pyav: in-process PyAV padding (no HTTP, runs in same process) # - modal: HTTP API client (works with Modal.com OR self-hosted gpu/self_hosted/) PADDING_BACKEND: str = "pyav" PADDING_URL: str | None = None PADDING_MODAL_API_KEY: str | None = None # Sentry SENTRY_DSN: str | None = None # User authentication (none, jwt, password) AUTH_BACKEND: str = "none" # User authentication using JWT AUTH_JWT_ALGORITHM: str = "RS256" AUTH_JWT_PUBLIC_KEY: str | None = "authentik.monadical.com_public.pem" AUTH_JWT_AUDIENCE: str | None = None # User authentication using password (selfhosted) ADMIN_EMAIL: str | None = None ADMIN_PASSWORD_HASH: str | None = None PUBLIC_MODE: bool = False PUBLIC_DATA_RETENTION_DAYS: PositiveInt = 7 # Min transcript length to generate topic + summary MIN_TRANSCRIPT_LENGTH: int = 750 # Celery CELERY_BROKER_URL: str = "redis://localhost:6379/1" CELERY_RESULT_BACKEND: str = "redis://localhost:6379/1" # Redis REDIS_HOST: str = "localhost" REDIS_PORT: int = 6379 REDIS_CACHE_DB: int = 2 # Secret key SECRET_KEY: str = "changeme-f02f86fd8b3e4fd892c6043e5a298e21" # Current hosting/domain BASE_URL: str = "http://localhost:1250" # Profiling PROFILING: bool = False # Healthcheck HEALTHCHECK_URL: str | None = None # Whereby integration WHEREBY_API_URL: str = "https://api.whereby.dev/v1" WHEREBY_API_KEY: NonEmptyString | None = None WHEREBY_WEBHOOK_SECRET: str | None = None AWS_PROCESS_RECORDING_QUEUE_URL: str | None = None SQS_POLLING_TIMEOUT_SECONDS: int = 60 CELERY_BEAT_POLL_INTERVAL: int = ( 0 # 0 = use individual defaults; set e.g. 300 for 5-min polling ) # Daily.co integration DAILY_API_KEY: str | None = None DAILY_WEBHOOK_SECRET: str | None = None DAILY_SUBDOMAIN: str | None = None DAILY_WEBHOOK_UUID: str | None = ( None # Webhook UUID for this environment. Not used by production code ) # Platform Configuration DEFAULT_VIDEO_PLATFORM: Platform = DAILY_PLATFORM # Zulip integration ZULIP_REALM: str | None = None ZULIP_API_KEY: str | None = None ZULIP_BOT_EMAIL: str | None = None # Hatchet workflow orchestration (always enabled for multitrack processing) HATCHET_CLIENT_TOKEN: str | None = None HATCHET_CLIENT_TLS_STRATEGY: str = "none" # none, tls, mtls HATCHET_DEBUG: bool = False settings = Settings()