-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #93 from small-thinking/async-agent
Sync agent
- Loading branch information
Showing
4 changed files
with
372 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,160 @@ | ||
from abc import ABC, abstractmethod | ||
from typing import Dict | ||
|
||
from pydantic import BaseModel, Field | ||
|
||
from polymind.core.logger import Logger | ||
from polymind.core.message import Message | ||
from polymind.core.tool import BaseTool, LLMTool | ||
from polymind.core.tool import (BaseTool, LLMTool, OptimizableBaseTool, | ||
SyncLLMTool) | ||
|
||
|
||
class Agent(BaseModel): | ||
class AbstractAgent(BaseModel, ABC): | ||
""" | ||
Abstract base class for all agent types. | ||
This class defines the common structure and interface for both synchronous | ||
and asynchronous agents. It includes shared attributes and methods, as well | ||
as abstract methods that must be implemented by subclasses. | ||
""" | ||
|
||
agent_name: str | ||
# Persona of the agent indicates the role of the agent. | ||
persona: str | ||
tools: Dict[str, BaseTool] = Field(default=None, description="The tools that the agent can use.") | ||
reasoner: LLMTool = Field(default=None, description="The reasoner that will be used in the thought process.") | ||
|
||
def __init__(self, **kwargs): | ||
super().__init__(**kwargs) | ||
self._logger = Logger(__file__) | ||
|
||
def __str__(self): | ||
def __str__(self) -> str: | ||
return self.agent_name | ||
|
||
def _input_preprocess(self, input: Message) -> None: | ||
"""Preprocess the input message before the agent starts working. | ||
Now now the only thing to do is to add the persona to the input message. | ||
""" | ||
Preprocess the input message before the agent starts working. | ||
Args: | ||
input (Message): The input message to preprocess. | ||
""" | ||
input.content["persona"] = self.persona | ||
|
||
async def _execute(self, input: Message) -> Message: | ||
"""Execute the agent and return the result. | ||
This method defines the behavior of the agent's thought process. | ||
@abstractmethod | ||
def _execute(self, input: Message) -> Message: | ||
""" | ||
Execute the agent and return the result. | ||
Args: | ||
input (Message): The input message to process. | ||
Returns: | ||
Message: The result of the agent's execution. | ||
""" | ||
pass | ||
|
||
@abstractmethod | ||
def __call__(self, input: Message) -> Message: | ||
""" | ||
Enable the agent to start working. | ||
Args: | ||
input (Message): The input to the thought process carried in a message. | ||
input (Message): The input message to process. | ||
Returns: | ||
Message: The result of the thought process carried in a message. | ||
Message: The result of the agent's work. | ||
""" | ||
if "requirement" in input.content: | ||
self._logger.thought_process_log( | ||
f"[{self.agent_name}], your requirement is: {input.content['requirement']}" | ||
) | ||
else: | ||
pass | ||
|
||
|
||
class Agent(AbstractAgent): | ||
""" | ||
Synchronous agent implementation. | ||
This class represents a synchronous agent that uses OptimizableBaseTool | ||
for its tools and SyncLLMTool for reasoning. | ||
""" | ||
|
||
tools: Dict[str, OptimizableBaseTool] = Field(default=None, description="The tools that the agent can use.") | ||
reasoner: SyncLLMTool = Field(default=None, description="The reasoner that will be used in the thought process.") | ||
|
||
def _execute(self, input: Message) -> Message: | ||
""" | ||
Synchronous execution of the agent. | ||
Args: | ||
input (Message): The input message to process. | ||
Returns: | ||
Message: The result of the agent's execution. | ||
Raises: | ||
ValueError: If the input message doesn't contain the 'requirement' field. | ||
""" | ||
if "requirement" not in input.content: | ||
raise ValueError("The input message must contain the 'requirement' field.") | ||
|
||
self._logger.thought_process_log(f"[{self.agent_name}], your requirement is: {input.content['requirement']}") | ||
|
||
# Add logic for executing the thought process using tools and reasoner. | ||
# This is a placeholder implementation. | ||
result_content = {"output": f"Processed requirement: {input.content['requirement']}"} | ||
return Message(content=result_content) | ||
|
||
def __call__(self, input: Message) -> Message: | ||
""" | ||
Synchronous call method. | ||
Args: | ||
input (Message): The input message to process. | ||
Returns: | ||
Message: The result of the agent's work. | ||
""" | ||
self._input_preprocess(input=input) | ||
return self._execute(input=input) | ||
|
||
|
||
class AsyncAgent(AbstractAgent): | ||
""" | ||
Asynchronous agent implementation. | ||
This class represents an asynchronous agent that uses BaseTool | ||
for its tools and LLMTool for reasoning. | ||
""" | ||
|
||
tools: Dict[str, BaseTool] = Field(default=None, description="The tools that the agent can use.") | ||
reasoner: LLMTool = Field(default=None, description="The reasoner that will be used in the thought process.") | ||
|
||
async def _execute(self, input: Message) -> Message: | ||
""" | ||
Asynchronous execution of the agent. | ||
Args: | ||
input (Message): The input message to process. | ||
Returns: | ||
Message: The result of the agent's execution. | ||
Raises: | ||
ValueError: If the input message doesn't contain the 'requirement' field. | ||
""" | ||
if "requirement" not in input.content: | ||
raise ValueError("The input message must contain the 'requirement' field.") | ||
|
||
self._logger.thought_process_log(f"[{self.agent_name}], your requirement is: {input.content['requirement']}") | ||
|
||
# Add async logic for executing the thought process using tools and reasoner. | ||
# This is a placeholder implementation. | ||
result_content = {"output": f"Processed requirement: {input.content['requirement']}"} | ||
return Message(content=result_content) | ||
|
||
async def __call__(self, input: Message) -> Message: | ||
"""Enable the agent to start working. | ||
The actual processing is driven by the agent itself. | ||
""" | ||
Asynchronous call method. | ||
Args: | ||
input (Message): The input message to the agent. | ||
input (Message): The input message to process. | ||
Returns: | ||
Message: The output message from the agent. | ||
Message: The result of the agent's work. | ||
""" | ||
self._input_preprocess(input=input) | ||
return await self._execute(input=input) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.