from functools import lru_cache from typing import Literal from pydantic import Field, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False, extra="ignore", ) app_name: str = "AI模型内部安全测试平台" app_env: Literal["test", "production"] = "test" app_host: str = "0.0.0.0" app_port: int = 8000 app_debug: bool = True app_api_prefix: str = "/api/v1" log_level_test: str = "INFO" log_level_production: str = "WARNING" log_json: bool = False database_url: str = "sqlite:///./data/platform.db" cors_allow_origins: str = "http://localhost:3000" cors_allow_credentials: bool = True cors_allow_methods: str = "*" cors_allow_headers: str = "*" target_api_type: str = "openai" target_base_url: str target_chat_path: str = "/chat/completions" target_models_path: str = "/models" target_model: str = "" target_auth_type: str = "none" target_api_key: str = "" target_auth_header: str = "Authorization" target_auth_prefix: str = "Bearer " judge_api_type: str = "openai" judge_base_url: str judge_chat_path: str = "/chat/completions" judge_models_path: str = "/models" judge_model: str = "" judge_auth_type: str = "bearer" judge_api_key: str = "" judge_auth_header: str = "Authorization" judge_auth_prefix: str = "Bearer " request_timeout_seconds: int = Field(default=180, gt=0) request_retry_count: int = Field(default=5, ge=0, le=10) request_retry_delay_seconds: int = Field(default=5, gt=0) request_retry_max_delay_seconds: int = Field(default=60, ge=5, le=300) request_verify_ssl: bool = True auth_token_secret: str = "" auth_token_ttl_seconds: int = Field(default=3600, ge=300, le=86400) auth_token_refresh_window_seconds: int = Field(default=1800, ge=60, le=43200) auth_refresh_token_ttl_seconds: int = Field(default=28800, ge=3600, le=31536000) dataset_path: str = "data/dataset.json" output_dir: str = "outputs" @model_validator(mode="after") def validate_auth_timing(self): if self.auth_token_refresh_window_seconds >= self.auth_token_ttl_seconds: raise ValueError( "AUTH_TOKEN_REFRESH_WINDOW_SECONDS must be less than AUTH_TOKEN_TTL_SECONDS" ) if self.auth_refresh_token_ttl_seconds < self.auth_token_ttl_seconds: raise ValueError( "AUTH_REFRESH_TOKEN_TTL_SECONDS must not be shorter than AUTH_TOKEN_TTL_SECONDS" ) return self @property def log_level(self) -> str: return self.log_level_production if self.app_env == "production" else self.log_level_test @property def cors_origins(self) -> list[str]: return [x.strip() for x in self.cors_allow_origins.split(",") if x.strip()] @property def cors_methods(self) -> list[str]: return ( ["*"] if self.cors_allow_methods.strip() == "*" else [x.strip() for x in self.cors_allow_methods.split(",") if x.strip()] ) @property def cors_headers(self) -> list[str]: return ( ["*"] if self.cors_allow_headers.strip() == "*" else [x.strip() for x in self.cors_allow_headers.split(",") if x.strip()] ) @lru_cache def get_settings() -> Settings: return Settings()