diff --git a/modules/datamodels/datamodelChat.py b/modules/datamodels/datamodelChat.py index 9b173fd1..680bd3cc 100644 --- a/modules/datamodels/datamodelChat.py +++ b/modules/datamodels/datamodelChat.py @@ -263,6 +263,23 @@ registerModelLabels( ) +class WorkflowModeEnum(str, Enum): + WORKFLOW_ACTIONPLAN = "Actionplan" + WORKFLOW_DYNAMIC = "Dynamic" + WORKFLOW_TEMPLATE = "Template" + + +registerModelLabels( + "WorkflowModeEnum", + {"en": "Workflow Mode", "fr": "Mode de workflow"}, + { + "WORKFLOW_ACTIONPLAN": {"en": "Actionplan", "fr": "Actionplan"}, + "WORKFLOW_DYNAMIC": {"en": "Dynamic", "fr": "Dynamique"}, + "WORKFLOW_TEMPLATE": {"en": "Template", "fr": "Modèle"}, + }, +) + + class ChatWorkflow(BaseModel): id: str = Field( default_factory=lambda: str(uuid.uuid4()), @@ -372,18 +389,25 @@ class ChatWorkflow(BaseModel): frontend_readonly=True, frontend_required=False, ) - workflowMode: str = Field( - default="Actionplan", + workflowMode: WorkflowModeEnum = Field( + default=WorkflowModeEnum.WORKFLOW_DYNAMIC, description="Workflow mode selector", frontend_type="select", frontend_readonly=False, frontend_required=False, frontend_options=[ { - "value": "Actionplan", - "label": {"en": "Action Plan", "fr": "Plan d'actions"}, + "value": WorkflowModeEnum.WORKFLOW_ACTIONPLAN.value, + "label": {"en": "Actionplan", "fr": "Actionplan"}, + }, + { + "value": WorkflowModeEnum.WORKFLOW_DYNAMIC.value, + "label": {"en": "Dynamic", "fr": "Dynamique"}, + }, + { + "value": WorkflowModeEnum.WORKFLOW_TEMPLATE.value, + "label": {"en": "Template", "fr": "Modèle"}, }, - {"value": "React", "label": {"en": "React", "fr": "Réactif"}}, ], ) maxSteps: int = Field( diff --git a/modules/features/chatPlayground/mainChatPlayground.py b/modules/features/chatPlayground/mainChatPlayground.py index 54d50cb4..8420cb41 100644 --- a/modules/features/chatPlayground/mainChatPlayground.py +++ b/modules/features/chatPlayground/mainChatPlayground.py @@ -2,13 +2,13 @@ import logging from typing import Optional from modules.datamodels.datamodelUam import User -from modules.datamodels.datamodelChat import ChatWorkflow, UserInputRequest +from modules.datamodels.datamodelChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum from modules.workflows.workflowManager import WorkflowManager from modules.services import getInterface as getServices logger = logging.getLogger(__name__) -async def chatStart(interfaceDbChat, currentUser: User, userInput: UserInputRequest, workflowId: Optional[str] = None, workflowMode: str = "Actionplan") -> ChatWorkflow: +async def chatStart(interfaceDbChat, currentUser: User, userInput: UserInputRequest, workflowId: Optional[str] = None, workflowMode: WorkflowModeEnum = WorkflowModeEnum.WORKFLOW_ACTIONPLAN) -> ChatWorkflow: """ Starts a new chat or continues an existing one, then launches processing asynchronously. @@ -17,10 +17,10 @@ async def chatStart(interfaceDbChat, currentUser: User, userInput: UserInputRequ currentUser: Current user userInput: User input request workflowId: Optional workflow ID to continue existing workflow - workflowMode: "Actionplan" for traditional task planning, "React" for iterative react-style processing + workflowMode: "Actionplan" for traditional task planning, "Dynamic" for iterative dynamic-style processing, "Template" for template-based processing - Example usage for React mode: - workflow = await chatStart(interfaceDbChat, currentUser, userInput, workflowMode="React") + Example usage for Dynamic mode: + workflow = await chatStart(interfaceDbChat, currentUser, userInput, workflowMode=WorkflowModeEnum.WORKFLOW_DYNAMIC) """ try: services = getServices(currentUser, None) diff --git a/modules/interfaces/interfaceDbChatObjects.py b/modules/interfaces/interfaceDbChatObjects.py index 78f8a2c5..87f0bf5b 100644 --- a/modules/interfaces/interfaceDbChatObjects.py +++ b/modules/interfaces/interfaceDbChatObjects.py @@ -17,7 +17,8 @@ from modules.datamodels.datamodelChat import ( ChatStat, ChatLog, ChatMessage, - ChatWorkflow + ChatWorkflow, + WorkflowModeEnum ) from modules.datamodels.datamodelUam import User @@ -367,7 +368,7 @@ class ChatObjects: messages=[], stats=[], mandateId=created.get("mandateId", self.currentUser.mandateId), - workflowMode=created.get("workflowMode", "Actionplan"), + workflowMode=created.get("workflowMode", WorkflowModeEnum.WORKFLOW_DYNAMIC), maxSteps=created.get("maxSteps", 1) ) diff --git a/modules/routes/routeChatPlayground.py b/modules/routes/routeChatPlayground.py index 300b415b..d376a12d 100644 --- a/modules/routes/routeChatPlayground.py +++ b/modules/routes/routeChatPlayground.py @@ -14,7 +14,7 @@ from modules.security.auth import limiter, getCurrentUser import modules.interfaces.interfaceDbChatObjects as interfaceDbChatObjects # Import models -from modules.datamodels.datamodelChat import ChatWorkflow, UserInputRequest +from modules.datamodels.datamodelChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum from modules.datamodels.datamodelUam import User # Import workflow control functions @@ -39,7 +39,7 @@ def getServiceChat(currentUser: User): async def start_workflow( request: Request, workflowId: Optional[str] = Query(None, description="Optional ID of the workflow to continue"), - workflowMode: str = Query("Actionplan", description="Workflow mode: 'Actionplan' or 'React'"), + workflowMode: WorkflowModeEnum = Query(WorkflowModeEnum.WORKFLOW_DYNAMIC, description="Workflow mode: 'Actionplan', 'Dynamic', or 'Template'"), userInput: UserInputRequest = Body(...), currentUser: User = Depends(getCurrentUser) ) -> ChatWorkflow: @@ -48,7 +48,7 @@ async def start_workflow( Corresponds to State 1 in the state machine documentation. Args: - workflowMode: "Actionplan" for traditional task planning, "React" for iterative react-style processing + workflowMode: "Actionplan" for traditional task planning, "Dynamic" for iterative dynamic-style processing, "Template" for template-based processing """ try: # Get service center diff --git a/modules/workflows/processing/adaptive/__init__.py b/modules/workflows/processing/adaptive/__init__.py index afc0c963..df4da54b 100644 --- a/modules/workflows/processing/adaptive/__init__.py +++ b/modules/workflows/processing/adaptive/__init__.py @@ -1,4 +1,4 @@ -# adaptive module for React mode +# adaptive module for Dynamic mode # Provides adaptive learning capabilities from .intentAnalyzer import IntentAnalyzer diff --git a/modules/workflows/processing/adaptive/contentValidator.py b/modules/workflows/processing/adaptive/contentValidator.py index 2709af7b..61bad944 100644 --- a/modules/workflows/processing/adaptive/contentValidator.py +++ b/modules/workflows/processing/adaptive/contentValidator.py @@ -1,5 +1,5 @@ # contentValidator.py -# Content validation for adaptive React mode +# Content validation for adaptive Dynamic mode # Generic, document-aware validation system import logging diff --git a/modules/workflows/processing/adaptive/intentAnalyzer.py b/modules/workflows/processing/adaptive/intentAnalyzer.py index caee37ce..4ad17861 100644 --- a/modules/workflows/processing/adaptive/intentAnalyzer.py +++ b/modules/workflows/processing/adaptive/intentAnalyzer.py @@ -1,5 +1,5 @@ # intentAnalyzer.py -# Intent analysis for adaptive React mode - AI-based, language-agnostic +# Intent analysis for adaptive Dynamic mode - AI-based, language-agnostic import json import logging diff --git a/modules/workflows/processing/adaptive/learningEngine.py b/modules/workflows/processing/adaptive/learningEngine.py index 7e8fe60b..5ed29e80 100644 --- a/modules/workflows/processing/adaptive/learningEngine.py +++ b/modules/workflows/processing/adaptive/learningEngine.py @@ -1,5 +1,5 @@ # learningEngine.py -# Learning engine for adaptive React mode +# Learning engine for adaptive Dynamic mode import logging from typing import Dict, Any, List diff --git a/modules/workflows/processing/adaptive/progressTracker.py b/modules/workflows/processing/adaptive/progressTracker.py index a94dacd8..573ed2ff 100644 --- a/modules/workflows/processing/adaptive/progressTracker.py +++ b/modules/workflows/processing/adaptive/progressTracker.py @@ -1,5 +1,5 @@ # progressTracker.py -# Progress tracking for adaptive React mode +# Progress tracking for adaptive Dynamic mode import logging from typing import Dict, Any, List diff --git a/modules/workflows/processing/modes/modeActionplan.py b/modules/workflows/processing/modes/modeActionplan.py index 40ce267c..e2406324 100644 --- a/modules/workflows/processing/modes/modeActionplan.py +++ b/modules/workflows/processing/modes/modeActionplan.py @@ -334,7 +334,7 @@ class ActionplanMode(BaseMode): taskIndex, actionNumber, totalActions) actionResults.append(result) - # Enhanced validation: Content validation after each action (like React mode) + # Enhanced validation: Content validation after each action (like Dynamic mode) if getattr(self, 'workflowIntent', None) and result.documents: # Pass ALL documents to validator - validator decides what to validate (generic approach) # Pass taskStep so validator can use task.objective and format fields diff --git a/modules/workflows/processing/modes/modeReact.py b/modules/workflows/processing/modes/modeDynamic.py similarity index 96% rename from modules/workflows/processing/modes/modeReact.py rename to modules/workflows/processing/modes/modeDynamic.py index 32bd5d62..8a2d691d 100644 --- a/modules/workflows/processing/modes/modeReact.py +++ b/modules/workflows/processing/modes/modeDynamic.py @@ -1,5 +1,5 @@ -# modeReact.py -# React mode implementation for workflows +# modeDynamic.py +# Dynamic mode implementation for workflows import json import logging @@ -14,10 +14,10 @@ from modules.datamodels.datamodelChat import ( from modules.datamodels.datamodelChat import ChatWorkflow from modules.workflows.processing.modes.modeBase import BaseMode from modules.workflows.processing.shared.executionState import TaskExecutionState, shouldContinue -from modules.workflows.processing.shared.promptGenerationActionsReact import ( - generateReactPlanSelectionPrompt, - generateReactParametersPrompt, - generateReactRefinementPrompt +from modules.workflows.processing.shared.promptGenerationActionsDynamic import ( + generateDynamicPlanSelectionPrompt, + generateDynamicParametersPrompt, + generateDynamicRefinementPrompt ) from modules.workflows.processing.shared.placeholderFactory import extractReviewContent from modules.workflows.processing.adaptive import IntentAnalyzer, ContentValidator, LearningEngine, ProgressTracker @@ -25,8 +25,8 @@ from modules.workflows.processing.adaptive.adaptiveLearningEngine import Adaptiv logger = logging.getLogger(__name__) -class ReactMode(BaseMode): - """React mode implementation - iterative plan-act-observe-refine loop""" +class DynamicMode(BaseMode): + """Dynamic mode implementation - iterative plan-act-observe-refine loop""" def __init__(self, services, workflow): super().__init__(services, workflow) @@ -41,13 +41,13 @@ class ReactMode(BaseMode): 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 + """Dynamic mode doesn't use batch action generation - actions are generated iteratively""" + # Dynamic mode generates actions one at a time in the execution loop return [] async def executeTask(self, taskStep: TaskStep, workflow: ChatWorkflow, context: TaskContext, taskIndex: int = None, totalTasks: int = None) -> TaskResult: - """Execute task using React mode - iterative plan-act-observe-refine loop""" + """Execute task using Dynamic mode - iterative plan-act-observe-refine loop""" logger.info(f"=== STARTING TASK {taskIndex or '?'}: {taskStep.objective} ===") # Use workflow-level intent from planning phase (stored in workflow object) @@ -79,9 +79,9 @@ class ReactMode(BaseMode): await self.messageCreator.createTaskStartMessage(taskStep, workflow, taskIndex, totalTasks) state = TaskExecutionState(taskStep) - # React mode uses max_steps instead of max_retries + # Dynamic mode uses max_steps instead of max_retries state.max_steps = max(1, int(getattr(workflow, 'maxSteps', 5))) - logger.info(f"Using React mode execution with max_steps: {state.max_steps}") + logger.info(f"Using Dynamic mode execution with max_steps: {state.max_steps}") step = 1 decision = None @@ -95,7 +95,7 @@ class ReactMode(BaseMode): try: t0 = time.time() selection = await self._planSelect(context) - logger.info(f"React step {step}: Selected action: {selection}") + logger.info(f"Dynamic step {step}: Selected action: {selection}") # Create user-friendly message BEFORE action execution # Action intention message is now handled by the standard message creator in _actExecute @@ -154,7 +154,7 @@ class ReactMode(BaseMode): # Action completion message is now handled by the standard message creator in _actExecute except Exception as e: - logger.error(f"React step {step} error: {e}") + logger.error(f"Dynamic step {step} error: {e}") break # NEW: Use adaptive stopping logic @@ -168,7 +168,7 @@ class ReactMode(BaseMode): break step += 1 - # Summarize task result for react mode + # Summarize task result for dynamic mode status = TaskStatus.COMPLETED success = True # Get feedback from last decision if available @@ -199,7 +199,7 @@ class ReactMode(BaseMode): async def _planSelect(self, context: TaskContext) -> Dict[str, Any]: """Plan: select exactly one action. Returns {"action": {method, name}}""" - bundle = generateReactPlanSelectionPrompt(self.services, context, self.adaptiveLearningEngine) + bundle = generateDynamicPlanSelectionPrompt(self.services, context, self.adaptiveLearningEngine) promptTemplate = bundle.prompt placeholders = bundle.placeholders @@ -297,7 +297,7 @@ class ReactMode(BaseMode): stage2Context.taskStep = getattr(context, 'taskStep', None) stage2Context.workflowId = getattr(context, 'workflowId', None) - # Set Stage 1 data directly on the permissive context (snake_case for promptGenerationActionsReact compatibility) + # Set Stage 1 data directly on the permissive context (snake_case for promptGenerationActionsDynamic compatibility) if isinstance(selection, dict): stage2Context.action_objective = selection.get('actionObjective', '') stage2Context.parameters_context = selection.get('parametersContext', '') @@ -308,7 +308,7 @@ class ReactMode(BaseMode): stage2Context.learnings = [] # Build and send the Stage 2 parameters prompt (always) - bundle = generateReactParametersPrompt(self.services, stage2Context, compoundActionName, self.adaptiveLearningEngine) + bundle = generateDynamicParametersPrompt(self.services, stage2Context, compoundActionName, self.adaptiveLearningEngine) promptTemplate = bundle.prompt placeholders = bundle.placeholders @@ -596,7 +596,7 @@ class ReactMode(BaseMode): reviewContext = ReviewContext( taskStep=context.taskStep, taskActions=[], - actionResults=[], # React mode doesn't have action results in this context + actionResults=[], # Dynamic mode doesn't have action results in this context stepResult={'observation': observationDict}, workflowId=context.workflowId, previousResults=[] @@ -640,7 +640,7 @@ class ReactMode(BaseMode): # Update placeholders with enhanced review content placeholders["REVIEW_CONTENT"] = enhancedReviewContent - bundle = generateReactRefinementPrompt(self.services, context, enhancedReviewContent) + bundle = generateDynamicRefinementPrompt(self.services, context, enhancedReviewContent) promptTemplate = bundle.prompt placeholders = bundle.placeholders @@ -707,10 +707,10 @@ class ReactMode(BaseMode): qualityScore=5.0 ) - async def _createReactActionMessage(self, workflow: ChatWorkflow, selection: Dict[str, Any], + async def _createDynamicActionMessage(self, workflow: ChatWorkflow, selection: Dict[str, Any], step: int, maxSteps: int, taskIndex: int, messageType: str, result: ActionResult = None, observation: Observation = None): - """Create user-friendly messages for React workflow actions""" + """Create user-friendly messages for Dynamic workflow actions""" try: action = selection.get('action', {}) method = action.get('method', '') @@ -757,7 +757,7 @@ class ReactMode(BaseMode): self.services.workflow.storeMessageWithDocuments(workflow, messageData, []) except Exception as e: - logger.error(f"Error creating React action message: {str(e)}") + logger.error(f"Error creating Dynamic action message: {str(e)}") async def _generateActionIntentionMessage(self, method: str, actionName: str, userLanguage: str): """Generate user-friendly message explaining what action will do""" @@ -817,7 +817,7 @@ Return only the user-friendly message, no technical details.""" return f"{method}.{actionName} action completed" def _createActionItem(self, actionData: Dict[str, Any]) -> ActionItem: - """Creates a new task action for React mode""" + """Creates a new task action for Dynamic mode""" try: import uuid @@ -908,7 +908,7 @@ Return only the user-friendly message, no technical details.""" logger.error(f"Error updating workflow before executing action: {str(e)}") def _createActionItem(self, actionData: Dict[str, Any]) -> ActionItem: - """Creates a new task action for React mode""" + """Creates a new task action for Dynamic mode""" try: import uuid diff --git a/modules/workflows/processing/modes/modeTemplate.py b/modules/workflows/processing/modes/modeTemplate.py new file mode 100644 index 00000000..6b476a2d --- /dev/null +++ b/modules/workflows/processing/modes/modeTemplate.py @@ -0,0 +1,41 @@ +# modeTemplate.py +# Template mode implementation for workflows (placeholder for future use) + +import logging +from typing import List, Dict, Any +from modules.datamodels.datamodelChat import ( + TaskStep, TaskContext, TaskResult, ActionItem, TaskStatus +) +from modules.datamodels.datamodelChat import ChatWorkflow +from modules.workflows.processing.modes.modeBase import BaseMode + +logger = logging.getLogger(__name__) + +class TemplateMode(BaseMode): + """Template mode implementation - placeholder for future template-based workflow execution""" + + def __init__(self, services, workflow): + super().__init__(services, workflow) + logger.warning("TemplateMode is not yet implemented - using placeholder implementation") + + async def generateActionItems(self, taskStep: TaskStep, workflow: ChatWorkflow, + previousResults: List = None, enhancedContext: TaskContext = None) -> List[ActionItem]: + """Generate actions for a given task step using template-based approach""" + logger.warning("TemplateMode.generateActionItems not yet implemented") + # TODO: Implement template-based action generation + return [] + + async def executeTask(self, taskStep: TaskStep, workflow: ChatWorkflow, context: TaskContext, + taskIndex: int = None, totalTasks: int = None) -> TaskResult: + """Execute task using Template mode - placeholder implementation""" + logger.warning("TemplateMode.executeTask not yet implemented - returning placeholder result") + + # Return placeholder task result + return TaskResult( + taskId=taskStep.id, + status=TaskStatus.COMPLETED, + success=False, + feedback="Template mode is not yet implemented", + error="Template mode is not yet implemented" + ) + diff --git a/modules/workflows/processing/shared/executionState.py b/modules/workflows/processing/shared/executionState.py index ba6a7d0c..a783e544 100644 --- a/modules/workflows/processing/shared/executionState.py +++ b/modules/workflows/processing/shared/executionState.py @@ -17,7 +17,7 @@ class TaskExecutionState: self.current_action_index = 0 self.retry_count = 0 self.max_retries = 3 - # Iterative loop (react mode) + # Iterative loop (dynamic mode) self.current_step = 0 self.max_steps = 5 diff --git a/modules/workflows/processing/shared/placeholderFactory.py b/modules/workflows/processing/shared/placeholderFactory.py index 31310d21..68f75e23 100644 --- a/modules/workflows/processing/shared/placeholderFactory.py +++ b/modules/workflows/processing/shared/placeholderFactory.py @@ -8,22 +8,22 @@ NAMING CONVENTION: - Placeholder names are in UPPER_CASE with underscores - Function names are in camelCase -MAPPING TABLE (keys → function) with usage [taskplan | actionplan | react]: -{{KEY:USER_PROMPT}} -> extractUserPrompt() [taskplan, actionplan, react] -{{KEY:USER_LANGUAGE}} -> extractUserLanguage() [actionplan, react] +MAPPING TABLE (keys → function) with usage [taskplan | actionplan | dynamic]: +{{KEY:USER_PROMPT}} -> extractUserPrompt() [taskplan, actionplan, dynamic] +{{KEY:USER_LANGUAGE}} -> extractUserLanguage() [actionplan, dynamic] {{KEY:LANGUAGE_USER_DETECTED}} -> extractLanguageUserDetected() [taskplan] -{{KEY:WORKFLOW_HISTORY}} -> extractWorkflowHistory() [taskplan, actionplan, react] -{{KEY:AVAILABLE_CONNECTIONS_INDEX}} -> extractAvailableConnectionsIndex() [actionplan, react] +{{KEY:WORKFLOW_HISTORY}} -> extractWorkflowHistory() [taskplan, actionplan, dynamic] +{{KEY:AVAILABLE_CONNECTIONS_INDEX}} -> extractAvailableConnectionsIndex() [actionplan, dynamic] {{KEY:AVAILABLE_CONNECTIONS_SUMMARY}} -> extractAvailableConnectionsSummary() [] -{{KEY:AVAILABLE_DOCUMENTS_SUMMARY}} -> extractAvailableDocumentsSummary() [taskplan, actionplan, react] -{{KEY:AVAILABLE_DOCUMENTS_INDEX}} -> extractAvailableDocumentsIndex() [react] -{{KEY:AVAILABLE_METHODS}} -> extractAvailableMethods() [actionplan, react] -{{KEY:REVIEW_CONTENT}} -> extractReviewContent() [actionplan, react] -{{KEY:PREVIOUS_ACTION_RESULTS}} -> extractPreviousActionResults() [react] -{{KEY:LEARNINGS_AND_IMPROVEMENTS}} -> extractLearningsAndImprovements() [react] -{{KEY:LATEST_REFINEMENT_FEEDBACK}} -> extractLatestRefinementFeedback() [react] +{{KEY:AVAILABLE_DOCUMENTS_SUMMARY}} -> extractAvailableDocumentsSummary() [taskplan, actionplan, dynamic] +{{KEY:AVAILABLE_DOCUMENTS_INDEX}} -> extractAvailableDocumentsIndex() [dynamic] +{{KEY:AVAILABLE_METHODS}} -> extractAvailableMethods() [actionplan, dynamic] +{{KEY:REVIEW_CONTENT}} -> extractReviewContent() [actionplan, dynamic] +{{KEY:PREVIOUS_ACTION_RESULTS}} -> extractPreviousActionResults() [dynamic] +{{KEY:LEARNINGS_AND_IMPROVEMENTS}} -> extractLearningsAndImprovements() [dynamic] +{{KEY:LATEST_REFINEMENT_FEEDBACK}} -> extractLatestRefinementFeedback() [dynamic] -Following placeholders are populated directly by prompt builders with according context in promptGenerationActionsReact module: +Following placeholders are populated directly by prompt builders with according context in promptGenerationActionsDynamic module: - ACTION_OBJECTIVE, - SELECTED_ACTION, - ACTION_SIGNATURE diff --git a/modules/workflows/processing/shared/promptGenerationActionsReact.py b/modules/workflows/processing/shared/promptGenerationActionsDynamic.py similarity index 96% rename from modules/workflows/processing/shared/promptGenerationActionsReact.py rename to modules/workflows/processing/shared/promptGenerationActionsDynamic.py index 338a3af6..9c1ac389 100644 --- a/modules/workflows/processing/shared/promptGenerationActionsReact.py +++ b/modules/workflows/processing/shared/promptGenerationActionsDynamic.py @@ -1,6 +1,6 @@ """ -React Mode Prompt Generation -Handles prompt templates for react mode action handling. +Dynamic Mode Prompt Generation +Handles prompt templates for dynamic mode action handling. """ import json @@ -20,7 +20,7 @@ from modules.workflows.processing.shared.placeholderFactory import ( ) from modules.workflows.processing.shared.methodDiscovery import methods, getActionParameterList -def generateReactPlanSelectionPrompt(services, context: Any, learningEngine=None) -> PromptBundle: +def generateDynamicPlanSelectionPrompt(services, context: Any, learningEngine=None) -> PromptBundle: """Define placeholders first, then the template; return PromptBundle.""" placeholders: List[PromptPlaceholder] = [ PromptPlaceholder(label="USER_PROMPT", content=extractUserPrompt(context), summaryAllowed=False), @@ -113,7 +113,7 @@ RULES: return PromptBundle(prompt=template, placeholders=placeholders) -def generateReactParametersPrompt(services, context: Any, compoundActionName: str, learningEngine=None) -> PromptBundle: +def generateDynamicParametersPrompt(services, context: Any, compoundActionName: str, learningEngine=None) -> PromptBundle: """Define placeholders first, then the template; return PromptBundle. Minimal Stage 2 (no fallback): consumes actionObjective, selectedAction, parametersContext only. @@ -265,7 +265,7 @@ RULES: return PromptBundle(prompt=template, placeholders=placeholders) -def generateReactRefinementPrompt(services, context: Any, reviewContent: str) -> PromptBundle: +def generateDynamicRefinementPrompt(services, context: Any, reviewContent: str) -> PromptBundle: """Define placeholders first, then the template; return PromptBundle.""" placeholders: List[PromptPlaceholder] = [ PromptPlaceholder(label="USER_PROMPT", content=extractUserPrompt(context), summaryAllowed=False), diff --git a/modules/workflows/processing/workflowProcessor.py b/modules/workflows/processing/workflowProcessor.py index 8e3798b8..36027d9e 100644 --- a/modules/workflows/processing/workflowProcessor.py +++ b/modules/workflows/processing/workflowProcessor.py @@ -4,10 +4,11 @@ import logging from typing import Dict, Any, Optional, List from modules.datamodels.datamodelChat import TaskStep, TaskContext, TaskPlan, TaskResult -from modules.datamodels.datamodelChat import ChatWorkflow +from modules.datamodels.datamodelChat import ChatWorkflow, WorkflowModeEnum from modules.workflows.processing.modes.modeBase import BaseMode from modules.workflows.processing.modes.modeActionplan import ActionplanMode -from modules.workflows.processing.modes.modeReact import ReactMode +from modules.workflows.processing.modes.modeDynamic import DynamicMode +from modules.workflows.processing.modes.modeTemplate import TemplateMode logger = logging.getLogger(__name__) @@ -21,15 +22,18 @@ class WorkflowProcessor: def __init__(self, services, workflow=None): self.services = services self.workflow = workflow - self.mode = self._createMode(workflow.workflowMode if workflow else "Actionplan") + self.mode = self._createMode(workflow.workflowMode if workflow else WorkflowModeEnum.WORKFLOW_DYNAMIC) - def _createMode(self, workflowMode: str) -> BaseMode: + def _createMode(self, workflowMode: WorkflowModeEnum) -> BaseMode: """Create the appropriate mode implementation based on workflow mode""" - if workflowMode == "React": - return ReactMode(self.services, self.workflow) - else: + if workflowMode == WorkflowModeEnum.WORKFLOW_DYNAMIC: + return DynamicMode(self.services, self.workflow) + elif workflowMode == WorkflowModeEnum.WORKFLOW_ACTIONPLAN: return ActionplanMode(self.services, self.workflow) - + elif workflowMode == WorkflowModeEnum.WORKFLOW_TEMPLATE: + return TemplateMode(self.services, self.workflow) + else: + raise ValueError(f"Invalid workflow mode: {workflowMode}") def _checkWorkflowStopped(self, workflow): """Check if workflow has been stopped by user and raise exception if so""" try: @@ -61,7 +65,7 @@ class WorkflowProcessor: operationId, "Workflow Planning", "Task Plan Generation", - f"Mode: {workflow.workflowMode}" + f"Mode: {workflow.workflowMode.value if hasattr(workflow.workflowMode, 'value') else workflow.workflowMode}" ) # Initialize currentUserLanguage to empty at workflow start @@ -70,7 +74,8 @@ class WorkflowProcessor: logger.info(f"=== STARTING TASK PLAN GENERATION ===") logger.info(f"Workflow ID: {workflow.id}") logger.info(f"User Input: {userInput}") - logger.info(f"Workflow Mode: {workflow.workflowMode}") + modeValue = workflow.workflowMode.value if hasattr(workflow.workflowMode, 'value') else workflow.workflowMode + logger.info(f"Workflow Mode: {modeValue}") # Update progress - generating task plan self.services.workflow.progressLogUpdate(operationId, 0.3, "Analyzing input") @@ -116,7 +121,8 @@ class WorkflowProcessor: logger.info(f"=== STARTING TASK EXECUTION ===") logger.info(f"Task: {taskStep.objective}") - logger.info(f"Mode: {workflow.workflowMode}") + modeValue = workflow.workflowMode.value if hasattr(workflow.workflowMode, 'value') else workflow.workflowMode + logger.info(f"Mode: {modeValue}") # Update progress - executing task self.services.workflow.progressLogUpdate(operationId, 0.2, "Executing") @@ -143,7 +149,8 @@ class WorkflowProcessor: logger.info(f"=== STARTING ACTION GENERATION ===") logger.info(f"Task: {taskStep.objective}") - logger.info(f"Mode: {workflow.workflowMode}") + modeValue = workflow.workflowMode.value if hasattr(workflow.workflowMode, 'value') else workflow.workflowMode + logger.info(f"Mode: {modeValue}") # Delegate to the appropriate mode return await self.mode.generateActionItems(taskStep, workflow, previousResults, enhancedContext) diff --git a/modules/workflows/workflowManager.py b/modules/workflows/workflowManager.py index 6b254007..9123d102 100644 --- a/modules/workflows/workflowManager.py +++ b/modules/workflows/workflowManager.py @@ -8,7 +8,8 @@ from modules.datamodels.datamodelChat import ( UserInputRequest, ChatMessage, ChatWorkflow, - ChatDocument + ChatDocument, + WorkflowModeEnum ) from modules.datamodels.datamodelChat import TaskContext from modules.workflows.processing.workflowProcessor import WorkflowProcessor, WorkflowStoppedException @@ -25,7 +26,7 @@ class WorkflowManager: # Exported functions - async def workflowStart(self, userInput: UserInputRequest, workflowId: Optional[str] = None, workflowMode: str = "React") -> ChatWorkflow: + async def workflowStart(self, userInput: UserInputRequest, workflowId: Optional[str] = None, workflowMode: WorkflowModeEnum = WorkflowModeEnum.WORKFLOW_DYNAMIC) -> ChatWorkflow: """Starts a new workflow or continues an existing one, then launches processing.""" try: # Debug log to check workflowMode parameter @@ -90,7 +91,7 @@ class WorkflowManager: "mandateId": self.services.user.mandateId, "messageIds": [], "workflowMode": workflowMode, - "maxSteps": 5 if workflowMode == "React" else 1, # Set maxSteps for React mode + "maxSteps": 5 if workflowMode == WorkflowModeEnum.WORKFLOW_DYNAMIC else 1, # Set maxSteps for Dynamic mode } workflow = self.services.workflow.createWorkflow(workflowData) @@ -320,7 +321,7 @@ class WorkflowManager: task_plan = await handling.generateTaskPlan(userInput.prompt, workflow) if not task_plan or not task_plan.tasks: raise Exception("No tasks generated in task plan.") - workflow_mode = getattr(workflow, 'workflowMode', 'Actionplan') + workflow_mode = getattr(workflow, 'workflowMode', WorkflowModeEnum.WORKFLOW_DYNAMIC) logger.info(f"Workflow object attributes: {workflow.__dict__ if hasattr(workflow, '__dict__') else 'No __dict__'}") logger.info(f"Executing workflow mode={workflow_mode} with {len(task_plan.tasks)} tasks") return task_plan