Skip to content

Commit

Permalink
Create AgentFileManager
Browse files Browse the repository at this point in the history
* Remove references to (broken) vector memory

* Move workspace setup to `WorkspaceMixin.attach_fs` hook

* Move directives into `BaseAgentSettings`
  • Loading branch information
Pwuts committed Oct 8, 2023
1 parent 2b8d91f commit 34352af
Show file tree
Hide file tree
Showing 19 changed files with 191 additions and 168 deletions.
10 changes: 4 additions & 6 deletions autogpts/autogpt/agbenchmark_config/benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
from autogpt.commands import COMMAND_CATEGORIES
from autogpt.config import AIProfile, ConfigBuilder
from autogpt.logs.config import configure_logging
from autogpt.memory.vector import get_memory
from autogpt.models.command_registry import CommandRegistry
from autogpt.workspace import Workspace

LOG_DIR = Path(__file__).parent / "logs"

Expand All @@ -28,8 +26,6 @@ def bootstrap_agent(task: str, continuous_mode: bool) -> Agent:
config.noninteractive_mode = True
config.plain_output = True
config.memory_backend = "no_memory"
config.workspace_path = Workspace.init_workspace_directory(config)
config.file_logger_path = Workspace.build_file_logger_path(config.workspace_path)

configure_logging(
debug_mode=config.debug_mode,
Expand All @@ -54,20 +50,22 @@ def bootstrap_agent(task: str, continuous_mode: bool) -> Agent:
config=AgentConfiguration(
fast_llm=config.fast_llm,
smart_llm=config.smart_llm,
allow_fs_access=not config.restrict_to_workspace,
use_functions_api=config.openai_functions,
plugins=config.plugins,
),
prompt_config=agent_prompt_config,
history=Agent.default_settings.history.copy(deep=True),
)

