106 lines
4.1 KiB
Python
106 lines
4.1 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""Utility datamodels: Prompt, TextMultilingual."""
|
|
|
|
from typing import Any, Dict, Optional
|
|
from pydantic import BaseModel, Field, field_validator
|
|
from modules.datamodels.datamodelBase import PowerOnModel
|
|
from modules.shared.i18nRegistry import i18nModel
|
|
import uuid
|
|
|
|
|
|
@i18nModel("Prompt")
|
|
class Prompt(PowerOnModel):
|
|
"""Benutzer- oder System-Prompt fuer die KI."""
|
|
id: str = Field(
|
|
default_factory=lambda: str(uuid.uuid4()),
|
|
description="Primary key",
|
|
json_schema_extra={"label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False},
|
|
)
|
|
mandateId: str = Field(
|
|
default="",
|
|
description="ID of the mandate this prompt belongs to",
|
|
json_schema_extra={"label": "Mandanten-ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False},
|
|
)
|
|
isSystem: bool = Field(
|
|
default=False,
|
|
description="System prompt visible to all users (read-only for non-SysAdmin)",
|
|
json_schema_extra={"label": "System", "frontend_type": "boolean", "frontend_readonly": True, "frontend_required": False},
|
|
)
|
|
content: str = Field(
|
|
description="Content of the prompt",
|
|
json_schema_extra={"label": "Inhalt", "frontend_type": "textarea", "frontend_readonly": False, "frontend_required": True},
|
|
)
|
|
name: str = Field(
|
|
description="Name of the prompt",
|
|
json_schema_extra={"label": "Name", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True},
|
|
)
|
|
|
|
@field_validator('isSystem', mode='before')
|
|
@classmethod
|
|
def _coerceIsSystem(cls, v):
|
|
"""Existing records may have isSystem=None (field didn't exist). Treat None as False."""
|
|
if v is None:
|
|
return False
|
|
return v
|
|
|
|
|
|
class TextMultilingual(BaseModel):
|
|
"""Multilingual text field. Language codes follow ISO 639-1 (en, de, fr, it, …)."""
|
|
en: str = Field(description="English text (default language, required)")
|
|
de: Optional[str] = Field(None, description="German text")
|
|
fr: Optional[str] = Field(None, description="French text")
|
|
it: Optional[str] = Field(None, description="Italian text")
|
|
|
|
@field_validator('en')
|
|
@classmethod
|
|
def _validateEnRequired(cls, v):
|
|
if not v or not v.strip():
|
|
raise ValueError("English text (en) is required and cannot be empty")
|
|
return v
|
|
|
|
def model_dump(self, **kwargs) -> Dict[str, str]:
|
|
result = {}
|
|
for key in self.model_fields:
|
|
value = getattr(self, key, None)
|
|
if value is not None:
|
|
result[key] = value
|
|
return result
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, str]) -> 'TextMultilingual':
|
|
fields = {k: data[k] for k in cls.model_fields if k in data}
|
|
fields.setdefault('en', '')
|
|
return cls(**fields)
|
|
|
|
def get_text(self, lang: str = 'en') -> str:
|
|
"""Get text for *lang*. Falls back to English."""
|
|
value = getattr(self, lang, None)
|
|
if value:
|
|
return value
|
|
return self.en
|
|
|
|
@classmethod
|
|
def fromUniform(cls, text: str) -> "TextMultilingual":
|
|
"""Same string in all languages (bootstrap / i18n key until per-language values exist in DB)."""
|
|
t = text.strip()
|
|
if not t:
|
|
raise ValueError("Text must be non-empty")
|
|
return cls(en=t, de=t, fr=t, it=t)
|
|
|
|
|
|
def coerce_text_multilingual(val: Any) -> TextMultilingual:
|
|
"""Normalize str, dict, or TextMultilingual for Role.description and similar fields."""
|
|
if isinstance(val, TextMultilingual):
|
|
return val
|
|
if isinstance(val, dict):
|
|
if not val:
|
|
return TextMultilingual.fromUniform("—")
|
|
d = {k: val[k] for k in TextMultilingual.model_fields if k in val and val[k] is not None}
|
|
if not d.get("en"):
|
|
d["en"] = (d.get("de") or d.get("fr") or "—").strip() or "—"
|
|
return TextMultilingual(**{k: d[k] for k in TextMultilingual.model_fields if k in d})
|
|
if isinstance(val, str) and val.strip():
|
|
return TextMultilingual.fromUniform(val)
|
|
return TextMultilingual.fromUniform("—")
|
|
|