Skip to content

Commit

Permalink
Use generic types (All-Hands-AI#1680)
Browse files Browse the repository at this point in the history
  • Loading branch information
yimothysu committed May 10, 2024
1 parent a173081 commit f8d4b1a
Show file tree
Hide file tree
Showing 33 changed files with 113 additions and 126 deletions.
21 changes: 16 additions & 5 deletions agenthub/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ Every agent also has a `self.llm` which it can use to interact with the LLM conf
See the [LiteLLM docs for `self.llm.completion`](https://docs.litellm.ai/docs/completion).

## State

The `state` contains:
* A history of actions taken by the agent, as well as any observations (e.g. file content, command output) from those actions
* A list of actions/observations that have happened since the most recent step
* A [`plan`](https://github.com/OpenDevin/OpenDevin/blob/main/opendevin/plan.py), which contains the main goal
* The agent can add and modify subtasks through the `AddTaskAction` and `ModifyTaskAction`

- A history of actions taken by the agent, as well as any observations (e.g. file content, command output) from those actions
- A list of actions/observations that have happened since the most recent step
- A [`plan`](https://github.com/OpenDevin/OpenDevin/blob/main/opendevin/plan.py), which contains the main goal
- The agent can add and modify subtasks through the `AddTaskAction` and `ModifyTaskAction`

## Actions

Here is a list of available Actions, which can be returned by `agent.step()`:

- [`CmdRunAction`](../opendevin/action/bash.py) - Runs a command inside a sandboxed terminal
- [`CmdKillAction`](../opendevin/action/bash.py) - Kills a background command
- [`IPythonRunCellAction`](../opendevin/action/bash.py) - Execute a block of Python code interactively (in Jupyter notebook) and receives `CmdOutputObservation`. Requires setting up `jupyter` [plugin](../opendevin/sandbox/plugins) as a requirement.
Expand All @@ -43,11 +47,13 @@ Here is a list of available Actions, which can be returned by `agent.step()`:
You can use `action.to_dict()` and `action_from_dict` to serialize and deserialize actions.

## Observations

There are also several types of Observations. These are typically available in the step following the corresponding Action.
But they may also appear as a result of asynchronous events (e.g. a message from the user, logs from a command running
in the background).

Here is a list of available Observations:

- [`CmdOutputObservation`](../opendevin/observation/run.py)
- [`BrowserOutputObservation`](../opendevin/observation/browse.py)
- [`FileReadObservation`](../opendevin/observation/files.py)
Expand All @@ -59,19 +65,24 @@ Here is a list of available Observations:
You can use `observation.to_dict()` and `observation_from_dict` to serialize and deserialize observations.

## Interface

Every agent must implement the following methods:

### `step`

```
def step(self, state: "State") -> "Action"
```

`step` moves the agent forward one step towards its goal. This probably means
sending a prompt to the LLM, then parsing the response into an `Action`.

### `search_memory`

```
def search_memory(self, query: str) -> List[str]:
def search_memory(self, query: str) -> list[str]:
```

`search_memory` should return a list of events that match the query. This will be used
for the `recall` action.

Expand Down
8 changes: 3 additions & 5 deletions agenthub/SWE_agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import List

from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import (
Expand Down Expand Up @@ -32,7 +30,7 @@ def __init__(self, llm: LLM):
super().__init__(llm)
self.memory_window = 4
self.max_retries = 2
self.running_memory: List[str] = []
self.running_memory: list[str] = []
self.cur_file: str = ''
self.cur_line: int = 0

Expand All @@ -41,7 +39,7 @@ def _remember(self, action: Action, observation: Observation) -> None:
memory = MEMORY_FORMAT(action.to_memory(), observation.to_memory())
self.running_memory.append(memory)

def _think_act(self, messages: List[dict]) -> tuple[Action, str]:
def _think_act(self, messages: list[dict]) -> tuple[Action, str]:
resp = self.llm.completion(
messages=messages,
temperature=0.05,
Expand Down Expand Up @@ -99,7 +97,7 @@ def step(self, state: State) -> Action:
self.latest_action = action
return action

def search_memory(self, query: str) -> List[str]:
def search_memory(self, query: str) -> list[str]:
return [item for item in self.running_memory if query in item]

def reset(self) -> None:
Expand Down
4 changes: 1 addition & 3 deletions agenthub/delegator_agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import List

from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import Action, AgentDelegateAction, AgentFinishAction
Expand Down Expand Up @@ -81,5 +79,5 @@ def step(self, state: State) -> Action:
else:
raise Exception('Invalid delegate state')

def search_memory(self, query: str) -> List[str]:
def search_memory(self, query: str) -> list[str]:
return []
8 changes: 4 additions & 4 deletions agenthub/dummy_agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import time
from typing import List, TypedDict
from typing import TypedDict

from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
Expand Down Expand Up @@ -35,7 +35,7 @@
"""

ActionObs = TypedDict(
'ActionObs', {'action': Action, 'observations': List[Observation]}
'ActionObs', {'action': Action, 'observations': list[Observation]}
)

BACKGROUND_CMD = 'echo "This is in the background" && sleep .1 && echo "This too"'
Expand All @@ -49,7 +49,7 @@ class DummyAgent(Agent):

def __init__(self, llm: LLM):
super().__init__(llm)
self.steps: List[ActionObs] = [
self.steps: list[ActionObs] = [
{
'action': AddTaskAction(parent='0', goal='check the current directory'),
'observations': [NullObservation('')],
Expand Down Expand Up @@ -160,5 +160,5 @@ def step(self, state: State) -> Action:
), f'Expected observation {expected_obs}, got {hist_obs}'
return self.steps[state.iteration]['action']

def search_memory(self, query: str) -> List[str]:
def search_memory(self, query: str) -> list[str]:
return ['I am a computer.']
5 changes: 2 additions & 3 deletions agenthub/micro/agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
from typing import Dict, List

from jinja2 import BaseLoader, Environment

Expand Down Expand Up @@ -59,7 +58,7 @@ def to_json(obj, **kwargs):

class MicroAgent(Agent):
prompt = ''
agent_definition: Dict = {}
agent_definition: dict = {}

def __init__(self, llm: LLM):
super().__init__(llm)
Expand All @@ -83,5 +82,5 @@ def step(self, state: State) -> Action:
action = parse_response(action_resp)
return action

def search_memory(self, query: str) -> List[str]:
def search_memory(self, query: str) -> list[str]:
return []
3 changes: 1 addition & 2 deletions agenthub/micro/instructions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import os
from typing import Dict

instructions: Dict = {}
instructions: dict = {}

base_dir = os.path.dirname(os.path.abspath(__file__)) + '/_instructions'
for root, dirs, files in os.walk(base_dir):
Expand Down
6 changes: 2 additions & 4 deletions agenthub/monologue_agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import List

import agenthub.monologue_agent.utils.prompts as prompts
from agenthub.monologue_agent.utils.monologue import Monologue
from opendevin.controller.agent import Agent
Expand Down Expand Up @@ -239,7 +237,7 @@ def step(self, state: State) -> Action:
self.latest_action = action
return action

def search_memory(self, query: str) -> List[str]:
def search_memory(self, query: str) -> list[str]:
"""
Uses VectorIndexRetriever to find related memories within the long term memory.
Uses search to produce top 10 results.
Expand All @@ -248,7 +246,7 @@ def search_memory(self, query: str) -> List[str]:
- query (str): The query that we want to find related memories for
Returns:
- List[str]: A list of top 10 text results that matched the query
- list[str]: A list of top 10 text results that matched the query
"""
if self.memory is None:
return []
Expand Down
2 changes: 1 addition & 1 deletion agenthub/monologue_agent/utils/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def search(self, query: str, k: int = 10):
- k (int): Number of top results to return
Returns:
- List[str]: List of top k results found in current memory
- list[str]: list of top k results found in current memory
"""
retriever = VectorIndexRetriever(
index=self.index,
Expand Down
2 changes: 1 addition & 1 deletion agenthub/monologue_agent/utils/monologue.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def get_thoughts(self):
Get the current thoughts of the agent.
Returns:
- List: The list of thoughts that the agent has.
- list: The list of thoughts that the agent has.
"""
return self.thoughts

Expand Down
15 changes: 7 additions & 8 deletions agenthub/monologue_agent/utils/prompts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import re
from json import JSONDecodeError
from typing import List

from opendevin.core.config import config
from opendevin.core.exceptions import LLMOutputError
Expand Down Expand Up @@ -98,7 +97,7 @@
"""


def get_summarize_monologue_prompt(thoughts: List[dict]):
def get_summarize_monologue_prompt(thoughts: list[dict]):
"""
Gets the prompt for summarizing the monologue
Expand All @@ -112,16 +111,16 @@ def get_summarize_monologue_prompt(thoughts: List[dict]):

def get_request_action_prompt(
task: str,
thoughts: List[dict],
background_commands_obs: List[CmdOutputObservation] = [],
thoughts: list[dict],
background_commands_obs: list[CmdOutputObservation] = [],
):
"""
Gets the action prompt formatted with appropriate values.
Parameters:
- task (str): The current task the agent is trying to accomplish
- thoughts (List[dict]): The agent's current thoughts
- background_commands_obs (List[CmdOutputObservation]): List of all observed background commands running
- thoughts (list[dict]): The agent's current thoughts
- background_commands_obs (list[CmdOutputObservation]): list of all observed background commands running
Returns:
- str: Formatted prompt string with hint, task, monologue, and background included
Expand Down Expand Up @@ -203,15 +202,15 @@ def rank(match):
return action_from_dict(action_dict)


def parse_summary_response(response: str) -> List[dict]:
def parse_summary_response(response: str) -> list[dict]:
"""
Parses a summary of the monologue
Parameters:
- response (str): The response string to be parsed
Returns:
- List[dict]: The list of summaries output by the model
- list[dict]: The list of summaries output by the model
"""
parsed = json.loads(response)
return parsed['new_monologue']
4 changes: 1 addition & 3 deletions agenthub/planner_agent/agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import List

from opendevin.controller.agent import Agent
from opendevin.controller.state.state import State
from opendevin.events.action import Action, AgentFinishAction
Expand Down Expand Up @@ -46,5 +44,5 @@ def step(self, state: State) -> Action:
action = parse_response(action_resp)
return action

def search_memory(self, query: str) -> List[str]:
def search_memory(self, query: str) -> list[str]:
return []
5 changes: 2 additions & 3 deletions agenthub/planner_agent/prompt.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
from typing import List, Tuple

from opendevin.controller.state.plan import Plan
from opendevin.core.logger import opendevin_logger as logger
Expand Down Expand Up @@ -124,14 +123,14 @@ def get_hint(latest_action_id: str) -> str:
return hints.get(latest_action_id, '')


def get_prompt(plan: Plan, history: List[Tuple[Action, Observation]]) -> str:
def get_prompt(plan: Plan, history: list[tuple[Action, Observation]]) -> str:
"""
Gets the prompt for the planner agent.
Formatted with the most recent action-observation pairs, current task, and hint based on last action
Parameters:
- plan (Plan): The original plan outlined by the user with LLM defined tasks
- history (List[Tuple[Action, Observation]]): List of corresponding action-observation pairs
- history (list[tuple[Action, Observation]]): list of corresponding action-observation pairs
Returns:
- str: The formatted string prompt with historical values
Expand Down
16 changes: 8 additions & 8 deletions docs/modules/usage/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ sidebar_position: 3

### Description

This agent implements the CodeAct idea ([paper](https://arxiv.org/abs/2402.13463), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) that consolidates LLM agents’ **act**ions into a unified **code** action space for both *simplicity* and *performance* (see paper for more details).
This agent implements the CodeAct idea ([paper](https://arxiv.org/abs/2402.13463), [tweet](https://twitter.com/xingyaow_/status/1754556835703751087)) that consolidates LLM agents’ **act**ions into a unified **code** action space for both _simplicity_ and _performance_ (see paper for more details).

The conceptual idea is illustrated below. At each turn, the agent can:

1. **Converse**: Communicate with humans in natural language to ask for clarification, confirmation, etc.
2. **CodeAct**: Choose to perform the task by executing code

- Execute any valid Linux `bash` command
- Execute any valid `Python` code with [an interactive Python interpreter](https://ipython.org/). This is simulated through `bash` command, see plugin system below for more details.

Expand All @@ -22,15 +23,15 @@ The conceptual idea is illustrated below. At each turn, the agent can:
### Plugin System

To make the CodeAct agent more powerful with only access to `bash` action space, CodeAct agent leverages OpenDevin's plugin system:

- [Jupyter plugin](https://github.com/OpenDevin/OpenDevin/tree/main/opendevin/runtime/plugins/jupyter): for IPython execution via bash command
- [SWE-agent tool plugin](https://github.com/OpenDevin/OpenDevin/tree/main/opendevin/runtime/plugins/swe_agent_commands): Powerful bash command line tools for software development tasks introduced by [swe-agent](https://github.com/princeton-nlp/swe-agent).

### Demo

https://github.com/OpenDevin/OpenDevin/assets/38853559/f592a192-e86c-4f48-ad31-d69282d5f6ac

*Example of CodeActAgent with `gpt-4-turbo-2024-04-09` performing a data science task (linear regression)*

_Example of CodeActAgent with `gpt-4-turbo-2024-04-09` performing a data science task (linear regression)_

### Actions

Expand All @@ -50,18 +51,17 @@ https://github.com/OpenDevin/OpenDevin/assets/38853559/f592a192-e86c-4f48-ad31-d

### Methods

| Method | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `__init__` | Initializes an agent with `llm` and a list of messages `List[Mapping[str, str]]` |
| Method | Description |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `__init__` | Initializes an agent with `llm` and a list of messages `list[Mapping[str, str]]` |
| `step` | Performs one step using the CodeAct Agent. This includes gathering info on previous steps and prompting the model to make a command to execute. |
| `search_memory` | Not yet implemented |
| `search_memory` | Not yet implemented |

### Work-in-progress & Next step

[] Support web-browsing
[] Complete the workflow for CodeAct agent to submit Github PRs


## Monologue Agent

### Description
Expand Down
8 changes: 4 additions & 4 deletions opendevin/controller/agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Dict, List, Type
from typing import TYPE_CHECKING, Type

if TYPE_CHECKING:
from opendevin.controller.state.state import State
Expand All @@ -20,8 +20,8 @@ class Agent(ABC):
It tracks the execution status and maintains a history of interactions.
"""

_registry: Dict[str, Type['Agent']] = {}
sandbox_plugins: List[PluginRequirement] = []
_registry: dict[str, Type['Agent']] = {}
sandbox_plugins: list[PluginRequirement] = []

def __init__(
self,
Expand Down Expand Up @@ -49,7 +49,7 @@ def step(self, state: 'State') -> 'Action':
pass

@abstractmethod
def search_memory(self, query: str) -> List[str]:
def search_memory(self, query: str) -> list[str]:
"""
Searches the agent's memory for information relevant to the given query.
Expand Down
Loading

0 comments on commit f8d4b1a

Please sign in to comment.