from typing import Dict, Any, List, Optional import logging from datetime import datetime, UTC import uuid import asyncio from modules.interfaces.interfaceAppObjects import User from modules.interfaces.interfaceChatModel import (UserInputRequest, ChatMessage, ChatWorkflow, TaskItem, TaskStatus) from modules.interfaces.interfaceChatObjects import ChatObjects from modules.chat.managerChat import ChatManager from modules.interfaces.interfaceChatModel import WorkflowResult logger = logging.getLogger(__name__) class WorkflowStoppedException(Exception): """Exception raised when workflow is stopped by user""" pass class WorkflowManager: """Manager for workflow processing and coordination""" def __init__(self, chatInterface: ChatObjects, currentUser: User): self.chatInterface = chatInterface self.chatManager = ChatManager(currentUser, chatInterface) self.currentUser = currentUser def _checkWorkflowStopped(self, workflow: ChatWorkflow) -> None: """Check if workflow has been stopped""" if workflow.status == "stopped": raise WorkflowStoppedException("Workflow was stopped by user") async def workflowProcess(self, userInput: UserInputRequest, workflow: ChatWorkflow) -> None: """Process a workflow with user input using unified workflow phases""" try: # Initialize chat manager await self.chatManager.initialize(workflow) # Set user language self.chatManager.setUserLanguage(userInput.userLanguage) # Send first message message = await self._sendFirstMessage(userInput, workflow) # Execute unified workflow workflow_result = await self.chatManager.executeUnifiedWorkflow(userInput, workflow) # Process workflow results await self._processWorkflowResults(workflow, workflow_result, message) # Send last message await self._sendLastMessage(workflow) except WorkflowStoppedException: logger.info("Workflow stopped by user") # Update workflow status to stopped workflow.status = "stopped" workflow.lastActivity = datetime.now(UTC).isoformat() self.chatInterface.updateWorkflow(workflow.id, { "status": "stopped", "lastActivity": workflow.lastActivity }) # Add log entry self.chatInterface.createWorkflowLog({ "workflowId": workflow.id, "message": "Workflow stopped by user", "type": "warning", "status": "stopped", "progress": 100 }) except Exception as e: logger.error(f"Workflow processing error: {str(e)}") # Update workflow status to failed workflow.status = "failed" workflow.lastActivity = datetime.now(UTC).isoformat() self.chatInterface.updateWorkflow(workflow.id, { "status": "failed", "lastActivity": workflow.lastActivity }) # Create error message error_message = { "workflowId": workflow.id, "role": "assistant", "message": f"Workflow processing failed: {str(e)}", "status": "last", "sequenceNr": len(workflow.messages) + 1, "publishedAt": datetime.now(UTC).isoformat() } message = self.chatInterface.createWorkflowMessage(error_message) if message: workflow.messages.append(message) # Add error log entry self.chatInterface.createWorkflowLog({ "workflowId": workflow.id, "message": f"Workflow failed: {str(e)}", "type": "error", "status": "failed", "progress": 100 }) raise async def _sendFirstMessage(self, userInput: UserInputRequest, workflow: ChatWorkflow) -> ChatMessage: """Send first message to start workflow""" try: # Create initial message using interface messageData = { "workflowId": workflow.id, "role": "user", "message": userInput.prompt, "status": "first", "sequenceNr": 1, "publishedAt": datetime.now(UTC).isoformat() } # Add documents if any if userInput.listFileId: # Process file IDs and add to message data documents = await self.chatManager.processFileIds(userInput.listFileId) messageData["documents"] = documents # Create message using interface message = self.chatInterface.createWorkflowMessage(messageData) if message: workflow.messages.append(message) return message else: raise Exception("Failed to create first message") except Exception as e: logger.error(f"Error sending first message: {str(e)}") raise async def _generateWorkflowFeedback(self, workflow: ChatWorkflow) -> str: """Generate feedback message for workflow completion""" try: # Count messages by role user_messages = [msg for msg in workflow.messages if msg.role == 'user'] assistant_messages = [msg for msg in workflow.messages if msg.role == 'assistant'] # Generate summary feedback feedback = f"Workflow completed.\n\n" feedback += f"Processed {len(user_messages)} user inputs and generated {len(assistant_messages)} responses.\n" # Add final status if workflow.status == "completed": feedback += "All tasks completed successfully." elif workflow.status == "partial": feedback += "Some tasks completed with partial success." else: feedback += f"Workflow status: {workflow.status}" return feedback except Exception as e: logger.error(f"Error generating workflow feedback: {str(e)}") return "Workflow processing completed." async def _sendLastMessage(self, workflow: ChatWorkflow) -> None: """Send last message to complete workflow""" try: # Generate feedback feedback = await self._generateWorkflowFeedback(workflow) # Create last message using interface messageData = { "workflowId": workflow.id, "role": "assistant", "message": feedback, "status": "last", "sequenceNr": len(workflow.messages) + 1, "publishedAt": datetime.now(UTC).isoformat() } # Create message using interface message = self.chatInterface.createWorkflowMessage(messageData) if message: workflow.messages.append(message) # Update workflow status to completed workflow.status = "completed" workflow.lastActivity = datetime.now(UTC).isoformat() # Update workflow in database self.chatInterface.updateWorkflow(workflow.id, { "status": "completed", "lastActivity": workflow.lastActivity }) # Add completion log entry self.chatInterface.createWorkflowLog({ "workflowId": workflow.id, "message": "Workflow completed", "type": "success", "status": "completed", "progress": 100 }) except Exception as e: logger.error(f"Error sending last message: {str(e)}") raise async def _processWorkflowResults(self, workflow: ChatWorkflow, workflow_result: WorkflowResult, initial_message: ChatMessage) -> None: """Process workflow results and create appropriate messages""" try: if workflow_result.status == 'failed': # Create error message error_message = { "workflowId": workflow.id, "role": "assistant", "message": f"Workflow failed: {workflow_result.error or 'Unknown error'}", "status": "last", "sequenceNr": len(workflow.messages) + 1, "publishedAt": datetime.now(UTC).isoformat() } message = self.chatInterface.createWorkflowMessage(error_message) if message: workflow.messages.append(message) # Update workflow status to failed workflow.status = "failed" workflow.lastActivity = datetime.now(UTC).isoformat() self.chatInterface.updateWorkflow(workflow.id, { "status": "failed", "lastActivity": workflow.lastActivity }) return # For successful workflows, create a simple completion message summary_message = { "workflowId": workflow.id, "role": "assistant", "message": f"Workflow completed successfully. Completed {workflow_result.completed_tasks}/{workflow_result.total_tasks} tasks in {workflow_result.execution_time:.2f} seconds.", "status": "last", "sequenceNr": len(workflow.messages) + 1, "publishedAt": datetime.now(UTC).isoformat() } message = self.chatInterface.createWorkflowMessage(summary_message) if message: workflow.messages.append(message) # Update workflow status to completed for successful workflows workflow.status = "completed" workflow.lastActivity = datetime.now(UTC).isoformat() self.chatInterface.updateWorkflow(workflow.id, { "status": "completed", "lastActivity": workflow.lastActivity }) except Exception as e: logger.error(f"Error processing workflow results: {str(e)}") # Create error message error_message = { "workflowId": workflow.id, "role": "assistant", "message": f"Error processing workflow results: {str(e)}", "status": "last", "sequenceNr": len(workflow.messages) + 1, "publishedAt": datetime.now(UTC).isoformat() } message = self.chatInterface.createWorkflowMessage(error_message) if message: workflow.messages.append(message) # Update workflow status to failed workflow.status = "failed" workflow.lastActivity = datetime.now(UTC).isoformat() self.chatInterface.updateWorkflow(workflow.id, { "status": "failed", "lastActivity": workflow.lastActivity })