Skip to content
This repository has been archived by the owner on Sep 13, 2023. It is now read-only.

Commit

Permalink
Agent (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
Swiftyos authored Aug 31, 2023
2 parents 6ae6037 + 1c93114 commit edb50d8
Show file tree
Hide file tree
Showing 16 changed files with 542 additions and 1,867 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,5 @@ agbenchmark
.benchmarks
.mypy_cache
.pytest_cache
.vscode
.vscode
ig_*
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ repos:
rev: 5.12.0
hooks:
- id: isort
language_version: python3.10
language_version: python3.11

- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
language_version: python3.10
language_version: python3.11

# - repo: https://github.com/pre-commit/mirrors-mypy
# rev: 'v1.3.0'
Expand Down
4 changes: 1 addition & 3 deletions autogpt/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
load_dotenv()
import autogpt.sdk.forge_log

ENABLE_TRACING = os.environ.get("ENABLE_TRACING", "false").lower() == "true"

autogpt.sdk.forge_log.setup_logger()


LOG = autogpt.sdk.forge_log.CustomLogger(__name__)
LOG = autogpt.sdk.forge_log.ForgeLogger(__name__)

if __name__ == "__main__":
"""Runs the agent server"""
Expand Down
110 changes: 102 additions & 8 deletions autogpt/agent.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,106 @@
import autogpt.sdk.agent
from autogpt.sdk.schema import Step, StepRequestBody
from autogpt.sdk import Agent, AgentDB, Step, StepRequestBody, Workspace


class AutoGPTAgent(autogpt.sdk.agent.Agent):
async def create_and_execute_step(
self, task_id: str, step_request: StepRequestBody
) -> Step:
class AutoGPTAgent(Agent):
"""
The goal of the Forge is to take care of the boilerplate code so you can focus on
agent design.
There is a great paper surveying the agent landscape: https://arxiv.org/abs/2308.11432
Which I would highly recommend reading as it will help you understand the possabilities.
Here is a summary of the key components of an agent:
Anatomy of an agent:
- Profile
- Memory
- Planning
- Action
Profile:
Agents typically perform a task by assuming specific roles. For example, a teacher,
a coder, a planner etc. In using the profile in the llm prompt it has been shown to
improve the quality of the output. https://arxiv.org/abs/2305.14688
Additionally baed on the profile selected, the agent could be configured to use a
different llm. The possabilities are endless and the profile can be selected selected
dynamically based on the task at hand.
Memory:
Memory is critical for the agent to acculmulate experiences, self-evolve, and behave
in a more consistent, reasonable, and effective manner. There are many approaches to
memory. However, some thoughts: there is long term and short term or working memory.
You may want different approaches for each. There has also been work exploring the
idea of memory reflection, which is the ability to assess its memories and re-evaluate
them. For example, condensting short term memories into long term memories.
Planning:
When humans face a complex task, they first break it down into simple subtasks and then
solve each subtask one by one. The planning module empowers LLM-based agents with the ability
to think and plan for solving complex tasks, which makes the agent more comprehensive,
powerful, and reliable. The two key methods to consider are: Planning with feedback and planning
without feedback.
Action:
Actions translate the agents decisions into specific outcomes. For example, if the agent
decides to write a file, the action would be to write the file. There are many approaches you
could implement actions.
The Forge has a basic module for each of these areas. However, you are free to implement your own.
This is just a starting point.
"""

def __init__(self, database: AgentDB, workspace: Workspace):
"""
The database is used to store tasks, steps and artifact metadata. The workspace is used to
store artifacts. The workspace is a directory on the file system.
Feel free to create subclasses of the database and workspace to implement your own storage
"""
Create a step for the task and execute it.
super().__init__(database, workspace)

async def execute_step(self, task_id: str, step_request: StepRequestBody) -> Step:
"""
The agent protocol, which is the core of the Forge, works by creating a task and then
executing steps for that task. This method is called when the agent is asked to execute
a step.
The task that is created contains an input string, for the bechmarks this is the task
the agent has been asked to solve and additional input, which is a dictionary and
could contain anything.
If you want to get the task use:
```
task = await self.db.get_task(task_id)
```
The step request body is essentailly the same as the task request and contains an input
string, for the bechmarks this is the task the agent has been asked to solve and
additional input, which is a dictionary and could contain anything.
You need to implement logic that will take in this step input and output the completed step
as a step object. You can do everything in a single step or you can break it down into
multiple steps. Returning a request to continue in the step output, the user can then decide
if they want the agent to continue or not.
"""
return await super().create_and_execute_step(task_id, step_request)

# An example that
self.workspace.write(task_id=task_id, path="output.txt", data=b"Washington D.C")
step = await self.db.create_step(
task_id=task_id, input=step_request, is_last=True
)
artifact = await self.db.create_artifact(
task_id=task_id,
step_id=step.step_id,
file_name="output.txt",
relative_path="",
agent_created=True,
)
step.output = "Washington D.C"

return step
24 changes: 24 additions & 0 deletions autogpt/sdk/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
The Forge SDK. This is the core of the Forge. It contains the agent protocol, which is the
core of the Forge.
"""
from .agent import Agent
from .db import AgentDB
from .forge_log import ForgeLogger
from .schema import (
Artifact,
ArtifactUpload,
Pagination,
Status,
Step,
StepInput,
StepOutput,
StepRequestBody,
Task,
TaskArtifactsListResponse,
TaskInput,
TaskListResponse,
TaskRequestBody,
TaskStepsListResponse,
)
from .workspace import LocalWorkspace, Workspace
71 changes: 7 additions & 64 deletions autogpt/sdk/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@
import os
from uuid import uuid4

from fastapi import APIRouter, FastAPI, Response, UploadFile
from fastapi import APIRouter, FastAPI, UploadFile
from fastapi.responses import FileResponse
from hypercorn.asyncio import serve
from hypercorn.config import Config
from prometheus_fastapi_instrumentator import Instrumentator

from .db import AgentDB
from .errors import NotFoundError
from .forge_log import CustomLogger
from .forge_log import ForgeLogger
from .middlewares import AgentMiddleware
from .routes.agent_protocol import base_router
from .schema import *
from .tracing import setup_tracing
from .utils import run
from .workspace import Workspace

LOG = CustomLogger(__name__)
LOG = ForgeLogger(__name__)


class Agent:
Expand All @@ -38,17 +35,8 @@ def start(self, port: int = 8000, router: APIRouter = base_router):
version="v0.4",
)

# Add Prometheus metrics to the agent
# https://github.com/trallnag/prometheus-fastapi-instrumentator
instrumentator = Instrumentator().instrument(app)

@app.on_event("startup")
async def _startup():
instrumentator.expose(app)

app.include_router(router)
app.add_middleware(AgentMiddleware, agent=self)
setup_tracing(app)
config.loglevel = "ERROR"
config.bind = [f"0.0.0.0:{port}"]

Expand Down Expand Up @@ -102,54 +90,11 @@ async def list_steps(
except Exception as e:
raise

async def create_and_execute_step(
self, task_id: str, step_request: StepRequestBody
) -> Step:
async def execute_step(self, task_id: str, step_request: StepRequestBody) -> Step:
"""
Create a step for the task.
"""
if step_request.input != "y":
step = await self.db.create_step(
task_id=task_id,
input=step_request,
additional_input=step_request.additional_input,
)
# utils.run
artifacts = run(step.input)
for artifact in artifacts:
art = await self.db.create_artifact(
task_id=step.task_id,
file_name=artifact["file_name"],
uri=artifact["uri"],
agent_created=True,
step_id=step.step_id,
)
assert isinstance(
art, Artifact
), f"Artifact not instance of Artifact {type(art)}"
step.artifacts.append(art)
step.status = "completed"
else:
steps, steps_pagination = await self.db.list_steps(
task_id, page=1, per_page=100
)
# Find the latest step that has not been completed
step = next((s for s in reversed(steps) if s.status != "completed"), None)
if step is None:
# If all steps have been completed, create a new placeholder step
step = await self.db.create_step(
task_id=task_id,
input="y",
additional_input={},
)
step.status = "completed"
step.is_last = True
step.output = "No more steps to run."
step = await self.db.update_step(step)
if isinstance(step.status, Status):
step.status = step.status.value
step.output = "Done some work"
return step
raise NotImplementedError

async def get_step(self, task_id: str, step_id: str) -> Step:
"""
Expand All @@ -171,10 +116,8 @@ async def list_artifacts(
artifacts, pagination = await self.db.list_artifacts(
task_id, page, pageSize
)
response = TaskArtifactsListResponse(
artifacts=artifacts, pagination=pagination
)
return Response(content=response.json(), media_type="application/json")
return TaskArtifactsListResponse(artifacts=artifacts, pagination=pagination)

except Exception as e:
raise

Expand Down
10 changes: 7 additions & 3 deletions autogpt/sdk/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
from sqlalchemy.orm import DeclarativeBase, joinedload, relationship, sessionmaker

from .errors import NotFoundError
from .forge_log import CustomLogger
from .forge_log import ForgeLogger
from .schema import Artifact, Pagination, Status, Step, StepRequestBody, Task, TaskInput

LOG = CustomLogger(__name__)
LOG = ForgeLogger(__name__)


class Base(DeclarativeBase):
Expand Down Expand Up @@ -218,7 +218,11 @@ async def create_artifact(
with self.Session() as session:
if (
existing_artifact := session.query(ArtifactModel)
.filter_by(relative_path=relative_path)
.filter_by(
task_id=task_id,
file_name=file_name,
relative_path=relative_path,
)
.first()
):
session.close()
Expand Down
5 changes: 2 additions & 3 deletions autogpt/sdk/forge_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import os
import queue

ENABLE_TRACING = os.environ.get("ENABLE_TRACING", "false").lower() == "true"
JSON_LOGGING = os.environ.get("JSON_LOGGING", "false").lower() == "true"

CHAT = 29
Expand Down Expand Up @@ -101,7 +100,7 @@ def format(self, record: logging.LogRecord) -> str:
return logging.Formatter.format(self, rec)


class CustomLogger(logging.Logger):
class ForgeLogger(logging.Logger):
"""
This adds extra logging functions such as logger.trade and also
sets the logger to use the custom formatter
Expand Down Expand Up @@ -173,7 +172,7 @@ def __init__(self, name: str, level: int = logging.NOTSET):
formatters={
"console": {
"()": ConsoleFormatter,
"format": CustomLogger.COLOR_FORMAT,
"format": ForgeLogger.COLOR_FORMAT,
},
},
handlers={
Expand Down
Loading

0 comments on commit edb50d8

Please sign in to comment.