Skip to content

Commit

Permalink
Feat: Regenerate message
Browse files Browse the repository at this point in the history
  • Loading branch information
SmartManoj committed Aug 13, 2024
1 parent b613c20 commit be74f06
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 7 deletions.
16 changes: 14 additions & 2 deletions frontend/src/components/chat/ChatInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import Chat from "./Chat";
import TypingIndicator from "./TypingIndicator";
import { RootState } from "#/store";
import AgentState from "#/types/AgentState";
import { sendChatMessage } from "#/services/chatService";
import { addUserMessage, addAssistantMessage } from "#/state/chatSlice";
import { sendChatMessage, regenerateLastMessage } from "#/services/chatService";
import { addUserMessage, addAssistantMessage, removeLastAssistantMessage } from "#/state/chatSlice";
import { I18nKey } from "#/i18n/declaration";
import { useScrollToBottom } from "#/hooks/useScrollToBottom";
import FeedbackModal from "../modals/feedback/FeedbackModal";
import beep from "#/utils/beep";
import { FaSyncAlt } from "react-icons/fa";

interface ScrollButtonProps {
onClick: () => void;
Expand Down Expand Up @@ -91,6 +92,12 @@ function ChatInterface() {
);
};


const handleRegenerateClick = () => {
dispatch(removeLastAssistantMessage());
regenerateLastMessage();
};

const scrollRef = useRef<HTMLDivElement>(null);

const { scrollDomToBottom, onChatBodyScroll, hitBottom } =
Expand Down Expand Up @@ -184,6 +191,11 @@ function ChatInterface() {
icon={<FaRegThumbsDown className="inline mr-2 w-3 h-3" />}
label=""
/>
<ScrollButton
onClick={handleRegenerateClick}
icon={<FaSyncAlt className="inline mr-2 w-3 h-3" />}
label=""
/>
</div>
)}
</div>
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/services/chatService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ export function sendChatMessage(message: string, images_urls: string[]): void {
const eventString = JSON.stringify(event);
Session.send(eventString);
}

export function regenerateLastMessage(): void {
const event = {
action: ActionType.REGENERATE,
args: {},
};
const eventString = JSON.stringify(event);
Session.send(eventString);
}
6 changes: 5 additions & 1 deletion frontend/src/state/chatSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ export const chatSlice = createSlice({
clearMessages(state) {
state.messages = [];
},

removeLastAssistantMessage(state) {
state.messages.pop();
},
},
});

export const { addUserMessage, addAssistantMessage, clearMessages } =
export const { addUserMessage, addAssistantMessage, clearMessages, removeLastAssistantMessage } =
chatSlice.actions;
export default chatSlice.reducer;
3 changes: 3 additions & 0 deletions frontend/src/types/ActionType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ enum ActionType {
// Represents a message from the user or agent.
MESSAGE = "message",

// Regenerates the last message from the agent.
REGENERATE = "regenerate",

// Reads the contents of a file.
READ = "read",

Expand Down
8 changes: 6 additions & 2 deletions opendevin/controller/agent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
MessageAction,
ModifyTaskAction,
NullAction,
RegenerateAction,
)
from opendevin.events.event import Event
from opendevin.events.observation import (
Expand Down Expand Up @@ -160,14 +161,17 @@ async def _start_step_loop(self):
async def on_event(self, event: Event):
if isinstance(event, ChangeAgentStateAction):
await self.set_agent_state_to(event.agent_state) # type: ignore
elif isinstance(event, RegenerateAction):
logger.info(event, extra={'msg_type': 'ACTION'})
self.event_stream.remove_latest_event()
await self.set_agent_state_to(AgentState.RUNNING)
elif isinstance(event, MessageAction):
if event.source == EventSource.USER:
logger.info(
event,
extra={'msg_type': 'ACTION', 'event_source': EventSource.USER},
)
if self.get_agent_state() != AgentState.RUNNING:
await self.set_agent_state_to(AgentState.RUNNING)
await self.set_agent_state_to(AgentState.RUNNING)
elif event.source == EventSource.AGENT and event.wait_for_response:
await self.set_agent_state_to(AgentState.AWAITING_USER_INPUT)
elif isinstance(event, AgentDelegateAction):
Expand Down
4 changes: 4 additions & 0 deletions opendevin/core/schema/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ class ActionTypeSchema(BaseModel):
"""Represents a message.
"""

REGENERATE: str = Field(default='regenerate')
"""Regenerates the message.
"""

START: str = Field(default='start')
"""Starts a new development task OR send chat from the user. Only sent by the client.
"""
Expand Down
3 changes: 2 additions & 1 deletion opendevin/events/action/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .commands import CmdRunAction, IPythonRunCellAction
from .empty import NullAction
from .files import FileReadAction, FileWriteAction
from .message import MessageAction
from .message import MessageAction, RegenerateAction
from .tasks import AddTaskAction, ModifyTaskAction

__all__ = [
Expand All @@ -30,5 +30,6 @@
'ChangeAgentStateAction',
'IPythonRunCellAction',
'MessageAction',
'RegenerateAction',
'ActionConfirmationStatus',
]
7 changes: 7 additions & 0 deletions opendevin/events/action/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,10 @@ def __str__(self) -> str:
for url in self.images_urls:
ret += f'\nIMAGE_URL: {url}'
return ret


class RegenerateAction(Action):
action = ActionType.REGENERATE

def __str__(self) -> str:
return f'**RegenerateAction** (source={self.source})\n'
3 changes: 2 additions & 1 deletion opendevin/events/serialization/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
)
from opendevin.events.action.empty import NullAction
from opendevin.events.action.files import FileReadAction, FileWriteAction
from opendevin.events.action.message import MessageAction
from opendevin.events.action.message import MessageAction, RegenerateAction
from opendevin.events.action.tasks import AddTaskAction, ModifyTaskAction

actions = (
Expand All @@ -32,6 +32,7 @@
ModifyTaskAction,
ChangeAgentStateAction,
MessageAction,
RegenerateAction,
AgentSummarizeAction,
)

Expand Down
9 changes: 9 additions & 0 deletions opendevin/events/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ def add_event(self, event: Event, source: EventSource):
callback = stack[-1]
asyncio.create_task(callback(event))

def remove_latest_event(self):
# Remove NullObservation, RegenerateAction, AgentStateChangedObservation, NullObservation and the previous Action
for _ in range(5):
logger.debug(f'Removing latest event id={self._cur_id - 1}')
logger.debug(f'Removing event: {self.get_latest_event()}')

self.file_store.delete(self._get_filename_for_id(self._cur_id - 1))
self._cur_id -= 1

def filtered_events_by_source(self, source: EventSource):
for event in self.get_events():
if event.source == source:
Expand Down

0 comments on commit be74f06

Please sign in to comment.