""" Pydantic models for translation provider request/response schemas. """ import re from typing import Optional, List from pydantic import BaseModel, ConfigDict, Field, field_validator LANGUAGE_CODE_PATTERN = re.compile(r"^[a-z]{2}(-[A-Z]{2})?$|^auto$") class TranslationRequest(BaseModel): """Request model for translation operations.""" text: str = Field(..., description="Text to translate") target_language: str = Field( ..., description="Target language code (e.g., 'en', 'fr', 'es')" ) source_language: str = Field( default="auto", description="Source language code (default: auto-detect)" ) metadata: Optional[dict] = Field( default=None, description="Optional metadata for provider-specific options (e.g., custom_prompt)", ) @field_validator("target_language", "source_language") @classmethod def validate_language_code(cls, v: str) -> str: if not LANGUAGE_CODE_PATTERN.match(v): raise ValueError( f"Invalid language code '{v}'. Expected format: 'xx' or 'xx-XX' (e.g., 'en', 'fr', 'en-US')" ) return v class TranslationResponse(BaseModel): """Response model for translation operations.""" translated_text: str = Field(..., description="Translated text") provider_name: str = Field( ..., description="Name of the provider that performed the translation" ) from_cache: bool = Field( default=False, description="Whether the result came from cache" ) source_language: Optional[str] = Field( default=None, description="Detected or specified source language" ) error: Optional[str] = Field( default=None, description="Error message if translation failed" ) error_code: Optional[str] = Field( default=None, description="Error code for programmatic error handling" ) error_details: Optional[dict] = Field( default=None, description="Additional error details" ) @property def success(self) -> bool: """Check if translation was successful.""" return self.error is None def to_error_dict(self) -> dict: """Convert error to dictionary format for API responses.""" if self.error is None: return {} result = { "error": self.error_code or "UNKNOWN_ERROR", "message": self.error, } if self.error_details: result["details"] = self.error_details return result class BatchTranslationRequest(BaseModel): """Request model for batch translation operations.""" texts: List[str] = Field(..., description="List of texts to translate") target_language: str = Field(..., description="Target language code") source_language: str = Field( default="auto", description="Source language code (default: auto-detect)" ) class BatchTranslationResponse(BaseModel): """Response model for batch translation operations.""" translated_texts: List[str] = Field(..., description="List of translated texts") provider_name: str = Field( ..., description="Name of the provider that performed the translations" ) from_cache_count: int = Field(default=0, description="Number of results from cache") class ProviderHealthStatus(BaseModel): """Health status model for a translation provider.""" model_config = ConfigDict(protected_namespaces=()) name: str = Field(..., description="Provider name") available: bool = Field(..., description="Whether the provider is available") latency_ms: Optional[float] = Field( default=None, description="Response latency in milliseconds" ) error: Optional[str] = Field( default=None, description="Error message if unavailable" ) last_check: Optional[str] = Field( default=None, description="ISO timestamp of last health check" ) model: Optional[str] = Field( default=None, description="Model name (e.g. for LLM providers)" ) model_available: Optional[bool] = Field( default=None, description="Whether the configured model is available" )