import re from pydantic import BaseModel, ConfigDict, Field, model_validator, field_validator class StrictModel(BaseModel): model_config = ConfigDict(extra="forbid") class ProtectionRule(StrictModel): name: str = Field(min_length=1, max_length=100) pattern: str = Field(min_length=1, max_length=1000) @field_validator("pattern") @classmethod def valid_pattern(cls, value: str) -> str: try: compiled = re.compile(value) except re.error as exc: raise ValueError("Invalid regular expression") from exc if compiled.match(""): raise ValueError("Protection patterns must not match empty text") return value class Project(StrictModel): name: str = Field(min_length=1, max_length=200) source_language: str = Field(default="English", min_length=1, max_length=100) target_language: str = Field(default="Russian", min_length=1, max_length=100) context: str = Field(default="", max_length=50000) instructions: str = Field(default="", max_length=20000) glossary: dict[str, str] = Field(default_factory=dict, max_length=2000) provider: str = "lmstudio" model: str | None = None disabled_rules: list[str] = Field(default_factory=list, max_length=30) protection_rules: list[ProtectionRule] = Field(default_factory=list, max_length=50) @model_validator(mode="after") def validate_rules(self): from .protection import BUILTIN_RULES unknown = set(self.disabled_rules) - BUILTIN_RULES.keys() if unknown: raise ValueError(f"Unknown built-in protection rules: {sorted(unknown)}") names = [rule.name for rule in self.protection_rules] if len(names) != len(set(names)): raise ValueError("Custom rule names must be unique") return self class TextItem(StrictModel): id: str = Field(min_length=1, max_length=200) text: str = Field(max_length=30000) class TranslationRequest(StrictModel): items: list[TextItem] = Field(min_length=1, max_length=500) batch_size: int = Field(default=10, ge=1, le=50) max_batch_chars: int = Field(default=12000, ge=100, le=100000) retries: int = Field(default=2, ge=0, le=5) use_cache: bool = True @model_validator(mode="after") def validate_items(self): ids = [item.id for item in self.items] if len(ids) != len(set(ids)): raise ValueError("Item IDs must be unique within a job") if sum(len(item.text) for item in self.items) > 1000000: raise ValueError("A job may contain at most 1,000,000 characters") return self