""" LucyDOM model classes for the workflow and document system. """ from pydantic import BaseModel, Field from typing import List, Dict, Any, Optional from datetime import datetime import uuid # Get all attributes of the model def getModelAttributes(modelClass): return [attr for attr in dir(modelClass) if not callable(getattr(modelClass, attr)) and not attr.startswith('_') and attr not in ('metadata', 'query', 'query_class', 'label', 'field_labels')] # CORE MODELS class Label(BaseModel): """Label for an attribute or a class with support for multiple languages""" default: str = Field(..., description="Default label text") translations: Dict[str, str] = Field(default_factory=dict, description="Translations for different languages") class Config: title = "Label" description = "A label with support for multiple languages" schema_extra = { "example": { "default": "Document", "translations": { "en": "Document", "fr": "Document" } } } def getLabel(self, language: str = None): """Returns the label in the specified language, or the default value if not available""" if language and language in self.translations: return self.translations[language] return self.default class Prompt(BaseModel): """Data model for a prompt""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the prompt") content: str = Field(description="Content of the prompt") name: str = Field(description="Display name of the prompt") mandateId: str = Field(description="ID of the mandate this prompt belongs to") label: Label = Field( default=Label(default="Prompt", translations={"en": "Prompt", "fr": "Invite"}), description="Label for the class" ) # Labels for attributes fieldLabels: Dict[str, Label] = { "id": Label(default="ID", translations={}), "content": Label(default="Content", translations={"en": "Content", "fr": "Contenu"}), "name": Label(default="Name", translations={"en": "Label", "fr": "Nom"}), "mandateId": Label(default="Mandate ID", translations={"en": "Mandate ID", "fr": "ID de mandat"}) } class FileItem(BaseModel): """Data model for a file""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the data object") mimeType: str = Field(description="Type of the file MIME type") fileName: str = Field(description="Name of the file") fileSize: int = Field(description="Size of the file in bytes") fileHash: str = Field(description="Hash code for deduplication") workflowId: Optional[str] = Field(None, description="ID of the associated workflow, if any") mandateId: str = Field(description="ID of the mandate this file belongs to") label: Label = Field( default=Label(default="Data Object", translations={"en": "Data Object", "fr": "Objet de données"}), description="Label for the class" ) # Labels for attributes fieldLabels: Dict[str, Label] = { "id": Label(default="ID", translations={}), "mimeType": Label(default="Type", translations={"en": "Type", "fr": "Type"}), "fileName": Label(default="Filename", translations={"en": "fileName", "fr": "Nom de fichier"}), "fileSize": Label(default="Size", translations={"en": "Size", "fr": "Taille"}), "fileHash": Label(default="File Hash", translations={"en": "Hash", "fr": "Hash"}), "workflowId": Label(default="Workflow ID", translations={"en": "Workflow ID", "fr": "ID du workflow"}), "mandateId": Label(default="Mandate ID", translations={"en": "Mandate ID", "fr": "ID de mandat"}) } class FileData(BaseModel): """Data model for file content""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the data object") data: str = Field(description="content of the file, text or base64 encoded based on base64Encoded flag") base64Encoded: bool = Field(description="Flag indicating whether the data is base64 encoded") workflowId: Optional[str] = Field(None, description="ID of the associated workflow, if any") # WORKFLOW MODELS class ChatContent(BaseModel): """Content of a document in the chat""" sequenceNr: int = Field(1, description="Sequence number of the content in the source document") name: str = Field(description="Designation") mimeType: str = Field(description="MIME type") data: str = Field(description="Actual content") metadata: Dict[str, Any] = Field(default_factory=dict, description="Metadata about the content") class ChatDocument(BaseModel): """Document in the chat workflow""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the document") fileId: str = Field(description="ID of the referenced file in the database") fileName: str = Field(description="Name of the file") fileSize: int = Field(description="Size of the file in bytes") mimeType: str = Field(description="MIME type") contents: List[ChatContent] = Field(default=[], description="Document contents") class ChatStat(BaseModel): """Statistics for performance and data usage""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the stats") workflowId: str = Field(description="ID of the associated workflow") processingTime: Optional[float] = Field(None, description="Processing time in seconds") tokenCount: Optional[int] = Field(None, description="Token count (for AI models)") bytesSent: Optional[int] = Field(None, description="Bytes sent") bytesReceived: Optional[int] = Field(None, description="Bytes received") label: Label = Field( default=Label(default="Chat Statistics", translations={"en": "Chat Statistics", "fr": "Statistiques de chat"}), description="Label for the class" ) # Labels for attributes fieldLabels: Dict[str, Label] = { "id": Label(default="ID", translations={}), "workflowId": Label(default="Workflow ID", translations={"en": "Workflow ID", "fr": "ID du workflow"}), "processingTime": Label(default="Processing Time", translations={"en": "Processing Time", "fr": "Temps de traitement"}), "tokenCount": Label(default="Token Count", translations={"en": "Token Count", "fr": "Nombre de tokens"}), "bytesSent": Label(default="Bytes Sent", translations={"en": "Bytes Sent", "fr": "Octets envoyés"}), "bytesReceived": Label(default="Bytes Received", translations={"en": "Bytes Received", "fr": "Octets reçus"}) } class ChatMessage(BaseModel): """Message object in the chat workflow""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the message") workflowId: str = Field(description="Reference to the parent workflow") parentMessageId: Optional[str] = Field(None, description="Reference to the replied message") agentName: Optional[str] = Field(None, description="Name of the agent used") documents: Optional[List[ChatDocument]] = Field(None, description="Documents in this message") message: Optional[str] = Field(None, description="Text content of the message") role: str = Field(description="Role of the sender ('system', 'user', 'assistant')") status: str = Field(description="Status of the message ('first', 'step', 'last')") sequenceNr: int = Field(description="Sequence number for sorting") startedAt: datetime = Field(description="Timestamp for message creation") finishedAt: Optional[datetime] = Field(None, description="Timestamp for message completion") stats: Optional[ChatStat] = Field(None, description="Statistics") class ChatLog(BaseModel): """Log entry for a chat workflow""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the log entry") workflowId: str = Field(description="ID of the associated workflow") message: str = Field(description="Log message content") type: str = Field(description="Type of log ('info', 'warning', 'error')") timestamp: str = Field(description="Timestamp of the log entry") agentName: str = Field(description="Name of the agent that created the log") status: str = Field(description="Status of the workflow at log time") progress: Optional[int] = Field(None, description="Progress value (0-100)") class ChatWorkflow(BaseModel): """Chat workflow object for multi-agent system""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the chat workflow") status: str = Field(description="Status of the chat workflow") name: Optional[str] = Field(None, description="Name of the chat workflow") currentRound: int = Field(default=1, description="Current round/iteration") lastActivity: str = Field(description="Timestamp of the last activity") startedAt: str = Field(description="Start timestamp") logs: List[ChatLog] = Field(default=[], description="Log entries") messages: List[ChatMessage] = Field(default=[], description="Message history") stats: Optional[ChatStat] = Field(None, description="Statistics") mandateId: str = Field(description="ID of the mandate this workflow belongs to") label: Label = Field( default=Label(default="Chat Workflow", translations={"en": "Chat Workflow", "fr": "Workflow de chat"}), description="Label for the class" ) # Labels for attributes fieldLabels: Dict[str, Label] = { "id": Label(default="ID", translations={}), "status": Label(default="Status", translations={"en": "Status", "fr": "Statut"}), "name": Label(default="Name", translations={"en": "Name", "fr": "Nom"}), "currentRound": Label(default="Current Round", translations={"en": "Current Round", "fr": "Tour actuel"}), "lastActivity": Label(default="Last Activity", translations={"en": "Last Activity", "fr": "Dernière activité"}), "startedAt": Label(default="Started At", translations={"en": "Started At", "fr": "Démarré à"}), "logs": Label(default="Logs", translations={"en": "Logs", "fr": "Journaux"}), "messages": Label(default="Messages", translations={"en": "Messages", "fr": "Messages"}), "stats": Label(default="Statistics", translations={"en": "Statistics", "fr": "Statistiques"}), "mandateId": Label(default="Mandate ID", translations={"en": "Mandate ID", "fr": "ID de mandat"}) } # AGENT AND TASK MODELS class Agent(BaseModel): """Data model for an agent""" id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the agent") name: str = Field(description="Name of the agent") description: str = Field(description="Description of the agent's functionality") capabilities: List[str] = Field(default=[], description="List of agent capabilities") class AgentResponse(BaseModel): """Response structure returned by agent processing""" response: str = Field(description="Text response from the agent") documents: List[ChatDocument] = Field(default=[], description="List of document objects created by the agent") class TaskItem(BaseModel): """Individual task in the workplan""" sequenceNr: int = Field(description="Sequence number of the task") agentName: str = Field(description="Name of an available agent") prompt: str = Field(description="Specific instructions to the agent") userLanguage: str = Field(description="Language code of the user's request") filesInput: List[str] = Field(default=[], description="List of input files in format 'fileName[;documentId]'") filesOutput: List[str] = Field(default=[], description="List of output files in format 'fileName'") class TaskPlan(BaseModel): """Work plan created by project manager""" fileList: List[str] = Field(default=[], description="List of required result documents in format 'fileName'") taskItems: List[TaskItem] = Field(default=[], description="Plan for executing agents") userResponse: str = Field(description="Response to the user explaining the plan") userLanguage: str = Field(default="en", description="Language code of the user's request") class UserInputRequest(BaseModel): """Request for user input to a running workflow""" prompt: str = Field(description="Message from the user") listFileId: List[str] = Field(default=[], description="List of FileItem IDs") metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata for the request")