medilang-tech / app /utils /config.py
Dama03's picture
first push of the AI
411a994
from pydantic_settings import BaseSettings
from pydantic import Field, field_validator, ConfigDict
from typing import List, Any, Optional, Dict
import os
import logging
import sys
from pathlib import Path
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
def setup_logging(log_level: str = "INFO", log_file: str = None) -> None:
"""Configure logging for the application."""
log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
# Configure root logger
logging.basicConfig(
level=log_level,
format=log_format,
handlers=[logging.StreamHandler(sys.stdout)]
)
# Add file handler if log file is specified
if log_file:
log_file = Path(log_file)
log_file.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_file)
file_handler.setFormatter(logging.Formatter(log_format))
logging.getLogger().addHandler(file_handler)
# Set log level for specific loggers
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("openai").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)
class Settings(BaseSettings):
model_config = ConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="allow",
env_nested_delimiter="__"
)
# App configuration
APP_NAME: str = Field(default="Carehelp")
ENVIRONMENT: str = Field(default="development")
# Logging configuration
LOG_LEVEL: str = Field(default="INFO")
LOG_FILE: Optional[str] = Field(default=None)
def configure_logging(self) -> None:
"""Configure logging based on settings."""
setup_logging(
log_level=self.LOG_LEVEL,
log_file=self.LOG_FILE
)
PORT: int = Field(default=8000)
# CORS configuration
CORS_ALLOW_ORIGINS: List[str] = Field(default_factory=lambda: ["*"])
# API Keys
GOOGLE_MAPS_API_KEY: str | None = None
SERPAPI_API_KEY: str | None = None
# Supabase configuration
SUPABASE_URL: str | None = None
SUPABASE_ANON_KEY: str | None = None
SUPABASE_SERVICE_ROLE_KEY: str | None = None
SUPABASE_DB_PASSWORD: str | None = None
# OpenAI configuration
OPENAI_API_KEY: str | None = None
OPENAI_MODEL: str = Field(default="gpt-4")
OPENAI_WHISPER_MODEL: str = Field(default="whisper-1")
OPENAI_WHISPER_FALLBACK_MODEL: str | None = Field(default="gpt-4-transcribe")
OPENAI_WHISPER_MAX_CHUNK_SECS: int = Field(default=120)
# JWT configuration
JWT_SECRET: str = Field(default="change_this_secret")
JWT_ALGORITHM: str = Field(default="HS256")
ACCESS_TOKEN_EXPIRE_MINUTES: int = Field(default=60 * 24 * 30)
# Hugging Face configuration
HF_API_TOKEN: str | None = None
HF_TRANSLATION_MODEL: str = Field(default="facebook/nllb-200-distilled-600M")
# Provider selection and models
# Options: 'hf' (Hugging Face Inference), 'ollama' (local), 'lmstudio' (local OpenAI-compatible)
AI_PROVIDER: str = Field(default="hf")
# Text generation (chat) models
HF_TEXT_MODEL: str = Field(default="meta-llama/Meta-Llama-3-8B-Instruct")
OLLAMA_MODEL: str = Field(default="llama3.1:8b")
OLLAMA_BASE_URL: str = Field(default="http://localhost:11434")
LMSTUDIO_MODEL: str | None = Field(default=None)
LMSTUDIO_BASE_URL: str = Field(default="http://localhost:1234/v1")
# Vision caption model (used to assist image analysis with HF)
HF_VISION_CAPTION_MODEL: str = Field(default="Salesforce/blip-image-captioning-large")
# Optional local vision model for Ollama (e.g., 'llava:latest')
OLLAMA_VISION_MODEL: str = Field(default="llava:latest")
# Automatic Speech Recognition (ASR)
HF_ASR_MODEL: str = Field(default="distil-whisper/distil-large-v3")
# Data paths
PATIENT_DATA_PATH: str = Field(default="../patient_records.json")
# Cameroon data configuration
CAMEROON_DATA_CSV: str = Field(default="../../clinical_summaries.csv")
CAMEROON_EMBEDDINGS_MODEL: str = Field(
default="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
CAMEROON_CACHE_DIR: str = Field(default=".cache_cameroon")
CAMEROON_MAX_ROWS: int | None = None # limit for dev/testing
@field_validator("CORS_ALLOW_ORIGINS", mode="before")
@classmethod
def parse_cors_origins(cls, v: Any) -> Any:
# Accept JSON array, comma-separated string, or single "*"
if isinstance(v, list):
return v
if isinstance(v, str):
s = v.strip()
if s == "":
return ["*"]
if s == "*":
return ["*"]
# comma separated
return [x.strip() for x in s.split(",") if x.strip()]
return v
settings = Settings()