Skip to content

swarm - Refactor agent APIs and introduce new implementations #1045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: mono/dev-newfeatures
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pkgs/core/swarmauri_core/ComponentBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,13 @@ class ResourceTypes(Enum):
CONTROL_PANEL = "ControlPanel"
TASK_MGT_STRATEGY = "TaskMgtStrategy"
MAS = "Mas"
AGENT_API = "AgentAPI"


def generate_id() -> str:
return str(uuid4())


class ComponentBase(BaseModel):
name: Optional[str] = None
id: str = Field(default_factory=generate_id)
Expand Down
15 changes: 15 additions & 0 deletions pkgs/core/swarmauri_core/agent_apis/IAgentAPI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from abc import ABC, abstractmethod
from typing import Any, Coroutine, Dict


class IAgentAPI(ABC):

@abstractmethod
def invoke(self, agent_id: str, **kwargs: Dict[str, Any]) -> Any:
"""Invoke an agent synchronously."""
pass

@abstractmethod
async def ainvoke(self, agent_id: str, **kwargs: Dict[str, Any]) -> Any:
"""Invoke an agent asynchronously."""
pass
83 changes: 0 additions & 83 deletions pkgs/core/swarmauri_core/agent_apis/IAgentCommands.py

This file was deleted.

56 changes: 0 additions & 56 deletions pkgs/core/swarmauri_core/agent_apis/IAgentRouterCRUD.py

This file was deleted.

4 changes: 0 additions & 4 deletions pkgs/core/swarmauri_core/agent_apis/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +0,0 @@
from .IAgentCommands import IAgentCommands
from .IAgentRouterCRUD import IAgentRouterCRUD

__all__ = ['IAgentCommands', 'IAgentRouterCRUD']
Empty file.
31 changes: 31 additions & 0 deletions pkgs/swarmauri/swarmauri/agent_apis/base/AgentAPIBase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Any, Dict, Literal, Optional

from pydantic import ConfigDict, Field
from swarmauri_core.ComponentBase import ComponentBase, ResourceTypes
from swarmauri_core.agent_apis.IAgentAPI import IAgentAPI
from swarmauri.service_registries.concrete.ServiceRegistry import ServiceRegistry


class AgentAPIBase(IAgentAPI, ComponentBase):

resource: Optional[str] = Field(default=ResourceTypes.AGENT_API.value, frozen=True)
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
type: Literal["AgentAPIBase"] = "AgentAPIBase"

agent_registry: ServiceRegistry

def invoke(self, agent_id: str, **kwargs: Dict[str, Any]) -> Any:
agent = self.agent_registry.get_service(agent_id)
if not agent:
raise ValueError(f"Agent with ID {agent_id} not found.")
return agent.exec(**kwargs)

async def ainvoke(self, agent_id: str, **kwargs: Dict[str, Any]) -> Any:
agent = self.agent_registry.get_service(agent_id)
if not agent:
raise ValueError(f"Agent with ID {agent_id} not found.")
if not hasattr(agent, "aexec"):
raise NotImplementedError(
f"Agent with ID {agent_id} does not support async execution."
)
return await agent.aexec(**kwargs)
Empty file.
10 changes: 10 additions & 0 deletions pkgs/swarmauri/swarmauri/agent_apis/concrete/AgentAPI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from typing import Literal
from swarmauri.agent_apis.base.AgentAPIBase import AgentAPIBase


class AgentAPI(AgentAPIBase):
"""
Concrete implementation of the AgentAPIBase.
"""

type: Literal["AgentAPI"] = "AgentAPI"
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,29 @@
from swarmauri_core.messages.IMessage import IMessage
from swarmauri_core.conversations.IMaxSize import IMaxSize
from swarmauri.conversations.base.ConversationBase import ConversationBase
from swarmauri.conversations.base.ConversationSystemContextMixin import ConversationSystemContextMixin
from swarmauri.messages.concrete import SystemMessage, AgentMessage, HumanMessage
from swarmauri.conversations.base.ConversationSystemContextMixin import (
ConversationSystemContextMixin,
)
from swarmauri.messages.concrete.SystemMessage import SystemMessage
from swarmauri.messages.concrete.AgentMessage import AgentMessage
from swarmauri.messages.concrete.HumanMessage import HumanMessage
from swarmauri.exceptions.concrete import IndexErrorWithContext

