This repository has been archived by the owner on Sep 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added in protocol changes and a method to keep it synced whilst we wa…
…it for the agent-protocol to be updated
- Loading branch information
Showing
16 changed files
with
966 additions
and
137 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 |
---|---|---|
@@ -0,0 +1,22 @@ | ||
.PHONY: update-protocol | ||
|
||
update-protocol: | ||
@if [ -d "../agent-protocol/sdk/python/agent_protocol" ]; then \ | ||
cp -r ../agent-protocol/sdk/python/agent_protocol autogpt; \ | ||
rm -Rf autogpt/agent_protocol/utils; \ | ||
rm -Rf autogpt/agent_protocol/cli.py; \ | ||
echo "Protocol updated successfully!"; \ | ||
else \ | ||
echo "Error: Source directory ../agent-protocol/sdk/python/agent_protocol does not exist."; \ | ||
exit 1; \ | ||
fi | ||
|
||
change-protocol: | ||
@if [ -d "autogpt/agent_protocol" ]; then \ | ||
cp -r autogpt/agent_protocol ../agent-protocol/sdk/python; \ | ||
rm ../agent-protocol/sdk/python/agent_protocol/README.md; \ | ||
echo "Protocol reversed successfully!"; \ | ||
else \ | ||
echo "Error: Target directory autogpt/agent_protocol does not exist."; \ | ||
exit 1; \ | ||
fi |
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
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 |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Autogpt Protocol Directory | ||
|
||
# DO NOT MODIFY ANY FILES IN THIS DIRECTORY | ||
|
||
This directory contains protocol definitions crucial for our project. The current setup is a temporary measure to allow for speedy updating of the protocol. | ||
|
||
## Background | ||
|
||
In an ideal scenario, we'd directly use a submodule pointing to the original repository. However, given our specific needs and to expedite our development process, we've chosen a slightly different approach. | ||
|
||
## Process | ||
|
||
1. **Fork and Clone**: We started by forking the original repository `e2b-dev/agent-protocol` (not `Swiftyos/agent-protocol` as previously mentioned) to have our own version. This allows us to have more control over updates and possibly any specific changes that our project might need in the future. | ||
|
||
2. **Manual Content Integration**: Instead of adding the entire forked repository as a submodule, we've manually copied over the contents of `sdk/python/agent_protocol` into this directory. This ensures we only have the parts we need, without any additional overhead. | ||
|
||
3. **Updates**: Any necessary updates to the protocol can be made directly in our fork, and subsequently, the required changes can be reflected in this directory. | ||
|
||
## Credits | ||
|
||
All credit for the original protocol definitions goes to [e2b-dev/agent-protocol](https://github.com/e2b-dev/agent-protocol). We deeply appreciate their efforts in building the protocol, and this temporary measure is in no way intended to diminish the significance of their work. It's purely a practical approach for our specific requirements at this point in our development phase. |
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 |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from .agent import Agent | ||
from .agent import base_router as router | ||
from .db import Step, Task, TaskDB | ||
from .models import Artifact, Status, StepRequestBody, TaskRequestBody | ||
|
||
__all__ = [ | ||
"Agent", | ||
"Artifact", | ||
"Status", | ||
"Step", | ||
"StepRequestBody", | ||
"Task", | ||
"TaskDB", | ||
"TaskRequestBody", | ||
"router", | ||
] |
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 |
---|---|---|
@@ -0,0 +1,240 @@ | ||
import asyncio | ||
import os | ||
import tempfile | ||
from typing import List | ||
from uuid import uuid4 | ||
|
||
from fastapi import APIRouter, Request, Response, UploadFile | ||
from fastapi.responses import FileResponse, Response | ||
from hypercorn.asyncio import serve | ||
from hypercorn.config import Config | ||
|
||
from .db import Step, Task, TaskDB | ||
from .middlewares import AgentMiddleware | ||
from .models import Artifact, Status, StepRequestBody, TaskRequestBody | ||
from .server import app | ||
|
||
base_router = APIRouter() | ||
|
||
|
||
@base_router.get("/heartbeat") | ||
async def heartbeat() -> Response: | ||
""" | ||
Heartbeat endpoint to check if the server is running. | ||
""" | ||
return Response(status_code=200) | ||
|
||
|
||
@base_router.post("/agent/tasks", response_model=Task, tags=["agent"]) | ||
async def create_agent_task( | ||
request: Request, body: TaskRequestBody | None = None | ||
) -> Task: | ||
""" | ||
Creates a task for the agent. | ||
""" | ||
agent: Agent = request["agent"] | ||
|
||
task = await agent.db.create_task( | ||
input=body.input if body else None, | ||
additional_input=body.additional_input if body else None, | ||
) | ||
await agent.create_task(task) | ||
|
||
return task | ||
|
||
|
||
@base_router.get("/agent/tasks", response_model=List[str], tags=["agent"]) | ||
async def list_agent_tasks_ids(request: Request) -> List[str]: | ||
""" | ||
List all tasks that have been created for the agent. | ||
""" | ||
agent: Agent = request["agent"] | ||
return [task.task_id for task in await agent.db.list_tasks()] | ||
|
||
|
||
@base_router.get("/agent/tasks/{task_id}", response_model=Task, tags=["agent"]) | ||
async def get_agent_task(request: Request, task_id: str) -> Task: | ||
""" | ||
Get details about a specified agent task. | ||
""" | ||
agent: Agent = request["agent"] | ||
return await agent.db.get_task(task_id) | ||
|
||
|
||
@base_router.get( | ||
"/agent/tasks/{task_id}/steps", | ||
response_model=List[str], | ||
tags=["agent"], | ||
) | ||
async def list_agent_task_steps(request: Request, task_id: str) -> List[str]: | ||
""" | ||
List all steps for the specified task. | ||
""" | ||
agent: Agent = request["agent"] | ||
task = await agent.db.get_task(task_id) | ||
return [s.step_id for s in task.steps] | ||
|
||
|
||
@base_router.post( | ||
"/agent/tasks/{task_id}/steps", | ||
response_model=Step, | ||
tags=["agent"], | ||
) | ||
async def execute_agent_task_step( | ||
request: Request, | ||
task_id: str, | ||
body: StepRequestBody | None = None, | ||
) -> Step: | ||
""" | ||
Execute a step in the specified agent task. | ||
""" | ||
agent: Agent = request["agent"] | ||
|
||
task = await agent.db.get_task(task_id) | ||
step = next(filter(lambda x: x.status == Status.created, task.steps), None) | ||
|
||
if not step: | ||
raise Exception("No steps to execute") | ||
|
||
step.status = Status.running | ||
|
||
step.input = body.input if body else None | ||
step.additional_input = body.additional_input if body else None | ||
|
||
step = await agent.run_step(step) | ||
|
||
step.status = Status.completed | ||
return step | ||
|
||
|
||
@base_router.get( | ||
"/agent/tasks/{task_id}/steps/{step_id}", | ||
response_model=Step, | ||
tags=["agent"], | ||
) | ||
async def get_agent_task_step(request: Request, task_id: str, step_id: str) -> Step: | ||
""" | ||
Get details about a specified task step. | ||
""" | ||
agent: Agent = request["agent"] | ||
return await agent.db.get_step(task_id, step_id) | ||
|
||
|
||
@base_router.get( | ||
"/agent/tasks/{task_id}/artifacts", | ||
response_model=List[Artifact], | ||
tags=["agent"], | ||
) | ||
async def list_agent_task_artifacts(request: Request, task_id: str) -> List[Artifact]: | ||
""" | ||
List all artifacts for the specified task. | ||
""" | ||
agent: Agent = request["agent"] | ||
task = await agent.db.get_task(task_id) | ||
return task.artifacts | ||
|
||
|
||
@base_router.post( | ||
"/agent/tasks/{task_id}/artifacts", | ||
response_model=Artifact, | ||
tags=["agent"], | ||
) | ||
async def upload_agent_task_artifacts( | ||
request: Request, | ||
task_id: str, | ||
file: UploadFile | None = None, | ||
uri: str | None = None, | ||
) -> Artifact: | ||
""" | ||
Upload an artifact for the specified task. | ||
""" | ||
agent: Agent = request["agent"] | ||
if not file and not uri: | ||
return Response(status_code=400, content="No file or uri provided") | ||
|
||
if uri: | ||
artifact = Artifact( | ||
task_id=task_id, | ||
file_name=uri.spit("/")[-1], | ||
uri=uri, | ||
) | ||
else: | ||
file_name = file.filename or str(uuid4()) | ||
try: | ||
data = b"" | ||
while contents := file.file.read(1024 * 1024): | ||
data += contents | ||
except Exception as e: | ||
return Response(status_code=500, content=str(e)) | ||
artifact = Artifact( | ||
task_id=task_id, | ||
file_name=file_name, | ||
data=contents, | ||
) | ||
|
||
artifact = await agent.save_artifact(task_id, artifact) | ||
agent.db.add_artifact(task_id, artifact) | ||
|
||
return artifact | ||
|
||
|
||
@base_router.get( | ||
"/agent/tasks/{task_id}/artifacts/{artifact_id}", | ||
tags=["agent"], | ||
) | ||
async def download_agent_task_artifacts( | ||
request: Request, task_id: str, artifact_id: str | ||
) -> FileResponse: | ||
""" | ||
Download the specified artifact. | ||
""" | ||
agent: Agent = request["agent"] | ||
artifact = await agent.db.get_artifact(task_id, artifact_id) | ||
retrieved_artifact: Artifact = agent.retrieve_artifact(task_id, artifact) | ||
with tempfile.TemporaryDirectory() as tmpdir: | ||
path = os.path.join(tmpdir, artifact.file_name) | ||
with open(path, "wb") as f: | ||
f.write(retrieved_artifact.data) | ||
return FileResponse( | ||
# Note: mimetype is guessed in the FileResponse constructor | ||
path=path, | ||
filename=artifact.file_name, | ||
) | ||
|
||
|
||
class Agent: | ||
def __init__(self, db: TaskDB): | ||
self.name = self.__class__.__name__ | ||
self.db = db | ||
|
||
def start(self, port: int = 8000, router: APIRouter = base_router): | ||
""" | ||
Start the agent server. | ||
""" | ||
config = Config() | ||
config.bind = [f"localhost:{port}"] # As an example configuration setting | ||
app.title = f"{self.name} - Agent Communication Protocol" | ||
app.include_router(router) | ||
app.add_middleware(AgentMiddleware, agent=self) | ||
asyncio.run(serve(app, config)) | ||
|
||
async def create_task(self, task: Task): | ||
""" | ||
Handles a new task | ||
""" | ||
return task | ||
|
||
async def run_step(self, step: Step) -> Step: | ||
return step | ||
|
||
async def retrieve_artifact(self, task_id: str, artifact: Artifact) -> Artifact: | ||
""" | ||
Retrieve the artifact data from wherever it is stored and return it as bytes. | ||
""" | ||
return artifact | ||
|
||
async def save_artifact(self, task_id: str, artifact: Artifact) -> Artifact: | ||
""" | ||
Save the artifact data to the agent's workspace, loading from uri if bytes are not available. | ||
""" | ||
return artifact |
Oops, something went wrong.