Эх сурвалжийг харах

Add AgentRejectAction across multiple modules (#1615)

* Add AgentRejectAction across multiple modules

This commit introduces the AgentRejectAction class and integrates it across various modules and actions. It includes updates to READMEs, action definitions, and agent controllers to handle the new 'reject' action. This functionality will allow agents to properly signal task rejection.

* Fix unit test

* Remove wrong generates attributes from a few micro-agents
Boxuan Li 1 жил өмнө
parent
commit
af5bdf67aa

+ 4 - 0
agenthub/README.md

@@ -33,6 +33,10 @@ Here is a list of available Actions, which can be returned by `agent.step()`:
 - [`AgentRecallAction`](../opendevin/action/agent.py) - Searches memory (e.g. a vector database)
 - [`AddTaskAction`](../opendevin/action/tasks.py) - Adds a subtask to the plan
 - [`ModifyTaskAction`](../opendevin/action/tasks.py) - Changes the state of a subtask
+- [`AgentThinkAction`](../opendevin/action/agent.py) - A no-op that allows the agent to add plaintext to the history (as well as the chat log)
+- [`AgentTalkAction`](../opendevin/action/agent.py) - A no-op that allows the agent to add plaintext to the history and talk to the user.
+- [`AgentFinishAction`](../opendevin/action/agent.py) - Stops the control loop, allowing the user/delegator agent to enter a new task
+- [`AgentRejectAction`](../opendevin/action/agent.py) - Stops the control loop, allowing the user/delegator agent to enter a new task
 - [`AgentFinishAction`](../opendevin/action/agent.py) - Stops the control loop, allowing the user to enter a new task
 - [`MessageAction`](../opendevin/action/message.py) - Represents a message from an agent or the user
 

+ 5 - 0
agenthub/dummy_agent/agent.py

@@ -8,6 +8,7 @@ from opendevin.events.action import (
     AddTaskAction,
     AgentFinishAction,
     AgentRecallAction,
+    AgentRejectAction,
     BrowseURLAction,
     CmdRunAction,
     FileReadAction,
@@ -123,6 +124,10 @@ class DummyAgent(Agent):
                 'action': AgentFinishAction(),
                 'observations': [],
             },
+            {
+                'action': AgentRejectAction(),
+                'observations': [],
+            },
         ]
 
     def step(self, state: State) -> Action:

+ 2 - 0
agenthub/micro/_instructions/actions/reject.md

@@ -0,0 +1,2 @@
+* `reject` - reject the task. Arguments:
+  * `outputs` - a dictionary representing the outputs of your task, if any

+ 0 - 1
agenthub/micro/commit_writer/agent.yaml

@@ -1,6 +1,5 @@
 name: CommitWriterAgent
 description: "Write a git commit message for files in the git staging area"
-generates: Action
 inputs: {}
 outputs:
   answer: string

+ 4 - 1
agenthub/micro/commit_writer/prompt.md

@@ -12,7 +12,9 @@ changes. The commit message should include:
 - Optionally, a detailed description if the changes are complex or need further explanation.
 
 You should find the diff using `git diff --cached`, compile a commit message,
-and call the `finish` action with `outputs.answer` set to the answer.
+and call the `finish` action with `outputs.answer` set to the answer. If current
+repo is not a valid git repo, or there is no diff in the staging area, please call
+the `reject` action with `outputs.answer` set to the reason.
 
 ## History
 {{ instructions.history_truncated }}
@@ -22,6 +24,7 @@ If the last item in the history is an error, you should try to fix it.
 
 ## Available Actions
 {{ instructions.actions.run }}
+{{ instructions.actions.reject }}
 {{ instructions.actions.finish }}
 
 ## Format

+ 0 - 1
agenthub/micro/math_agent/agent.yaml

@@ -1,6 +1,5 @@
 name: MathAgent
 description: "Solves simple and complex math problems using python"