return Agent(
agent = Agent(
settings=agent_settings,
llm_provider=_configure_openai_provider(config),
command_registry=command_registry,
memory=get_memory(config),
legacy_config=config,
)
agent.attach_fs(config.app_data_dir / "agents" / "AutoGPT-benchmark") # HACK
return agent


if __name__ == "__main__":
Expand Down
20 changes: 8 additions & 12 deletions autogpts/autogpt/autogpt/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

if TYPE_CHECKING:
from autogpt.config import Config
from autogpt.memory.vector import VectorMemory
from autogpt.models.command_registry import CommandRegistry

from autogpt.config import AIProfile
from pydantic import Field

from autogpt.core.configuration import Configurable
from autogpt.core.prompting import ChatPrompt
from autogpt.core.resource.model_providers import (
Expand Down Expand Up @@ -54,8 +54,12 @@ class AgentConfiguration(BaseAgentConfiguration):


class AgentSettings(BaseAgentSettings):
config: AgentConfiguration
prompt_config: OneShotAgentPromptConfiguration
config: AgentConfiguration = Field(default_factory=AgentConfiguration)
prompt_config: OneShotAgentPromptConfiguration = Field(
default_factory=(
lambda: OneShotAgentPromptStrategy.default_configuration.copy(deep=True)
)
)


class Agent(
Expand All @@ -70,18 +74,13 @@ class Agent(
default_settings: AgentSettings = AgentSettings(
name="Agent",
description=__doc__,
ai_profile=AIProfile(ai_name="AutoGPT"),
config=AgentConfiguration(),
prompt_config=OneShotAgentPromptStrategy.default_configuration,
history=BaseAgent.default_settings.history,
)

def __init__(
self,
settings: AgentSettings,
llm_provider: ChatModelProvider,
command_registry: CommandRegistry,
memory: VectorMemory,
legacy_config: Config,
):
prompt_strategy = OneShotAgentPromptStrategy(
Expand All @@ -96,9 +95,6 @@ def __init__(
legacy_config=legacy_config,
)

self.memory = memory
"""VectorMemoryProvider used to manage the agent's context (TODO)"""

self.created_at = datetime.now().strftime("%Y%m%d_%H%M%S")
"""Timestamp the agent was created; only used for structured debug logging."""

Expand Down
55 changes: 39 additions & 16 deletions autogpts/autogpt/autogpt/agents/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import logging
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Literal, Optional
from typing import TYPE_CHECKING, Any, Optional

from auto_gpt_plugin_template import AutoGPTPluginTemplate
from pydantic import Field, validator

if TYPE_CHECKING:
from pathlib import Path

from autogpt.config import Config
from autogpt.core.prompting.base import PromptStrategy
from autogpt.core.resource.model_providers.schema import (
Expand All @@ -18,6 +20,7 @@
from autogpt.models.command_registry import CommandRegistry

from autogpt.agents.utils.prompt_scratchpad import PromptScratchpad
from autogpt.config import ConfigBuilder
from autogpt.config.ai_profile import AIProfile
from autogpt.config.ai_directives import AIDirectives
from autogpt.core.configuration import (
Expand All @@ -40,6 +43,8 @@
from autogpt.models.action_history import ActionResult, EpisodicActionHistory
from autogpt.prompts.prompt import DEFAULT_TRIGGERING_PROMPT

from .utils.agent_file_manager import AgentFileManager

logger = logging.getLogger(__name__)

CommandName = str
Expand All @@ -48,6 +53,8 @@


class BaseAgentConfiguration(SystemConfiguration):
allow_fs_access: bool = UserConfigurable(default=False)

fast_llm: OpenAIModelName = UserConfigurable(default=OpenAIModelName.GPT3_16k)
smart_llm: OpenAIModelName = UserConfigurable(default=OpenAIModelName.GPT4)
use_functions_api: bool = UserConfigurable(default=False)
Expand Down Expand Up @@ -118,27 +125,33 @@ def validate_openai_functions(cls, v: bool, values: dict[str, Any]):


class BaseAgentSettings(SystemSettings):
agent_data_dir: Optional[Path] = None

ai_profile: AIProfile = Field(default_factory=lambda: AIProfile(ai_name="AutoGPT"))
"""The AIProfile or "personality" of this agent."""
"""The AI profile or "personality" of the agent."""

config: BaseAgentConfiguration
directives: AIDirectives = Field(
default_factory=lambda: AIDirectives.from_file(
ConfigBuilder.default_settings.prompt_settings_file
)
)
"""Directives (general instructional guidelines) for the agent."""

config: BaseAgentConfiguration = Field(default_factory=BaseAgentConfiguration)
"""The configuration for this BaseAgent subsystem instance."""

history: EpisodicActionHistory
history: EpisodicActionHistory = Field(default_factory=EpisodicActionHistory)
"""(STATE) The action history of the agent."""


class BaseAgent(Configurable[BaseAgentSettings], ABC):
"""Base class for all AutoGPT agent classes."""

ThoughtProcessID = Literal["one-shot"]
ThoughtProcessOutput = tuple[CommandName, CommandArgs, AgentThoughts]

default_settings = BaseAgentSettings(
name="BaseAgent",
description=__doc__,
config=BaseAgentConfiguration(),
history=EpisodicActionHistory(),
)

def __init__(
Expand All @@ -149,8 +162,20 @@ def __init__(
command_registry: CommandRegistry,
legacy_config: Config,
):
self.state = settings
self.config = settings.config
self.ai_profile = settings.ai_profile
self.ai_directives = AIDirectives.from_file(legacy_config.prompt_settings_file)
self.ai_directives = settings.directives
self.event_history = settings.history

self.legacy_config = legacy_config
"""LEGACY: Monolithic application configuration."""

self.file_manager = (
AgentFileManager(settings.agent_data_dir)
if settings.agent_data_dir
else None
)

self.llm_provider = llm_provider

Expand All @@ -159,21 +184,19 @@ def __init__(
self.command_registry = command_registry
"""The registry containing all commands available to the agent."""

self.llm_provider = llm_provider

self.legacy_config = legacy_config
self.config = settings.config
"""The applicable application configuration."""

self.event_history = settings.history

self._prompt_scratchpad: PromptScratchpad | None = None

# Support multi-inheritance and mixins for subclasses
super(BaseAgent, self).__init__()

logger.debug(f"Created {__class__} '{self.ai_profile.ai_name}'")

def attach_fs(self, agent_dir: Path) -> AgentFileManager:
self.file_manager = AgentFileManager(agent_dir)
self.file_manager.initialize()
self.state.agent_data_dir = agent_dir
return self.file_manager

@property
def llm(self) -> ChatModelInfo:
"""The LLM that the agent uses to think."""
Expand Down
38 changes: 28 additions & 10 deletions autogpts/autogpt/autogpt/agents/features/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,51 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path

from ..base import BaseAgent

from autogpt.config import Config
from autogpt.workspace import Workspace

from ..base import AgentFileManager, BaseAgentConfiguration


class WorkspaceMixin:
"""Mixin that adds workspace support to a class"""

workspace: Workspace
workspace: Workspace | None
"""Workspace that the agent has access to, e.g. for reading/writing files."""

def __init__(self, **kwargs):
# Initialize other bases first, because we need the config from BaseAgent
super(WorkspaceMixin, self).__init__(**kwargs)

legacy_config: Config = getattr(self, "legacy_config")
if not isinstance(legacy_config, Config):
raise ValueError(f"Cannot initialize Workspace for Agent without Config")
if not legacy_config.workspace_path:
config: BaseAgentConfiguration = getattr(self, "config")
if not isinstance(config, BaseAgentConfiguration):
raise ValueError(
f"Cannot set up Workspace: no WORKSPACE_PATH in legacy_config"
"Cannot initialize Workspace for Agent without compatible .config"
)
file_manager: AgentFileManager = getattr(self, "file_manager")
if not file_manager:
return

self.workspace = _setup_workspace(file_manager, config)

def attach_fs(self, agent_dir: Path):
res = super(WorkspaceMixin, self).attach_fs(agent_dir)

self.workspace = _setup_workspace(self.file_manager, self.config)

return res


self.workspace = Workspace(
legacy_config.workspace_path, legacy_config.restrict_to_workspace
)
def _setup_workspace(file_manager: AgentFileManager, config: BaseAgentConfiguration):
workspace = Workspace(
file_manager.root / "workspace",
restrict_to_workspace=not config.allow_fs_access,
)
workspace.initialize()
return workspace


def get_agent_workspace(agent: BaseAgent) -> Workspace | None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import TYPE_CHECKING, Callable, Optional

import distro
from pydantic import Field

if TYPE_CHECKING:
from autogpt.agents.agent import Agent
Expand Down Expand Up @@ -166,7 +167,9 @@ class OneShotAgentPromptConfiguration(SystemConfiguration):
#########
# State #
#########
progress_summaries: dict[tuple[int, int], str] = {(0, 0): ""}
progress_summaries: dict[tuple[int, int], str] = Field(
default_factory=lambda: {(0, 0): ""}
)


class OneShotAgentPromptStrategy(PromptStrategy):
Expand Down
37 changes: 37 additions & 0 deletions autogpts/autogpt/autogpt/agents/utils/agent_file_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import annotations

import logging
from pathlib import Path

logger = logging.getLogger(__name__)


class AgentFileManager:
"""A class that represents a workspace for an AutoGPT agent."""

def __init__(self, agent_data_dir: Path):
self._root = agent_data_dir.resolve()

@property
def root(self) -> Path:
"""The root directory of the workspace."""
return self._root

def initialize(self) -> None:
self.root.mkdir(exist_ok=True, parents=True)
self.init_file_ops_log(self.file_ops_log_path)

@property
def state_file_path(self) -> Path:
return self.root / "state.json"

@property
def file_ops_log_path(self) -> Path:
return self.root / "file_logger.log"

@staticmethod
def init_file_ops_log(file_logger_path: Path) -> Path:
if not file_logger_path.exists():
with file_logger_path.open(mode="w", encoding="utf-8") as f:
f.write("File Operation Logger ")
return file_logger_path
Loading

0 comments on commit 34352af

Please sign in to comment.