""" Configuration Models for Flare Platform """ from pydantic import BaseModel, Field, field_serializer from datetime import datetime from typing import Optional, List, Dict, Any class BaseModelWithDatetime(BaseModel): """Base model with consistent datetime serialization""" class Config: # Datetime'ları her zaman ISO 8601 formatında serialize et json_encoders = { datetime: lambda v: v.isoformat() if v else None } # ===================== User & Auth ===================== class UserConfig(BaseModelWithDatetime): username: str password_hash: str salt: Optional[str] = None # ===================== Provider Models ===================== class ProviderDefinition(BaseModelWithDatetime): type: str # llm, tts, stt name: str display_name: str requires_endpoint: bool requires_api_key: bool requires_repo_info: Optional[bool] = False description: str class ProviderSettings(BaseModelWithDatetime): name: str api_key: Optional[str] = None endpoint: Optional[str] = None settings: Optional[Dict[str, Any]] = Field(default_factory=dict) # ===================== Global Config ===================== class GlobalConfig(BaseModelWithDatetime): llm_provider: ProviderSettings = Field( default_factory=lambda: ProviderSettings( name="spark_cloud", api_key="", endpoint="http://localhost:8080", settings={} ) ) tts_provider: ProviderSettings = Field( default_factory=lambda: ProviderSettings( name="no_tts", api_key="", endpoint=None, settings={} ) ) stt_provider: ProviderSettings = Field( default_factory=lambda: ProviderSettings( name="no_stt", api_key="", endpoint=None, settings={} ) ) providers: List[ProviderDefinition] = Field(default_factory=list) users: List[UserConfig] = Field(default_factory=list) last_update_date: Optional[str] = None last_update_user: Optional[str] = None def is_cloud_mode(self) -> bool: """Check if running in cloud mode""" import os return bool(os.environ.get("SPACE_ID")) def get_provider_config(self, provider_type: str, provider_name: str) -> Optional[ProviderDefinition]: """Get provider definition by type and name""" return next( (p for p in self.providers if p.type == provider_type and p.name == provider_name), None ) # ===================== Localization Models ===================== class LocalizedExample(BaseModelWithDatetime): locale_code: str example: str class LocalizedCaption(BaseModelWithDatetime): locale_code: str caption: str # ===================== Parameter Models ===================== class ParameterConfig(BaseModelWithDatetime): name: str caption: List[LocalizedCaption] type: str # str, int, float, bool, date required: bool = True variable_name: str extraction_prompt: Optional[str] = None validation_regex: Optional[str] = None invalid_prompt: Optional[str] = None type_error_prompt: Optional[str] = None def canonical_type(self) -> str: """Get canonical type name""" return self.type.lower() def get_caption_for_locale(self, locale: str) -> str: """Get caption for specific locale""" for cap in self.caption: if cap.locale_code == locale: return cap.caption # Fallback to first caption return self.caption[0].caption if self.caption else self.name # ===================== Intent Models ===================== class IntentConfig(BaseModelWithDatetime): name: str caption: str requiresApproval: Optional[bool] = False dependencies: List[str] = Field(default_factory=list) examples: List[LocalizedExample] = Field(default_factory=list) detection_prompt: str parameters: List[ParameterConfig] = Field(default_factory=list) action: str fallback_timeout_prompt: Optional[str] = None fallback_error_prompt: Optional[str] = None # ===================== LLM Configuration ===================== class GenerationConfig(BaseModelWithDatetime): max_new_tokens: int = 512 temperature: float = 0.7 top_p: float = 0.9 top_k: Optional[int] = None repetition_penalty: Optional[float] = None do_sample: Optional[bool] = True num_beams: Optional[int] = None length_penalty: Optional[float] = None early_stopping: Optional[bool] = None class LLMConfiguration(BaseModelWithDatetime): repo_id: str generation_config: GenerationConfig = Field(default_factory=GenerationConfig) use_fine_tune: bool = False fine_tune_zip: Optional[str] = "" # ===================== Version Models ===================== class VersionConfig(BaseModelWithDatetime): no: int caption: str description: Optional[str] = None published: bool = False deleted: bool = False general_prompt: str welcome_prompt: Optional[str] = None llm: LLMConfiguration intents: List[IntentConfig] = Field(default_factory=list) created_date: str created_by: str publish_date: Optional[str] = None published_by: Optional[str] = None last_update_date: Optional[str] = None last_update_user: Optional[str] = None # ===================== Project Models ===================== class ProjectConfig(BaseModelWithDatetime): id: int name: str caption: str icon: Optional[str] = "folder" description: Optional[str] = None enabled: bool = True default_locale: str = "tr" supported_locales: List[str] = Field(default_factory=lambda: ["tr"]) timezone: str = "Europe/Istanbul" region: str = "tr-TR" versions: List[VersionConfig] = Field(default_factory=list) version_id_counter: int = 1 deleted: bool = False created_date: str created_by: str last_update_date: Optional[str] = None last_update_user: Optional[str] = None # ===================== API Models ===================== class RetryConfig(BaseModelWithDatetime): retry_count: int = 3 backoff_seconds: int = 2 strategy: str = "static" # static, exponential class AuthConfig(BaseModelWithDatetime): enabled: bool token_endpoint: Optional[str] = None response_token_path: Optional[str] = None token_request_body: Optional[Dict[str, Any]] = None token_refresh_endpoint: Optional[str] = None token_refresh_body: Optional[Dict[str, Any]] = None class ResponseMapping(BaseModelWithDatetime): variable_name: str type: str # str, int, float, bool, date json_path: str caption: List[LocalizedCaption] class APIConfig(BaseModelWithDatetime): name: str url: str method: str = "POST" headers: Dict[str, str] = Field(default_factory=dict) body_template: Dict[str, Any] = Field(default_factory=dict) timeout_seconds: int = 10 retry: RetryConfig = Field(default_factory=RetryConfig) proxy: Optional[str] = None auth: Optional[AuthConfig] = None response_prompt: Optional[str] = None response_mappings: List[ResponseMapping] = Field(default_factory=list) deleted: bool = False created_date: Optional[str] = None created_by: Optional[str] = None last_update_date: Optional[str] = None last_update_user: Optional[str] = None # ===================== Activity Log ===================== class ActivityLogEntry(BaseModelWithDatetime): id: Optional[int] = None timestamp: str username: str action: str entity_type: str entity_name: Optional[str] = None details: Optional[str] = None # ===================== Root Configuration ===================== class ServiceConfig(BaseModelWithDatetime): global_config: GlobalConfig = Field(alias="config") projects: List[ProjectConfig] = Field(default_factory=list) apis: List[APIConfig] = Field(default_factory=list) activity_log: List[ActivityLogEntry] = Field(default_factory=list) project_id_counter: int = 1 last_update_date: Optional[str] = None last_update_user: Optional[str] = None class Config: populate_by_name = True def build_index(self) -> None: """Build indexes for quick lookup""" # This method can be extended to build various indexes pass def get_api(self, api_name: str) -> Optional[APIConfig]: """Get API config by name""" return next((api for api in self.apis if api.name == api_name), None) # For backward compatibility - alias GlobalConfiguration = GlobalConfig