12 KiB
12 KiB
Refactoring Recommendations for Workflow Processing
Current Issues
1. File Size Problem
- handlingTasks.py: 2,192 lines - too large for maintainability
- Mixed responsibilities: Task planning, execution, messaging, validation all in one class
- Complex conditional logic: Mode switching creates nested conditions
2. Code Duplication
- Message creation: Similar logic in
createActionMessage()andcreateReactActionMessage() - AI calls: Repeated patterns for different prompt types
- Validation: Similar validation logic across modes
3. Tight Coupling
- Mode-specific logic: Mixed with common functionality
- Hard to test: Large methods with multiple responsibilities
- Hard to extend: Adding new modes requires modifying existing code
Proposed Refactoring Structure
gateway/modules/workflows/processing/
├── core/ # Common functionality
│ ├── __init__.py
│ ├── taskPlanner.py # Task planning (common)
│ ├── actionExecutor.py # Action execution (common)
│ ├── messageCreator.py # Generic message creation (all workflow phases)
│ ├── validator.py # Validation logic (common)
│ └── workflowCoordinator.py # Main workflow coordination
├── modes/ # Mode-specific implementations
│ ├── __init__.py
│ ├── baseMode.py # Abstract base class
│ ├── actionplanMode.py # Actionplan mode implementation
│ └── reactMode.py # React mode implementation
├── shared/ # Shared utilities
│ ├── __init__.py
│ ├── executionState.py # State management
│ ├── promptFactory.py # Prompt generation (reusable functions)
│ └── promptFactoryPlaceholders.py
└── workflowProcessor.py # Main processor (renamed from handlingTasks.py)
Detailed Refactoring Plan
Phase 1: Extract Common Functionality
1.1 Create Core Modules
taskPlanner.py
class TaskPlanner:
async def generateTaskPlan(self, userInput: str, workflow) -> TaskPlan:
# Move from handlingTasks.py:87-267
# Common task planning logic
actionExecutor.py
class ActionExecutor:
async def executeSingleAction(self, action, workflow, task_step, ...):
# Move from handlingTasks.py:1562-1710
# Common action execution logic
messageCreator.py
class MessageCreator:
async def createTaskPlanMessage(self, task_plan, workflow):
# Move from handlingTasks.py:269-308
# Generic task plan messaging
async def createTaskStartMessage(self, task_step, workflow, task_index, total_tasks):
# Move from handlingTasks.py:882-910
# Generic task start messaging
async def createActionMessage(self, action, result, workflow, ...):
# Move from handlingTasks.py:1712-1789
# Generic action messaging (success/failure)
async def createTaskCompletionMessage(self, task_step, workflow, task_index, total_tasks, review_result):
# Move from handlingTasks.py:1069-1103
# Generic task completion messaging
async def createRetryMessage(self, task_step, workflow, task_index, review_result):
# Move from handlingTasks.py:1176-1195
# Generic retry messaging
async def createErrorMessage(self, task_step, workflow, task_index, error_details):
# Move from handlingTasks.py:1201-1252
# Generic error messaging
validator.py
class WorkflowValidator:
def validateTask(self, task_plan: Dict[str, Any]) -> bool:
# Move from handlingTasks.py:1793-1850
# Renamed from _validateTaskPlan, removed underscore
def validateAction(self, actions: List[Dict[str, Any]], context) -> bool:
# Move from handlingTasks.py:1906-1938
# Renamed from _validateActions, removed underscore, singular form
Phase 2: Create Mode-Specific Implementations
2.1 Base Mode Class
baseMode.py
from abc import ABC, abstractmethod
class BaseMode(ABC):
def __init__(self, services, workflow):
self.services = services
self.workflow = workflow
self.taskPlanner = TaskPlanner(services)
self.actionExecutor = ActionExecutor(services)
self.messageCreator = MessageCreator(services)
self.validator = WorkflowValidator(services)
@abstractmethod
async def executeTask(self, task_step, workflow, context, ...) -> TaskResult:
pass
@abstractmethod
async def generateTaskActions(self, task_step, workflow, ...) -> List[TaskAction]:
pass
2.2 Actionplan Mode
actionplanMode.py
class ActionplanMode(BaseMode):
async def executeTask(self, task_step, workflow, context, ...) -> TaskResult:
# Move from handlingTasks.py:972-1261
# Actionplan-specific execution logic
async def generateTaskActions(self, task_step, workflow, ...) -> List[TaskAction]:
# Move from handlingTasks.py:445-643
# Batch action generation
2.3 React Mode
reactMode.py
class ReactMode(BaseMode):
async def executeTask(self, task_step, workflow, context, ...) -> TaskResult:
# Move from handlingTasks.py:916-970
# React-specific execution logic
async def plan_select(self, context: TaskContext) -> Dict[str, Any]:
# Move from handlingTasks.py:647-692
async def act_execute(self, context: TaskContext, selection: Dict[str, Any], ...):
# Move from handlingTasks.py:694-773
def observe_build(self, action_result: ActionResult) -> Dict[str, Any]:
# Move from handlingTasks.py:775-822
async def refine_decide(self, context: TaskContext, observation: Dict[str, Any]) -> Dict[str, Any]:
# Move from handlingTasks.py:824-863
Phase 3: Simplify Main Coordinator
workflowProcessor.py (Simplified)
class WorkflowProcessor:
def __init__(self, services, workflow=None):
self.services = services
self.workflow = workflow
self.mode = self._createMode(workflow.workflowMode)
def _createMode(self, workflowMode: str) -> BaseMode:
if workflowMode == "React":
return ReactMode(self.services, self.workflow)
else:
return ActionplanMode(self.services, self.workflow)
async def generateTaskPlan(self, userInput: str, workflow) -> TaskPlan:
return await self.mode.taskPlanner.generateTaskPlan(userInput, workflow)
async def executeTask(self, task_step, workflow, context, ...) -> TaskResult:
return await self.mode.executeTask(task_step, workflow, context, ...)
# Delegate other methods to appropriate components
Phase 4: Create Shared Utilities
4.1 Enhanced Prompt Factory (Reusable Functions)
promptFactory.py (Enhanced)
class PromptFactory:
def __init__(self, services):
self.services = services
def createPrompt(self, template_type: str, context: Any) -> str:
# Centralized prompt creation
# Move from promptFactory.py and promptFactoryPlaceholders.py
def extractPlaceholders(self, context: Any) -> Dict[str, str]:
# Centralized placeholder extraction
# Reusable prompt element functions (called by both modes)
def getAvailableDocuments(self, context: Any) -> str:
# Move from promptFactory.py:_getAvailableDocuments()
def getWorkflowHistory(self, services, context: Any) -> str:
# Move from promptFactory.py:_getPreviousRoundContext()
def getAvailableMethods(self, services) -> str:
# Move from promptFactory.py:getMethodsList()
def getEnhancedDocumentContext(self, services) -> str:
# Move from promptFactory.py:getEnhancedDocumentContext()
def getConnectionReferenceList(self, services) -> str:
# Move from promptFactory.py:_getConnectionReferenceList()
Benefits of Refactoring
1. Maintainability
- Smaller files: Each file has single responsibility
- Clear separation: Mode-specific vs common logic
- Easier debugging: Isolated functionality
2. Testability
- Unit testing: Each component can be tested independently
- Mocking: Easier to mock dependencies
- Integration testing: Clear interfaces between components
3. Extensibility
- New modes: Add new mode by implementing BaseMode
- New features: Add to appropriate component
- Configuration: Mode-specific configuration
4. Code Reuse
- Common logic: Shared across modes
- Reduced duplication: Single implementation of common functionality
- Consistent behavior: Same logic for same operations
Migration Strategy
Step 1: Create New Structure
- Create new directory structure
- Create base classes and interfaces
- Move common functionality to core modules
Step 2: Extract Mode-Specific Logic
- Create ActionplanMode class
- Create ReactMode class
- Move mode-specific methods
Step 3: Update Main Coordinator
- Simplify HandlingTasks class
- Use composition instead of inheritance
- Delegate to appropriate components
Step 4: Testing and Validation
- Create unit tests for each component
- Integration tests for each mode
- End-to-end workflow tests
Step 5: Cleanup
- Remove old code
- Update imports
- Update documentation
File Size Reduction
Current State
- handlingTasks.py: 2,192 lines
After Refactoring
- workflowProcessor.py: ~200 lines (main processor)
- core/taskPlanner.py: ~300 lines
- core/actionExecutor.py: ~400 lines
- core/messageCreator.py: ~400 lines (generic messages for all workflow phases)
- modes/actionplanMode.py: ~400 lines
- modes/reactMode.py: ~350 lines
- shared/promptFactory.py: ~300 lines (reusable prompt element functions)
- shared/executionState.py: ~100 lines
Total: ~2,450 lines (slightly more due to better organization and generic message handling)
Implementation Priority
- High Priority: Extract common functionality (Phase 1)
- Medium Priority: Create mode-specific implementations (Phase 2)
- Low Priority: Create shared utilities (Phase 4)
- Final: Simplify main coordinator (Phase 3)
This refactoring will make the codebase more maintainable, testable, and extensible while preserving all existing functionality.
Key Changes Based on Review
✅ Prompt Elements as Reusable Functions
- Before: Duplicated prompt content in each mode
- After: Reusable functions like
getAvailableDocuments(),getWorkflowHistory(), etc. - Benefit: Single source of truth, easier maintenance
✅ Group Imports with __init__.py
- Pattern:
import corethencore.taskPlanner.xxx() - Benefit: Clear calling references, better maintainability
- Files: All modules have
__init__.pyfor group imports
✅ Generic MessageCreator
- Before: Mode-specific messages (
createReactActionMessage) - After: Generic messages for all workflow phases
- Messages: Task plan, task start/end, action start/end, retry, error
- Benefit: Consistent messaging across modes
✅ Clean Function Names
- Before:
_validateTaskPlan(),_validateActions() - After:
validateTask(),validateAction() - Benefit: No underscores, singular forms, clearer naming
✅ No Unnecessary AICallManager
- Removed: AICallManager wrapper
- Reason: AI calls already well-parameterized with centralized service
- Benefit: Avoid over-engineering, keep existing good patterns
✅ Enhanced PromptFactory
- Focus: Reusable prompt element functions
- Functions:
getAvailableDocuments(),getWorkflowHistory(),getAvailableMethods(), etc. - Benefit: Both modes use same prompt building logic
✅ Better Naming: workflowProcessor.py
- Before:
handlingTasks.py(unclear role) - After:
workflowProcessor.py(clear processing role) - Class:
HandlingTasks→WorkflowProcessor - Benefit: Clear relationship with
workflowManager.py