Minor changes to agent state management (#1597)

* move towards event stream

* refactor agent state changes

* move agent state logic

* fix callbacks

* break on finish

* closer to working

* change frontend to accomodate new flow

* handle start action

* fix locked stream

* revert message

* logspam

* no async on close

* get rid of agent_task

* fix up closing

* better asyncio handling

* sleep to give back control

* fix key

* logspam

* update frontend agent state actions

* fix pause and cancel

* delint

* fix map

* delint

* wait for agent to finish

* fix unit test

* event stream enums

* fix merge issues

* fix lint

* fix test

* fix test

* add user message action

* add user message action

* fix up user messages

* fix main.py flow

* refactor message waiting

* lint

* fix test

* fix test

* simplify if/else

* fix state reset

* logspam

* add error status

* minor changes to control bar

* handle user messages when not awaiting

* restart agent after stopping

* Update opendevin/controller/agent_controller.py

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>

* delint

* refactor initialize

* delint

* fix dispatch

---------

Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
This commit is contained in:
Robert Brennan 2024-05-06 08:29:48 -04:00 committed by GitHub
parent c9970e6817
commit d0967122f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 45 additions and 30 deletions

View File

@ -16,7 +16,6 @@ import AgentControlBar from "./components/AgentControlBar";
import AgentStatusBar from "./components/AgentStatusBar";
import Terminal from "./components/terminal/Terminal";
import { initializeAgent } from "./services/agent";
import { getSettings } from "./services/settings";
interface Props {
setSettingOpen: (isOpen: boolean) => void;
@ -73,7 +72,7 @@ function App(): JSX.Element {
if (initOnce) return;
initOnce = true;
initializeAgent(getSettings());
initializeAgent();
Socket.registerCallback("open", [getMsgTotal]);

View File

@ -25,11 +25,7 @@ const IgnoreTaskStateMap: { [k: string]: AgentState[] } = {
AgentState.FINISHED,
AgentState.AWAITING_USER_INPUT,
],
[AgentState.STOPPED]: [
AgentState.INIT,
AgentState.STOPPED,
AgentState.FINISHED,
],
[AgentState.STOPPED]: [AgentState.INIT, AgentState.STOPPED],
};
interface ButtonProps {
@ -76,18 +72,15 @@ function AgentControlBar() {
return;
}
let act = action;
if (act === AgentState.STOPPED) {
act = AgentState.STOPPED;
if (action === AgentState.STOPPED) {
clearMsgs().then().catch();
store.dispatch(clearMessages());
} else {
setIsLoading(true);
}
setDesiredState(act);
changeAgentState(act);
setDesiredState(action);
changeAgentState(action);
};
useEffect(() => {
@ -125,7 +118,7 @@ function AgentControlBar() {
isLoading ||
IgnoreTaskStateMap[AgentState.PAUSED].includes(curAgentState)
}
content="Pause the agent task"
content="Pause the current task"
action={AgentState.PAUSED}
handleAction={handleAction}
large
@ -135,7 +128,7 @@ function AgentControlBar() {
)}
<ActionButton
isDisabled={isLoading}
content="Restart a new agent task"
content="Start a new task"
action={AgentState.STOPPED}
handleAction={handleAction}
>

View File

@ -31,6 +31,10 @@ const AgentStatusMap: { [k: string]: { message: string; indicator: string } } =
message: "Agent has finished the task.",
indicator: "bg-green-500",
},
[AgentState.ERROR]: {
message: "Agent encountered an error.",
indicator: "bg-red-500",
},
};
function AgentStatusBar() {

View File

@ -14,7 +14,9 @@ function ChatInterface() {
const { curAgentState } = useSelector((state: RootState) => state.agent);
const handleSendMessage = (content: string) => {
const isTask = curAgentState === AgentState.INIT;
const isTask =
curAgentState === AgentState.INIT ||
curAgentState === AgentState.FINISHED;
dispatch(addUserMessage(content));
sendChatMessage(content, isTask);
};

View File

@ -74,7 +74,7 @@ function SettingsModal({ isOpen, onOpenChange }: SettingsProps) {
const updatedSettings = getSettingsDifference(settings);
saveSettings(settings);
i18next.changeLanguage(settings.LANGUAGE);
initializeAgent(settings); // reinitialize the agent with the new settings
initializeAgent(); // reinitialize the agent with the new settings
const sensitiveKeys = ["LLM_API_KEY"];

View File

@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest";
import ActionType from "#/types/ActionType";
import { initializeAgent } from "./agent";
import { Settings } from "./settings";
import { Settings, saveSettings } from "./settings";
import Socket from "./socket";
const sendSpy = vi.spyOn(Socket, "send");
@ -21,7 +21,8 @@ describe("initializeAgent", () => {
args: settings,
};
initializeAgent(settings);
saveSettings(settings);
initializeAgent();
expect(sendSpy).toHaveBeenCalledWith(JSON.stringify(event));
});

View File

@ -1,12 +1,13 @@
import ActionType from "#/types/ActionType";
import { Settings } from "./settings";
import { getSettings } from "./settings";
import Socket from "./socket";
/**
* Initialize the agent with the current settings.
* @param settings - The new settings.
*/
export const initializeAgent = (settings: Settings) => {
export const initializeAgent = () => {
const settings = getSettings();
const event = { action: ActionType.INIT, args: settings };
const eventString = JSON.stringify(event);
Socket.send(eventString);

View File

@ -1,11 +1,19 @@
import ActionType from "#/types/ActionType";
import AgentState from "#/types/AgentState";
import Socket from "./socket";
import { initializeAgent } from "./agent";
export function changeAgentState(message: AgentState): void {
const INIT_DELAY = 1000;
export function changeAgentState(state: AgentState): void {
const eventString = JSON.stringify({
action: ActionType.CHANGE_AGENT_STATE,
args: { agent_state: message },
args: { agent_state: state },
});
Socket.send(eventString);
if (state === AgentState.STOPPED) {
setTimeout(() => {
initializeAgent();
}, INIT_DELAY);
}
}

View File

@ -54,6 +54,7 @@ class AgentController:
state: State | None = None
_agent_state: AgentState = AgentState.LOADING
_cur_step: int = 0
_pending_talk_action: AgentTalkAction | None = None
def __init__(
self,
@ -175,13 +176,19 @@ class AgentController:
if isinstance(event, ChangeAgentStateAction):
await self.set_agent_state_to(event.agent_state) # type: ignore
elif isinstance(event, MessageAction) and event.source == EventSource.USER:
# FIXME: we're hacking a message action into a user message observation, for the benefit of CodeAct
await self.add_history(
self._pending_talk_action,
UserMessageObservation(event.content),
add_to_stream=False,
)
await self.set_agent_state_to(AgentState.RUNNING)
if self._pending_talk_action is None:
await self.add_history(
NullAction(), UserMessageObservation(event.content)
)
else:
# FIXME: we're hacking a message action into a user message observation, for the benefit of CodeAct
await self.add_history(
self._pending_talk_action,
UserMessageObservation(event.content),
add_to_stream=False,
)
self._pending_talk_action = None
await self.set_agent_state_to(AgentState.RUNNING)
async def reset_task(self):
if self.agent_task is not None: