From d2b6820812b9d34119c7660a32ec0c2f7730b6e9 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Sun, 5 Oct 2025 16:28:44 +0200
Subject: [PATCH] cleaned pydantic model classes
---
modules/datamodels/__init__.py | 1 -
modules/datamodels/datamodelChat.py | 443 ++++++++++++++---
modules/datamodels/datamodelExtraction.py | 2 +-
modules/datamodels/datamodelFiles.py | 10 -
modules/datamodels/datamodelNeutralizer.py | 5 -
modules/datamodels/datamodelSecurity.py | 6 -
modules/datamodels/datamodelTickets.py | 1 -
modules/datamodels/datamodelUam.py | 16 -
modules/datamodels/datamodelUtils.py | 2 -
modules/datamodels/datamodelVoice.py | 1 -
modules/datamodels/datamodelWeb.py | 19 +-
modules/datamodels/datamodelWorkflow.py | 446 ------------------
modules/interfaces/interfaceAiObjects.py | 2 +-
modules/interfaces/interfaceDbChatObjects.py | 4 +-
modules/services/serviceAi/mainServiceAi.py | 4 +-
.../mainServiceExtraction.py | 22 +-
.../services/serviceExtraction/subPipeline.py | 10 +-
.../serviceWorkflow/mainServiceWorkflow.py | 2 +-
modules/workflows/methods/methodAi.py | 4 +-
modules/workflows/methods/methodDocument.py | 4 +-
modules/workflows/methods/methodOutlook.py | 2 +-
modules/workflows/methods/methodSharepoint.py | 2 +-
.../processing/core/actionExecutor.py | 6 +-
.../processing/core/messageCreator.py | 2 +-
.../workflows/processing/core/taskPlanner.py | 2 +-
.../processing/modes/modeActionplan.py | 44 +-
.../workflows/processing/modes/modeBase.py | 6 +-
.../workflows/processing/modes/modeReact.py | 38 +-
.../processing/shared/executionState.py | 4 +-
.../processing/shared/methodDiscovery.py | 2 +-
.../promptGenerationActionsActionplan.py | 2 +-
.../shared/promptGenerationActionsReact.py | 2 +-
.../shared/promptGenerationTaskplan.py | 2 +-
.../workflows/processing/workflowProcessor.py | 8 +-
modules/workflows/workflowManager.py | 33 +-
35 files changed, 492 insertions(+), 667 deletions(-)
delete mode 100644 modules/datamodels/datamodelWorkflow.py
diff --git a/modules/datamodels/__init__.py b/modules/datamodels/__init__.py
index bc18cabd..2ddc1189 100644
--- a/modules/datamodels/__init__.py
+++ b/modules/datamodels/__init__.py
@@ -10,7 +10,6 @@ from . import datamodelWeb as web
from . import datamodelUam as uam
from . import datamodelSecurity as security
from . import datamodelNeutralizer as neutralizer
-from . import datamodelWorkflow as workflow
from . import datamodelChat as chat
from . import datamodelFiles as files
from . import datamodelVoice as voice
diff --git a/modules/datamodels/datamodelChat.py b/modules/datamodels/datamodelChat.py
index a1640b5d..b7b42aad 100644
--- a/modules/datamodels/datamodelChat.py
+++ b/modules/datamodels/datamodelChat.py
@@ -6,7 +6,6 @@ from modules.shared.attributeUtils import register_model_labels, ModelMixin
from modules.shared.timezoneUtils import get_utc_timestamp
import uuid
-
class ChatStat(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key")
workflowId: Optional[str] = Field(None, description="Foreign key to workflow (for workflow stats)")
@@ -17,8 +16,6 @@ class ChatStat(BaseModel, ModelMixin):
bytesReceived: Optional[int] = Field(None, description="Number of bytes received")
successRate: Optional[float] = Field(None, description="Success rate of operations")
errorCount: Optional[int] = Field(None, description="Number of errors encountered")
-
-
register_model_labels(
"ChatStat",
{"en": "Chat Statistics", "fr": "Statistiques de chat"},
@@ -35,7 +32,6 @@ register_model_labels(
},
)
-
class ChatLog(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key")
workflowId: str = Field(description="Foreign key to workflow")
@@ -45,8 +41,6 @@ class ChatLog(BaseModel, ModelMixin):
status: Optional[str] = Field(None, description="Status of the log entry")
progress: Optional[float] = Field(None, description="Progress indicator (0.0 to 1.0)")
performance: Optional[Dict[str, Any]] = Field(None, description="Performance metrics")
-
-
register_model_labels(
"ChatLog",
{"en": "Chat Log", "fr": "Journal de chat"},
@@ -62,7 +56,6 @@ register_model_labels(
},
)
-
class ChatDocument(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key")
messageId: str = Field(description="Foreign key to message")
@@ -74,8 +67,6 @@ class ChatDocument(BaseModel, ModelMixin):
taskNumber: Optional[int] = Field(None, description="Task number within round")
actionNumber: Optional[int] = Field(None, description="Action number within task")
actionId: Optional[str] = Field(None, description="ID of the action that created this document")
-
-
register_model_labels(
"ChatDocument",
{"en": "Chat Document", "fr": "Document de chat"},
@@ -93,7 +84,6 @@ register_model_labels(
},
)
-
class ContentMetadata(BaseModel, ModelMixin):
size: int = Field(description="Content size in bytes")
pages: Optional[int] = Field(None, description="Number of pages for multi-page content")
@@ -105,8 +95,6 @@ class ContentMetadata(BaseModel, ModelMixin):
durationSec: Optional[float] = Field(None, description="Duration in seconds for media")
mimeType: str = Field(description="MIME type of the content")
base64Encoded: bool = Field(description="Whether the data is base64 encoded")
-
-
register_model_labels(
"ContentMetadata",
{"en": "Content Metadata", "fr": "Métadonnées du contenu"},
@@ -124,13 +112,10 @@ register_model_labels(
},
)
-
class ContentItem(BaseModel, ModelMixin):
label: str = Field(description="Content label")
data: str = Field(description="Extracted text content")
metadata: ContentMetadata = Field(description="Content metadata")
-
-
register_model_labels(
"ContentItem",
{"en": "Content Item", "fr": "Élément de contenu"},
@@ -141,14 +126,11 @@ register_model_labels(
},
)
-
-class ExtractedContent(BaseModel, ModelMixin):
+class ChatContentExtracted(BaseModel, ModelMixin):
id: str = Field(description="Reference to source ChatDocument")
contents: List[ContentItem] = Field(default_factory=list, description="List of content items")
-
-
register_model_labels(
- "ExtractedContent",
+ "ChatContentExtracted",
{"en": "Extracted Content", "fr": "Contenu extrait"},
{
"id": {"en": "Object ID", "fr": "ID de l'objet"},
@@ -177,8 +159,6 @@ class ChatMessage(BaseModel, ModelMixin):
actionNumber: Optional[int] = Field(None, description="Action number within task")
taskProgress: Optional[str] = Field(None, description="Task progress status: pending, running, success, fail, retry")
actionProgress: Optional[str] = Field(None, description="Action progress status: pending, running, success, fail")
-
-
register_model_labels(
"ChatMessage",
{"en": "Chat Message", "fr": "Message de chat"},
@@ -206,7 +186,6 @@ register_model_labels(
},
)
-
class ChatWorkflow(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key", frontend_type="text", frontend_readonly=True, frontend_required=False)
mandateId: str = Field(description="ID of the mandate this workflow belongs to", frontend_type="text", frontend_readonly=True, frontend_required=False)
@@ -233,8 +212,6 @@ class ChatWorkflow(BaseModel, ModelMixin):
{"value": "React", "label": {"en": "React", "fr": "Réactif"}},
])
maxSteps: int = Field(default=5, description="Maximum number of iterations in react mode", frontend_type="integer", frontend_readonly=False, frontend_required=False)
-
-
register_model_labels(
"ChatWorkflow",
{"en": "Chat Workflow", "fr": "Flux de travail de chat"},
@@ -259,41 +236,12 @@ register_model_labels(
},
)
-
-class WorkflowResult(BaseModel, ModelMixin):
- status: str
- completed_tasks: int
- total_tasks: int
- execution_time: float
- final_results_count: int
- error: Optional[str] = None
- phase: Optional[str] = None
-
-
-register_model_labels(
- "WorkflowResult",
- {"en": "Workflow Result", "fr": "Résultat du workflow"},
- {
- "status": {"en": "Status", "fr": "Statut"},
- "completed_tasks": {"en": "Completed Tasks", "fr": "Tâches terminées"},
- "total_tasks": {"en": "Total Tasks", "fr": "Total des tâches"},
- "execution_time": {"en": "Execution Time", "fr": "Temps d'exécution"},
- "final_results_count": {"en": "Final Results Count", "fr": "Nombre de résultats finaux"},
- "error": {"en": "Error", "fr": "Erreur"},
- "phase": {"en": "Phase", "fr": "Phase"},
- },
-)
-
-
class UserInputRequest(BaseModel, ModelMixin):
prompt: str = Field(description="Prompt for the user")
listFileId: List[str] = Field(default_factory=list, description="List of file IDs")
userLanguage: str = Field(default="en", description="User's preferred language")
-
-
register_model_labels(
- "UserInputRequest",
- {"en": "User Input Request", "fr": "Demande de saisie utilisateur"},
+ "UserInputRequest", {"en": "User Input Request", "fr": "Demande de saisie utilisateur"},
{
"prompt": {"en": "Prompt", "fr": "Invite"},
"listFileId": {"en": "File IDs", "fr": "IDs des fichiers"},
@@ -301,4 +249,389 @@ register_model_labels(
},
)
+class ActionDocument(BaseModel, ModelMixin):
+ """Clear document structure for action results"""
+ documentName: str = Field(description="Name of the document")
+ documentData: Any = Field(description="Content/data of the document")
+ mimeType: str = Field(description="MIME type of the document")
+register_model_labels(
+ "ActionDocument",
+ {"en": "Action Document", "fr": "Document d'action"},
+ {
+ "documentName": {"en": "Document Name", "fr": "Nom du document"},
+ "documentData": {"en": "Document Data", "fr": "Données du document"},
+ "mimeType": {"en": "MIME Type", "fr": "Type MIME"},
+ },
+)
+class ActionResult(BaseModel, ModelMixin):
+ """Clean action result with documents as primary output
+
+ IMPORTANT: Action methods should NOT set resultLabel in their return value.
+ The resultLabel is managed by the action handler using the action's execResultLabel
+ from the action plan. This ensures consistent document routing throughout the workflow.
+ """
+
+ success: bool = Field(description="Whether execution succeeded")
+ error: Optional[str] = Field(None, description="Error message if failed")
+ documents: List[ActionDocument] = Field(default_factory=list, description="Document outputs")
+ resultLabel: Optional[str] = Field(None, description="Label for document routing (set by action handler, not by action methods)")
+
+ @classmethod
+ def isSuccess(cls, documents: List[ActionDocument] = None) -> "ActionResult":
+ return cls(success=True, documents=documents or [])
+
+ @classmethod
+ def isFailure(cls, error: str, documents: List[ActionDocument] = None) -> "ActionResult":
+ return cls(success=False, documents=documents or [], error=error)
+register_model_labels(
+ "ActionResult",
+ {"en": "Action Result", "fr": "Résultat de l'action"},
+ {
+ "success": {"en": "Success", "fr": "Succès"},
+ "error": {"en": "Error", "fr": "Erreur"},
+ "documents": {"en": "Documents", "fr": "Documents"},
+ "resultLabel": {"en": "Result Label", "fr": "Étiquette du résultat"},
+ },
+)
+
+class ActionSelection(BaseModel, ModelMixin):
+ method: str = Field(description="Method to execute (e.g., web, document, ai)")
+ name: str = Field(description="Action name within the method (e.g., search, extract)")
+register_model_labels(
+ "ActionSelection",
+ {"en": "Action Selection", "fr": "Sélection d'action"},
+ {
+ "method": {"en": "Method", "fr": "Méthode"},
+ "name": {"en": "Action Name", "fr": "Nom de l'action"},
+ },
+)
+
+class ActionParameters(BaseModel, ModelMixin):
+ parameters: Dict[str, Any] = Field(default_factory=dict, description="Parameters to execute the selected action")
+register_model_labels(
+ "ActionParameters",
+ {"en": "Action Parameters", "fr": "Paramètres d'action"},
+ {
+ "parameters": {"en": "Parameters", "fr": "Paramètres"},
+ },
+)
+
+class ObservationPreview(BaseModel, ModelMixin):
+ name: str = Field(description="Document name or URL label")
+ mime: str = Field(description="MIME type or kind")
+ snippet: str = Field(description="Short snippet or summary")
+register_model_labels(
+ "ObservationPreview",
+ {"en": "Observation Preview", "fr": "Aperçu d'observation"},
+ {
+ "name": {"en": "Name", "fr": "Nom"},
+ "mime": {"en": "MIME", "fr": "MIME"},
+ "snippet": {"en": "Snippet", "fr": "Extrait"},
+ },
+)
+
+class Observation(BaseModel, ModelMixin):
+ success: bool = Field(description="Action execution success flag")
+ resultLabel: str = Field(description="Deterministic label for produced documents")
+ documentsCount: int = Field(description="Number of produced documents")
+ previews: List[ObservationPreview] = Field(default_factory=list, description="Compact previews of outputs")
+ notes: List[str] = Field(default_factory=list, description="Short notes or key facts")
+register_model_labels(
+ "Observation",
+ {"en": "Observation", "fr": "Observation"},
+ {
+ "success": {"en": "Success", "fr": "Succès"},
+ "resultLabel": {"en": "Result Label", "fr": "Étiquette du résultat"},
+ "documentsCount": {"en": "Documents Count", "fr": "Nombre de documents"},
+ "previews": {"en": "Previews", "fr": "Aperçus"},
+ "notes": {"en": "Notes", "fr": "Notes"},
+ },
+)
+
+class TaskStatus(str):
+ PENDING = "pending"
+ RUNNING = "running"
+ COMPLETED = "completed"
+ FAILED = "failed"
+ CANCELLED = "cancelled"
+register_model_labels(
+ "TaskStatus",
+ {"en": "Task Status", "fr": "Statut de la tâche"},
+ {
+ "PENDING": {"en": "Pending", "fr": "En attente"},
+ "RUNNING": {"en": "Running", "fr": "En cours"},
+ "COMPLETED": {"en": "Completed", "fr": "Terminé"},
+ "FAILED": {"en": "Failed", "fr": "Échec"},
+ "CANCELLED": {"en": "Cancelled", "fr": "Annulé"},
+ },
+)
+
+class DocumentExchange(BaseModel, ModelMixin):
+ documentsLabel: str = Field(description="Label for the set of documents")
+ documents: List[str] = Field(default_factory=list, description="List of document references")
+register_model_labels(
+ "DocumentExchange",
+ {"en": "Document Exchange", "fr": "Échange de documents"},
+ {
+ "documentsLabel": {"en": "Documents Label", "fr": "Label des documents"},
+ "documents": {"en": "Documents", "fr": "Documents"},
+ },
+)
+
+class ActionItem(BaseModel, ModelMixin):
+ id: str = Field(..., description="Action ID")
+ execMethod: str = Field(..., description="Method to execute")
+ execAction: str = Field(..., description="Action to perform")
+ execParameters: Dict[str, Any] = Field(default_factory=dict, description="Action parameters")
+ execResultLabel: Optional[str] = Field(None, description="Label for the set of result documents")
+ expectedDocumentFormats: Optional[List[Dict[str, str]]] = Field(None, description="Expected document formats (optional)")
+ userMessage: Optional[str] = Field(None, description="User-friendly message in user's language")
+ status: TaskStatus = Field(default=TaskStatus.PENDING, description="Action status")
+ error: Optional[str] = Field(None, description="Error message if action failed")
+ retryCount: int = Field(default=0, description="Number of retries attempted")
+ retryMax: int = Field(default=3, description="Maximum number of retries")
+ processingTime: Optional[float] = Field(None, description="Processing time in seconds")
+ timestamp: float = Field(..., description="When the action was executed (UTC timestamp in seconds)")
+ result: Optional[str] = Field(None, description="Result of the action")
+
+ def setSuccess(self, result: str = None) -> None:
+ """Set the action as successful with optional result"""
+ self.status = TaskStatus.COMPLETED
+ self.error = None
+ if result is not None:
+ self.result = result
+
+ def setError(self, error_message: str) -> None:
+ """Set the action as failed with error message"""
+ self.status = TaskStatus.FAILED
+ self.error = error_message
+register_model_labels(
+ "ActionItem",
+ {"en": "Task Action", "fr": "Action de tâche"},
+ {
+ "id": {"en": "Action ID", "fr": "ID de l'action"},
+ "execMethod": {"en": "Method", "fr": "Méthode"},
+ "execAction": {"en": "Action", "fr": "Action"},
+ "execParameters": {"en": "Parameters", "fr": "Paramètres"},
+ "execResultLabel": {"en": "Result Label", "fr": "Label du résultat"},
+ "expectedDocumentFormats": {"en": "Expected Document Formats", "fr": "Formats de documents attendus"},
+ "userMessage": {"en": "User Message", "fr": "Message utilisateur"},
+ "status": {"en": "Status", "fr": "Statut"},
+ "error": {"en": "Error", "fr": "Erreur"},
+ "retryCount": {"en": "Retry Count", "fr": "Nombre de tentatives"},
+ "retryMax": {"en": "Max Retries", "fr": "Tentatives max"},
+ "processingTime": {"en": "Processing Time", "fr": "Temps de traitement"},
+ "timestamp": {"en": "Timestamp", "fr": "Horodatage"},
+ "result": {"en": "Result", "fr": "Résultat"},
+ },
+)
+
+class TaskResult(BaseModel, ModelMixin):
+ taskId: str = Field(..., description="Task ID")
+ status: TaskStatus = Field(default=TaskStatus.PENDING, description="Task status")
+ success: bool = Field(..., description="Whether the task was successful")
+ feedback: Optional[str] = Field(None, description="Task feedback message")
+ error: Optional[str] = Field(None, description="Error message if task failed")
+register_model_labels(
+ "TaskResult",
+ {"en": "Task Result", "fr": "Résultat de tâche"},
+ {
+ "taskId": {"en": "Task ID", "fr": "ID de la tâche"},
+ "status": {"en": "Status", "fr": "Statut"},
+ "success": {"en": "Success", "fr": "Succès"},
+ "feedback": {"en": "Feedback", "fr": "Retour"},
+ "error": {"en": "Error", "fr": "Erreur"},
+ },
+)
+
+class TaskItem(BaseModel, ModelMixin):
+ id: str = Field(..., description="Task ID")
+ workflowId: str = Field(..., description="Workflow ID")
+ userInput: str = Field(..., description="User input that triggered the task")
+ status: TaskStatus = Field(default=TaskStatus.PENDING, description="Task status")
+ error: Optional[str] = Field(None, description="Error message if task failed")
+ startedAt: Optional[float] = Field(None, description="When the task started (UTC timestamp in seconds)")
+ finishedAt: Optional[float] = Field(None, description="When the task finished (UTC timestamp in seconds)")
+ actionList: List[ActionItem] = Field(default_factory=list, description="List of actions to execute")
+ retryCount: int = Field(default=0, description="Number of retries attempted")
+ retryMax: int = Field(default=3, description="Maximum number of retries")
+ rollbackOnFailure: bool = Field(default=True, description="Whether to rollback on failure")
+ dependencies: List[str] = Field(default_factory=list, description="List of task IDs this task depends on")
+ feedback: Optional[str] = Field(None, description="Task feedback message")
+ processingTime: Optional[float] = Field(None, description="Total processing time in seconds")
+ resultLabels: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Map of result labels to their values")
+register_model_labels(
+ "TaskItem",
+ {"en": "Task", "fr": "Tâche"},
+ {
+ "id": {"en": "Task ID", "fr": "ID de la tâche"},
+ "workflowId": {"en": "Workflow ID", "fr": "ID du workflow"},
+ "userInput": {"en": "User Input", "fr": "Entrée utilisateur"},
+ "status": {"en": "Status", "fr": "Statut"},
+ "error": {"en": "Error", "fr": "Erreur"},
+ "startedAt": {"en": "Started At", "fr": "Démarré à"},
+ "finishedAt": {"en": "Finished At", "fr": "Terminé à"},
+ "actionList": {"en": "Actions", "fr": "Actions"},
+ "retryCount": {"en": "Retry Count", "fr": "Nombre de tentatives"},
+ "retryMax": {"en": "Max Retries", "fr": "Tentatives max"},
+ "processingTime": {"en": "Processing Time", "fr": "Temps de traitement"},
+ },
+)
+
+class TaskStep(BaseModel, ModelMixin):
+ id: str
+ objective: str
+ dependencies: Optional[list[str]] = Field(default_factory=list)
+ success_criteria: Optional[list[str]] = Field(default_factory=list)
+ estimated_complexity: Optional[str] = None
+ userMessage: Optional[str] = Field(None, description="User-friendly message in user's language")
+register_model_labels(
+ "TaskStep",
+ {"en": "Task Step", "fr": "Étape de tâche"},
+ {
+ "id": {"en": "ID", "fr": "ID"},
+ "objective": {"en": "Objective", "fr": "Objectif"},
+ "dependencies": {"en": "Dependencies", "fr": "Dépendances"},
+ "success_criteria": {"en": "Success Criteria", "fr": "Critères de succès"},
+ "estimated_complexity": {"en": "Estimated Complexity", "fr": "Complexité estimée"},
+ "userMessage": {"en": "User Message", "fr": "Message utilisateur"},
+ },
+)
+
+class TaskHandover(BaseModel, ModelMixin):
+ taskId: str = Field(description="Target task ID")
+ sourceTask: Optional[str] = Field(None, description="Source task ID")
+ inputDocuments: List[DocumentExchange] = Field(default_factory=list, description="Available input documents")
+ outputDocuments: List[DocumentExchange] = Field(default_factory=list, description="Produced output documents")
+ context: Dict[str, Any] = Field(default_factory=dict, description="Task context")
+ previousResults: List[str] = Field(default_factory=list, description="Previous result summaries")
+ improvements: List[str] = Field(default_factory=list, description="Improvement suggestions")
+ workflowSummary: Optional[str] = Field(None, description="Summarized workflow context")
+ messageHistory: List[str] = Field(default_factory=list, description="Key message summaries")
+ timestamp: float = Field(..., description="When the handover was created (UTC timestamp in seconds)")
+ handoverType: str = Field(default="task", description="Type of handover: task, phase, or workflow")
+register_model_labels(
+ "TaskHandover",
+ {"en": "Task Handover", "fr": "Transfert de tâche"},
+ {
+ "taskId": {"en": "Task ID", "fr": "ID de la tâche"},
+ "sourceTask": {"en": "Source Task", "fr": "Tâche source"},
+ "inputDocuments": {"en": "Input Documents", "fr": "Documents d'entrée"},
+ "outputDocuments": {"en": "Output Documents", "fr": "Documents de sortie"},
+ "context": {"en": "Context", "fr": "Contexte"},
+ "previousResults": {"en": "Previous Results", "fr": "Résultats précédents"},
+ "improvements": {"en": "Improvements", "fr": "Améliorations"},
+ "workflowSummary": {"en": "Workflow Summary", "fr": "Résumé du workflow"},
+ "messageHistory": {"en": "Message History", "fr": "Historique des messages"},
+ "timestamp": {"en": "Timestamp", "fr": "Horodatage"},
+ "handoverType": {"en": "Handover Type", "fr": "Type de transfert"},
+ },
+)
+
+class TaskContext(BaseModel, ModelMixin):
+ task_step: TaskStep
+ workflow: Optional['ChatWorkflow'] = None
+ workflow_id: Optional[str] = None
+ available_documents: Optional[str] = "No documents available"
+ available_connections: Optional[list[str]] = Field(default_factory=list)
+ previous_results: Optional[list[str]] = Field(default_factory=list)
+ previous_handover: Optional[TaskHandover] = None
+ improvements: Optional[list[str]] = Field(default_factory=list)
+ retry_count: Optional[int] = 0
+ previous_action_results: Optional[list] = Field(default_factory=list)
+ previous_review_result: Optional[dict] = None
+ is_regeneration: Optional[bool] = False
+ failure_patterns: Optional[list[str]] = Field(default_factory=list)
+ failed_actions: Optional[list] = Field(default_factory=list)
+ successful_actions: Optional[list] = Field(default_factory=list)
+ criteria_progress: Optional[dict] = None
+
+ def getDocumentReferences(self) -> List[str]:
+ docs = []
+ if self.previous_handover:
+ for doc_exchange in self.previous_handover.inputDocuments:
+ docs.extend(doc_exchange.documents)
+ return list(set(docs))
+
+ def addImprovement(self, improvement: str) -> None:
+ if improvement not in (self.improvements or []):
+ if self.improvements is None:
+ self.improvements = []
+ self.improvements.append(improvement)
+
+class ReviewContext(BaseModel, ModelMixin):
+ task_step: TaskStep
+ task_actions: Optional[list] = Field(default_factory=list)
+ action_results: Optional[list] = Field(default_factory=list)
+ step_result: Optional[dict] = Field(default_factory=dict)
+ workflow_id: Optional[str] = None
+ previous_results: Optional[list[str]] = Field(default_factory=list)
+
+class ReviewResult(BaseModel, ModelMixin):
+ status: str
+ reason: Optional[str] = None
+ improvements: Optional[list[str]] = Field(default_factory=list)
+ quality_score: Optional[int] = 5
+ missing_outputs: Optional[list[str]] = Field(default_factory=list)
+ met_criteria: Optional[list[str]] = Field(default_factory=list)
+ unmet_criteria: Optional[list[str]] = Field(default_factory=list)
+ confidence: Optional[float] = 0.5
+ userMessage: Optional[str] = Field(None, description="User-friendly message in user's language")
+register_model_labels(
+ "ReviewResult",
+ {"en": "Review Result", "fr": "Résultat de l'évaluation"},
+ {
+ "status": {"en": "Status", "fr": "Statut"},
+ "reason": {"en": "Reason", "fr": "Raison"},
+ "improvements": {"en": "Improvements", "fr": "Améliorations"},
+ "quality_score": {"en": "Quality Score", "fr": "Score de qualité"},
+ "missing_outputs": {"en": "Missing Outputs", "fr": "Sorties manquantes"},
+ "met_criteria": {"en": "Met Criteria", "fr": "Critères respectés"},
+ "unmet_criteria": {"en": "Unmet Criteria", "fr": "Critères non respectés"},
+ "confidence": {"en": "Confidence", "fr": "Confiance"},
+ "userMessage": {"en": "User Message", "fr": "Message utilisateur"},
+ },
+)
+
+class TaskPlan(BaseModel, ModelMixin):
+ overview: str
+ tasks: list[TaskStep]
+ userMessage: Optional[str] = Field(None, description="Overall user-friendly message for the task plan")
+register_model_labels(
+ "TaskPlan",
+ {"en": "Task Plan", "fr": "Plan de tâches"},
+ {
+ "overview": {"en": "Overview", "fr": "Aperçu"},
+ "tasks": {"en": "Tasks", "fr": "Tâches"},
+ "userMessage": {"en": "User Message", "fr": "Message utilisateur"},
+ },
+)
+
+# Resolve forward references
+TaskContext.update_forward_refs()
+
+class PromptPlaceholder(BaseModel, ModelMixin):
+ label: str
+ content: str
+ summaryAllowed: bool = Field(default=False, description="Whether host may summarize content before sending to AI")
+register_model_labels(
+ "PromptPlaceholder",
+ {"en": "Prompt Placeholder", "fr": "Espace réservé d'invite"},
+ {
+ "label": {"en": "Label", "fr": "Libellé"},
+ "content": {"en": "Content", "fr": "Contenu"},
+ "summaryAllowed": {"en": "Summary Allowed", "fr": "Résumé autorisé"},
+ },
+)
+
+class PromptBundle(BaseModel, ModelMixin):
+ prompt: str
+ placeholders: List[PromptPlaceholder] = Field(default_factory=list)
+register_model_labels(
+ "PromptBundle", {"en": "Prompt Bundle", "fr": "Lot d'invite"},
+ {
+ "prompt": {"en": "Prompt", "fr": "Invite"},
+ "placeholders": {"en": "Placeholders", "fr": "Espaces réservés"},
+ },
+)
diff --git a/modules/datamodels/datamodelExtraction.py b/modules/datamodels/datamodelExtraction.py
index 46977b78..ff44aa19 100644
--- a/modules/datamodels/datamodelExtraction.py
+++ b/modules/datamodels/datamodelExtraction.py
@@ -12,7 +12,7 @@ class ContentPart(BaseModel):
metadata: Dict[str, Any] = Field(default_factory=dict, description="Arbitrary metadata for the part")
-class ExtractedContent(BaseModel):
+class ContentExtracted(BaseModel):
id: str = Field(description="Extraction id or source document id")
parts: List[ContentPart] = Field(default_factory=list, description="List of extracted parts")
summary: Optional[Dict[str, Any]] = Field(default=None, description="Optional extraction summary")
diff --git a/modules/datamodels/datamodelFiles.py b/modules/datamodels/datamodelFiles.py
index d561c79e..41cb0cb6 100644
--- a/modules/datamodels/datamodelFiles.py
+++ b/modules/datamodels/datamodelFiles.py
@@ -19,8 +19,6 @@ class FileItem(BaseModel, ModelMixin):
def to_dict(self) -> Dict[str, Any]:
return super().to_dict()
-
-
register_model_labels(
"FileItem",
{"en": "File Item", "fr": "Élément de fichier"},
@@ -35,7 +33,6 @@ register_model_labels(
},
)
-
class FilePreview(BaseModel, ModelMixin):
content: Union[str, bytes] = Field(description="File content (text or binary)")
mimeType: str = Field(description="MIME type of the file")
@@ -49,8 +46,6 @@ class FilePreview(BaseModel, ModelMixin):
if isinstance(data.get("content"), bytes):
data["content"] = base64.b64encode(data["content"]).decode("utf-8")
return data
-
-
register_model_labels(
"FilePreview",
{"en": "File Preview", "fr": "Aperçu du fichier"},
@@ -64,13 +59,10 @@ register_model_labels(
},
)
-
class FileData(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key")
data: str = Field(description="File data content")
base64Encoded: bool = Field(description="Whether the data is base64 encoded")
-
-
register_model_labels(
"FileData",
{"en": "File Data", "fr": "Données de fichier"},
@@ -80,5 +72,3 @@ register_model_labels(
"base64Encoded": {"en": "Base64 Encoded", "fr": "Encodé en Base64"},
},
)
-
-
diff --git a/modules/datamodels/datamodelNeutralizer.py b/modules/datamodels/datamodelNeutralizer.py
index 475b1146..998cb17f 100644
--- a/modules/datamodels/datamodelNeutralizer.py
+++ b/modules/datamodels/datamodelNeutralizer.py
@@ -14,8 +14,6 @@ class DataNeutraliserConfig(BaseModel, ModelMixin):
namesToParse: str = Field(default="", description="Multiline list of names to parse for neutralization", frontend_type="textarea", frontend_readonly=False, frontend_required=False)
sharepointSourcePath: str = Field(default="", description="SharePoint path to read files for neutralization", frontend_type="text", frontend_readonly=False, frontend_required=False)
sharepointTargetPath: str = Field(default="", description="SharePoint path to store neutralized files", frontend_type="text", frontend_readonly=False, frontend_required=False)
-
-
register_model_labels(
"DataNeutraliserConfig",
{"en": "Data Neutralization Config", "fr": "Configuration de neutralisation des données"},
@@ -30,7 +28,6 @@ register_model_labels(
},
)
-
class DataNeutralizerAttributes(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the attribute mapping (used as UID in neutralized files)", frontend_type="text", frontend_readonly=True, frontend_required=False)
mandateId: str = Field(description="ID of the mandate this attribute belongs to", frontend_type="text", frontend_readonly=True, frontend_required=True)
@@ -38,8 +35,6 @@ class DataNeutralizerAttributes(BaseModel, ModelMixin):
originalText: str = Field(description="Original text that was neutralized", frontend_type="text", frontend_readonly=True, frontend_required=True)
fileId: Optional[str] = Field(default=None, description="ID of the file this attribute belongs to", frontend_type="text", frontend_readonly=True, frontend_required=False)
patternType: str = Field(description="Type of pattern that matched (email, phone, name, etc.)", frontend_type="text", frontend_readonly=True, frontend_required=True)
-
-
register_model_labels(
"DataNeutralizerAttributes",
{"en": "Neutralized Data Attribute", "fr": "Attribut de données neutralisées"},
diff --git a/modules/datamodels/datamodelSecurity.py b/modules/datamodels/datamodelSecurity.py
index ff6a3f6f..400d2c56 100644
--- a/modules/datamodels/datamodelSecurity.py
+++ b/modules/datamodels/datamodelSecurity.py
@@ -13,7 +13,6 @@ class TokenStatus(str, Enum):
ACTIVE = "active"
REVOKED = "revoked"
-
class Token(BaseModel, ModelMixin):
id: Optional[str] = None
userId: str
@@ -33,8 +32,6 @@ class Token(BaseModel, ModelMixin):
class Config:
use_enum_values = True
-
-
register_model_labels(
"Token",
{"en": "Token", "fr": "Jeton"},
@@ -57,7 +54,6 @@ register_model_labels(
},
)
-
class AuthEvent(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the auth event", frontend_type="text", frontend_readonly=True, frontend_required=False)
userId: str = Field(description="ID of the user this event belongs to", frontend_type="text", frontend_readonly=True, frontend_required=True)
@@ -67,8 +63,6 @@ class AuthEvent(BaseModel, ModelMixin):
userAgent: Optional[str] = Field(default=None, description="User agent string from the request", frontend_type="text", frontend_readonly=True, frontend_required=False)
success: bool = Field(default=True, description="Whether the authentication event was successful", frontend_type="boolean", frontend_readonly=True, frontend_required=True)
details: Optional[str] = Field(default=None, description="Additional details about the event", frontend_type="text", frontend_readonly=True, frontend_required=False)
-
-
register_model_labels(
"AuthEvent",
{"en": "Authentication Event", "fr": "Événement d'authentification"},
diff --git a/modules/datamodels/datamodelTickets.py b/modules/datamodels/datamodelTickets.py
index d11606c6..40478bc6 100644
--- a/modules/datamodels/datamodelTickets.py
+++ b/modules/datamodels/datamodelTickets.py
@@ -9,7 +9,6 @@ class TicketFieldAttribute(BaseModel):
fieldName: str = Field(description="Human-readable field name")
field: str = Field(description="Ticket field ID/key")
-
class TicketBase(ABC):
@abstractmethod
async def read_attributes(self) -> list[TicketFieldAttribute]: ...
diff --git a/modules/datamodels/datamodelUam.py b/modules/datamodels/datamodelUam.py
index 283ff882..8bd24d8c 100644
--- a/modules/datamodels/datamodelUam.py
+++ b/modules/datamodels/datamodelUam.py
@@ -13,20 +13,17 @@ class AuthAuthority(str, Enum):
GOOGLE = "google"
MSFT = "msft"
-
class UserPrivilege(str, Enum):
SYSADMIN = "sysadmin"
ADMIN = "admin"
USER = "user"
-
class ConnectionStatus(str, Enum):
ACTIVE = "active"
EXPIRED = "expired"
REVOKED = "revoked"
PENDING = "pending"
-
class Mandate(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the mandate", frontend_type="text", frontend_readonly=True, frontend_required=False)
name: str = Field(description="Name of the mandate", frontend_type="text", frontend_readonly=False, frontend_required=True)
@@ -37,8 +34,6 @@ class Mandate(BaseModel, ModelMixin):
{"value": "it", "label": {"en": "Italiano", "fr": "Italien"}},
])
enabled: bool = Field(default=True, description="Indicates whether the mandate is enabled", frontend_type="checkbox", frontend_readonly=False, frontend_required=False)
-
-
register_model_labels(
"Mandate",
{"en": "Mandate", "fr": "Mandat"},
@@ -50,7 +45,6 @@ register_model_labels(
},
)
-
class UserConnection(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the connection", frontend_type="text", frontend_readonly=True, frontend_required=False)
userId: str = Field(description="ID of the user this connection belongs to", frontend_type="text", frontend_readonly=True, frontend_required=False)
@@ -77,8 +71,6 @@ class UserConnection(BaseModel, ModelMixin):
{"value": "none", "label": {"en": "None", "fr": "Aucun"}},
])
tokenExpiresAt: Optional[float] = Field(None, description="When the current token expires (UTC timestamp in seconds)", frontend_type="timestamp", frontend_readonly=True, frontend_required=False)
-
-
register_model_labels(
"UserConnection",
{"en": "User Connection", "fr": "Connexion utilisateur"},
@@ -98,7 +90,6 @@ register_model_labels(
},
)
-
class User(BaseModel, ModelMixin):
id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the user", frontend_type="text", frontend_readonly=True, frontend_required=False)
username: str = Field(description="Username for login", frontend_type="text", frontend_readonly=False, frontend_required=True)
@@ -122,8 +113,6 @@ class User(BaseModel, ModelMixin):
{"value": "msft", "label": {"en": "Microsoft", "fr": "Microsoft"}},
])
mandateId: Optional[str] = Field(None, description="ID of the mandate this user belongs to", frontend_type="text", frontend_readonly=True, frontend_required=False)
-
-
register_model_labels(
"User",
{"en": "User", "fr": "Utilisateur"},
@@ -140,15 +129,10 @@ register_model_labels(
},
)
-
class UserInDB(User):
hashedPassword: Optional[str] = Field(None, description="Hash of the user password")
-
-
register_model_labels(
"UserInDB",
{"en": "User Access", "fr": "Accès de l'utilisateur"},
{"hashedPassword": {"en": "Password hash", "fr": "Hachage de mot de passe"}},
)
-
-
diff --git a/modules/datamodels/datamodelUtils.py b/modules/datamodels/datamodelUtils.py
index 82a888e7..ccd01c04 100644
--- a/modules/datamodels/datamodelUtils.py
+++ b/modules/datamodels/datamodelUtils.py
@@ -10,8 +10,6 @@ class Prompt(BaseModel, ModelMixin):
mandateId: str = Field(description="ID of the mandate this prompt belongs to", frontend_type="text", frontend_readonly=True, frontend_required=False)
content: str = Field(description="Content of the prompt", frontend_type="textarea", frontend_readonly=False, frontend_required=True)
name: str = Field(description="Name of the prompt", frontend_type="text", frontend_readonly=False, frontend_required=True)
-
-
register_model_labels(
"Prompt",
{"en": "Prompt", "fr": "Invite"},
diff --git a/modules/datamodels/datamodelVoice.py b/modules/datamodels/datamodelVoice.py
index 3fc69cd8..274a507b 100644
--- a/modules/datamodels/datamodelVoice.py
+++ b/modules/datamodels/datamodelVoice.py
@@ -22,7 +22,6 @@ class VoiceSettings(BaseModel, ModelMixin):
def to_dict(self) -> Dict[str, Any]:
return super().to_dict()
-
register_model_labels(
"VoiceSettings",
{"en": "Voice Settings", "fr": "Paramètres vocaux"},
diff --git a/modules/datamodels/datamodelWeb.py b/modules/datamodels/datamodelWeb.py
index 6ef5a8a3..bc1e03e3 100644
--- a/modules/datamodels/datamodelWeb.py
+++ b/modules/datamodels/datamodelWeb.py
@@ -1,10 +1,8 @@
"""Web-related modules"""
-
-from abc import ABC, abstractmethod
from pydantic import BaseModel, Field, HttpUrl
from typing import List, Optional, Literal, Dict, Any
from modules.shared.configuration import APP_CONFIG
-from modules.datamodels.datamodelWorkflow import ActionDocument, ActionResult
+from modules.datamodels.datamodelChat import ActionDocument, ActionResult
WEB_SEARCH_MAX_QUERY_LENGTH: int = int(APP_CONFIG.get("Web_Search_MAX_QUERY_LENGTH", "400"))
@@ -27,7 +25,6 @@ class WebResearchOptions(BaseModel):
include_answer: Optional[bool] = Field(default=None, description="Include AI answer")
include_raw_content: Optional[bool] = Field(default=None, description="Include raw content")
-
class WebResearchRequest(BaseModel):
"""Main web research request"""
user_prompt: str = Field(min_length=1, max_length=WEB_SEARCH_MAX_QUERY_LENGTH, description="User's research question or prompt")
@@ -35,20 +32,17 @@ class WebResearchRequest(BaseModel):
max_results: int = Field(default=5, ge=1, le=WEB_SEARCH_MAX_RESULTS, description="Max search results")
options: WebResearchOptions = Field(default_factory=WebResearchOptions, description="Advanced options")
-
class WebSearchResultItem(BaseModel):
"""Individual search result"""
title: str
url: HttpUrl
raw_content: Optional[str] = Field(default=None, description="Raw HTML content")
-
class WebCrawlResultItem(BaseModel):
"""Individual crawl result"""
url: HttpUrl
content: str
-
class WebResearchDocumentData(BaseModel):
"""Complete web research results"""
user_prompt: str
@@ -60,21 +54,14 @@ class WebResearchDocumentData(BaseModel):
individual_content: Optional[Dict[str, str]] = None # URL -> content mapping
debug_info: Optional[Dict[str, Any]] = None
-
class WebResearchActionDocument(ActionDocument):
documentData: WebResearchDocumentData
-
class WebResearchActionResult(ActionResult):
documents: List[WebResearchActionDocument] = Field(default_factory=list)
-
-class WebResearchBase(ABC):
- @abstractmethod
- async def web_research(self, request: WebResearchRequest) -> WebResearchActionResult: ...
-
-
# Legacy models for connector compatibility
+
class WebSearchDocumentData(BaseModel):
"""Search results document data"""
query: str
@@ -153,5 +140,3 @@ class WebScrapeResultItem(BaseModel):
"""Individual scrape result"""
url: HttpUrl
content: str
-
-
diff --git a/modules/datamodels/datamodelWorkflow.py b/modules/datamodels/datamodelWorkflow.py
deleted file mode 100644
index c1da40c4..00000000
--- a/modules/datamodels/datamodelWorkflow.py
+++ /dev/null
@@ -1,446 +0,0 @@
-"""Workflow-related base datamodels and step/task structures."""
-
-from typing import List, Dict, Any, Optional
-from pydantic import BaseModel, Field
-from modules.shared.attributeUtils import register_model_labels, ModelMixin
-
-
-class ActionDocument(BaseModel, ModelMixin):
- """Clear document structure for action results"""
- documentName: str = Field(description="Name of the document")
- documentData: Any = Field(description="Content/data of the document")
- mimeType: str = Field(description="MIME type of the document")
-register_model_labels(
- "ActionDocument",
- {"en": "Action Document", "fr": "Document d'action"},
- {
- "documentName": {"en": "Document Name", "fr": "Nom du document"},
- "documentData": {"en": "Document Data", "fr": "Données du document"},
- "mimeType": {"en": "MIME Type", "fr": "Type MIME"},
- },
-)
-
-
-class ActionResult(BaseModel, ModelMixin):
- """Clean action result with documents as primary output
-
- IMPORTANT: Action methods should NOT set resultLabel in their return value.
- The resultLabel is managed by the action handler using the action's execResultLabel
- from the action plan. This ensures consistent document routing throughout the workflow.
- """
-
- success: bool = Field(description="Whether execution succeeded")
- error: Optional[str] = Field(None, description="Error message if failed")
- documents: List[ActionDocument] = Field(default_factory=list, description="Document outputs")
- resultLabel: Optional[str] = Field(None, description="Label for document routing (set by action handler, not by action methods)")
-
- @classmethod
- def isSuccess(cls, documents: List[ActionDocument] = None) -> "ActionResult":
- return cls(success=True, documents=documents or [])
-
- @classmethod
- def isFailure(cls, error: str, documents: List[ActionDocument] = None) -> "ActionResult":
- return cls(success=False, documents=documents or [], error=error)
-register_model_labels(
- "ActionResult",
- {"en": "Action Result", "fr": "Résultat de l'action"},
- {
- "success": {"en": "Success", "fr": "Succès"},
- "error": {"en": "Error", "fr": "Erreur"},
- "documents": {"en": "Documents", "fr": "Documents"},
- "resultLabel": {"en": "Result Label", "fr": "Étiquette du résultat"},
- },
-)
-
-
-class ActionSelection(BaseModel, ModelMixin):
- method: str = Field(description="Method to execute (e.g., web, document, ai)")
- name: str = Field(description="Action name within the method (e.g., search, extract)")
-
-
-register_model_labels(
- "ActionSelection",
- {"en": "Action Selection", "fr": "Sélection d'action"},
- {
- "method": {"en": "Method", "fr": "Méthode"},
- "name": {"en": "Action Name", "fr": "Nom de l'action"},
- },
-)
-
-
-class ActionParameters(BaseModel, ModelMixin):
- parameters: Dict[str, Any] = Field(default_factory=dict, description="Parameters to execute the selected action")
-
-
-register_model_labels(
- "ActionParameters",
- {"en": "Action Parameters", "fr": "Paramètres d'action"},
- {
- "parameters": {"en": "Parameters", "fr": "Paramètres"},
- },
-)
-
-
-class ObservationPreview(BaseModel, ModelMixin):
- name: str = Field(description="Document name or URL label")
- mime: str = Field(description="MIME type or kind")
- snippet: str = Field(description="Short snippet or summary")
-
-
-register_model_labels(
- "ObservationPreview",
- {"en": "Observation Preview", "fr": "Aperçu d'observation"},
- {
- "name": {"en": "Name", "fr": "Nom"},
- "mime": {"en": "MIME", "fr": "MIME"},
- "snippet": {"en": "Snippet", "fr": "Extrait"},
- },
-)
-
-
-class Observation(BaseModel, ModelMixin):
- success: bool = Field(description="Action execution success flag")
- resultLabel: str = Field(description="Deterministic label for produced documents")
- documentsCount: int = Field(description="Number of produced documents")
- previews: List[ObservationPreview] = Field(default_factory=list, description="Compact previews of outputs")
- notes: List[str] = Field(default_factory=list, description="Short notes or key facts")
-
-
-register_model_labels(
- "Observation",
- {"en": "Observation", "fr": "Observation"},
- {
- "success": {"en": "Success", "fr": "Succès"},
- "resultLabel": {"en": "Result Label", "fr": "Étiquette du résultat"},
- "documentsCount": {"en": "Documents Count", "fr": "Nombre de documents"},
- "previews": {"en": "Previews", "fr": "Aperçus"},
- "notes": {"en": "Notes", "fr": "Notes"},
- },
-)
-
-
-class TaskStatus(str):
- PENDING = "pending"
- RUNNING = "running"
- COMPLETED = "completed"
- FAILED = "failed"
- CANCELLED = "cancelled"
-
-
-register_model_labels(
- "TaskStatus",
- {"en": "Task Status", "fr": "Statut de la tâche"},
- {
- "PENDING": {"en": "Pending", "fr": "En attente"},
- "RUNNING": {"en": "Running", "fr": "En cours"},
- "COMPLETED": {"en": "Completed", "fr": "Terminé"},
- "FAILED": {"en": "Failed", "fr": "Échec"},
- "CANCELLED": {"en": "Cancelled", "fr": "Annulé"},
- },
-)
-
-
-class DocumentExchange(BaseModel, ModelMixin):
- documentsLabel: str = Field(description="Label for the set of documents")
- documents: List[str] = Field(default_factory=list, description="List of document references")
-
-
-register_model_labels(
- "DocumentExchange",
- {"en": "Document Exchange", "fr": "Échange de documents"},
- {
- "documentsLabel": {"en": "Documents Label", "fr": "Label des documents"},
- "documents": {"en": "Documents", "fr": "Documents"},
- },
-)
-
-
-class TaskAction(BaseModel, ModelMixin):
- id: str = Field(..., description="Action ID")
- execMethod: str = Field(..., description="Method to execute")
- execAction: str = Field(..., description="Action to perform")
- execParameters: Dict[str, Any] = Field(default_factory=dict, description="Action parameters")
- execResultLabel: Optional[str] = Field(None, description="Label for the set of result documents")
- expectedDocumentFormats: Optional[List[Dict[str, str]]] = Field(None, description="Expected document formats (optional)")
- userMessage: Optional[str] = Field(None, description="User-friendly message in user's language")
- status: TaskStatus = Field(default=TaskStatus.PENDING, description="Action status")
- error: Optional[str] = Field(None, description="Error message if action failed")
- retryCount: int = Field(default=0, description="Number of retries attempted")
- retryMax: int = Field(default=3, description="Maximum number of retries")
- processingTime: Optional[float] = Field(None, description="Processing time in seconds")
- timestamp: float = Field(..., description="When the action was executed (UTC timestamp in seconds)")
- result: Optional[str] = Field(None, description="Result of the action")
-
- def setSuccess(self, result: str = None) -> None:
- """Set the action as successful with optional result"""
- self.status = TaskStatus.COMPLETED
- self.error = None
- if result is not None:
- self.result = result
-
- def setError(self, error_message: str) -> None:
- """Set the action as failed with error message"""
- self.status = TaskStatus.FAILED
- self.error = error_message
-
-
-register_model_labels(
- "TaskAction",
- {"en": "Task Action", "fr": "Action de tâche"},
- {
- "id": {"en": "Action ID", "fr": "ID de l'action"},
- "execMethod": {"en": "Method", "fr": "Méthode"},
- "execAction": {"en": "Action", "fr": "Action"},
- "execParameters": {"en": "Parameters", "fr": "Paramètres"},
- "execResultLabel": {"en": "Result Label", "fr": "Label du résultat"},
- "expectedDocumentFormats": {"en": "Expected Document Formats", "fr": "Formats de documents attendus"},
- "userMessage": {"en": "User Message", "fr": "Message utilisateur"},
- "status": {"en": "Status", "fr": "Statut"},
- "error": {"en": "Error", "fr": "Erreur"},
- "retryCount": {"en": "Retry Count", "fr": "Nombre de tentatives"},
- "retryMax": {"en": "Max Retries", "fr": "Tentatives max"},
- "processingTime": {"en": "Processing Time", "fr": "Temps de traitement"},
- "timestamp": {"en": "Timestamp", "fr": "Horodatage"},
- "result": {"en": "Result", "fr": "Résultat"},
- },
-)
-
-
-class TaskResult(BaseModel, ModelMixin):
- taskId: str = Field(..., description="Task ID")
- status: TaskStatus = Field(default=TaskStatus.PENDING, description="Task status")
- success: bool = Field(..., description="Whether the task was successful")
- feedback: Optional[str] = Field(None, description="Task feedback message")
- error: Optional[str] = Field(None, description="Error message if task failed")
-
-
-register_model_labels(
- "TaskResult",
- {"en": "Task Result", "fr": "Résultat de tâche"},
- {
- "taskId": {"en": "Task ID", "fr": "ID de la tâche"},
- "status": {"en": "Status", "fr": "Statut"},
- "success": {"en": "Success", "fr": "Succès"},
- "feedback": {"en": "Feedback", "fr": "Retour"},
- "error": {"en": "Error", "fr": "Erreur"},
- },
-)
-
-
-class TaskItem(BaseModel, ModelMixin):
- id: str = Field(..., description="Task ID")
- workflowId: str = Field(..., description="Workflow ID")
- userInput: str = Field(..., description="User input that triggered the task")
- status: TaskStatus = Field(default=TaskStatus.PENDING, description="Task status")
- error: Optional[str] = Field(None, description="Error message if task failed")
- startedAt: Optional[float] = Field(None, description="When the task started (UTC timestamp in seconds)")
- finishedAt: Optional[float] = Field(None, description="When the task finished (UTC timestamp in seconds)")
- actionList: List[TaskAction] = Field(default_factory=list, description="List of actions to execute")
- retryCount: int = Field(default=0, description="Number of retries attempted")
- retryMax: int = Field(default=3, description="Maximum number of retries")
- rollbackOnFailure: bool = Field(default=True, description="Whether to rollback on failure")
- dependencies: List[str] = Field(default_factory=list, description="List of task IDs this task depends on")
- feedback: Optional[str] = Field(None, description="Task feedback message")
- processingTime: Optional[float] = Field(None, description="Total processing time in seconds")
- resultLabels: Optional[Dict[str, Any]] = Field(default_factory=dict, description="Map of result labels to their values")
-
-
-register_model_labels(
- "TaskItem",
- {"en": "Task", "fr": "Tâche"},
- {
- "id": {"en": "Task ID", "fr": "ID de la tâche"},
- "workflowId": {"en": "Workflow ID", "fr": "ID du workflow"},
- "userInput": {"en": "User Input", "fr": "Entrée utilisateur"},
- "status": {"en": "Status", "fr": "Statut"},
- "error": {"en": "Error", "fr": "Erreur"},
- "startedAt": {"en": "Started At", "fr": "Démarré à"},
- "finishedAt": {"en": "Finished At", "fr": "Terminé à"},
- "actionList": {"en": "Actions", "fr": "Actions"},
- "retryCount": {"en": "Retry Count", "fr": "Nombre de tentatives"},
- "retryMax": {"en": "Max Retries", "fr": "Tentatives max"},
- "processingTime": {"en": "Processing Time", "fr": "Temps de traitement"},
- },
-)
-
-
-class TaskStep(BaseModel, ModelMixin):
- id: str
- objective: str
- dependencies: Optional[list[str]] = Field(default_factory=list)
- success_criteria: Optional[list[str]] = Field(default_factory=list)
- estimated_complexity: Optional[str] = None
- userMessage: Optional[str] = Field(None, description="User-friendly message in user's language")
-
-
-register_model_labels(
- "TaskStep",
- {"en": "Task Step", "fr": "Étape de tâche"},
- {
- "id": {"en": "ID", "fr": "ID"},
- "objective": {"en": "Objective", "fr": "Objectif"},
- "dependencies": {"en": "Dependencies", "fr": "Dépendances"},
- "success_criteria": {"en": "Success Criteria", "fr": "Critères de succès"},
- "estimated_complexity": {"en": "Estimated Complexity", "fr": "Complexité estimée"},
- "userMessage": {"en": "User Message", "fr": "Message utilisateur"},
- },
-)
-
-
-class TaskHandover(BaseModel, ModelMixin):
- taskId: str = Field(description="Target task ID")
- sourceTask: Optional[str] = Field(None, description="Source task ID")
- inputDocuments: List[DocumentExchange] = Field(default_factory=list, description="Available input documents")
- outputDocuments: List[DocumentExchange] = Field(default_factory=list, description="Produced output documents")
- context: Dict[str, Any] = Field(default_factory=dict, description="Task context")
- previousResults: List[str] = Field(default_factory=list, description="Previous result summaries")
- improvements: List[str] = Field(default_factory=list, description="Improvement suggestions")
- workflowSummary: Optional[str] = Field(None, description="Summarized workflow context")
- messageHistory: List[str] = Field(default_factory=list, description="Key message summaries")
- timestamp: float = Field(..., description="When the handover was created (UTC timestamp in seconds)")
- handoverType: str = Field(default="task", description="Type of handover: task, phase, or workflow")
-
-
-register_model_labels(
- "TaskHandover",
- {"en": "Task Handover", "fr": "Transfert de tâche"},
- {
- "taskId": {"en": "Task ID", "fr": "ID de la tâche"},
- "sourceTask": {"en": "Source Task", "fr": "Tâche source"},
- "inputDocuments": {"en": "Input Documents", "fr": "Documents d'entrée"},
- "outputDocuments": {"en": "Output Documents", "fr": "Documents de sortie"},
- "context": {"en": "Context", "fr": "Contexte"},
- "previousResults": {"en": "Previous Results", "fr": "Résultats précédents"},
- "improvements": {"en": "Improvements", "fr": "Améliorations"},
- "workflowSummary": {"en": "Workflow Summary", "fr": "Résumé du workflow"},
- "messageHistory": {"en": "Message History", "fr": "Historique des messages"},
- "timestamp": {"en": "Timestamp", "fr": "Horodatage"},
- "handoverType": {"en": "Handover Type", "fr": "Type de transfert"},
- },
-)
-
-
-class TaskContext(BaseModel, ModelMixin):
- task_step: TaskStep
- workflow: Optional['ChatWorkflow'] = None
- workflow_id: Optional[str] = None
- available_documents: Optional[str] = "No documents available"
- available_connections: Optional[list[str]] = Field(default_factory=list)
- previous_results: Optional[list[str]] = Field(default_factory=list)
- previous_handover: Optional[TaskHandover] = None
- improvements: Optional[list[str]] = Field(default_factory=list)
- retry_count: Optional[int] = 0
- previous_action_results: Optional[list] = Field(default_factory=list)
- previous_review_result: Optional[dict] = None
- is_regeneration: Optional[bool] = False
- failure_patterns: Optional[list[str]] = Field(default_factory=list)
- failed_actions: Optional[list] = Field(default_factory=list)
- successful_actions: Optional[list] = Field(default_factory=list)
- criteria_progress: Optional[dict] = None
-
- def getDocumentReferences(self) -> List[str]:
- docs = []
- if self.previous_handover:
- for doc_exchange in self.previous_handover.inputDocuments:
- docs.extend(doc_exchange.documents)
- return list(set(docs))
-
- def addImprovement(self, improvement: str) -> None:
- if improvement not in (self.improvements or []):
- if self.improvements is None:
- self.improvements = []
- self.improvements.append(improvement)
-
-
-class ReviewContext(BaseModel, ModelMixin):
- task_step: TaskStep
- task_actions: Optional[list] = Field(default_factory=list)
- action_results: Optional[list] = Field(default_factory=list)
- step_result: Optional[dict] = Field(default_factory=dict)
- workflow_id: Optional[str] = None
- previous_results: Optional[list[str]] = Field(default_factory=list)
-
-
-class ReviewResult(BaseModel, ModelMixin):
- status: str
- reason: Optional[str] = None
- improvements: Optional[list[str]] = Field(default_factory=list)
- quality_score: Optional[int] = 5
- missing_outputs: Optional[list[str]] = Field(default_factory=list)
- met_criteria: Optional[list[str]] = Field(default_factory=list)
- unmet_criteria: Optional[list[str]] = Field(default_factory=list)
- confidence: Optional[float] = 0.5
- userMessage: Optional[str] = Field(None, description="User-friendly message in user's language")
-
-
-register_model_labels(
- "ReviewResult",
- {"en": "Review Result", "fr": "Résultat de l'évaluation"},
- {
- "status": {"en": "Status", "fr": "Statut"},
- "reason": {"en": "Reason", "fr": "Raison"},
- "improvements": {"en": "Improvements", "fr": "Améliorations"},
- "quality_score": {"en": "Quality Score", "fr": "Score de qualité"},
- "missing_outputs": {"en": "Missing Outputs", "fr": "Sorties manquantes"},
- "met_criteria": {"en": "Met Criteria", "fr": "Critères respectés"},
- "unmet_criteria": {"en": "Unmet Criteria", "fr": "Critères non respectés"},
- "confidence": {"en": "Confidence", "fr": "Confiance"},
- "userMessage": {"en": "User Message", "fr": "Message utilisateur"},
- },
-)
-
-
-class TaskPlan(BaseModel, ModelMixin):
- overview: str
- tasks: list[TaskStep]
- userMessage: Optional[str] = Field(None, description="Overall user-friendly message for the task plan")
-
-
-register_model_labels(
- "TaskPlan",
- {"en": "Task Plan", "fr": "Plan de tâches"},
- {
- "overview": {"en": "Overview", "fr": "Aperçu"},
- "tasks": {"en": "Tasks", "fr": "Tâches"},
- "userMessage": {"en": "User Message", "fr": "Message utilisateur"},
- },
-)
-
-# Resolve forward references
-from modules.datamodels.datamodelChat import ChatWorkflow
-TaskContext.update_forward_refs()
-
-# -----------------------------
-# Prompt helper datamodels
-# -----------------------------
-
-class PromptPlaceholder(BaseModel, ModelMixin):
- label: str
- content: str
- summaryAllowed: bool = Field(default=False, description="Whether host may summarize content before sending to AI")
-
-
-register_model_labels(
- "PromptPlaceholder",
- {"en": "Prompt Placeholder", "fr": "Espace réservé d'invite"},
- {
- "label": {"en": "Label", "fr": "Libellé"},
- "content": {"en": "Content", "fr": "Contenu"},
- "summaryAllowed": {"en": "Summary Allowed", "fr": "Résumé autorisé"},
- },
-)
-
-
-class PromptBundle(BaseModel, ModelMixin):
- prompt: str
- placeholders: List[PromptPlaceholder] = Field(default_factory=list)
-
-
-register_model_labels(
- "PromptBundle",
- {"en": "Prompt Bundle", "fr": "Lot d'invite"},
- {
- "prompt": {"en": "Prompt", "fr": "Invite"},
- "placeholders": {"en": "Placeholders", "fr": "Espaces réservés"},
- },
-)
diff --git a/modules/interfaces/interfaceAiObjects.py b/modules/interfaces/interfaceAiObjects.py
index 4be38930..3e9c744d 100644
--- a/modules/interfaces/interfaceAiObjects.py
+++ b/modules/interfaces/interfaceAiObjects.py
@@ -27,7 +27,7 @@ from modules.datamodels.datamodelWeb import (
WebSearchRequest,
WebCrawlRequest,
)
-from modules.datamodels.datamodelWorkflow import ActionDocument
+from modules.datamodels.datamodelChat import ActionDocument
# Comprehensive model registry with capability tags and function mapping
diff --git a/modules/interfaces/interfaceDbChatObjects.py b/modules/interfaces/interfaceDbChatObjects.py
index 88af1d81..48ed60d6 100644
--- a/modules/interfaces/interfaceDbChatObjects.py
+++ b/modules/interfaces/interfaceDbChatObjects.py
@@ -12,8 +12,8 @@ from typing import Dict, Any, List, Optional, Union, get_origin, get_args
import asyncio
from modules.interfaces.interfaceDbChatAccess import ChatAccess
-from modules.datamodels.datamodelWorkflow import (
- TaskAction,
+from modules.datamodels.datamodelChat import (
+ ActionItem,
TaskResult,
TaskItem,
TaskStatus,
diff --git a/modules/services/serviceAi/mainServiceAi.py b/modules/services/serviceAi/mainServiceAi.py
index 19e2e1f0..168733c3 100644
--- a/modules/services/serviceAi/mainServiceAi.py
+++ b/modules/services/serviceAi/mainServiceAi.py
@@ -1,6 +1,6 @@
import logging
from typing import Dict, Any, List, Optional, Tuple, Union
-from modules.datamodels.datamodelWorkflow import PromptPlaceholder
+from modules.datamodels.datamodelChat import PromptPlaceholder
from modules.datamodels.datamodelChat import ChatDocument
from modules.services.serviceExtraction.mainServiceExtraction import ExtractionService
@@ -909,7 +909,7 @@ class AiService:
logger.info(f"Extraction completed: {len(extracted_content)} documents")
- # Build context from list of ExtractedContent
+ # Build context from list of extracted content
if isinstance(extracted_content, list):
context_parts = []
chunk_count = 0
diff --git a/modules/services/serviceExtraction/mainServiceExtraction.py b/modules/services/serviceExtraction/mainServiceExtraction.py
index 156f21b7..6d313463 100644
--- a/modules/services/serviceExtraction/mainServiceExtraction.py
+++ b/modules/services/serviceExtraction/mainServiceExtraction.py
@@ -4,7 +4,7 @@ import logging
from .subRegistry import ExtractorRegistry, ChunkerRegistry
from .subPipeline import runExtraction, poolAndLimit, applyAiIfRequested
-from modules.datamodels.datamodelExtraction import ExtractedContent, ContentPart, MergeStrategy
+from modules.datamodels.datamodelExtraction import ContentExtracted, ContentPart, MergeStrategy
from modules.datamodels.datamodelChat import ChatDocument
@@ -17,7 +17,7 @@ class ExtractionService:
self._extractorRegistry = ExtractorRegistry()
self._chunkerRegistry = ChunkerRegistry()
- def extractContent(self, documents: List[ChatDocument], options: Dict[str, Any]) -> List[ExtractedContent]:
+ def extractContent(self, documents: List[ChatDocument], options: Dict[str, Any]) -> List[ContentExtracted]:
"""
Extract content from a list of ChatDocument objects.
@@ -26,9 +26,9 @@ class ExtractionService:
options: Extraction options including maxSize, chunkAllowed, mergeStrategy, etc.
Returns:
- List of ExtractedContent objects, one per input document
+ List of ContentExtracted objects, one per input document
"""
- results: List[ExtractedContent] = []
+ results: List[ContentExtracted] = []
# Lazy import to avoid circular deps and heavy init at module import
from modules.interfaces.interfaceDbComponentObjects import getInterface
@@ -90,20 +90,20 @@ class ExtractionService:
def mergeAiResults(
self,
- extractedContent: List[ExtractedContent],
+ extractedContent: List[ContentExtracted],
aiResults: List[str],
strategy: MergeStrategy
- ) -> ExtractedContent:
+ ) -> ContentExtracted:
"""
- Merge AI results from chunked content back into a single ExtractedContent.
+ Merge AI results from chunked content back into a single ContentExtracted.
Args:
- extractedContent: List of ExtractedContent objects that were processed
+ extractedContent: List of ContentExtracted objects that were processed
aiResults: List of AI response strings, one per chunk
strategy: Merge strategy configuration (dict or MergeStrategy object)
Returns:
- Single ExtractedContent with merged AI results
+ Single ContentExtracted with merged AI results
"""
logger.debug(f"=== MERGING AI RESULTS ===")
logger.debug(f"Extracted content: {len(extractedContent)} documents")
@@ -150,8 +150,8 @@ class ExtractionService:
# Default to concatenate
mergedParts = self._mergeConcatenate(allParts, aiResultParts, mergeStrategy)
- # Create final ExtractedContent
- mergedContent = ExtractedContent(
+ # Create final ContentExtracted
+ mergedContent = ContentExtracted(
id=f"merged_{uuid.uuid4()}",
parts=mergedParts
)
diff --git a/modules/services/serviceExtraction/subPipeline.py b/modules/services/serviceExtraction/subPipeline.py
index 1c1bfc85..1dda4c0e 100644
--- a/modules/services/serviceExtraction/subPipeline.py
+++ b/modules/services/serviceExtraction/subPipeline.py
@@ -2,7 +2,7 @@ from typing import Any, Dict, List
import logging
import os
-from modules.datamodels.datamodelExtraction import ExtractedContent, ContentPart
+from modules.datamodels.datamodelExtraction import ContentExtracted, ContentPart
from .subUtils import makeId
from .subRegistry import ExtractorRegistry, ChunkerRegistry
from .merging.text_merger import TextMerger
@@ -55,7 +55,7 @@ def _mergeParts(parts: List[ContentPart], mergeStrategy: Dict[str, Any]) -> List
return merged_parts
-def runExtraction(extractorRegistry: ExtractorRegistry, chunkerRegistry: ChunkerRegistry, documentBytes: bytes, fileName: str, mimeType: str, options: Dict[str, Any]) -> ExtractedContent:
+def runExtraction(extractorRegistry: ExtractorRegistry, chunkerRegistry: ChunkerRegistry, documentBytes: bytes, fileName: str, mimeType: str, options: Dict[str, Any]) -> ContentExtracted:
extractor = extractorRegistry.resolve(mimeType, fileName)
if extractor is None:
# fallback: single binary part
@@ -68,7 +68,7 @@ def runExtraction(extractorRegistry: ExtractorRegistry, chunkerRegistry: Chunker
data="",
metadata={"warning": "No extractor registered"}
)
- return ExtractedContent(id=makeId(), parts=[part])
+ return ContentExtracted(id=makeId(), parts=[part])
parts = extractor.extract(documentBytes, {"fileName": fileName, "mimeType": mimeType, "options": options})
@@ -119,7 +119,7 @@ def runExtraction(extractorRegistry: ExtractorRegistry, chunkerRegistry: Chunker
except Exception as _e:
logger.debug(f"Debug dump skipped: {_e}")
- return ExtractedContent(id=makeId(), parts=parts)
+ return ContentExtracted(id=makeId(), parts=parts)
def poolAndLimit(parts: List[ContentPart], chunkerRegistry: ChunkerRegistry, options: Dict[str, Any]) -> List[ContentPart]:
@@ -268,7 +268,7 @@ def _applySizeLimit(parts: List[ContentPart], maxSize: int) -> List[ContentPart]
return kept
-def applyAiIfRequested(extracted: ExtractedContent, options: Dict[str, Any]) -> ExtractedContent:
+def applyAiIfRequested(extracted: ContentExtracted, options: Dict[str, Any]) -> ContentExtracted:
"""
Apply AI processing if requested in options.
This is a placeholder for actual AI integration.
diff --git a/modules/services/serviceWorkflow/mainServiceWorkflow.py b/modules/services/serviceWorkflow/mainServiceWorkflow.py
index a3b402dd..180779b5 100644
--- a/modules/services/serviceWorkflow/mainServiceWorkflow.py
+++ b/modules/services/serviceWorkflow/mainServiceWorkflow.py
@@ -3,7 +3,7 @@ import uuid
from typing import Dict, Any, List, Optional
from modules.datamodels.datamodelUam import User, UserConnection
from modules.datamodels.datamodelChat import ChatDocument, ChatMessage
-from modules.datamodels.datamodelChat import ExtractedContent
+from modules.datamodels.datamodelChat import ChatContentExtracted
from modules.services.serviceExtraction.mainServiceExtraction import ExtractionService
from modules.services.serviceGeneration.subDocumentUtility import getFileExtension, getMimeTypeFromExtension, detectContentTypeFromData
from modules.shared.timezoneUtils import get_utc_timestamp
diff --git a/modules/workflows/methods/methodAi.py b/modules/workflows/methods/methodAi.py
index 691298dd..9e0d8954 100644
--- a/modules/workflows/methods/methodAi.py
+++ b/modules/workflows/methods/methodAi.py
@@ -8,7 +8,7 @@ from typing import Dict, Any, List, Optional
from datetime import datetime, UTC
from modules.workflows.methods.methodBase import MethodBase, action
-from modules.datamodels.datamodelWorkflow import ActionResult
+from modules.datamodels.datamodelChat import ActionResult
from modules.datamodels.datamodelAi import AiCallOptions, OperationType, Priority
from modules.datamodels.datamodelChat import ChatDocument
from modules.datamodels.datamodelWeb import WebResearchRequest, WebResearchOptions
@@ -204,7 +204,7 @@ For large datasets, set "continue": true to indicate more data is coming, and we
# Parse JSON response from AI with streaming support
import json
import re
- from modules.datamodels.datamodelWorkflow import ActionDocument
+ from modules.datamodels.datamodelChat import ActionDocument
action_documents = []
all_data_chunks = [] # Store all data chunks for merging
diff --git a/modules/workflows/methods/methodDocument.py b/modules/workflows/methods/methodDocument.py
index 95531406..96441314 100644
--- a/modules/workflows/methods/methodDocument.py
+++ b/modules/workflows/methods/methodDocument.py
@@ -9,7 +9,7 @@ from typing import Dict, Any, List, Optional
from datetime import datetime, UTC
from modules.workflows.methods.methodBase import MethodBase, action
-from modules.datamodels.datamodelWorkflow import ActionResult, ActionDocument
+from modules.datamodels.datamodelChat import ActionResult, ActionDocument
from modules.datamodels.datamodelChat import ChatDocument
from modules.datamodels.datamodelAi import AiCallOptions, OperationType, Priority
@@ -136,7 +136,7 @@ class MethodDocument(MethodBase):
action_documents = []
for i, chatDocument in enumerate(chatDocuments):
- # Extract text content from this document using new ExtractedContent structure
+ # Extract text content from this document using new extracted content structure
text_content = ""
try:
ec = all_extracted_content[i] if i < len(all_extracted_content) else None
diff --git a/modules/workflows/methods/methodOutlook.py b/modules/workflows/methods/methodOutlook.py
index 2593b9cd..1997e75a 100644
--- a/modules/workflows/methods/methodOutlook.py
+++ b/modules/workflows/methods/methodOutlook.py
@@ -12,7 +12,7 @@ import uuid
import requests
from modules.workflows.methods.methodBase import MethodBase, action
-from modules.datamodels.datamodelWorkflow import ActionResult, ActionDocument
+from modules.datamodels.datamodelChat import ActionResult, ActionDocument
from modules.datamodels.datamodelAi import AiCallOptions, OperationType, Priority, AiCallRequest
from modules.datamodels.datamodelChat import ChatDocument
from modules.datamodels.datamodelUam import ConnectionStatus
diff --git a/modules/workflows/methods/methodSharepoint.py b/modules/workflows/methods/methodSharepoint.py
index c58cf89d..f29a0ad3 100644
--- a/modules/workflows/methods/methodSharepoint.py
+++ b/modules/workflows/methods/methodSharepoint.py
@@ -14,7 +14,7 @@ import aiohttp
import asyncio
from modules.workflows.methods.methodBase import MethodBase, action
-from modules.datamodels.datamodelWorkflow import ActionResult, ActionDocument
+from modules.datamodels.datamodelChat import ActionResult, ActionDocument
logger = logging.getLogger(__name__)
diff --git a/modules/workflows/processing/core/actionExecutor.py b/modules/workflows/processing/core/actionExecutor.py
index 7ac012f3..95a41606 100644
--- a/modules/workflows/processing/core/actionExecutor.py
+++ b/modules/workflows/processing/core/actionExecutor.py
@@ -3,7 +3,7 @@
import logging
from typing import Dict, Any, List
-from modules.datamodels.datamodelWorkflow import ActionResult, TaskAction, TaskStep
+from modules.datamodels.datamodelChat import ActionResult, ActionItem, TaskStep
from modules.datamodels.datamodelChat import ChatWorkflow
from modules.workflows.processing.shared.methodDiscovery import methods
@@ -65,7 +65,7 @@ class ActionExecutor:
logger.error(f"Error executing compound action {compoundActionName}: {str(e)}")
raise
- async def executeSingleAction(self, action: TaskAction, workflow: ChatWorkflow, taskStep: TaskStep,
+ async def executeSingleAction(self, action: ActionItem, workflow: ChatWorkflow, taskStep: TaskStep,
taskIndex: int = None, actionIndex: int = None, totalActions: int = None) -> ActionResult:
"""Execute a single action and return ActionResult with enhanced document processing"""
try:
@@ -199,7 +199,7 @@ class ActionExecutor:
# Join all document results with separators
return "\n\n---\n\n".join(resultParts) if resultParts else ""
- async def _createActionCompletionMessage(self, action: TaskAction, result: ActionResult, workflow: ChatWorkflow,
+ async def _createActionCompletionMessage(self, action: ActionItem, result: ActionResult, workflow: ChatWorkflow,
taskStep: TaskStep, taskIndex: int, actionIndex: int, totalActions: int):
"""Create action completion message with documents (generic)"""
try:
diff --git a/modules/workflows/processing/core/messageCreator.py b/modules/workflows/processing/core/messageCreator.py
index ce4cabdf..ccd718d0 100644
--- a/modules/workflows/processing/core/messageCreator.py
+++ b/modules/workflows/processing/core/messageCreator.py
@@ -3,7 +3,7 @@
import logging
from typing import Dict, Any, Optional, List
-from modules.datamodels.datamodelWorkflow import TaskPlan, TaskStep, ActionResult, ReviewResult
+from modules.datamodels.datamodelChat import TaskPlan, TaskStep, ActionResult, ReviewResult
from modules.datamodels.datamodelChat import ChatWorkflow
logger = logging.getLogger(__name__)
diff --git a/modules/workflows/processing/core/taskPlanner.py b/modules/workflows/processing/core/taskPlanner.py
index 237a3711..d8fd11a8 100644
--- a/modules/workflows/processing/core/taskPlanner.py
+++ b/modules/workflows/processing/core/taskPlanner.py
@@ -4,7 +4,7 @@
import json
import logging
from typing import Dict, Any
-from modules.datamodels.datamodelWorkflow import TaskStep, TaskContext, TaskPlan
+from modules.datamodels.datamodelChat import TaskStep, TaskContext, TaskPlan
from modules.datamodels.datamodelAi import AiCallOptions, OperationType, ProcessingMode, Priority
from modules.workflows.processing.shared.promptGenerationTaskplan import (
createTaskPlanningPromptTemplate
diff --git a/modules/workflows/processing/modes/modeActionplan.py b/modules/workflows/processing/modes/modeActionplan.py
index 547c266b..fdee8ff7 100644
--- a/modules/workflows/processing/modes/modeActionplan.py
+++ b/modules/workflows/processing/modes/modeActionplan.py
@@ -5,8 +5,8 @@ import json
import logging
import uuid
from typing import List, Dict, Any
-from modules.datamodels.datamodelWorkflow import (
- TaskStep, TaskContext, TaskResult, TaskAction, TaskStatus,
+from modules.datamodels.datamodelChat import (
+ TaskStep, TaskContext, TaskResult, ActionItem, TaskStatus,
ActionResult, ReviewResult, ReviewContext
)
from modules.datamodels.datamodelChat import ChatWorkflow
@@ -26,8 +26,8 @@ class ActionplanMode(BaseMode):
def __init__(self, services, workflow):
super().__init__(services, workflow)
- async def generateTaskActions(self, taskStep: TaskStep, workflow: ChatWorkflow,
- previousResults: List = None, enhancedContext: TaskContext = None) -> List[TaskAction]:
+ async def generateActionItems(self, taskStep: TaskStep, workflow: ChatWorkflow,
+ previousResults: List = None, enhancedContext: TaskContext = None) -> List[ActionItem]:
"""Generate actions for a given task step using batch planning approach"""
try:
# Check workflow status before generating actions
@@ -176,7 +176,7 @@ class ActionplanMode(BaseMode):
logger.error("Generated actions failed validation")
raise Exception("AI-generated actions failed validation - AI is required for action generation")
- # Convert to TaskAction objects
+ # Convert to ActionItem objects
taskActions = []
for i, a in enumerate(actions):
if not isinstance(a, dict):
@@ -193,7 +193,7 @@ class ActionplanMode(BaseMode):
# Old separate format: method + action fields
method_name = a.get('method', 'unknown')
- taskAction = self._createTaskAction({
+ taskAction = self._createActionItem({
"execMethod": method_name,
"execAction": action_name,
"execParameters": a.get('parameters', {}),
@@ -207,7 +207,7 @@ class ActionplanMode(BaseMode):
if taskAction:
taskActions.append(taskAction)
else:
- logger.warning(f"Skipping invalid action {i+1}: failed to create TaskAction")
+ logger.warning(f"Skipping invalid action {i+1}: failed to create ActionItem")
validActions = [ta for ta in taskActions if ta]
@@ -216,7 +216,7 @@ class ActionplanMode(BaseMode):
return validActions
except Exception as e:
- logger.error(f"Error in generateTaskActions: {str(e)}")
+ logger.error(f"Error in generateActionItems: {str(e)}")
return []
@@ -250,7 +250,7 @@ class ActionplanMode(BaseMode):
if retryContext:
retryContext.retry_count = attempt + 1
- actions = await self.generateTaskActions(taskStep, workflow,
+ actions = await self.generateActionItems(taskStep, workflow,
previousResults=retryContext.previous_results,
enhancedContext=retryContext)
@@ -415,7 +415,7 @@ class ActionplanMode(BaseMode):
error="Task failed after all retries."
)
- async def _reviewTaskCompletion(self, taskStep: TaskStep, taskActions: List[TaskAction],
+ async def _reviewTaskCompletion(self, taskStep: TaskStep, taskActions: List[ActionItem],
actionResults: List[ActionResult], workflow: ChatWorkflow) -> ReviewResult:
"""Review task completion and determine success/failure/retry"""
try:
@@ -562,7 +562,7 @@ class ActionplanMode(BaseMode):
quality_score=0
)
- def _createTaskAction(self, actionData: Dict[str, Any]) -> TaskAction:
+ def _createActionItem(self, actionData: Dict[str, Any]) -> ActionItem:
"""Creates a new task action"""
try:
# Ensure ID is present
@@ -584,14 +584,14 @@ class ActionplanMode(BaseMode):
if "execParameters" not in actionData:
actionData["execParameters"] = {}
- # Use generic field separation based on TaskAction model
- simpleFields, objectFields = self.services.interfaceDbChat._separate_object_fields(TaskAction, actionData)
+ # Use generic field separation based on ActionItem model
+ simpleFields, objectFields = self.services.interfaceDbChat._separate_object_fields(ActionItem, actionData)
# Create action in database
- createdAction = self.services.interfaceDbChat.db.recordCreate(TaskAction, simpleFields)
+ createdAction = self.services.interfaceDbChat.db.recordCreate(ActionItem, simpleFields)
- # Convert to TaskAction model
- return TaskAction(
+ # Convert to ActionItem model
+ return ActionItem(
id=createdAction["id"],
execMethod=createdAction["execMethod"],
execAction=createdAction["execAction"],
@@ -704,7 +704,7 @@ class ActionplanMode(BaseMode):
except Exception as e:
logger.error(f"Error setting workflow totals: {str(e)}")
- def _createTaskAction(self, actionData: Dict[str, Any]) -> TaskAction:
+ def _createActionItem(self, actionData: Dict[str, Any]) -> ActionItem:
"""Creates a new task action"""
try:
import uuid
@@ -728,14 +728,14 @@ class ActionplanMode(BaseMode):
if "execParameters" not in actionData:
actionData["execParameters"] = {}
- # Use generic field separation based on TaskAction model
- simpleFields, objectFields = self.services.interfaceDbChat._separate_object_fields(TaskAction, actionData)
+ # Use generic field separation based on ActionItem model
+ simpleFields, objectFields = self.services.interfaceDbChat._separate_object_fields(ActionItem, actionData)
# Create action in database
- createdAction = self.services.interfaceDbChat.db.recordCreate(TaskAction, simpleFields)
+ createdAction = self.services.interfaceDbChat.db.recordCreate(ActionItem, simpleFields)
- # Convert to TaskAction model
- return TaskAction(
+ # Convert to ActionItem model
+ return ActionItem(
id=createdAction["id"],
execMethod=createdAction["execMethod"],
execAction=createdAction["execAction"],
diff --git a/modules/workflows/processing/modes/modeBase.py b/modules/workflows/processing/modes/modeBase.py
index 611be32a..a318ca7d 100644
--- a/modules/workflows/processing/modes/modeBase.py
+++ b/modules/workflows/processing/modes/modeBase.py
@@ -4,7 +4,7 @@
from abc import ABC, abstractmethod
import logging
from typing import List, Dict, Any
-from modules.datamodels.datamodelWorkflow import TaskStep, TaskContext, TaskResult, TaskAction
+from modules.datamodels.datamodelChat import TaskStep, TaskContext, TaskResult, ActionItem
from modules.datamodels.datamodelChat import ChatWorkflow
from modules.workflows.processing.core.taskPlanner import TaskPlanner
from modules.workflows.processing.core.actionExecutor import ActionExecutor
@@ -46,8 +46,8 @@ class BaseMode(ABC):
pass
@abstractmethod
- async def generateTaskActions(self, taskStep: TaskStep, workflow: ChatWorkflow,
- previousResults: List = None, enhancedContext: TaskContext = None) -> List[TaskAction]:
+ async def generateActionItems(self, taskStep: TaskStep, workflow: ChatWorkflow,
+ previousResults: List = None, enhancedContext: TaskContext = None) -> List[ActionItem]:
"""Generate actions for a task step - must be implemented by concrete modes"""
pass
diff --git a/modules/workflows/processing/modes/modeReact.py b/modules/workflows/processing/modes/modeReact.py
index 107022a5..40309e65 100644
--- a/modules/workflows/processing/modes/modeReact.py
+++ b/modules/workflows/processing/modes/modeReact.py
@@ -7,8 +7,8 @@ import re
import time
from datetime import datetime, timezone
from typing import List, Dict, Any
-from modules.datamodels.datamodelWorkflow import (
- TaskStep, TaskContext, TaskResult, TaskAction, TaskStatus,
+from modules.datamodels.datamodelChat import (
+ TaskStep, TaskContext, TaskResult, ActionItem, TaskStatus,
ActionResult
)
from modules.datamodels.datamodelChat import ChatWorkflow
@@ -38,8 +38,8 @@ class ReactMode(BaseMode):
self.currentIntent = None
# Placeholder service no longer used; prompts are generated directly
- async def generateTaskActions(self, taskStep: TaskStep, workflow: ChatWorkflow,
- previousResults: List = None, enhancedContext: TaskContext = None) -> List[TaskAction]:
+ async def generateActionItems(self, taskStep: TaskStep, workflow: ChatWorkflow,
+ previousResults: List = None, enhancedContext: TaskContext = None) -> List[ActionItem]:
"""React mode doesn't use batch action generation - actions are generated iteratively"""
# React mode generates actions one at a time in the execution loop
return []
@@ -264,12 +264,12 @@ class ReactMode(BaseMode):
if 'language' not in parameters and hasattr(self.services, 'user') and getattr(self.services.user, 'language', None):
parameters['language'] = self.services.user.language
- # Build a synthetic TaskAction for execution routing and labels
+ # Build a synthetic ActionItem for execution routing and labels
currentRound = getattr(self.workflow, 'currentRound', 0)
currentTask = getattr(self.workflow, 'currentTask', 0)
resultLabel = f"round{currentRound}_task{currentTask}_action{stepIndex}_results"
- taskAction = self._createTaskAction({
+ taskAction = self._createActionItem({
"execMethod": methodName,
"execAction": actionName,
"execParameters": parameters,
@@ -463,7 +463,7 @@ class ReactMode(BaseMode):
async def _refineDecide(self, context: TaskContext, observation: Dict[str, Any]) -> Dict[str, Any]:
"""Refine: decide continue or stop, with reason"""
# Create proper ReviewContext for extractReviewContent
- from modules.datamodels.datamodelWorkflow import ReviewContext
+ from modules.datamodels.datamodelChat import ReviewContext
reviewContext = ReviewContext(
task_step=context.task_step,
task_actions=[],
@@ -662,7 +662,7 @@ Return only the user-friendly message, no technical details."""
logger.error(f"Error generating action result message: {str(e)}")
return f"{method}.{actionName} action completed"
- def _createTaskAction(self, actionData: Dict[str, Any]) -> TaskAction:
+ def _createActionItem(self, actionData: Dict[str, Any]) -> ActionItem:
"""Creates a new task action for React mode"""
try:
import uuid
@@ -686,14 +686,14 @@ Return only the user-friendly message, no technical details."""
if "execParameters" not in actionData:
actionData["execParameters"] = {}
- # Use generic field separation based on TaskAction model
- simpleFields, objectFields = self.services.interfaceDbChat._separate_object_fields(TaskAction, actionData)
+ # Use generic field separation based on ActionItem model
+ simpleFields, objectFields = self.services.interfaceDbChat._separate_object_fields(ActionItem, actionData)
# Create action in database
- createdAction = self.services.interfaceDbChat.db.recordCreate(TaskAction, simpleFields)
+ createdAction = self.services.interfaceDbChat.db.recordCreate(ActionItem, simpleFields)
- # Convert to TaskAction model
- return TaskAction(
+ # Convert to ActionItem model
+ return ActionItem(
id=createdAction["id"],
execMethod=createdAction["execMethod"],
execAction=createdAction["execAction"],
@@ -753,7 +753,7 @@ Return only the user-friendly message, no technical details."""
except Exception as e:
logger.error(f"Error updating workflow before executing action: {str(e)}")
- def _createTaskAction(self, actionData: Dict[str, Any]) -> TaskAction:
+ def _createActionItem(self, actionData: Dict[str, Any]) -> ActionItem:
"""Creates a new task action for React mode"""
try:
import uuid
@@ -777,14 +777,14 @@ Return only the user-friendly message, no technical details."""
if "execParameters" not in actionData:
actionData["execParameters"] = {}
- # Use generic field separation based on TaskAction model
- simpleFields, objectFields = self.services.interfaceDbChat._separate_object_fields(TaskAction, actionData)
+ # Use generic field separation based on ActionItem model
+ simpleFields, objectFields = self.services.interfaceDbChat._separate_object_fields(ActionItem, actionData)
# Create action in database
- createdAction = self.services.interfaceDbChat.db.recordCreate(TaskAction, simpleFields)
+ createdAction = self.services.interfaceDbChat.db.recordCreate(ActionItem, simpleFields)
- # Convert to TaskAction model
- return TaskAction(
+ # Convert to ActionItem model
+ return ActionItem(
id=createdAction["id"],
execMethod=createdAction["execMethod"],
execAction=createdAction["execAction"],
diff --git a/modules/workflows/processing/shared/executionState.py b/modules/workflows/processing/shared/executionState.py
index d6368f5f..8504033a 100644
--- a/modules/workflows/processing/shared/executionState.py
+++ b/modules/workflows/processing/shared/executionState.py
@@ -4,8 +4,8 @@
import logging
from typing import List
from datetime import datetime, UTC
-from modules.datamodels.datamodelWorkflow import TaskStep
-from modules.datamodels.datamodelWorkflow import ActionResult
+from modules.datamodels.datamodelChat import TaskStep
+from modules.datamodels.datamodelChat import ActionResult
logger = logging.getLogger(__name__)
diff --git a/modules/workflows/processing/shared/methodDiscovery.py b/modules/workflows/processing/shared/methodDiscovery.py
index 16b4b30c..4a371700 100644
--- a/modules/workflows/processing/shared/methodDiscovery.py
+++ b/modules/workflows/processing/shared/methodDiscovery.py
@@ -7,7 +7,7 @@ import importlib
import pkgutil
import inspect
from typing import Any, Dict, List
-from modules.datamodels.datamodelWorkflow import TaskContext, ReviewContext, DocumentExchange
+from modules.datamodels.datamodelChat import TaskContext, ReviewContext, DocumentExchange
from modules.workflows.methods.methodBase import MethodBase
# Set up logger
diff --git a/modules/workflows/processing/shared/promptGenerationActionsActionplan.py b/modules/workflows/processing/shared/promptGenerationActionsActionplan.py
index e0d29ea4..c94179e6 100644
--- a/modules/workflows/processing/shared/promptGenerationActionsActionplan.py
+++ b/modules/workflows/processing/shared/promptGenerationActionsActionplan.py
@@ -6,7 +6,7 @@ Handles prompt templates and extraction functions for actionplan mode action han
import json
import logging
from typing import Dict, Any, List
-from modules.datamodels.datamodelWorkflow import PromptBundle, PromptPlaceholder
+from modules.datamodels.datamodelChat import PromptBundle, PromptPlaceholder
from modules.workflows.processing.shared.placeholderFactory import (
extractUserPrompt,
extractAvailableDocumentsSummary,
diff --git a/modules/workflows/processing/shared/promptGenerationActionsReact.py b/modules/workflows/processing/shared/promptGenerationActionsReact.py
index c922ae71..3ff6c79d 100644
--- a/modules/workflows/processing/shared/promptGenerationActionsReact.py
+++ b/modules/workflows/processing/shared/promptGenerationActionsReact.py
@@ -4,7 +4,7 @@ Handles prompt templates for react mode action handling.
"""
from typing import Any, List
-from modules.datamodels.datamodelWorkflow import PromptBundle, PromptPlaceholder
+from modules.datamodels.datamodelChat import PromptBundle, PromptPlaceholder
from modules.workflows.processing.shared.placeholderFactory import (
extractUserPrompt,
extractUserLanguage,
diff --git a/modules/workflows/processing/shared/promptGenerationTaskplan.py b/modules/workflows/processing/shared/promptGenerationTaskplan.py
index c0b7bbb9..49085f80 100644
--- a/modules/workflows/processing/shared/promptGenerationTaskplan.py
+++ b/modules/workflows/processing/shared/promptGenerationTaskplan.py
@@ -6,7 +6,7 @@ Handles prompt templates and extraction functions for task planning phase.
import json
import logging
from typing import Dict, Any, List
-from modules.datamodels.datamodelWorkflow import PromptBundle, PromptPlaceholder
+from modules.datamodels.datamodelChat import PromptBundle, PromptPlaceholder
from modules.workflows.processing.shared.placeholderFactory import (
extractUserPrompt,
extractAvailableDocumentsSummary,
diff --git a/modules/workflows/processing/workflowProcessor.py b/modules/workflows/processing/workflowProcessor.py
index 5041bb2f..7de1e114 100644
--- a/modules/workflows/processing/workflowProcessor.py
+++ b/modules/workflows/processing/workflowProcessor.py
@@ -3,7 +3,7 @@
import logging
from typing import Dict, Any, Optional, List
-from modules.datamodels.datamodelWorkflow import TaskStep, TaskContext, TaskPlan, TaskResult, ReviewResult
+from modules.datamodels.datamodelChat import TaskStep, TaskContext, TaskPlan, TaskResult, ReviewResult
from modules.datamodels.datamodelChat import ChatWorkflow
from modules.workflows.processing.modes.modeBase import BaseMode
from modules.workflows.processing.modes.modeActionplan import ActionplanMode
@@ -84,7 +84,7 @@ class WorkflowProcessor:
logger.error(f"Error in executeTask: {str(e)}")
raise
- async def generateTaskActions(self, taskStep: TaskStep, workflow: ChatWorkflow,
+ async def generateActionItems(self, taskStep: TaskStep, workflow: ChatWorkflow,
previousResults: List = None, enhancedContext: TaskContext = None) -> List:
"""Generate actions for a task step using the appropriate mode"""
try:
@@ -96,9 +96,9 @@ class WorkflowProcessor:
logger.info(f"Mode: {workflow.workflowMode}")
# Delegate to the appropriate mode
- return await self.mode.generateTaskActions(taskStep, workflow, previousResults, enhancedContext)
+ return await self.mode.generateActionItems(taskStep, workflow, previousResults, enhancedContext)
except Exception as e:
- logger.error(f"Error in generateTaskActions: {str(e)}")
+ logger.error(f"Error in generateActionItems: {str(e)}")
raise
def updateWorkflowAfterTaskPlanCreated(self, totalTasks: int):
diff --git a/modules/workflows/workflowManager.py b/modules/workflows/workflowManager.py
index db0d6652..4ac6b26c 100644
--- a/modules/workflows/workflowManager.py
+++ b/modules/workflows/workflowManager.py
@@ -8,10 +8,9 @@ from modules.datamodels.datamodelChat import (
UserInputRequest,
ChatMessage,
ChatWorkflow,
- ChatDocument,
- WorkflowResult
+ ChatDocument
)
-from modules.datamodels.datamodelWorkflow import TaskItem, TaskStatus, TaskContext
+from modules.datamodels.datamodelChat import TaskItem, TaskStatus, TaskContext
from modules.workflows.processing.workflowProcessor import WorkflowProcessor, WorkflowStoppedException
from modules.shared.timezoneUtils import get_utc_timestamp
@@ -160,8 +159,8 @@ class WorkflowManager:
self.workflowProcessor = WorkflowProcessor(self.services, workflow)
message = await self._sendFirstMessage(userInput, workflow)
task_plan = await self._planTasks(userInput, workflow)
- workflow_result = await self._executeTasks(task_plan, workflow)
- await self._processWorkflowResults(workflow, workflow_result, message)
+ await self._executeTasks(task_plan, workflow)
+ await self._processWorkflowResults(workflow, message)
except WorkflowStoppedException:
self._handleWorkflowStop(workflow)
@@ -359,8 +358,8 @@ class WorkflowManager:
logger.info(f"Executing workflow mode={workflow_mode} with {len(task_plan.tasks)} tasks")
return task_plan
- async def _executeTasks(self, task_plan, workflow: ChatWorkflow) -> WorkflowResult:
- """Execute all tasks in the task plan"""
+ async def _executeTasks(self, task_plan, workflow: ChatWorkflow) -> None:
+ """Execute all tasks in the task plan and update workflow status."""
handling = self.workflowProcessor
total_tasks = len(task_plan.tasks)
all_task_results: List = []
@@ -404,16 +403,12 @@ class WorkflowManager:
if task_result.success and task_result.feedback:
previous_results.append(task_result.feedback)
- return WorkflowResult(
- status="completed",
- completed_tasks=len(all_task_results),
- total_tasks=total_tasks,
- execution_time=0.0,
- final_results_count=len(all_task_results)
- )
+ # Mark workflow as completed; error/stop cases update status elsewhere
+ workflow.status = "completed"
+ return None
- async def _processWorkflowResults(self, workflow: ChatWorkflow, workflow_result: WorkflowResult, initial_message: ChatMessage) -> None:
- """Process workflow results and create appropriate messages"""
+ async def _processWorkflowResults(self, workflow: ChatWorkflow, initial_message: ChatMessage) -> None:
+ """Process workflow results based on workflow status and create appropriate messages"""
try:
try:
self.workflowProcessor._checkWorkflowStopped(workflow)
@@ -451,7 +446,7 @@ class WorkflowManager:
})
return
- if workflow_result.status == 'stopped':
+ if workflow.status == 'stopped':
# Create stopped message
stopped_message = {
"workflowId": workflow.id,
@@ -493,12 +488,12 @@ class WorkflowManager:
"progress": 100
})
return
- elif workflow_result.status == 'failed':
+ elif workflow.status == 'failed':
# Create error message
error_message = {
"workflowId": workflow.id,
"role": "assistant",
- "message": f"Workflow failed: {workflow_result.error or 'Unknown error'}",
+ "message": f"Workflow failed: {'Unknown error'}",
"status": "last",
"sequenceNr": len(workflow.messages) + 1,
"publishedAt": self.services.utils.getUtcTimestamp(),