diff --git a/agenthub/SWE_agent/agent.py b/agenthub/SWE_agent/agent.py index 808063eaff..b09e1a5fb1 100644 --- a/agenthub/SWE_agent/agent.py +++ b/agenthub/SWE_agent/agent.py @@ -1,14 +1,14 @@ from typing import List -from opendevin.action import ( +from opendevin.agent import Agent +from opendevin.events.action import ( Action, AgentThinkAction, FileReadAction, FileWriteAction, ) -from opendevin.agent import Agent +from opendevin.events.observation import Observation from opendevin.llm.llm import LLM -from opendevin.observation import Observation from opendevin.state import State from .parser import parse_command diff --git a/agenthub/SWE_agent/parser.py b/agenthub/SWE_agent/parser.py index d4600291ef..6704a4a8e2 100644 --- a/agenthub/SWE_agent/parser.py +++ b/agenthub/SWE_agent/parser.py @@ -1,6 +1,6 @@ import re -from opendevin.action import ( +from opendevin.events.action import ( Action, AgentEchoAction, AgentFinishAction, diff --git a/agenthub/codeact_agent/codeact_agent.py b/agenthub/codeact_agent/codeact_agent.py index 7afa8165d8..fc95261253 100644 --- a/agenthub/codeact_agent/codeact_agent.py +++ b/agenthub/codeact_agent/codeact_agent.py @@ -2,7 +2,8 @@ import re from typing import List, Mapping from agenthub.codeact_agent.prompt import EXAMPLES, SYSTEM_MESSAGE -from opendevin.action import ( +from opendevin.agent import Agent +from opendevin.events.action import ( Action, AgentEchoAction, AgentFinishAction, @@ -11,14 +12,13 @@ from opendevin.action import ( IPythonRunCellAction, NullAction, ) -from opendevin.agent import Agent -from opendevin.llm.llm import LLM -from opendevin.observation import ( +from opendevin.events.observation import ( AgentMessageObservation, CmdOutputObservation, IPythonRunCellObservation, UserMessageObservation, ) +from opendevin.llm.llm import LLM from opendevin.sandbox.plugins import ( JupyterRequirement, PluginRequirement, diff --git a/agenthub/delegator_agent/agent.py b/agenthub/delegator_agent/agent.py index 57ecd7bcd4..0c42948afb 100644 --- a/agenthub/delegator_agent/agent.py +++ b/agenthub/delegator_agent/agent.py @@ -1,9 +1,9 @@ from typing import List -from opendevin.action import Action, AgentDelegateAction, AgentFinishAction from opendevin.agent import Agent +from opendevin.events.action import Action, AgentDelegateAction, AgentFinishAction +from opendevin.events.observation import AgentDelegateObservation from opendevin.llm.llm import LLM -from opendevin.observation import AgentDelegateObservation from opendevin.state import State diff --git a/agenthub/dummy_agent/agent.py b/agenthub/dummy_agent/agent.py index 64fe3bfe91..f1c7016e15 100644 --- a/agenthub/dummy_agent/agent.py +++ b/agenthub/dummy_agent/agent.py @@ -1,7 +1,8 @@ import time from typing import List, TypedDict -from opendevin.action import ( +from opendevin.agent import Agent +from opendevin.events.action import ( Action, AddTaskAction, AgentFinishAction, @@ -13,9 +14,7 @@ from opendevin.action import ( FileWriteAction, ModifyTaskAction, ) -from opendevin.agent import Agent -from opendevin.llm.llm import LLM -from opendevin.observation import ( +from opendevin.events.observation import ( AgentRecallObservation, CmdOutputObservation, FileReadObservation, @@ -23,6 +22,7 @@ from opendevin.observation import ( NullObservation, Observation, ) +from opendevin.llm.llm import LLM from opendevin.state import State """ diff --git a/agenthub/micro/agent.py b/agenthub/micro/agent.py index a0f6de3795..32df7f5ac3 100644 --- a/agenthub/micro/agent.py +++ b/agenthub/micro/agent.py @@ -3,8 +3,8 @@ from typing import Dict, List from jinja2 import BaseLoader, Environment -from opendevin.action import Action, action_from_dict from opendevin.agent import Agent +from opendevin.events.action import Action, action_from_dict from opendevin.exceptions import LLMOutputError from opendevin.llm.llm import LLM from opendevin.state import State diff --git a/agenthub/monologue_agent/agent.py b/agenthub/monologue_agent/agent.py index 59c5510d24..fd3a701362 100644 --- a/agenthub/monologue_agent/agent.py +++ b/agenthub/monologue_agent/agent.py @@ -3,7 +3,8 @@ from typing import List import agenthub.monologue_agent.utils.prompts as prompts from agenthub.monologue_agent.utils.monologue import Monologue from opendevin import config -from opendevin.action import ( +from opendevin.agent import Agent +from opendevin.events.action import ( Action, AgentRecallAction, AgentThinkAction, @@ -14,10 +15,7 @@ from opendevin.action import ( GitHubPushAction, NullAction, ) -from opendevin.agent import Agent -from opendevin.exceptions import AgentNoInstructionError -from opendevin.llm.llm import LLM -from opendevin.observation import ( +from opendevin.events.observation import ( AgentRecallObservation, BrowserOutputObservation, CmdOutputObservation, @@ -25,6 +23,8 @@ from opendevin.observation import ( NullObservation, Observation, ) +from opendevin.exceptions import AgentNoInstructionError +from opendevin.llm.llm import LLM from opendevin.schema import ActionType from opendevin.schema.config import ConfigType from opendevin.state import State diff --git a/agenthub/monologue_agent/utils/prompts.py b/agenthub/monologue_agent/utils/prompts.py index 8767aecaa3..95fbff85b6 100644 --- a/agenthub/monologue_agent/utils/prompts.py +++ b/agenthub/monologue_agent/utils/prompts.py @@ -3,14 +3,14 @@ from json import JSONDecodeError from typing import List from opendevin import config -from opendevin.action import ( +from opendevin.events.action import ( Action, action_from_dict, ) -from opendevin.exceptions import LLMOutputError -from opendevin.observation import ( +from opendevin.events.observation import ( CmdOutputObservation, ) +from opendevin.exceptions import LLMOutputError from opendevin.schema.config import ConfigType from . import json diff --git a/agenthub/planner_agent/agent.py b/agenthub/planner_agent/agent.py index 44413dfeea..43623a157a 100644 --- a/agenthub/planner_agent/agent.py +++ b/agenthub/planner_agent/agent.py @@ -1,7 +1,7 @@ from typing import List -from opendevin.action import Action, AgentFinishAction from opendevin.agent import Agent +from opendevin.events.action import Action, AgentFinishAction from opendevin.llm.llm import LLM from opendevin.state import State diff --git a/agenthub/planner_agent/prompt.py b/agenthub/planner_agent/prompt.py index 234b958590..282280ed8e 100644 --- a/agenthub/planner_agent/prompt.py +++ b/agenthub/planner_agent/prompt.py @@ -1,7 +1,7 @@ import json from typing import Dict, List, Tuple, Type -from opendevin.action import ( +from opendevin.events.action import ( Action, AddTaskAction, AgentFinishAction, @@ -17,11 +17,11 @@ from opendevin.action import ( NullAction, action_from_dict, ) -from opendevin.logger import opendevin_logger as logger -from opendevin.observation import ( +from opendevin.events.observation import ( NullObservation, Observation, ) +from opendevin.logger import opendevin_logger as logger from opendevin.plan import Plan from opendevin.schema import ActionType diff --git a/docs/modules/python/opendevin/events/action/__init__.md b/docs/modules/python/opendevin/events/action/__init__.md new file mode 100644 index 0000000000..b016bed3ef --- /dev/null +++ b/docs/modules/python/opendevin/events/action/__init__.md @@ -0,0 +1,9 @@ +--- +sidebar_label: action +title: opendevin.events.action +--- + +#### ACTION\_TYPE\_TO\_CLASS + +type: ignore[attr-defined] + diff --git a/docs/modules/python/opendevin/events/action/empty.md b/docs/modules/python/opendevin/events/action/empty.md new file mode 100644 index 0000000000..8f92a1b0eb --- /dev/null +++ b/docs/modules/python/opendevin/events/action/empty.md @@ -0,0 +1,14 @@ +--- +sidebar_label: empty +title: opendevin.events.action.empty +--- + +## NullAction Objects + +```python +@dataclass +class NullAction(Action) +``` + +An action that does nothing. + diff --git a/docs/modules/python/opendevin/events/action/files.md b/docs/modules/python/opendevin/events/action/files.md new file mode 100644 index 0000000000..fcb1bb0fa5 --- /dev/null +++ b/docs/modules/python/opendevin/events/action/files.md @@ -0,0 +1,16 @@ +--- +sidebar_label: files +title: opendevin.events.action.files +--- + +## FileReadAction Objects + +```python +@dataclass +class FileReadAction(Action) +``` + +Reads a file from a given path. +Can be set to read specific lines using start and end +Default lines 0:-1 (whole file) + diff --git a/docs/modules/python/opendevin/events/action/github.md b/docs/modules/python/opendevin/events/action/github.md new file mode 100644 index 0000000000..712a129d3b --- /dev/null +++ b/docs/modules/python/opendevin/events/action/github.md @@ -0,0 +1,46 @@ +--- +sidebar_label: github +title: opendevin.events.action.github +--- + +## GitHubPushAction Objects + +```python +@dataclass +class GitHubPushAction(Action) +``` + +This pushes the current branch to github. + +To use this, you need to set the GITHUB_TOKEN environment variable. +The agent will return a message with a URL that you can click to make a pull +request. + +**Attributes**: + +- `owner` - The owner of the source repo +- `repo` - The name of the source repo +- `branch` - The branch to push +- `action` - The action identifier + +## GitHubSendPRAction Objects + +```python +@dataclass +class GitHubSendPRAction(Action) +``` + +An action to send a github PR. + +To use this, you need to set the GITHUB_TOKEN environment variable. + +**Attributes**: + +- `owner` - The owner of the source repo +- `repo` - The name of the source repo +- `title` - The title of the PR +- `head` - The branch to send the PR from +- `head_repo` - The repo to send the PR from +- `base` - The branch to send the PR to +- `body` - The body of the PR + diff --git a/docs/modules/python/opendevin/events/action/tasks.md b/docs/modules/python/opendevin/events/action/tasks.md new file mode 100644 index 0000000000..dc171802eb --- /dev/null +++ b/docs/modules/python/opendevin/events/action/tasks.md @@ -0,0 +1,14 @@ +--- +sidebar_label: tasks +title: opendevin.events.action.tasks +--- + +## TaskStateChangedAction Objects + +```python +@dataclass +class TaskStateChangedAction(Action) +``` + +Fake action, just to notify the client that a task state has changed. + diff --git a/docs/modules/python/opendevin/events/observation/__init__.md b/docs/modules/python/opendevin/events/observation/__init__.md new file mode 100644 index 0000000000..275d13e694 --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/__init__.md @@ -0,0 +1,9 @@ +--- +sidebar_label: observation +title: opendevin.events.observation +--- + +#### OBSERVATION\_TYPE\_TO\_CLASS + +type: ignore[attr-defined] + diff --git a/docs/modules/python/opendevin/events/observation/browse.md b/docs/modules/python/opendevin/events/observation/browse.md new file mode 100644 index 0000000000..5ef28ff60c --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/browse.md @@ -0,0 +1,14 @@ +--- +sidebar_label: browse +title: opendevin.events.observation.browse +--- + +## BrowserOutputObservation Objects + +```python +@dataclass +class BrowserOutputObservation(Observation) +``` + +This data class represents the output of a browser. + diff --git a/docs/modules/python/opendevin/events/observation/commands.md b/docs/modules/python/opendevin/events/observation/commands.md new file mode 100644 index 0000000000..10dd74f4c2 --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/commands.md @@ -0,0 +1,23 @@ +--- +sidebar_label: commands +title: opendevin.events.observation.commands +--- + +## CmdOutputObservation Objects + +```python +@dataclass +class CmdOutputObservation(Observation) +``` + +This data class represents the output of a command. + +## IPythonRunCellObservation Objects + +```python +@dataclass +class IPythonRunCellObservation(Observation) +``` + +This data class represents the output of a IPythonRunCellAction. + diff --git a/docs/modules/python/opendevin/events/observation/delegate.md b/docs/modules/python/opendevin/events/observation/delegate.md new file mode 100644 index 0000000000..3d3ee74e98 --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/delegate.md @@ -0,0 +1,14 @@ +--- +sidebar_label: delegate +title: opendevin.events.observation.delegate +--- + +## AgentDelegateObservation Objects + +```python +@dataclass +class AgentDelegateObservation(Observation) +``` + +This data class represents the result from delegating to another agent + diff --git a/docs/modules/python/opendevin/events/observation/empty.md b/docs/modules/python/opendevin/events/observation/empty.md new file mode 100644 index 0000000000..49086de2c4 --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/empty.md @@ -0,0 +1,15 @@ +--- +sidebar_label: empty +title: opendevin.events.observation.empty +--- + +## NullObservation Objects + +```python +@dataclass +class NullObservation(Observation) +``` + +This data class represents a null observation. +This is used when the produced action is NOT executable. + diff --git a/docs/modules/python/opendevin/events/observation/error.md b/docs/modules/python/opendevin/events/observation/error.md new file mode 100644 index 0000000000..670521ea3d --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/error.md @@ -0,0 +1,14 @@ +--- +sidebar_label: error +title: opendevin.events.observation.error +--- + +## AgentErrorObservation Objects + +```python +@dataclass +class AgentErrorObservation(Observation) +``` + +This data class represents an error encountered by the agent. + diff --git a/docs/modules/python/opendevin/events/observation/files.md b/docs/modules/python/opendevin/events/observation/files.md new file mode 100644 index 0000000000..b87bf75e1b --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/files.md @@ -0,0 +1,23 @@ +--- +sidebar_label: files +title: opendevin.events.observation.files +--- + +## FileReadObservation Objects + +```python +@dataclass +class FileReadObservation(Observation) +``` + +This data class represents the content of a file. + +## FileWriteObservation Objects + +```python +@dataclass +class FileWriteObservation(Observation) +``` + +This data class represents a file write operation + diff --git a/docs/modules/python/opendevin/events/observation/message.md b/docs/modules/python/opendevin/events/observation/message.md new file mode 100644 index 0000000000..619543d772 --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/message.md @@ -0,0 +1,23 @@ +--- +sidebar_label: message +title: opendevin.events.observation.message +--- + +## UserMessageObservation Objects + +```python +@dataclass +class UserMessageObservation(Observation) +``` + +This data class represents a message sent by the user. + +## AgentMessageObservation Objects + +```python +@dataclass +class AgentMessageObservation(Observation) +``` + +This data class represents a message sent by the agent. + diff --git a/docs/modules/python/opendevin/events/observation/observation.md b/docs/modules/python/opendevin/events/observation/observation.md new file mode 100644 index 0000000000..a6c5a8784b --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/observation.md @@ -0,0 +1,20 @@ +--- +sidebar_label: observation +title: opendevin.events.observation.observation +--- + +## Observation Objects + +```python +@dataclass +class Observation(Event) +``` + +#### to\_memory + +```python +def to_memory() -> dict +``` + +Converts the observation to a dictionary. + diff --git a/docs/modules/python/opendevin/events/observation/recall.md b/docs/modules/python/opendevin/events/observation/recall.md new file mode 100644 index 0000000000..b2cb94a28b --- /dev/null +++ b/docs/modules/python/opendevin/events/observation/recall.md @@ -0,0 +1,14 @@ +--- +sidebar_label: recall +title: opendevin.events.observation.recall +--- + +## AgentRecallObservation Objects + +```python +@dataclass +class AgentRecallObservation(Observation) +``` + +This data class represents a list of memories recalled by the agent. + diff --git a/opendevin/action/base.py b/opendevin/action/base.py deleted file mode 100644 index d459713f3f..0000000000 --- a/opendevin/action/base.py +++ /dev/null @@ -1,61 +0,0 @@ -from dataclasses import asdict, dataclass -from typing import TYPE_CHECKING - -from opendevin.schema import ActionType - -if TYPE_CHECKING: - from opendevin.controller import AgentController - from opendevin.observation import Observation - - -@dataclass -class Action: - async def run(self, controller: 'AgentController') -> 'Observation': - raise NotImplementedError - - def to_memory(self): - d = asdict(self) - try: - v = d.pop('action') - except KeyError: - raise NotImplementedError(f'{self=} does not have action attribute set') - return {'action': v, 'args': d} - - def to_dict(self): - d = self.to_memory() - d['message'] = self.message - return d - - @property - def executable(self) -> bool: - raise NotImplementedError - - @property - def message(self) -> str: - raise NotImplementedError - - -@dataclass -class ExecutableAction(Action): - @property - def executable(self) -> bool: - return True - - -@dataclass -class NotExecutableAction(Action): - @property - def executable(self) -> bool: - return False - - -@dataclass -class NullAction(NotExecutableAction): - """An action that does nothing. - """ - - action: str = ActionType.NULL - - @property - def message(self) -> str: - return 'No action' diff --git a/opendevin/agent.py b/opendevin/agent.py index a51a214bd5..1a75767562 100644 --- a/opendevin/agent.py +++ b/opendevin/agent.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Dict, List, Type if TYPE_CHECKING: - from opendevin.action import Action + from opendevin.events.action import Action from opendevin.state import State from opendevin.exceptions import AgentAlreadyRegisteredError, AgentNotRegisteredError from opendevin.llm.llm import LLM diff --git a/opendevin/controller/action_manager.py b/opendevin/controller/action_manager.py index 504146935d..e831c80ebe 100644 --- a/opendevin/controller/action_manager.py +++ b/opendevin/controller/action_manager.py @@ -1,13 +1,12 @@ from typing import List from opendevin import config -from opendevin.action import ( +from opendevin.events.action import ( Action, ) -from opendevin.observation import ( +from opendevin.events.observation import ( AgentErrorObservation, CmdOutputObservation, - NullObservation, Observation, ) from opendevin.sandbox import DockerExecBox, DockerSSHBox, E2BBox, LocalBox, Sandbox @@ -43,9 +42,6 @@ class ActionManager: self.sandbox.init_plugins(plugins) async def run_action(self, action: Action, agent_controller) -> Observation: - observation: Observation = NullObservation('') - if not action.executable: - return observation observation = await action.run(agent_controller) return observation diff --git a/opendevin/controller/agent_controller.py b/opendevin/controller/agent_controller.py index 2065c16b1c..13398d4c7e 100644 --- a/opendevin/controller/agent_controller.py +++ b/opendevin/controller/agent_controller.py @@ -3,14 +3,23 @@ from typing import Callable, List, Type from agenthub.codeact_agent.codeact_agent import CodeActAgent from opendevin import config -from opendevin.action import ( +from opendevin.agent import Agent +from opendevin.controller.action_manager import ActionManager +from opendevin.events.action import ( Action, AgentDelegateAction, AgentFinishAction, AgentTalkAction, NullAction, + TaskStateChangedAction, +) +from opendevin.events.observation import ( + AgentDelegateObservation, + AgentErrorObservation, + NullObservation, + Observation, + UserMessageObservation, ) -from opendevin.action.tasks import TaskStateChangedAction from opendevin.agent import Agent from opendevin.browser.browser_env import BrowserEnv from opendevin.controller.action_manager import ActionManager @@ -21,13 +30,6 @@ from opendevin.exceptions import ( MaxCharsExceedError, ) from opendevin.logger import opendevin_logger as logger -from opendevin.observation import ( - AgentDelegateObservation, - AgentErrorObservation, - NullObservation, - Observation, - UserMessageObservation, -) from opendevin.plan import Plan from opendevin.sandbox import DockerSSHBox from opendevin.schema import TaskState diff --git a/opendevin/events/__init__.py b/opendevin/events/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/opendevin/action/__init__.py b/opendevin/events/action/__init__.py similarity index 83% rename from opendevin/action/__init__.py rename to opendevin/events/action/__init__.py index 9e3d939d3f..c9218d70ac 100644 --- a/opendevin/action/__init__.py +++ b/opendevin/events/action/__init__.py @@ -1,4 +1,7 @@ -from ..exceptions import AgentMalformedActionError + +from opendevin.exceptions import AgentMalformedActionError + +from .action import Action from .agent import ( AgentDelegateAction, AgentEchoAction, @@ -8,12 +11,12 @@ from .agent import ( AgentTalkAction, AgentThinkAction, ) -from .base import Action, NullAction -from .bash import CmdKillAction, CmdRunAction, IPythonRunCellAction from .browse import BrowseURLAction -from .fileop import FileReadAction, FileWriteAction +from .commands import CmdKillAction, CmdRunAction, IPythonRunCellAction +from .empty import NullAction +from .files import FileReadAction, FileWriteAction from .github import GitHubPushAction -from .tasks import AddTaskAction, ModifyTaskAction +from .tasks import AddTaskAction, ModifyTaskAction, TaskStateChangedAction actions = ( CmdKillAction, @@ -29,6 +32,7 @@ actions = ( AgentDelegateAction, AddTaskAction, ModifyTaskAction, + TaskStateChangedAction, GitHubPushAction, ) @@ -71,5 +75,6 @@ __all__ = [ 'AgentSummarizeAction', 'AddTaskAction', 'ModifyTaskAction', + 'TaskStateChangedAction', 'IPythonRunCellAction' ] diff --git a/opendevin/events/action/action.py b/opendevin/events/action/action.py new file mode 100644 index 0000000000..9824e66a57 --- /dev/null +++ b/opendevin/events/action/action.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass +from typing import TYPE_CHECKING + +from opendevin.events.event import Event +from opendevin.events.observation import NullObservation, Observation + +if TYPE_CHECKING: + from opendevin.controller import AgentController + + +@dataclass +class Action(Event): + async def run(self, controller: 'AgentController') -> 'Observation': + return NullObservation('') + + def to_memory(self): + d = super().to_memory() + try: + v = d.pop('action') + except KeyError: + raise NotImplementedError(f'{self=} does not have action attribute set') + return {'action': v, 'args': d} diff --git a/opendevin/action/agent.py b/opendevin/events/action/agent.py similarity index 70% rename from opendevin/action/agent.py rename to opendevin/events/action/agent.py index 063d095bd7..22b8fad4d8 100644 --- a/opendevin/action/agent.py +++ b/opendevin/events/action/agent.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING, Dict -from opendevin.observation import ( +from opendevin.events.observation import ( AgentMessageObservation, AgentRecallObservation, NullObservation, @@ -9,14 +9,14 @@ from opendevin.observation import ( ) from opendevin.schema import ActionType -from .base import ExecutableAction, NotExecutableAction +from .action import Action if TYPE_CHECKING: from opendevin.controller import AgentController @dataclass -class AgentRecallAction(ExecutableAction): +class AgentRecallAction(Action): query: str thought: str = '' action: str = ActionType.RECALL @@ -33,24 +33,21 @@ class AgentRecallAction(ExecutableAction): @dataclass -class AgentThinkAction(NotExecutableAction): +class AgentThinkAction(Action): thought: str action: str = ActionType.THINK - async def run(self, controller: 'AgentController') -> 'Observation': - raise NotImplementedError - @property def message(self) -> str: return self.thought @dataclass -class AgentTalkAction(NotExecutableAction): +class AgentTalkAction(Action): content: str action: str = ActionType.TALK - async def run(self, controller: 'AgentController') -> 'Observation': + async def run(self, controller: 'AgentController') -> Observation: raise NotImplementedError @property @@ -62,11 +59,11 @@ class AgentTalkAction(NotExecutableAction): @dataclass -class AgentEchoAction(ExecutableAction): +class AgentEchoAction(Action): content: str action: str = 'echo' - async def run(self, controller: 'AgentController') -> 'Observation': + async def run(self, controller: 'AgentController') -> Observation: return AgentMessageObservation(self.content) @property @@ -75,7 +72,7 @@ class AgentEchoAction(ExecutableAction): @dataclass -class AgentSummarizeAction(NotExecutableAction): +class AgentSummarizeAction(Action): summary: str action: str = ActionType.SUMMARIZE @@ -85,27 +82,24 @@ class AgentSummarizeAction(NotExecutableAction): @dataclass -class AgentFinishAction(NotExecutableAction): +class AgentFinishAction(Action): outputs: Dict = field(default_factory=dict) thought: str = '' action: str = ActionType.FINISH - async def run(self, controller: 'AgentController') -> 'Observation': - raise NotImplementedError - @property def message(self) -> str: return "All done! What's next on the agenda?" @dataclass -class AgentDelegateAction(ExecutableAction): +class AgentDelegateAction(Action): agent: str inputs: dict thought: str = '' action: str = ActionType.DELEGATE - async def run(self, controller: 'AgentController') -> 'Observation': + async def run(self, controller: 'AgentController') -> Observation: await controller.start_delegate(self) return NullObservation('') diff --git a/opendevin/action/browse.py b/opendevin/events/action/browse.py similarity index 93% rename from opendevin/action/browse.py rename to opendevin/events/action/browse.py index 3b12edc714..e7a4aec0cb 100644 --- a/opendevin/action/browse.py +++ b/opendevin/events/action/browse.py @@ -2,17 +2,18 @@ import os from dataclasses import dataclass from typing import TYPE_CHECKING -from opendevin.observation import BrowserOutputObservation + +from opendevin.events.observation import BrowserOutputObservation from opendevin.schema import ActionType -from .base import ExecutableAction +from .action import Action if TYPE_CHECKING: from opendevin.controller import AgentController @dataclass -class BrowseURLAction(ExecutableAction): +class BrowseURLAction(Action): url: str thought: str = '' action: str = ActionType.BROWSE diff --git a/opendevin/action/bash.py b/opendevin/events/action/commands.py similarity index 89% rename from opendevin/action/bash.py rename to opendevin/events/action/commands.py index 160a795491..a0977eee8f 100644 --- a/opendevin/action/bash.py +++ b/opendevin/events/action/commands.py @@ -6,17 +6,17 @@ from typing import TYPE_CHECKING from opendevin import config from opendevin.schema import ActionType, ConfigType -from .base import ExecutableAction +from .action import Action if TYPE_CHECKING: from opendevin.controller import AgentController - from opendevin.observation import CmdOutputObservation, Observation + from opendevin.events.observation import CmdOutputObservation, Observation -from opendevin.observation import IPythonRunCellObservation +from opendevin.events.observation import IPythonRunCellObservation @dataclass -class CmdRunAction(ExecutableAction): +class CmdRunAction(Action): command: str background: bool = False thought: str = '' @@ -38,7 +38,7 @@ class CmdRunAction(ExecutableAction): @dataclass -class CmdKillAction(ExecutableAction): +class CmdKillAction(Action): id: int thought: str = '' action: str = ActionType.KILL @@ -55,7 +55,7 @@ class CmdKillAction(ExecutableAction): @dataclass -class IPythonRunCellAction(ExecutableAction): +class IPythonRunCellAction(Action): code: str thought: str = '' action: str = ActionType.RUN_IPYTHON diff --git a/opendevin/events/action/empty.py b/opendevin/events/action/empty.py new file mode 100644 index 0000000000..49a92e9ce8 --- /dev/null +++ b/opendevin/events/action/empty.py @@ -0,0 +1,16 @@ +from dataclasses import dataclass + +from opendevin.schema import ActionType + +from .action import Action + + +@dataclass +class NullAction(Action): + """An action that does nothing. + """ + action: str = ActionType.NULL + + @property + def message(self) -> str: + return 'No action' diff --git a/opendevin/action/fileop.py b/opendevin/events/action/files.py similarity index 97% rename from opendevin/action/fileop.py rename to opendevin/events/action/files.py index 4e630c9383..ee19ee8494 100644 --- a/opendevin/action/fileop.py +++ b/opendevin/events/action/files.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from pathlib import Path from opendevin import config -from opendevin.observation import ( +from opendevin.events.observation import ( AgentErrorObservation, FileReadObservation, FileWriteObservation, @@ -13,7 +13,7 @@ from opendevin.sandbox import E2BBox from opendevin.schema import ActionType from opendevin.schema.config import ConfigType -from .base import ExecutableAction +from .action import Action def resolve_path(file_path, working_directory): @@ -41,7 +41,7 @@ def resolve_path(file_path, working_directory): @dataclass -class FileReadAction(ExecutableAction): +class FileReadAction(Action): """ Reads a file from a given path. Can be set to read specific lines using start and end @@ -95,7 +95,7 @@ class FileReadAction(ExecutableAction): @dataclass -class FileWriteAction(ExecutableAction): +class FileWriteAction(Action): path: str content: str start: int = 0 diff --git a/opendevin/action/github.py b/opendevin/events/action/github.py similarity index 93% rename from opendevin/action/github.py rename to opendevin/events/action/github.py index 78dd81fba2..d42c906290 100644 --- a/opendevin/action/github.py +++ b/opendevin/events/action/github.py @@ -6,20 +6,23 @@ from typing import TYPE_CHECKING import requests from opendevin import config -from opendevin.observation import AgentErrorObservation, Observation -from opendevin.observation.message import AgentMessageObservation -from opendevin.observation.run import CmdOutputObservation +from opendevin.events.observation import ( + AgentErrorObservation, + AgentMessageObservation, + CmdOutputObservation, + Observation, +) from opendevin.schema import ActionType from opendevin.schema.config import ConfigType -from .base import ExecutableAction +from .action import Action if TYPE_CHECKING: from opendevin.controller import AgentController @dataclass -class GitHubPushAction(ExecutableAction): +class GitHubPushAction(Action): """This pushes the current branch to github. To use this, you need to set the GITHUB_TOKEN environment variable. @@ -85,7 +88,7 @@ class GitHubPushAction(ExecutableAction): @dataclass -class GitHubSendPRAction(ExecutableAction): +class GitHubSendPRAction(Action): """An action to send a github PR. To use this, you need to set the GITHUB_TOKEN environment variable. diff --git a/opendevin/action/tasks.py b/opendevin/events/action/tasks.py similarity index 85% rename from opendevin/action/tasks.py rename to opendevin/events/action/tasks.py index 5eff530e66..61d49a095d 100644 --- a/opendevin/action/tasks.py +++ b/opendevin/events/action/tasks.py @@ -1,17 +1,17 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING -from opendevin.observation import NullObservation +from opendevin.events.observation import NullObservation from opendevin.schema import ActionType -from .base import ExecutableAction, NotExecutableAction +from .action import Action if TYPE_CHECKING: from opendevin.controller import AgentController @dataclass -class AddTaskAction(ExecutableAction): +class AddTaskAction(Action): parent: str goal: str subtasks: list = field(default_factory=list) @@ -29,7 +29,7 @@ class AddTaskAction(ExecutableAction): @dataclass -class ModifyTaskAction(ExecutableAction): +class ModifyTaskAction(Action): id: str state: str thought: str = '' @@ -46,7 +46,7 @@ class ModifyTaskAction(ExecutableAction): @dataclass -class TaskStateChangedAction(NotExecutableAction): +class TaskStateChangedAction(Action): """Fake action, just to notify the client that a task state has changed.""" task_state: str thought: str = '' diff --git a/opendevin/events/event.py b/opendevin/events/event.py new file mode 100644 index 0000000000..38c508297b --- /dev/null +++ b/opendevin/events/event.py @@ -0,0 +1,16 @@ +from dataclasses import asdict, dataclass + + +@dataclass +class Event: + def to_memory(self): + return asdict(self) + + def to_dict(self): + d = self.to_memory() + d['message'] = self.message + return d + + @property + def message(self) -> str: + return self.message diff --git a/opendevin/observation/__init__.py b/opendevin/events/observation/__init__.py similarity index 92% rename from opendevin/observation/__init__.py rename to opendevin/events/observation/__init__.py index 174e90bc9c..1b10722033 100644 --- a/opendevin/observation/__init__.py +++ b/opendevin/events/observation/__init__.py @@ -1,11 +1,12 @@ -from .base import NullObservation, Observation from .browse import BrowserOutputObservation +from .commands import CmdOutputObservation, IPythonRunCellObservation from .delegate import AgentDelegateObservation +from .empty import NullObservation from .error import AgentErrorObservation from .files import FileReadObservation, FileWriteObservation from .message import AgentMessageObservation, UserMessageObservation +from .observation import Observation from .recall import AgentRecallObservation -from .run import CmdOutputObservation, IPythonRunCellObservation observations = ( CmdOutputObservation, diff --git a/opendevin/observation/browse.py b/opendevin/events/observation/browse.py similarity index 97% rename from opendevin/observation/browse.py rename to opendevin/events/observation/browse.py index 250450f113..9ed2cf082b 100644 --- a/opendevin/observation/browse.py +++ b/opendevin/events/observation/browse.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field from opendevin.schema import ObservationType -from .base import Observation +from .observation import Observation @dataclass diff --git a/opendevin/observation/run.py b/opendevin/events/observation/commands.py similarity index 96% rename from opendevin/observation/run.py rename to opendevin/events/observation/commands.py index 8de5fbac5c..973715343a 100644 --- a/opendevin/observation/run.py +++ b/opendevin/events/observation/commands.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from opendevin.schema import ObservationType -from .base import Observation +from .observation import Observation @dataclass diff --git a/opendevin/observation/delegate.py b/opendevin/events/observation/delegate.py similarity index 66% rename from opendevin/observation/delegate.py rename to opendevin/events/observation/delegate.py index 3b7b586320..2fd504d9bd 100644 --- a/opendevin/observation/delegate.py +++ b/opendevin/events/observation/delegate.py @@ -2,14 +2,13 @@ from dataclasses import dataclass from opendevin.schema import ObservationType -from .base import Observation +from .observation import Observation @dataclass class AgentDelegateObservation(Observation): """ - This data class represents a delegate observation. - This is used when the produced action is NOT executable. + This data class represents the result from delegating to another agent """ outputs: dict diff --git a/opendevin/events/observation/empty.py b/opendevin/events/observation/empty.py new file mode 100644 index 0000000000..700b4ed21f --- /dev/null +++ b/opendevin/events/observation/empty.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass + +from opendevin.schema import ObservationType + +from .observation import Observation + + +@dataclass +class NullObservation(Observation): + """ + This data class represents a null observation. + This is used when the produced action is NOT executable. + """ + + observation: str = ObservationType.NULL + + @property + def message(self) -> str: + return 'No observation' diff --git a/opendevin/observation/error.py b/opendevin/events/observation/error.py similarity index 90% rename from opendevin/observation/error.py rename to opendevin/events/observation/error.py index ac47b4eafd..7751e8a557 100644 --- a/opendevin/observation/error.py +++ b/opendevin/events/observation/error.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from opendevin.schema import ObservationType -from .base import Observation +from .observation import Observation @dataclass diff --git a/opendevin/observation/files.py b/opendevin/events/observation/files.py similarity index 94% rename from opendevin/observation/files.py rename to opendevin/events/observation/files.py index 60112b8ef8..fe4462901f 100644 --- a/opendevin/observation/files.py +++ b/opendevin/events/observation/files.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from opendevin.schema import ObservationType -from .base import Observation +from .observation import Observation @dataclass diff --git a/opendevin/observation/message.py b/opendevin/events/observation/message.py similarity index 94% rename from opendevin/observation/message.py rename to opendevin/events/observation/message.py index 3d82166caa..8c4ce34a22 100644 --- a/opendevin/observation/message.py +++ b/opendevin/events/observation/message.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from opendevin.schema import ObservationType -from .base import Observation +from .observation import Observation @dataclass diff --git a/opendevin/events/observation/observation.py b/opendevin/events/observation/observation.py new file mode 100644 index 0000000000..2535905639 --- /dev/null +++ b/opendevin/events/observation/observation.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass + +from opendevin.events.event import Event + + +@dataclass +class Observation(Event): + content: str + + def to_memory(self) -> dict: + """Converts the observation to a dictionary.""" + extras = super().to_memory() + content = extras.pop('content', '') + observation = extras.pop('observation', '') + return { + 'observation': observation, + 'content': content, + 'extras': extras, + } diff --git a/opendevin/observation/recall.py b/opendevin/events/observation/recall.py similarity index 92% rename from opendevin/observation/recall.py rename to opendevin/events/observation/recall.py index 7fffbcdffc..8783d48499 100644 --- a/opendevin/observation/recall.py +++ b/opendevin/events/observation/recall.py @@ -3,7 +3,7 @@ from typing import List from opendevin.schema import ObservationType -from .base import Observation +from .observation import Observation @dataclass diff --git a/opendevin/observation/base.py b/opendevin/observation/base.py deleted file mode 100644 index 078f5b289d..0000000000 --- a/opendevin/observation/base.py +++ /dev/null @@ -1,52 +0,0 @@ -import copy -from dataclasses import dataclass - -from opendevin.schema import ObservationType - - -@dataclass -class Observation: - """ - This data class represents an observation of the environment. - """ - - content: str - - def __str__(self) -> str: - return self.content - - def to_dict(self) -> dict: - """Converts the observation to a dictionary and adds user message.""" - memory_dict = self.to_memory() - memory_dict['message'] = self.message - return memory_dict - - def to_memory(self) -> dict: - """Converts the observation to a dictionary.""" - extras = copy.deepcopy(self.__dict__) - content = extras.pop('content', '') - observation = extras.pop('observation', '') - return { - 'observation': observation, - 'content': content, - 'extras': extras, - } - - @property - def message(self) -> str: - """Returns a message describing the observation.""" - return '' - - -@dataclass -class NullObservation(Observation): - """ - This data class represents a null observation. - This is used when the produced action is NOT executable. - """ - - observation: str = ObservationType.NULL - - @property - def message(self) -> str: - return '' diff --git a/opendevin/server/agent/agent.py b/opendevin/server/agent/agent.py index 87a187bd11..a834160582 100644 --- a/opendevin/server/agent/agent.py +++ b/opendevin/server/agent/agent.py @@ -2,15 +2,19 @@ import asyncio from typing import Dict, List, Optional from opendevin import config -from opendevin.action import ( +from opendevin.agent import Agent +from opendevin.controller import AgentController +from opendevin.events.action import ( Action, NullAction, ) -from opendevin.agent import Agent -from opendevin.controller import AgentController +from opendevin.events.observation import ( + NullObservation, + Observation, + UserMessageObservation, +) from opendevin.llm.llm import LLM from opendevin.logger import opendevin_logger as logger -from opendevin.observation import NullObservation, Observation, UserMessageObservation from opendevin.schema import ActionType, ConfigType, TaskState, TaskStateAction from opendevin.server.session import session_manager diff --git a/opendevin/state.py b/opendevin/state.py index c3d02c9c72..7fc44b870b 100644 --- a/opendevin/state.py +++ b/opendevin/state.py @@ -1,10 +1,10 @@ from dataclasses import dataclass, field from typing import Dict, List, Tuple -from opendevin.action import ( +from opendevin.events.action import ( Action, ) -from opendevin.observation import ( +from opendevin.events.observation import ( CmdOutputObservation, Observation, ) diff --git a/tests/test_fileops.py b/tests/test_fileops.py index 3f22422a66..0a6c3d3f0c 100644 --- a/tests/test_fileops.py +++ b/tests/test_fileops.py @@ -3,25 +3,26 @@ from pathlib import Path import pytest from opendevin import config -from opendevin.action import fileop +from opendevin.events.action import files from opendevin.schema import ConfigType +SANDBOX_PATH_PREFIX = '/workspace' def test_resolve_path(): - assert fileop.resolve_path('test.txt', '/workspace') == Path(config.get(ConfigType.WORKSPACE_BASE)) / 'test.txt' - assert fileop.resolve_path('subdir/test.txt', '/workspace') == \ + assert files.resolve_path('test.txt', '/workspace') == Path(config.get(ConfigType.WORKSPACE_BASE)) / 'test.txt' + assert files.resolve_path('subdir/test.txt', '/workspace') == \ Path(config.get(ConfigType.WORKSPACE_BASE)) / 'subdir' / 'test.txt' - assert fileop.resolve_path(Path(fileop.SANDBOX_PATH_PREFIX) / 'test.txt', '/workspace') == \ + assert files.resolve_path(Path(SANDBOX_PATH_PREFIX) / 'test.txt', '/workspace') == \ Path(config.get(ConfigType.WORKSPACE_BASE)) / 'test.txt' - assert fileop.resolve_path(Path(fileop.SANDBOX_PATH_PREFIX) / 'subdir' / 'test.txt', + assert files.resolve_path(Path(SANDBOX_PATH_PREFIX) / 'subdir' / 'test.txt', '/workspace') == Path(config.get(ConfigType.WORKSPACE_BASE)) / 'subdir' / 'test.txt' - assert fileop.resolve_path(Path(fileop.SANDBOX_PATH_PREFIX) / 'subdir' / '..' / 'test.txt', + assert files.resolve_path(Path(SANDBOX_PATH_PREFIX) / 'subdir' / '..' / 'test.txt', '/workspace') == Path(config.get(ConfigType.WORKSPACE_BASE)) / 'test.txt' with pytest.raises(PermissionError): - fileop.resolve_path(Path(fileop.SANDBOX_PATH_PREFIX) / '..' / 'test.txt', '/workspace') + files.resolve_path(Path(SANDBOX_PATH_PREFIX) / '..' / 'test.txt', '/workspace') with pytest.raises(PermissionError): - fileop.resolve_path(Path('..') / 'test.txt', '/workspace') + files.resolve_path(Path('..') / 'test.txt', '/workspace') with pytest.raises(PermissionError): - fileop.resolve_path(Path('/') / 'test.txt', '/workspace') - assert fileop.resolve_path('test.txt', '/workspace/test') == \ + files.resolve_path(Path('/') / 'test.txt', '/workspace') + assert files.resolve_path('test.txt', '/workspace/test') == \ Path(config.get(ConfigType.WORKSPACE_BASE)) / 'test' / 'test.txt' diff --git a/tests/unit/test_action_github.py b/tests/unit/test_action_github.py index bb26f5d660..1e3cc8d8da 100644 --- a/tests/unit/test_action_github.py +++ b/tests/unit/test_action_github.py @@ -5,12 +5,12 @@ import pytest from agenthub.dummy_agent.agent import DummyAgent from opendevin import config -from opendevin.action.github import GitHubPushAction, GitHubSendPRAction from opendevin.controller.agent_controller import AgentController +from opendevin.events.action.github import GitHubPushAction, GitHubSendPRAction +from opendevin.events.observation.commands import CmdOutputObservation +from opendevin.events.observation.error import AgentErrorObservation +from opendevin.events.observation.message import AgentMessageObservation from opendevin.llm.llm import LLM -from opendevin.observation.error import AgentErrorObservation -from opendevin.observation.message import AgentMessageObservation -from opendevin.observation.run import CmdOutputObservation from opendevin.schema.config import ConfigType diff --git a/tests/unit/test_action_serialization.py b/tests/unit/test_action_serialization.py index 33632e2820..2d699a95b6 100644 --- a/tests/unit/test_action_serialization.py +++ b/tests/unit/test_action_serialization.py @@ -1,4 +1,4 @@ -from opendevin.action import ( +from opendevin.events.action import ( Action, AddTaskAction, AgentFinishAction, diff --git a/tests/unit/test_observation_serialization.py b/tests/unit/test_observation_serialization.py index e7870f88bf..4159f3c13d 100644 --- a/tests/unit/test_observation_serialization.py +++ b/tests/unit/test_observation_serialization.py @@ -1,4 +1,4 @@ -from opendevin.observation import ( +from opendevin.events.observation import ( CmdOutputObservation, Observation, observation_from_dict,