Skip to content

Commit

Permalink
support upload source code into agent server
Browse files Browse the repository at this point in the history
  • Loading branch information
pan-x-c committed May 21, 2024
1 parent eca9592 commit d82c4df
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 27 deletions.
11 changes: 10 additions & 1 deletion src/agentscope/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ def get_agent_class(cls, agent_class_name: str) -> Type[AgentBase]:
Type[AgentBase]: the AgentBase sub-class.
"""
if agent_class_name not in cls._registry:
raise ValueError(f"Agent [{agent_class_name}] not found.")
logger.error(f"Agent class <{agent_class_name}> not found.")
raise ValueError(f"Agent class <{agent_class_name}> not found.")
return cls._registry[agent_class_name] # type: ignore[return-value]

@classmethod
Expand Down Expand Up @@ -374,6 +375,7 @@ def to_dist(
max_timeout_seconds: int = 1800,
local_mode: bool = True,
lazy_launch: bool = True,
upload_source_code: bool = False,
launch_server: bool = None,
) -> AgentBase:
"""Convert current agent instance into a distributed version.
Expand All @@ -400,6 +402,12 @@ def to_dist(
Only takes effect when `host` and `port` are not filled in.
If `True`, launch the agent server when the agent is called,
otherwise, launch the agent server immediately.
upload_source_code (`bool`, defaults to `False`):
Upload the source code of the agent to the agent server.
Only takes effect when connecting to an existing server.
When you are using an agent that doens't exist on the server
(such as your customized agent that is not officially provided
by AgentScope), please set this value to `True`.
launch_server(`bool`, defaults to `None`):
This field has been deprecated and will be removed in
future releases.
Expand Down Expand Up @@ -429,4 +437,5 @@ def to_dist(
local_mode=local_mode,
lazy_launch=lazy_launch,
agent_id=self.agent_id,
upload_source_code=upload_source_code,
)
2 changes: 1 addition & 1 deletion src/agentscope/rpc/rpc_agent.proto
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ message StatusResponse {
message CreateAgentRequest {
string agent_id = 1;
bytes agent_init_args = 2;
string agent_source_code = 3;
bytes agent_source_code = 3;
}

message AgentIds {
Expand Down
67 changes: 51 additions & 16 deletions src/agentscope/rpc/rpc_agent_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
""" Client of rpc agent server """

import threading
import json
from typing import Optional, Sequence, Union
from loguru import logger

Expand Down Expand Up @@ -133,13 +134,12 @@ def create_agent(
with grpc.insecure_channel(f"{self.host}:{self.port}") as channel:
stub = RpcAgentStub(channel)
if upload_source_code:
import inspect
from agentscope.agents import AgentBase

agent_class = AgentBase.get_agent_class(
agent_configs["class_name"],
)
source_code = inspect.getsource(agent_class)
source_code = dill.dumps(agent_class)
else:
source_code = None
status = stub.create_agent(
Expand Down Expand Up @@ -211,30 +211,65 @@ def clone_agent(self, agent_id: str) -> Optional[str]:
return resp.agent_ids[0]

def update_placeholder(self, task_id: int) -> str:
"""Update the placeholder value."""
"""Update the placeholder value.
Args:
task_id (`int`): task_id of the PlaceholderMessage.
Returns:
str: serialized message value.
"""
with grpc.insecure_channel(f"{self.host}:{self.port}") as channel:
stub = RpcAgentStub(channel)
result_msg = stub.update_placeholder(
agent_pb2.UpdatePlaceholderRequest(task_id=task_id),
)
return result_msg.value

# def get_agent_id_list(self) -> Sequence[str]:
# """
# Get id of all agents on the server as a list.
# """
# pass
def get_agent_id_list(self, agent_id: str) -> Sequence[str]:
"""
Get id of all agents on the server as a list.
Returns:
Sequence[str]: list of agent_id
"""
with grpc.insecure_channel(f"{self.host}:{self.port}") as channel:
stub = RpcAgentStub(channel)
resp = stub.get_agent_id_list(
agent_pb2.AgentIds(agent_ids=[agent_id]),
)
return resp.agent_ids

def get_agent_info(self, agent_id: str = None) -> dict:
"""Get the agent information of the specific agent_id
# def get_agent_info(self, agent_id: str = None) -> dict:
# """Get the agent information of the specific agent_id
Args:
agent_id (`str`, optional): the id of the agent. Defaults to None.
# Args:
# agent_id (`str`, optional): the id of the agent. Defaults to None.
Returns:
`dict`: the information of the agent as a `dict`
"""
with grpc.insecure_channel(f"{self.host}:{self.port}") as channel:
stub = RpcAgentStub(channel)
resp = stub.get_agent_info(
agent_pb2.AgentIds(agent_ids=[agent_id]),
)
if not resp.ok:
logger.error(
f"Error in get_agent_info({agent_id}): {resp.message}",
)
return {}
return json.loads(resp.message)

# Returns:
# `dict`: the information of the agent as a `dict`
# """
# pass
def get_server_info(self) -> dict:
"""Get the agent server resource usage information."""
with grpc.insecure_channel(f"{self.host}:{self.port}") as channel:
stub = RpcAgentStub(channel)
resp = stub.get_server_info(Empty())
if not resp.ok:
logger.error(f"Error in get_server_info: {resp.message}")
return {}
return json.loads(resp.message)


class ResponseStub:
Expand Down
2 changes: 1 addition & 1 deletion src/agentscope/rpc/rpc_agent_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 2 additions & 8 deletions src/agentscope/server/servicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,11 @@ def create_agent(
)
agent_configs = dill.loads(request.agent_init_args)
if len(request.agent_source_code) > 0:
exec(request.agent_source_code, globals())
cls_name = (
request.agent_source_code.split("class ")[1]
.split(":")[0]
.split("(")[0]
.strip()
)
cls = dill.loads(request.agent_source_code)
cls_name = cls.__name__
logger.info(
f"Load class [{cls_name}] from uploaded source code.",
)
cls = globals()[cls_name]
else:
cls_name = agent_configs["class_name"]
cls = AgentBase.get_agent_class(cls_name)
Expand Down
41 changes: 41 additions & 0 deletions tests/rpc_agent_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,3 +607,44 @@ def test_agent_nesting(self) -> None:
self.assertTrue(0.5 < r2.content["time"] < 2)
launcher1.shutdown()
launcher2.shutdown()

def test_customized_agent(self) -> None:
"""Test customized agent"""
launcher = RpcAgentServerLauncher(
host="localhost",
port=12010,
local_mode=False,
)
# launch without customized agent
launcher.launch()

class CustomizedAgent(AgentBase):
"""A customized agent that not supported by agent server."""

def __init__( # type: ignore[no-untyped-def]
self,
**kwargs,
) -> None:
super().__init__(**kwargs)
self.id = 0

def reply(self, x: dict = None) -> dict:
return Msg(
name=self.name,
role="assistant",
content="Customized",
)

agent = CustomizedAgent(name="customized")
self.assertRaises(
Exception,
agent.to_dist,
host=launcher.host,
port=launcher.port,
)
agent = agent.to_dist(
host=launcher.host,
port=launcher.port,
upload_source_code=True,
)
launcher.shutdown()

0 comments on commit d82c4df

Please sign in to comment.