""" Flare – Configuration Loader ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ • JSONC (comment-json) desteği • Pydantic şema + runtime validation • Global erişim için singleton ConfigProvider.get() """ import pathlib from functools import lru_cache from typing import Dict, List, Optional import commentjson as jsonc from pydantic import BaseModel, Field, validator CONFIG_PATH = pathlib.Path(__file__).parent / "service_config.jsonc" # === Pydantic Schemas ======================================================= class RetryConfig(BaseModel): max_attempts: int = 3 backoff_seconds: int = 2 strategy: str = Field("static", regex="^(static|exponential)$") class ProxyConfig(BaseModel): enabled: bool = False url: Optional[str] = None class AuthConfig(BaseModel): enabled: bool = False token_endpoint: Optional[str] = None body_template: Dict[str, str] = {} response_token_path: Optional[str] = None token_refresh_endpoint: Optional[str] = None token_refresh_body: Dict[str, str] = {} @validator("token_endpoint", always=True) def _require_endpoint(cls, v, values): if values["enabled"] and not v: raise ValueError("Auth enabled but token_endpoint missing") return v class APIConfig(BaseModel): url: str method: str = Field("POST", regex="^(GET|POST|PUT|DELETE|PATCH)$") headers: Dict[str, str] = {} body_template: Dict[str, str] = {} timeout_seconds: int = 10 retry: RetryConfig = RetryConfig() auth: AuthConfig = AuthConfig() proxy: ProxyConfig = ProxyConfig() response_prompt: str class ParameterConfig(BaseModel): name: str type: str required: bool = True extraction_prompt: str validation_regex: Optional[str] = None invalid_prompt: Optional[str] = None type_error_prompt: Optional[str] = None class IntentConfig(BaseModel): name: str caption: Optional[str] locale: str = "tr-TR" dependencies: List[str] = [] examples: List[str] detection_prompt: str parameters: List[ParameterConfig] = [] action: str fallback_timeout_prompt: Optional[str] = None fallback_error_prompt: Optional[str] = None class LLMConfig(BaseModel): repo_id: str generation_config: Dict[str, float] use_fine_tune: bool = False fine_tune_zip: Optional[str] = None class VersionConfig(BaseModel): version_number: int published: bool general_prompt: str llm: LLMConfig intents: List[IntentConfig] class ProjectConfig(BaseModel): name: str enabled: bool = True last_version_number: int versions: List[VersionConfig] class UserConfig(BaseModel): username: str password_hash: str salt: str class GlobalConfig(BaseModel): work_mode: str = Field(..., regex="^(hfcloud|cloud|on-premise)$") cloud_token: Optional[str] spark_endpoint: str users: List[UserConfig] class RootConfig(BaseModel): config: GlobalConfig projects: List[ProjectConfig] apis: Dict[str, APIConfig] # === Singleton Loader ======================================================= class ConfigProvider: @staticmethod @lru_cache def get(path: str = None) -> RootConfig: cfg_file = pathlib.Path(path) if path else CONFIG_PATH with cfg_file.open("r", encoding="utf-8") as f: data = jsonc.load(f) return RootConfig.parse_obj(data)