Просмотр исходного кода

Frontend support for delegation and rejection (#2608)

1. Add support for rejection action on frontend
2. Show users the reason for rejection
3. Get rid of weird empty box after delegation
4. On web GUI, show customer when a delegation starts and ends
Boxuan Li 1 год назад
Родитель
Сommit
ee86d8d25e

+ 2 - 0
frontend/src/components/AgentControlBar.tsx

@@ -15,6 +15,7 @@ const IgnoreTaskStateMap: { [k: string]: AgentState[] } = {
     AgentState.PAUSED,
     AgentState.STOPPED,
     AgentState.FINISHED,
+    AgentState.REJECTED,
     AgentState.AWAITING_USER_INPUT,
   ],
   [AgentState.RUNNING]: [
@@ -22,6 +23,7 @@ const IgnoreTaskStateMap: { [k: string]: AgentState[] } = {
     AgentState.RUNNING,
     AgentState.STOPPED,
     AgentState.FINISHED,
+    AgentState.REJECTED,
     AgentState.AWAITING_USER_INPUT,
   ],
   [AgentState.STOPPED]: [AgentState.INIT, AgentState.STOPPED],

+ 4 - 0
frontend/src/components/AgentStatusBar.tsx

@@ -49,6 +49,10 @@ function AgentStatusBar() {
       message: t(I18nKey.CHAT_INTERFACE$AGENT_FINISHED_MESSAGE),
       indicator: IndicatorColor.GREEN,
     },
+    [AgentState.REJECTED]: {
+      message: t(I18nKey.CHAT_INTERFACE$AGENT_REJECTED_MESSAGE),
+      indicator: IndicatorColor.YELLOW,
+    },
     [AgentState.ERROR]: {
       message: t(I18nKey.CHAT_INTERFACE$AGENT_ERROR_MESSAGE),
       indicator: IndicatorColor.RED,

+ 5 - 0
frontend/src/i18n/translation.json

@@ -397,6 +397,11 @@
     "de": "Agent hat die Aufgabe erledigt.",
     "zh-CN": "智能体已完成任务"
   },
+  "CHAT_INTERFACE$AGENT_REJECTED_MESSAGE": {
+    "en": "Agent has rejected the task.",
+    "de": "Agent hat die Aufgabe abgelehnt.",
+    "zh-CN": "智能体拒绝任务"
+  },
   "CHAT_INTERFACE$AGENT_ERROR_MESSAGE": {
     "en": "Agent encountered an error.",
     "de": "Agent ist auf einen Fehler gelaufen.",

+ 6 - 0
frontend/src/services/actions.ts

@@ -36,6 +36,12 @@ const messageActions = {
   [ActionType.FINISH]: (message: ActionMessage) => {
     store.dispatch(addAssistantMessage(message.message));
   },
+  [ActionType.REJECT]: (message: ActionMessage) => {
+    store.dispatch(addAssistantMessage(message.message));
+  },
+  [ActionType.DELEGATE]: (message: ActionMessage) => {
+    store.dispatch(addAssistantMessage(message.message));
+  },
   [ActionType.RUN]: (message: ActionMessage) => {
     if (message.args.thought) {
       store.dispatch(addAssistantMessage(message.args.thought));

+ 6 - 0
frontend/src/services/observations.ts

@@ -27,6 +27,12 @@ export function handleObservationMessage(message: ObservationMessage) {
     case ObservationType.AGENT_STATE_CHANGED:
       store.dispatch(changeAgentState(message.extras.agent_state));
       break;
+    case ObservationType.DELEGATE:
+      // TODO: better UI for delegation result (#2309)
+      if (message.content) {
+        store.dispatch(addAssistantMessage(message.content));
+      }
+      break;
     default:
       store.dispatch(addAssistantMessage(message.message));
       break;

+ 6 - 0
frontend/src/types/ActionType.tsx

@@ -26,6 +26,9 @@ enum ActionType {
   // Interact with the browser instance.
   BROWSE_INTERACTIVE = "browse_interactive",
 
+  // Delegate a (sub)task to another agent.
+  DELEGATE = "delegate",
+
   // Searches long-term memory.
   RECALL = "recall",
 
@@ -33,6 +36,9 @@ enum ActionType {
   // use the finish action to stop working.
   FINISH = "finish",
 
+  // Reject a request from user or another agent.
+  REJECT = "reject",
+
   // Adds a task to the plan.
   ADD_TASK = "add_task",
 

+ 1 - 0
frontend/src/types/AgentState.tsx

@@ -6,6 +6,7 @@ enum AgentState {
   PAUSED = "paused",
   STOPPED = "stopped",
   FINISHED = "finished",
+  REJECTED = "rejected",
   ERROR = "error",
 }
 

+ 3 - 0
frontend/src/types/ObservationType.tsx

@@ -19,6 +19,9 @@ enum ObservationType {
 
   // Agent state has changed
   AGENT_STATE_CHANGED = "agent_state_changed",
+
+  // Delegate result
+  DELEGATE = "delegate",
 }
 
 export default ObservationType;

+ 8 - 1
opendevin/controller/agent_controller.py

@@ -284,7 +284,14 @@ class AgentController:
                 self.delegateAction = None
 
                 # update delegate result observation
-                obs: Observation = AgentDelegateObservation(outputs=outputs, content='')
+                # TODO: replace this with AI-generated summary (#2395)
+                formatted_output = ', '.join(
+                    f'{key}: {value}' for key, value in outputs.items()
+                )
+                content = f'Delegate agent finishes task with {formatted_output}'
+                obs: Observation = AgentDelegateObservation(
+                    outputs=outputs, content=content
+                )
                 await self.event_stream.add_event(obs, EventSource.AGENT)
             return
 

+ 4 - 1
opendevin/events/action/agent.py

@@ -65,7 +65,10 @@ class AgentRejectAction(Action):
 
     @property
     def message(self) -> str:
-        return 'Task is rejected by the agent.'
+        msg: str = 'Task is rejected by the agent.'
+        if 'reason' in self.outputs:
+            msg += ' Reason: ' + self.outputs['reason']
+        return msg
 
 
 @dataclass

+ 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.
 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": "", "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": "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"}]}}}}]]
 
 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.

+ 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.
 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": "", "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": "Delegate agent 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
 cannot fix it, call the `reject` action.