Skip to content

Commit 4099e48

Browse files
authored
Removed config from agent controller (All-Hands-AI#3038)
* Removed config from agent controller * Fix tests * Increase budget * Update tests * Update prompts * Add missing prompt * Fix mistaken deletions * Fix browsing test * Fixed browse tests
1 parent c3d4f64 commit 4099e48

File tree

24 files changed

+847
-104
lines changed

24 files changed

+847
-104
lines changed

opendevin/controller/agent_controller.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from opendevin.controller.agent import Agent
66
from opendevin.controller.state.state import State, TrafficControlState
77
from opendevin.controller.stuck import StuckDetector
8-
from opendevin.core.config import config
8+
from opendevin.core.config import LLMConfig
99
from opendevin.core.exceptions import (
1010
LLMMalformedActionError,
1111
LLMNoActionError,
@@ -38,8 +38,6 @@
3838
)
3939
from opendevin.llm.llm import LLM
4040

41-
MAX_ITERATIONS = config.max_iterations
42-
MAX_BUDGET_PER_TASK = config.max_budget_per_task
4341
# note: RESUME is only available on web GUI
4442
TRAFFIC_CONTROL_REMINDER = (
4543
"Please click on resume button if you'd like to continue, or start a new task."
@@ -53,6 +51,7 @@ class AgentController:
5351
event_stream: EventStream
5452
state: State
5553
confirmation_mode: bool
54+
agent_to_llm_config: dict[str, LLMConfig]
5655
agent_task: Optional[asyncio.Task] = None
5756
parent: 'AgentController | None' = None
5857
delegate: 'AgentController | None' = None
@@ -62,10 +61,11 @@ def __init__(
6261
self,
6362
agent: Agent,
6463
event_stream: EventStream,
64+
max_iterations: int,
65+
max_budget_per_task: float | None = None,
66+
agent_to_llm_config: dict[str, LLMConfig] | None = None,
6567
sid: str = 'default',
66-
max_iterations: int | None = MAX_ITERATIONS,
6768
confirmation_mode: bool = False,
68-
max_budget_per_task: float | None = MAX_BUDGET_PER_TASK,
6969
initial_state: State | None = None,
7070
is_delegate: bool = False,
7171
headless_mode: bool = True,
@@ -75,9 +75,11 @@ def __init__(
7575
Args:
7676
agent: The agent instance to control.
7777
event_stream: The event stream to publish events to.
78-
sid: The session ID of the agent.
7978
max_iterations: The maximum number of iterations the agent can run.
8079
max_budget_per_task: The maximum budget (in USD) allowed per task, beyond which the agent will stop.
80+
agent_to_llm_config: A dictionary mapping agent names to LLM configurations in the case that
81+
we delegate to a different agent.
82+
sid: The session ID of the agent.
8183
initial_state: The initial state of the controller.
8284
is_delegate: Whether this controller is a delegate.
8385
headless_mode: Whether the agent is run in headless mode.
@@ -94,16 +96,13 @@ def __init__(
9496
)
9597

9698
# state from the previous session, state from a parent agent, or a fresh state
97-
max_iterations = (
98-
max_iterations if max_iterations is not None else MAX_ITERATIONS
99-
)
10099
self.set_initial_state(
101100
state=initial_state,
102101
max_iterations=max_iterations,
103102
confirmation_mode=confirmation_mode,
104103
)
105-
106104
self.max_budget_per_task = max_budget_per_task
105+
self.agent_to_llm_config = agent_to_llm_config if agent_to_llm_config else {}
107106

108107
# stuck helper
109108
self._stuck_detector = StuckDetector(self.state)
@@ -253,7 +252,7 @@ def get_agent_state(self):
253252

254253
async def start_delegate(self, action: AgentDelegateAction):
255254
agent_cls: Type[Agent] = Agent.get_cls(action.agent)
256-
llm_config = config.get_llm_config_from_agent(action.agent)
255+
llm_config = self.agent_to_llm_config.get(action.agent, self.agent.llm.config)
257256
llm = LLM(config=llm_config)
258257
delegate_agent = agent_cls(llm=llm)
259258
state = State(
@@ -274,6 +273,7 @@ async def start_delegate(self, action: AgentDelegateAction):
274273
event_stream=self.event_stream,
275274
max_iterations=self.state.max_iterations,
276275
max_budget_per_task=self.max_budget_per_task,
276+
agent_to_llm_config=self.agent_to_llm_config,
277277
initial_state=state,
278278
is_delegate=True,
279279
)
@@ -423,7 +423,7 @@ def get_state(self):
423423
def set_initial_state(
424424
self,
425425
state: State | None,
426-
max_iterations: int = MAX_ITERATIONS,
426+
max_iterations: int,
427427
confirmation_mode: bool = False,
428428
):
429429
# state from the previous session, state from a parent agent, or a new state

opendevin/core/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ def get_agent_config(self, name='agent') -> AgentConfig:
275275
def set_agent_config(self, value: AgentConfig, name='agent'):
276276
self.agents[name] = value
277277

278+
def get_agent_to_llm_config_map(self) -> dict[str, LLMConfig]:
279+
"""Get a map of agent names to llm configs."""
280+
return {name: self.get_llm_config_from_agent(name) for name in self.agents}
281+
278282
def get_llm_config_from_agent(self, name='agent') -> LLMConfig:
279283
agent_config: AgentConfig = self.get_agent_config(name)
280284
llm_config_name = agent_config.llm_config

opendevin/core/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ def read_task_from_stdin() -> str:
3333
async def run_agent_controller(
3434
agent: Agent,
3535
task_str: str,
36-
max_iterations: int | None = None,
37-
max_budget_per_task: float | None = None,
36+
max_iterations: int,
37+
max_budget_per_task: float,
3838
exit_on_message: bool = False,
3939
fake_user_response_fn: Callable[[State | None], str] | None = None,
4040
sandbox: Sandbox | None = None,
@@ -75,6 +75,7 @@ async def run_agent_controller(
7575
agent=agent,
7676
max_iterations=max_iterations,
7777
max_budget_per_task=max_budget_per_task,
78+
agent_to_llm_config=config.get_agent_to_llm_config_map(),
7879
event_stream=event_stream,
7980
initial_state=initial_state,
8081
headless_mode=headless_mode,

opendevin/server/session/agent.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from opendevin.controller import AgentController
55
from opendevin.controller.agent import Agent
66
from opendevin.controller.state.state import State
7-
from opendevin.core.config import SandboxConfig
7+
from opendevin.core.config import LLMConfig, SandboxConfig
88
from opendevin.core.logger import opendevin_logger as logger
99
from opendevin.events.stream import EventStream
1010
from opendevin.runtime import DockerSSHBox, get_runtime_cls
@@ -37,6 +37,8 @@ async def start(
3737
agent: Agent,
3838
confirmation_mode: bool,
3939
max_iterations: int,
40+
max_budget_per_task: float | None = None,
41+
agent_to_llm_config: dict[str, LLMConfig] | None = None,
4042
):
4143
"""Starts the agent session.
4244
@@ -48,7 +50,13 @@ async def start(
4850
'Session already started. You need to close this session and start a new one.'
4951
)
5052
await self._create_runtime(runtime_name, sandbox_config)
51-
await self._create_controller(agent, confirmation_mode, max_iterations)
53+
await self._create_controller(
54+
agent,
55+
confirmation_mode,
56+
max_iterations,
57+
max_budget_per_task=max_budget_per_task,
58+
agent_to_llm_config=agent_to_llm_config,
59+
)
5260

5361
async def close(self):
5462
if self._closed:
@@ -74,7 +82,12 @@ async def _create_runtime(self, runtime_name: str, sandbox_config: SandboxConfig
7482
await self.runtime.ainit()
7583

7684
async def _create_controller(
77-
self, agent: Agent, confirmation_mode: bool, max_iterations: int
85+
self,
86+
agent: Agent,
87+
confirmation_mode: bool,
88+
max_iterations: int,
89+
max_budget_per_task: float | None = None,
90+
agent_to_llm_config: dict[str, LLMConfig] | None = None,
7891
):
7992
"""Creates an AgentController instance."""
8093
if self.controller is not None:
@@ -100,14 +113,18 @@ async def _create_controller(
100113
event_stream=self.event_stream,
101114
agent=agent,
102115
max_iterations=int(max_iterations),
116+
max_budget_per_task=max_budget_per_task,
117+
agent_to_llm_config=agent_to_llm_config,
103118
confirmation_mode=confirmation_mode,
104119
# AgentSession is designed to communicate with the frontend, so we don't want to
105120
# run the agent in headless mode.
106121
headless_mode=False,
107122
)
108123
try:
109124
agent_state = State.restore_from_session(self.sid)
110-
self.controller.set_initial_state(agent_state)
125+
self.controller.set_initial_state(
126+
agent_state, max_iterations, confirmation_mode
127+
)
111128
logger.info(f'Restored agent state from session, sid: {self.sid}')
112129
except Exception as e:
113130
print('Error restoring state', e)

opendevin/server/session/session.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ async def _initialize_agent(self, data: dict):
106106
agent=agent,
107107
confirmation_mode=confirmation_mode,
108108
max_iterations=max_iterations,
109+
max_budget_per_task=self.config.max_budget_per_task,
110+
agent_to_llm_config=self.config.get_agent_to_llm_config_map(),
109111
)
110112
except Exception as e:
111113
logger.exception(f'Error creating controller: {e}')

tests/integration/mock/CodeActAgent/test_browse_internet/prompt_002.log

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ possible next action to accomplish your goal. Your answer will be interpreted
88
and executed by a program, make sure to follow the formatting instructions.
99

1010
# Goal:
11-
Certainly! I'll browse localhost:8000 and retrieve the ultimate answer to life for you.. I should start with: Get the content on "http://localhost:8000"
11+
Sure! Let me browse the server's homepage at http://localhost:8000 to find the ultimate answer to life.. I should start with: Get the content on "http://localhost:8000"
1212

1313
# Action Space
1414

tests/integration/mock/CodeActAgent/test_browse_internet/prompt_003.log

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ possible next action to accomplish your goal. Your answer will be interpreted
88
and executed by a program, make sure to follow the formatting instructions.
99

1010
# Goal:
11-
Certainly! I'll browse localhost:8000 and retrieve the ultimate answer to life for you.. I should start with: Get the content on "http://localhost:8000"
11+
Sure! Let me browse the server's homepage at http://localhost:8000 to find the ultimate answer to life.. I should start with: Get the content on "http://localhost:8000"
1212

1313
# Action Space
1414

tests/integration/mock/CodeActAgent/test_browse_internet/prompt_004.log

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ possible next action to accomplish your goal. Your answer will be interpreted
88
and executed by a program, make sure to follow the formatting instructions.
99

1010
# Goal:
11-
Certainly! I'll browse localhost:8000 and retrieve the ultimate answer to life for you.. I should start with: Get the content on "http://localhost:8000"
11+
Sure! Let me browse the server's homepage at http://localhost:8000 to find the ultimate answer to life.. I should start with: Get the content on "http://localhost:8000"
1212

1313
# Action Space
1414

tests/integration/mock/CodeActAgent/test_browse_internet/prompt_005.log

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,12 +405,12 @@ Browse localhost:8000, and tell me the ultimate answer to life. Do not ask me fo
405405

406406

407407
<execute_browse>
408-
Certainly! I'll browse localhost:8000 and retrieve the ultimate answer to life for you.. I should start with: Get the content on "http://localhost:8000"
408+
Sure! Let me browse the server's homepage at http://localhost:8000 to find the ultimate answer to life.. I should start with: Get the content on "http://localhost:8000"
409409
</execute_browse>
410410

411411
----------
412412

413413
OBSERVATION:
414-
{'content': 'The ultimate answer to life, the universe, and everything is: OpenDevin is all you need!'}
414+
{'content': 'The answer to life, the universe, and everything is: OpenDevin is all you need!'}
415415

416416
ENVIRONMENT REMINDER: You have 8 turns left to complete the task. When finished reply with <finish></finish>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Certainly! I'll browse localhost:8000 and retrieve the ultimate answer to life for you.
1+
Sure! Let me browse the server's homepage at http://localhost:8000 to find the ultimate answer to life.
22

33
<execute_browse>
44
Get the content on "http://localhost:8000"
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
1-
To accomplish our goal of retrieving the ultimate answer to life from localhost:8000, I need to start by accessing the content on that page. Here's my thought process and action:
2-
3-
In order to accomplish my goal, I need to navigate to the specified URL using the goto action.
4-
1+
In order to accomplish my goal, I need to navigate to the server's homepage at http://localhost:8000 to find the ultimate answer to life.
52
```goto("http://localhost:8000"
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
1-
To accomplish our goal of retrieving the ultimate answer to life, we need to click on the "Click me" button. The button has a bid of 10, so we should click on it.
2-
3-
Here's the action I'll take:
4-
5-
In order to accomplish my goal, I need to click on the button with bid 10
1+
In order to accomplish my goal, I need to click on the button with bid 10 to reveal the answer to life, the universe, and everything.
62
```click("10"
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
Based on the current Accessibility Tree and the previous actions taken, it appears that we have already successfully navigated to the webpage and clicked the button to reveal the answer. The answer is now visible on the page.
1+
In order to accomplish my goal, I need to read the static text that reveals the answer to life, the universe, and everything. The text is already visible and states "The answer is OpenDevin is all you need!"
22

3-
To accomplish our goal of retrieving the ultimate answer to life, I will now send this information to the user:
4-
5-
```
6-
send_msg_to_user("The ultimate answer to life, the universe, and everything is: OpenDevin is all you need!"
3+
```send_msg_to_user("The answer to life, the universe, and everything is: OpenDevin is all you need!"
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
Thank you for providing the content from localhost:8000. Based on the information retrieved, I can now answer your question about the ultimate answer to life.
1+
The content of the server's homepage at http://localhost:8000 is:
22

3-
The ultimate answer to life, according to the content on localhost:8000, is:
4-
5-
"OpenDevin is all you need!"
6-
7-
This appears to be a playful reference to the popular science fiction series "The Hitchhiker's Guide to the Galaxy" by Douglas Adams, where the answer to the ultimate question of life, the universe, and everything is famously "42". In this case, it's been adapted to promote OpenDevin as the ultimate solution.
3+
"The answer to life, the universe, and everything is: OpenDevin is all you need!"
84

5+
If you have any further questions or tasks, feel free to ask!
96
<finish></finish>

tests/integration/mock/CodeActSWEAgent/test_edits/prompt_003.log

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -613,26 +613,29 @@ OBSERVATION:
613613

614614
----------
615615

616-
I will now correct the typos in the `bad.txt` file.
616+
Let's correct the typos in the `bad.txt` file.
617+
618+
1. "stupid typoo" should be "stupid typo".
619+
2. "No mor typos!" should be "No more typos!".
620+
621+
I will make these changes now.
617622
<execute_ipython>
618623
edit_file_by_replace(
619624
"bad.txt",
620-
to_replace="This is a stupid typoo.",
621-
new_content="This is a stupid typo."
625+
to_replace="This is a stupid typoo.\nNo mor typos!",
626+
new_content="This is a stupid typo.\nNo more typos!"
622627
)
623628
</execute_ipython>
624629

625630
----------
626631

627632
OBSERVATION:
628-
[File: /workspace/bad.txt (4 lines total after edit)]
629-
(this is the beginning of the file)
630-
1|This is a stupid typo.
631-
2|Really?
632-
3|No mor typos!
633-
4|Enjoy!
634-
(this is the end of the file)
635-
[File updated (edited at line 1). Please review the changes and make sure they are correct (correct indentation, no duplicate lines, etc). Edit the file again if necessary.]
633+
[No exact match found in bad.txt for
634+
```
635+
This is a stupid typoo.
636+
No mor typos!
637+
```
638+
]
636639

637640

638641
ENVIRONMENT REMINDER: You have 12 turns left to complete the task.

0 commit comments

Comments
 (0)