Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a /clear command to clear the chat history #78

Merged
merged 3 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions packages/jupyter-ai/jupyter_ai/actors.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import time
from typing import Union
from uuid import uuid4
from jupyter_ai.models import AgentChatMessage, ChatMessage, HumanChatMessage
from jupyter_ai.models import AgentChatMessage, ChatMessage, HumanChatMessage, ClearMessage
from jupyter_ai_magics.providers import ChatOpenAINewProvider
from langchain import ConversationChain, OpenAI
from langchain.chains import ConversationalRetrievalChain
Expand Down Expand Up @@ -43,21 +43,25 @@ class Router():
add a corresponding command in the `COMMANDS` dictionary.
"""

def __init__(self, log: Logger):
def __init__(self, log: Logger, reply_queue: Queue):
self.log = log
self.reply_queue = reply_queue

def route_message(self, message):

# assign default actor
actor = ray.get_actor(ACTOR_TYPE.DEFAULT)
default = ray.get_actor(ACTOR_TYPE.DEFAULT)

if(message.body.startswith("/")):
if message.body.startswith("/"):
command = message.body.split(' ', 1)[0]
if command in COMMANDS.keys():
actor = ray.get_actor(COMMANDS[command].value)


actor.process_message.remote(message)
actor.process_message.remote(message)
if command == '/clear':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than having a special check here, should we just add this to the commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to use an actor, but we can figure out how to improve this.

reply_message = ClearMessage()
self.reply_queue.put(reply_message)
else:
default.process_message.remote(message)

class BaseActor():
"""Base actor implemented by actors that are called by the `Router`"""
Expand Down
5 changes: 4 additions & 1 deletion packages/jupyter-ai/jupyter_ai/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ def initialize_settings(self):
reply_queue = Queue()
self.settings["reply_queue"] = reply_queue

router = Router.options(name="router").remote(log=self.log)
router = Router.options(name="router").remote(
reply_queue=reply_queue,
log=self.log
)
default_actor = DefaultActor.options(name=ACTOR_TYPE.DEFAULT.value).remote(
reply_queue=reply_queue,
log=self.log
Expand Down
19 changes: 11 additions & 8 deletions packages/jupyter-ai/jupyter_ai/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from jupyter_server.utils import ensure_async

from .task_manager import TaskManager
from .models import ChatHistory, PromptRequest, ChatRequest, ChatMessage, AgentChatMessage, HumanChatMessage, ConnectionMessage, ChatClient
from .models import ChatHistory, PromptRequest, ChatRequest, ChatMessage, Message, AgentChatMessage, HumanChatMessage, ConnectionMessage, ChatClient
from langchain.schema import HumanMessage

class APIHandler(BaseAPIHandler):
Expand Down Expand Up @@ -205,7 +205,7 @@ def open(self):
self.log.info(f"Client connected. ID: {client_id}")
self.log.debug("Clients are : %s", self.chat_handlers.keys())

def broadcast_message(self, message: ChatMessage):
def broadcast_message(self, message: Message):
"""Broadcasts message to all connected clients.
Appends message to `self.chat_history`.
"""
Expand All @@ -218,7 +218,9 @@ def broadcast_message(self, message: ChatMessage):
if client:
client.write_message(message.dict())

self.chat_history.append(message)
# Only append ChatMessage instances to history, not control messages
if isinstance(message, HumanChatMessage) or isinstance(message, AgentChatMessage):
self.chat_history.append(message)

async def on_message(self, message):
self.log.debug("Message recieved: %s", message)
Expand All @@ -230,11 +232,6 @@ async def on_message(self, message):
self.log.error(e)
return

# message sent to the agent instance
message = HumanMessage(
content=chat_request.prompt,
additional_kwargs=dict(user=asdict(self.current_user))
)
# message broadcast to chat clients
chat_message_id = str(uuid.uuid4())
chat_message = HumanChatMessage(
Expand All @@ -247,6 +244,12 @@ async def on_message(self, message):
# broadcast the message to other clients
self.broadcast_message(message=chat_message)

# Clear the message history if given the /clear command
if chat_request.prompt.startswith('/'):
command = chat_request.prompt.split(' ', 1)[0]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will help to extract the split code into a utility function that can be reused everywhere, but can do this in another PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

if command == '/clear':
self.chat_history.clear()

# process through the router
router = ray.get_actor("router")
router.route_message.remote(chat_message)
Expand Down
6 changes: 5 additions & 1 deletion packages/jupyter-ai/jupyter_ai/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class ConnectionMessage(BaseModel):
type: Literal["connection"] = "connection"
client_id: str

class ClearMessage(BaseModel):
type: Literal["clear"] = "clear"

# the type of messages being broadcast to clients
ChatMessage = Union[
AgentChatMessage,
Expand All @@ -51,7 +54,8 @@ class ConnectionMessage(BaseModel):
Message = Union[
AgentChatMessage,
HumanChatMessage,
ConnectionMessage
ConnectionMessage,
ClearMessage
]

class ListEnginesEntry(BaseModel):
Expand Down
3 changes: 3 additions & 0 deletions packages/jupyter-ai/src/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ function ChatBody({ chatHandler }: ChatBodyProps): JSX.Element {
function handleChatEvents(message: AiService.Message) {
if (message.type === 'connection') {
return;
} else if (message.type === 'clear') {
setMessages([]);
return;
}

setMessages(messageGroups => [...messageGroups, message]);
Expand Down
6 changes: 5 additions & 1 deletion packages/jupyter-ai/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,12 @@ export namespace AiService {
client_id: string;
};

export type ClearMessage = {
type: 'clear'
}

export type ChatMessage = AgentChatMessage | HumanChatMessage;
export type Message = AgentChatMessage | HumanChatMessage | ConnectionMessage;
export type Message = AgentChatMessage | HumanChatMessage | ConnectionMessage | ClearMessage;

export type ChatHistory = {
messages: ChatMessage[];
Expand Down