Kaynağa Gözat

CodeActAgent: Fix delegate history (#2672)

Boxuan Li 1 yıl önce
ebeveyn
işleme
7766a3283e

+ 4 - 8
agenthub/codeact_agent/codeact_agent.py

@@ -10,15 +10,14 @@ from opendevin.controller.agent import Agent
 from opendevin.controller.state.state import State
 from opendevin.controller.state.state import State
 from opendevin.events.action import (
 from opendevin.events.action import (
     Action,
     Action,
+    AgentDelegateAction,
     AgentFinishAction,
     AgentFinishAction,
-    BrowseInteractiveAction,
     CmdRunAction,
     CmdRunAction,
     IPythonRunCellAction,
     IPythonRunCellAction,
     MessageAction,
     MessageAction,
 )
 )
 from opendevin.events.observation import (
 from opendevin.events.observation import (
     AgentDelegateObservation,
     AgentDelegateObservation,
-    BrowserOutputObservation,
     CmdOutputObservation,
     CmdOutputObservation,
     IPythonRunCellObservation,
     IPythonRunCellObservation,
 )
 )
@@ -38,8 +37,8 @@ def action_to_str(action: Action) -> str:
         return f'{action.thought}\n<execute_bash>\n{action.command}\n</execute_bash>'
         return f'{action.thought}\n<execute_bash>\n{action.command}\n</execute_bash>'
     elif isinstance(action, IPythonRunCellAction):
     elif isinstance(action, IPythonRunCellAction):
         return f'{action.thought}\n<execute_ipython>\n{action.code}\n</execute_ipython>'
         return f'{action.thought}\n<execute_ipython>\n{action.code}\n</execute_ipython>'
-    elif isinstance(action, BrowseInteractiveAction):
-        return f'{action.thought}\n<execute_browse>\n{action.browser_actions}\n</execute_browse>'
+    elif isinstance(action, AgentDelegateAction):
+        return f'{action.thought}\n<execute_browse>\n{action.inputs["task"]}\n</execute_browse>'
     elif isinstance(action, MessageAction):
     elif isinstance(action, MessageAction):
         return action.content
         return action.content
     return ''
     return ''
@@ -47,7 +46,7 @@ def action_to_str(action: Action) -> str:
 
 
 def get_action_message(action: Action) -> dict[str, str] | None:
 def get_action_message(action: Action) -> dict[str, str] | None:
     if (
     if (
-        isinstance(action, BrowseInteractiveAction)
+        isinstance(action, AgentDelegateAction)
         or isinstance(action, CmdRunAction)
         or isinstance(action, CmdRunAction)
         or isinstance(action, IPythonRunCellAction)
         or isinstance(action, IPythonRunCellAction)
         or isinstance(action, MessageAction)
         or isinstance(action, MessageAction)
@@ -78,9 +77,6 @@ def get_observation_message(obs) -> dict[str, str] | None:
         content = '\n'.join(splitted)
         content = '\n'.join(splitted)
         content = truncate_observation(content)
         content = truncate_observation(content)
         return {'role': 'user', 'content': content}
         return {'role': 'user', 'content': content}
-    elif isinstance(obs, BrowserOutputObservation):
-        content = 'OBSERVATION:\n' + truncate_observation(obs.content)
-        return {'role': 'user', 'content': content}
     elif isinstance(obs, AgentDelegateObservation):
     elif isinstance(obs, AgentDelegateObservation):
         content = 'OBSERVATION:\n' + truncate_observation(str(obs.outputs))
         content = 'OBSERVATION:\n' + truncate_observation(str(obs.outputs))
         return {'role': 'user', 'content': content}
         return {'role': 'user', 'content': content}

+ 4 - 0
opendevin/controller/agent.py

@@ -72,6 +72,10 @@ class Agent(ABC):
         """
         """
         self._complete = False
         self._complete = False
 
 
+    @property
+    def name(self):
+        return self.__class__.__name__
+
     @classmethod
     @classmethod
     def register(cls, name: str, agent_cls: Type['Agent']):
     def register(cls, name: str, agent_cls: Type['Agent']):
         """
         """

+ 9 - 7
opendevin/controller/agent_controller.py

@@ -195,7 +195,7 @@ class AgentController:
 
 
     async def set_agent_state_to(self, new_state: AgentState):
     async def set_agent_state_to(self, new_state: AgentState):
         logger.info(
         logger.info(
-            f'[Agent Controller {self.id}] Setting agent({type(self.agent).__name__}) state from {self.state.agent_state} to {new_state}'
+            f'[Agent Controller {self.id}] Setting agent({self.agent.name}) state from {self.state.agent_state} to {new_state}'
         )
         )
 
 
         if new_state == self.state.agent_state:
         if new_state == self.state.agent_state:
@@ -280,19 +280,21 @@ class AgentController:
                 # close delegate controller: we must close the delegate controller before adding new events
                 # close delegate controller: we must close the delegate controller before adding new events
                 await self.delegate.close()
                 await self.delegate.close()
 
 
-                # clean up delegate status
-                self.delegate = None
-                self.delegateAction = None
-
                 # update delegate result observation
                 # update delegate result observation
                 # TODO: replace this with AI-generated summary (#2395)
                 # TODO: replace this with AI-generated summary (#2395)
                 formatted_output = ', '.join(
                 formatted_output = ', '.join(
                     f'{key}: {value}' for key, value in outputs.items()
                     f'{key}: {value}' for key, value in outputs.items()
                 )
                 )
-                content = f'Delegate agent finishes task with {formatted_output}'
+                content = (
+                    f'{self.delegate.agent.name} finishes task with {formatted_output}'
+                )
                 obs: Observation = AgentDelegateObservation(
                 obs: Observation = AgentDelegateObservation(
                     outputs=outputs, content=content
                     outputs=outputs, content=content
                 )
                 )
+
+                # clean up delegate status
+                self.delegate = None
+                self.delegateAction = None
                 await self.event_stream.add_event(obs, EventSource.AGENT)
                 await self.event_stream.add_event(obs, EventSource.AGENT)
             return
             return
 
 
@@ -300,7 +302,7 @@ class AgentController:
             raise MaxCharsExceedError(self.state.num_of_chars, self.max_chars)
             raise MaxCharsExceedError(self.state.num_of_chars, self.max_chars)
 
 
         logger.info(
         logger.info(
-            f'{type(self.agent).__name__} LEVEL {self.state.delegate_level} STEP {self.state.iteration}',
+            f'{self.agent.name} LEVEL {self.state.delegate_level} STEP {self.state.iteration}',
             extra={'msg_type': 'STEP'},
             extra={'msg_type': 'STEP'},
         )
         )
         if self.state.iteration >= self.state.max_iterations:
         if self.state.iteration >= self.state.max_iterations:

+ 7 - 0
tests/integration/mock/CodeActAgent/test_browse_internet/prompt_005.log

@@ -332,6 +332,13 @@ Browse localhost:8000, and tell me the ultimate answer to life. Do not ask me fo
 
 
 ----------
 ----------
 
 
+
+<execute_browse>
+Sure! Let me browse the server's homepage at http://localhost:8000 and find the ultimate answer to life.. I should start with: Get the content on "http://localhost:8000"
+</execute_browse>
+
+----------
+
 OBSERVATION:
 OBSERVATION:
 {'content': 'The answer to life, the universe, and everything is: OpenDevin is all you need!'}
 {'content': 'The answer to life, the universe, and everything is: OpenDevin is all you need!'}
 
 

+ 1 - 1
tests/integration/mock/ManagerAgent/test_edits/prompt_006.log

@@ -70,7 +70,7 @@ as well as observations you've made. This only includes the MOST RECENT
 actions and observations--more may have happened before that.
 actions and observations--more may have happened before that.
 They are time-ordered, with your most recent action at the bottom.
 They are time-ordered, with your most recent action at the bottom.
 
 
-[[{"source": "user", "action": "message", "args": {"content": "Fix typos in bad.txt. Do not ask me for confirmation at any point.", "wait_for_response": false}}, {"observation": "null", "content": "", "extras": {}}], [{"source": "agent", "action": "delegate", "args": {"agent": "TypoFixerAgent", "inputs": {"task": "Fix typos in bad.txt"}, "thought": ""}}, {"observation": "null", "content": "", "extras": {}}], [{"action": "null", "args": {}}, {"source": "agent", "observation": "delegate", "content": "Delegate agent finishes task with summary: {'file': 'bad.txt', 'typos_fixed': [{'original': 'typoo', 'corrected': 'typo'}, {'original': 'mor', 'corrected': 'more'}]}", "extras": {"outputs": {"summary": {"file": "bad.txt", "typos_fixed": [{"original": "typoo", "corrected": "typo"}, {"original": "mor", "corrected": "more"}]}}}}]]
+[[{"source": "user", "action": "message", "args": {"content": "Fix typos in bad.txt. Do not ask me for confirmation at any point.", "wait_for_response": false}}, {"observation": "null", "content": "", "extras": {}}], [{"source": "agent", "action": "delegate", "args": {"agent": "TypoFixerAgent", "inputs": {"task": "Fix typos in bad.txt"}, "thought": ""}}, {"observation": "null", "content": "", "extras": {}}], [{"action": "null", "args": {}}, {"source": "agent", "observation": "delegate", "content": "TypoFixerAgent finishes task with summary: {'file': 'bad.txt', 'typos_fixed': [{'original': 'typoo', 'corrected': 'typo'}, {'original': 'mor', 'corrected': 'more'}]}", "extras": {"outputs": {"summary": {"file": "bad.txt", "typos_fixed": [{"original": "typoo", "corrected": "typo"}, {"original": "mor", "corrected": "more"}]}}}}]]
 
 
 If the last item in the history is an error, you should try to fix it. If you
 If the last item in the history is an error, you should try to fix it. If you
 cannot fix it, call the `reject` action.
 cannot fix it, call the `reject` action.

+ 1 - 1
tests/integration/mock/ManagerAgent/test_simple_task_rejection/prompt_006.log

@@ -70,7 +70,7 @@ as well as observations you've made. This only includes the MOST RECENT
 actions and observations--more may have happened before that.
 actions and observations--more may have happened before that.
 They are time-ordered, with your most recent action at the bottom.
 They are time-ordered, with your most recent action at the bottom.
 
 
-[[{"source": "user", "action": "message", "args": {"content": "Write a git commit message for the current staging area. Do not ask me for confirmation at any point.", "wait_for_response": false}}, {"observation": "null", "content": "", "extras": {}}], [{"source": "agent", "action": "delegate", "args": {"agent": "CommitWriterAgent", "inputs": {}, "thought": ""}}, {"observation": "null", "content": "", "extras": {}}], [{"action": "null", "args": {}}, {"source": "agent", "observation": "delegate", "content": "Delegate agent finishes task with reason: Not a valid git repository.", "extras": {"outputs": {"reason": "Not a valid git repository."}}}]]
+[[{"source": "user", "action": "message", "args": {"content": "Write a git commit message for the current staging area. Do not ask me for confirmation at any point.", "wait_for_response": false}}, {"observation": "null", "content": "", "extras": {}}], [{"source": "agent", "action": "delegate", "args": {"agent": "CommitWriterAgent", "inputs": {}, "thought": ""}}, {"observation": "null", "content": "", "extras": {}}], [{"action": "null", "args": {}}, {"source": "agent", "observation": "delegate", "content": "CommitWriterAgent finishes task with reason: Not a valid git repository.", "extras": {"outputs": {"reason": "Not a valid git repository."}}}]]
 
 
 If the last item in the history is an error, you should try to fix it. If you
 If the last item in the history is an error, you should try to fix it. If you
 cannot fix it, call the `reject` action.
 cannot fix it, call the `reject` action.