Refactored workflow modes to the standard Dynamic (default), Actionplan (option), Template (future)

This commit is contained in:
ValueOn AG 2025-11-02 19:22:01 +01:00
parent 53bfe06dbe
commit 46fcd089c4
17 changed files with 156 additions and 82 deletions

View file

@ -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(

View file

@ -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)

View file

@ -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)
)

View file

@ -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

View file

@ -1,4 +1,4 @@
# adaptive module for React mode
# adaptive module for Dynamic mode
# Provides adaptive learning capabilities
from .intentAnalyzer import IntentAnalyzer

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"
)

View file

@ -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

View file

@ -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

View file

@ -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),

View file

@ -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)

View file

@ -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