class MaxSystemContextConversation(IMaxSize, ConversationSystemContextMixin, ConversationBase):

class MaxSystemContextConversation(
IMaxSize, ConversationSystemContextMixin, ConversationBase
):
system_context: Optional[SystemMessage] = SystemMessage(content="")
max_size: int = Field(default=2, gt=1)
model_config = ConfigDict(extra='forbid', arbitrary_types_allowed=True)
type: Literal['MaxSystemContextConversation'] = 'MaxSystemContextConversation'
@field_validator('system_context', mode='before')
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
type: Literal["MaxSystemContextConversation"] = "MaxSystemContextConversation"

@field_validator("system_context", mode="before")
def set_system_context(cls, value: Union[str, SystemMessage]) -> SystemMessage:
if isinstance(value, str):
return SystemMessage(content=value)
return value

@property
def history(self) -> List[IMessage]:
"""
Expand All @@ -41,11 +48,16 @@ def history(self) -> List[IMessage]:
# Build history from the first 'user' message ensuring alternating roles.
res.append(self.system_context)
alternating = True
count = 0
count = 0
for message in self._history[user_start_index:]:
if count >= self.max_size: # max size
if count >= self.max_size: # max size
break
if alternating and isinstance(message, HumanMessage) or not alternating and isinstance(message, AgentMessage):
if (
alternating
and isinstance(message, HumanMessage)
or not alternating
and isinstance(message, AgentMessage)
):
res.append(message)
alternating = not alternating
count += 1
Expand All @@ -63,13 +75,15 @@ def add_message(self, message: IMessage):
Adds a message to the conversation history and ensures history does not exceed the max size.
"""
if isinstance(message, SystemMessage):
raise ValueError(f"System context cannot be set through this method on {self.__class_name__}.")
raise ValueError(
f"System context cannot be set through this method on {self.__class_name__}."
)
elif isinstance(message, IMessage):
self._history.append(message)
else:
raise ValueError(f"Must use a subclass of IMessage")
self._enforce_max_size_limit()

def _enforce_max_size_limit(self):
"""
Remove messages from the beginning of the conversation history if the limit is exceeded.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import logging
from typing import Any, Callable, Dict, Literal
from swarmauri.factories.base.FactoryBase import FactoryBase
from swarmauri.utils._get_subclasses import get_classes_from_module


class AgentFactory(FactoryBase):
Expand All @@ -8,7 +10,7 @@ class AgentFactory(FactoryBase):
"""

type: Literal["AgentFactory"] = "AgentFactory"
_registry: Dict[str, Callable] = {}
_registry: Dict[str, Callable] = get_classes_from_module("Agent")

def register(self, type: str, resource_class: Callable) -> None:
"""
Expand All @@ -22,6 +24,7 @@ def create(self, type: str, *args: Any, **kwargs: Any) -> Any:
"""
Create an instance of the class associated with the given type name.
"""
logging.info(self._registry)
if type not in self._registry:
raise ValueError(f"Type '{type}' is not registered.")

Expand Down
9 changes: 6 additions & 3 deletions pkgs/swarmauri/swarmauri/factories/concrete/Factory.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging
from typing import Any, Callable, Dict, Literal
from swarmauri.factories.base.FactoryBase import FactoryBase
from swarmauri.utils._get_subclasses import get_classes_from_module


class Factory(FactoryBase):
"""
Expand All @@ -15,6 +14,7 @@ def register(self, resource: str, type: str, resource_class: Callable) -> None:
"""
Register a resource class under a specific resource.
"""
from swarmauri.utils._get_subclasses import get_classes_from_module
if type in self._resource_registry.get(resource, {}):
raise ValueError(
f"Type '{type}' is already registered under resource '{resource}'."
Expand All @@ -30,10 +30,13 @@ def create(self, resource: str, type: str, *args: Any, **kwargs: Any) -> Any:
"""
Create an instance of the class associated with the given resource and type.
"""
from swarmauri.utils._get_subclasses import get_classes_from_module

if resource not in self._resource_registry:
self._resource_registry[resource] = get_classes_from_module(resource)
logging.info(self._resource_registry)

if type not in self._resource_registry[resource]:
if type not in self._resource_registry[resource].keys():
raise ValueError(
f"Type '{type}' is not registered under resource '{resource}'."
)
Expand Down
Loading
Loading