autogen/python/samples/demos/software_consultancy.py
Eric Zhu ed0890525d Make RunContext internal (#386)
* Make RunContext internal

* Mypy
2024-08-21 13:59:59 -07:00

306 lines
13 KiB
Python

"""This is an example demonstrates event-driven orchestration using a
group chat manager agnent.
WARNING: do not run this example in your local machine as it involves
executing arbitrary code. Use a secure environment like a docker container
or GitHub Codespaces to run this example.
"""
import argparse
import asyncio
import base64
import logging
import os
import sys
import aiofiles
import aiohttp
import openai
from agnext.application import SingleThreadedAgentRuntime
from agnext.components.models import SystemMessage
from agnext.components.tools import FunctionTool
from agnext.core import AgentInstantiationContext, AgentRuntime
from markdownify import markdownify # type: ignore
from tqdm import tqdm
from typing_extensions import Annotated
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from agnext.core import AgentId
from common.agents import ChatCompletionAgent
from common.memory import HeadAndTailChatMemory
from common.patterns._group_chat_manager import GroupChatManager
from common.utils import get_chat_completion_client_from_envs
from utils import TextualChatApp, TextualUserAgent
async def write_file(filename: str, content: str) -> str:
async with aiofiles.open(filename, "w") as file:
await file.write(content)
return f"Content written to {filename}."
async def execute_command(command: str) -> Annotated[str, "The standard output and error of the executed command."]:
process = await asyncio.subprocess.create_subprocess_shell(
command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await process.communicate()
return f"stdout: {stdout.decode()}\nstderr: {stderr.decode()}"
async def read_file(filename: str) -> Annotated[str, "The content of the file."]:
async with aiofiles.open(filename, "r") as file:
return await file.read()
async def remove_file(filename: str) -> str:
process = await asyncio.subprocess.create_subprocess_exec("rm", filename)
await process.wait()
if process.returncode != 0:
raise ValueError(f"Error occurred while removing file: {filename}")
return f"File removed: {filename}."
async def list_files(directory: str) -> Annotated[str, "The list of files in the directory."]:
# Ask for confirmation first.
# await confirm(f"Are you sure you want to list files in {directory}?")
process = await asyncio.subprocess.create_subprocess_exec(
"ls",
directory,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await process.communicate()
if stderr:
raise ValueError(f"Error occurred while listing files: {stderr.decode()}")
return stdout.decode()
async def browse_web(url: str) -> Annotated[str, "The content of the web page in Markdown format."]:
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
html = await response.text()
markdown = markdownify(html) # type: ignore
if isinstance(markdown, str):
return markdown
return f"Unable to parse content from {url}."
async def create_image(
description: Annotated[str, "Describe the image to create"],
filename: Annotated[str, "The path to save the created image"],
) -> str:
# Use Dalle to generate an image from the description.
with tqdm(desc="Generating image...", leave=False) as pbar:
client = openai.AsyncClient()
response = await client.images.generate(model="dall-e-2", prompt=description, response_format="b64_json")
pbar.close()
assert len(response.data) > 0 and response.data[0].b64_json is not None
# Save the image to a file.
async with aiofiles.open(filename, "wb") as file:
image_data = base64.b64decode(response.data[0].b64_json)
await file.write(image_data)
return f"Image created and saved to {filename}."
async def software_consultancy(runtime: AgentRuntime, app: TextualChatApp) -> None: # type: ignore
await runtime.register(
"Customer",
lambda: TextualUserAgent(
description="A customer looking for help.",
app=app,
),
)
await runtime.register(
"Developer",
lambda: ChatCompletionAgent(
description="A Python software developer.",
system_messages=[
SystemMessage(
"Your are a Python developer. \n"
"You can read, write, and execute code. \n"
"You can browse files and directories. \n"
"You can also browse the web for documentation. \n"
"You are entering a work session with the customer, product manager, UX designer, and illustrator. \n"
"When you are given a task, you should immediately start working on it. \n"
"Be concise and deliver now."
)
],
model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
tools=[
FunctionTool(
write_file,
name="write_file",
description="Write code to a file.",
),
FunctionTool(
read_file,
name="read_file",
description="Read code from a file.",
),
FunctionTool(
execute_command,
name="execute_command",
description="Execute a unix shell command.",
),
FunctionTool(list_files, name="list_files", description="List files in a directory."),
FunctionTool(browse_web, name="browse_web", description="Browse a web page."),
],
tool_approver=AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
),
)
await runtime.register(
"ProductManager",
lambda: ChatCompletionAgent(
description="A product manager. "
"Responsible for interfacing with the customer, planning and managing the project. ",
system_messages=[
SystemMessage(
"You are a product manager. \n"
"You can browse files and directories. \n"
"You are entering a work session with the customer, developer, UX designer, and illustrator. \n"
"Keep the project on track. Don't hire any more people. \n"
"When a milestone is reached, stop and ask for customer feedback. Make sure the customer is satisfied. \n"
"Be VERY concise."
)
],
model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
tools=[
FunctionTool(
read_file,
name="read_file",
description="Read from a file.",
),
FunctionTool(list_files, name="list_files", description="List files in a directory."),
FunctionTool(browse_web, name="browse_web", description="Browse a web page."),
],
tool_approver=AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
),
)
await runtime.register(
"UserExperienceDesigner",
lambda: ChatCompletionAgent(
description="A user experience designer for creating user interfaces.",
system_messages=[
SystemMessage(
"You are a user experience designer. \n"
"You can create user interfaces from descriptions. \n"
"You can browse files and directories. \n"
"You are entering a work session with the customer, developer, product manager, and illustrator. \n"
"When you are given a task, you should immediately start working on it. \n"
"Be concise and deliver now."
)
],
model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
tools=[
FunctionTool(
write_file,
name="write_file",
description="Write code to a file.",
),
FunctionTool(
read_file,
name="read_file",
description="Read code from a file.",
),
FunctionTool(list_files, name="list_files", description="List files in a directory."),
],
tool_approver=AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
),
)
await runtime.register(
"Illustrator",
lambda: ChatCompletionAgent(
description="An illustrator for creating images.",
system_messages=[
SystemMessage(
"You are an illustrator. "
"You can create images from descriptions. "
"You are entering a work session with the customer, developer, product manager, and UX designer. \n"
"When you are given a task, you should immediately start working on it. \n"
"Be concise and deliver now."
)
],
model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
tools=[
FunctionTool(
create_image,
name="create_image",
description="Create an image from a description.",
),
],
tool_approver=AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
),
)
await runtime.register(
"GroupChatManager",
lambda: GroupChatManager(
description="A group chat manager.",
memory=HeadAndTailChatMemory(head_size=1, tail_size=10),
model_client=get_chat_completion_client_from_envs(model="gpt-4-turbo"),
participants=[
AgentId("Developer", AgentInstantiationContext.current_agent_id().key),
AgentId("ProductManager", AgentInstantiationContext.current_agent_id().key),
AgentId("UserExperienceDesigner", AgentInstantiationContext.current_agent_id().key),
AgentId("Illustrator", AgentInstantiationContext.current_agent_id().key),
AgentId("Customer", AgentInstantiationContext.current_agent_id().key),
],
),
)
art = r"""
+----------------------------------------------------------+
| ____ __ _ |
| / ___| ___ / _| |___ ____ _ _ __ ___ |
| \___ \ / _ \| |_| __\ \ /\ / / _` | '__/ _ \ |
| ___) | (_) | _| |_ \ V V / (_| | | | __/ |
| |____/ \___/|_| \__| \_/\_/ \__,_|_| \___| |
| |
| ____ _ _ |
| / ___|___ _ __ ___ _ _| | |_ __ _ _ __ ___ _ _ |
| | | / _ \| '_ \/ __| | | | | __/ _` | '_ \ / __| | | | |
| | |__| (_) | | | \__ \ |_| | | || (_| | | | | (__| |_| | |
| \____\___/|_| |_|___/\__,_|_|\__\__,_|_| |_|\___|\__, | |
| |___/ |
| |
| Work with a software development consultancy to create |
| your own Python application. You are working with a team |
| of the following agents: |
| 1. 🤖 Developer: A Python software developer. |
| 2. 🤖 ProductManager: A product manager. |
| 3. 🤖 UserExperienceDesigner: A user experience designer. |
| 4. 🤖 Illustrator: An illustrator. |
+----------------------------------------------------------+
"""
app.welcoming_notice = art
async def main() -> None:
runtime = SingleThreadedAgentRuntime()
app = TextualChatApp(runtime, user_name="You")
await software_consultancy(runtime, app)
# Start the runtime.
runtime.start()
# Start the app.
await app.run_async()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Software consultancy demo.")
parser.add_argument("--verbose", action="store_true", help="Enable verbose logging.")
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.WARNING)
logging.getLogger("agnext").setLevel(logging.DEBUG)
handler = logging.FileHandler("software_consultancy.log")
logging.getLogger("agnext").addHandler(handler)
asyncio.run(main())