Project Plan: Refactoring TaskExecutor for LLM Interoperability
1. Introduction
This document outlines the plan to refactor the TaskExecutor in submodules/core/coditect-core/orchestration/executor.py to achieve complete interoperability with various Large Language Models (LLMs) through a new abstraction layer (llm_abstractions). The goal is to replace the current script-based LLM execution with a more modular, object-oriented approach, enhancing flexibility, maintainability, and scalability.
2. Goals
- Integrate the newly created
llm_abstractions(e.g.,BaseLlm,Gemini) into theTaskExecutor. - Decouple
TaskExecutorfrom direct calls to external LLM execution scripts (e.g.,execute_gemini.py). - Enable
TaskExecutorto dynamically select and use any LLM that adheres to theBaseLlminterface. - Centralize LLM interaction logic, making it easier to add new LLMs in the future.
- Improve code readability and maintainability.
3. High-Level Plan
- Modify
TaskExecutor._execute_api: Update this method to use thellm_abstractionsinstead of executing external scripts. - Implement an LLM Factory/Resolver: Create a mechanism within
TaskExecutorto mapAgentType(fromagent_registry.py) to the appropriate concrete LLM class (e.g.,Gemini,Claude,Gpt) fromllm_abstractions. - Instantiate and Execute LLM: Dynamically instantiate the selected LLM class and call its
generate_content_asyncmethod. - Remove Legacy Script References: Clean up or deprecate the old
_get_execution_scriptmethod and its associated logic. - Update Agent Registry (if necessary): Ensure
AgentRegistryis configured to correctly provide the necessary parameters for the new LLM classes. - Test the Refactoring: Implement or update unit and integration tests to ensure the new execution flow works as expected for all supported LLMs.
4. Detailed Steps
Step 4.1: Update executor.py Imports
- Add imports for
BaseLlmand specific LLM implementations (e.g.,Gemini) fromllm_abstractions.# submodules/core/coditect-core/orchestration/executor.py
# ...
from ..llm_abstractions.base_llm import BaseLlm
from ..llm_abstractions.gemini import Gemini
# from ..llm_abstractions.claude import Claude # Future
# from ..llm_abstractions.gpt import Gpt # Future
# ...
Step 4.2: Implement LLM Factory/Resolver in TaskExecutor
-
Add a new attribute to
TaskExecutor(e.g.,_llm_factory) that mapsAgentTypeto the correspondingBaseLlmsubclass.# In TaskExecutor.__init__
self._llm_factory: Dict[AgentType, Type[BaseLlm]] = {
AgentType.GOOGLE_GEMINI: Gemini,
# AgentType.ANTHROPIC_CLAUDE: Claude, # Future
# AgentType.OPENAI_GPT: Gpt, # Future
}
Step 4.3: Modify TaskExecutor._execute_api
-
Refactor the
_execute_apimethod to:- Get the
AgentConfigfrom theAgentRegistry. - Use the
_llm_factoryto get the appropriate LLM class based onagent_config.agent_type. - Instantiate the LLM class, passing relevant parameters from
agent_config(e.g.,model,api_key). - Prepare the
messageslist for the LLM. - Call the LLM's
generate_content_asyncmethod. - Process the response and update the
ExecutionResult.
# In TaskExecutor._execute_api
async def _execute_api(
self,
task: AgentTask,
agent_config: AgentConfig,
result: ExecutionResult
) -> ExecutionResult:
result.status = ExecutionStatus.IN_PROGRESS
try:
llm_class = self._llm_factory.get(agent_config.agent_type)
if not llm_class:
raise ValueError(f"No LLM implementation found for agent type: {agent_config.agent_type.value}")
# Instantiate the LLM
llm_instance = llm_class(
model=agent_config.model,
api_key=agent_config.api_key # Assuming API key is passed here
)
# Prepare messages (simplified for plan, actual implementation might be more complex)
messages = [
{"role": "user", "content": task.description} # Example
]
# Call the LLM's async method
llm_output = await llm_instance.generate_content_async(messages)
result.status = ExecutionStatus.SUCCESS
result.output = llm_output
result.completed_at = datetime.now()
except Exception as e:
result.status = ExecutionStatus.FAILED
result.error = str(e)
result.completed_at = datetime.now()
return result- Note: The
_execute_apimethod is currently notasync. I will need to make itasyncor find a way to run thegenerate_content_asyncsynchronously, but running it asynchronously is the preferred way. This will require changes toProjectOrchestrator.execute_taskas well. This is a significant change, so I will address this in a separate sub-step.
- Get the
Step 4.4: Update ProjectOrchestrator.execute_task for Async Calls
-
The
execute_taskmethod inProjectOrchestratorwill need to be madeasyncto correctly await the_execute_apimethod. This will propagate through the call stack.# In ProjectOrchestrator.execute_task
# ...
async def execute_task(
self,
task_id: str,
agent: Optional[str] = None
) -> ExecutionResult:
# ...
result = await self.executor.execute(task, agent=agent) # Await the executor
# ...- This will also require
ProjectOrchestratorto be used in an async context.
- This will also require
Step 4.5: Deprecate/Remove _get_execution_script
- The
_get_execution_scriptmethod withinTaskExecutorwill no longer be necessary for API-based LLM execution. It can be removed or modified to only handle interactive/CLI modes if those persist.
Step 4.6: Update AgentRegistry (Review)
- No direct changes might be needed, but it's important to ensure
AgentConfigcorrectly storesmodelandapi_keyfor the newBaseLlmimplementations. (Already handled by previous changes).
Step 4.7: Testing
- Create or modify unit tests for
TaskExecutorto ensure it correctly instantiates and calls the new LLM classes. - Verify that
ProjectOrchestratorcan still execute tasks using the new flow.
5. Potential Challenges and Risks
- Asynchronous Propagation: Making
execute_taskand potentially its callersasyncwill require changes throughout the system's execution flow. - Error Handling: Ensuring robust error handling and logging for the new LLM integration.
- API Key Management: The
api_keypassing mechanism needs to be secure and consistent. - LLM-specific parameters: Different LLMs might have unique parameters that need to be passed. The
**kwargsingenerate_content_asyncshould handle this, but the orchestration needs to provide them.
6. Future Work
- Implement
ClaudeandGptclasses inllm_abstractions. - Migrate existing
execute_*.pyscript logic into their respectivellm_abstractionsclasses. - Remove the
scripts/llm_executiondirectory and its contents entirely. - Enhance
AgentConfigto store more LLM-specific parameters if needed.
7. Confirmation
This plan outlines a significant architectural shift. Your confirmation is required before proceeding with these changes.