-generates: Action
 container: python:3.12.3-bookworm
 inputs:
   task: string

+ 0 - 1
agenthub/micro/postgres_agent/agent.yaml

@@ -1,6 +1,5 @@
 name: PostgresAgent
 description: Writes and maintains PostgreSQL migrations
-generates: Action
 inputs:
   task: string
 outputs: {}

+ 4 - 1
opendevin/controller/agent_controller.py

@@ -20,6 +20,7 @@ from opendevin.events.action import (
     Action,
     AgentDelegateAction,
     AgentFinishAction,
+    AgentRejectAction,
     ChangeAgentStateAction,
     MessageAction,
     NullAction,
@@ -271,7 +272,9 @@ class AgentController:
             await self.set_agent_state_to(AgentState.AWAITING_USER_INPUT)
             return False
 
-        finished = isinstance(action, AgentFinishAction)
+        finished = isinstance(action, AgentFinishAction) or isinstance(
+            action, AgentRejectAction
+        )
         if finished:
             self.state.outputs = action.outputs  # type: ignore[attr-defined]
             logger.info(action, extra={'msg_type': 'INFO'})

+ 5 - 0
opendevin/core/schema/action.py

@@ -61,6 +61,11 @@ class ActionTypeSchema(BaseModel):
     use the finish action to stop working.
     """
 
+    REJECT: str = Field(default='reject')
+    """If you're absolutely certain that you cannot complete the task with given requirements,
+    use the reject action to stop working.
+    """
+
     NULL: str = Field(default='null')
 
     SUMMARIZE: str = Field(default='summarize')

+ 3 - 0
opendevin/events/action/__init__.py

@@ -5,6 +5,7 @@ from .agent import (
     AgentDelegateAction,
     AgentFinishAction,
     AgentRecallAction,
+    AgentRejectAction,
     AgentSummarizeAction,
     ChangeAgentStateAction,
 )
@@ -25,6 +26,7 @@ actions = (
     FileWriteAction,
     AgentRecallAction,
     AgentFinishAction,
+    AgentRejectAction,
     AgentDelegateAction,
     AddTaskAction,
     ModifyTaskAction,
@@ -69,6 +71,7 @@ __all__ = [
     'FileWriteAction',
     'AgentRecallAction',
     'AgentFinishAction',
+    'AgentRejectAction',
     'AgentDelegateAction',
     'AgentSummarizeAction',
     'AddTaskAction',

+ 11 - 0
opendevin/events/action/agent.py

@@ -65,6 +65,17 @@ class AgentFinishAction(Action):
         return "All done! What's next on the agenda?"
 
 
+@dataclass
+class AgentRejectAction(Action):
+    outputs: Dict = field(default_factory=dict)
+    thought: str = ''
+    action: str = ActionType.REJECT
+
+    @property
+    def message(self) -> str:
+        return 'Task is rejected by the agent.'
+
+
 @dataclass
 class AgentDelegateAction(Action):
     agent: str

+ 6 - 0
tests/unit/test_action_serialization.py

@@ -3,6 +3,7 @@ from opendevin.events.action import (
     AddTaskAction,
     AgentFinishAction,
     AgentRecallAction,
+    AgentRejectAction,
     BrowseURLAction,
     CmdKillAction,
     CmdRunAction,
@@ -58,6 +59,11 @@ def test_agent_finish_action_serialization_deserialization():
     serialization_deserialization(original_action_dict, AgentFinishAction)
 
 
+def test_agent_reject_action_serialization_deserialization():
+    original_action_dict = {'action': 'reject', 'args': {'outputs': {}, 'thought': ''}}
+    serialization_deserialization(original_action_dict, AgentRejectAction)
+
+
 def test_cmd_kill_action_serialization_deserialization():
     original_action_dict = {'action': 'kill', 'args': {'id': '1337', 'thought': ''}}
     serialization_deserialization(original_action_dict, CmdKillAction)