Skip to content

Commit

Permalink
Refactor code to handle chat completion and update chat messages and …
Browse files Browse the repository at this point in the history
…storage in real-time
  • Loading branch information
Royal-lobster committed Sep 19, 2023
1 parent a48fbb3 commit b73cc3b
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 22 deletions.
5 changes: 2 additions & 3 deletions src/components/Sidebar/chat/chatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import TextareaAutosize from 'react-textarea-autosize'
import { GiMagicBroom } from 'react-icons/gi'
import { IoSend } from 'react-icons/io5'
import { HiHand } from 'react-icons/hi'
import { ChatMessageParams, ChatRole } from '../../../hooks/useOpenAI'

interface SidebarInputProps {
loading: boolean
submitMessage: (messages: ChatMessageParams[]) => void
submitMessage: (prompt: string) => void
clearMessages: () => void
chatIsEmpty: boolean
cancelRequest: () => void
Expand All @@ -33,7 +32,7 @@ export function SidebarInput({
}, [loading])

const handleSubmit = () => {
submitMessage([{ content: text, role: ChatRole.USER }])
submitMessage(text)
setText('')
}

Expand Down
14 changes: 8 additions & 6 deletions src/components/Sidebar/chat/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import React, { useEffect } from 'react'
import { useEffect } from 'react'
import ChatList from './chatList'
import { SidebarInput } from './chatInput'
import { useChatCompletion } from '../../../hooks/useOpenAI'
import { useChatCompletion } from '../../../hooks/useChatCompletion'
import { SYSTEM_PROMPT } from '../../../config/prompts'
import { Settings } from '../../../config/settings'

interface ChatProps {
settings: Settings
chatId: string
}

const Chat = ({ settings }: ChatProps) => {
const { messages, submitQuery, clearMessages, loading, cancelRequest } =
const Chat = ({ settings, chatId }: ChatProps) => {
const { messages, submitQuery, clearMessages, generating, cancelRequest } =
useChatCompletion({
model: settings.chat.modal,
apiKey: settings.chat.openAIKey!,
mode: settings.chat.mode,
systemPrompt: SYSTEM_PROMPT,
chatId
})

useEffect(() => {
Expand All @@ -25,7 +27,7 @@ const Chat = ({ settings }: ChatProps) => {
prompt: string
}
if (action === 'generate') {
submitQuery([{ content: prompt, role: 'user' }])
submitQuery(prompt)
}
}
window.addEventListener('message', handleWindowMessage)
Expand All @@ -39,7 +41,7 @@ const Chat = ({ settings }: ChatProps) => {
<>
<ChatList messages={messages} />
<SidebarInput
loading={loading}
loading={generating}
submitMessage={submitQuery}
chatIsEmpty={messages.length <= 1}
clearMessages={clearMessages}
Expand Down
57 changes: 46 additions & 11 deletions src/hooks/useChatCompletion.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,68 @@
import { AvailableModels, Mode } from "../config/settings"
import { ChatOpenAI } from "langchain/chat_models/openai"
import { useStorage } from "./useStorage"
import { useCurrentChat, Role } from "./useCurrentChat"
import { useMemo } from "react"
import { AIMessage, HumanMessage, SystemMessage } from "langchain/dist/schema"
import { useState } from "react"

interface UseChatCompletionProps {
model: AvailableModels
apiKey: string
mode: Mode
systemPrompt: string
chatId: string
}


type Role = "user" | "assistant" | "system"

type Message = {
role: Role
message: string
timestamp: number
}

export const useChatCompletion = ({
model,
apiKey,
mode,
systemPrompt,
chatId,
}: UseChatCompletionProps) => {
const chat = new ChatOpenAI({
const { messages, updateAssistantMessage, updateStoredMessages, clearMessages } = useCurrentChat(chatId)
const [generating, setGenerating] = useState(false)

const controller = new AbortController();

const chat = useMemo(() => new ChatOpenAI({
streaming: true,
});
}), [])



const submitQuery = async (query: string) => {
const previousMessages = messages.map((msg) => {
switch (msg.role) {
case Role.ASSISTANT:
return new AIMessage(msg.message)
case Role.SYSTEM:
return new SystemMessage(msg.message)
case Role.USER:
return new HumanMessage(msg.message)
}
})
setGenerating(true)
const response = await chat.call([...previousMessages, new HumanMessage(query)], {
signal: controller.signal,
callbacks: [
{
handleLLMNewToken: updateAssistantMessage
},
],
});
setGenerating(false)
updateStoredMessages()
return response.content
}

const cancelRequest = () => {
controller.abort()
setGenerating(false)
}

const [messages, setMessages] = useStorage()

return { messages, submitQuery, generating, cancelRequest, clearMessages }
}
55 changes: 55 additions & 0 deletions src/hooks/useCurrentChat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useEffect } from "react"
import { useState } from "react"
import { useStorage } from "./useStorage"


export enum Role {
"USER",
"ASSISTANT",
"SYSTEM"
}

export type Message = {
role: Role
message: string
timestamp: number
}

export const useCurrentChat = (chatId: string) => {
const [storedMessages, setStoredMessages] = useStorage<Message[]>(`CHAT-${chatId}`, [])
const [messages, setMessages] = useState<Message[]>(storedMessages) // we don't directly update storedMessages for performance reasons

const updateAssistantMessage = (chunk: string) => {
setMessages(messages => {
const lastMessage = messages[messages.length - 1]
lastMessage.message += chunk
return [...messages]
})
}

const addNewMessage = (role: Role, message: string) => {
const newMessage: Message = {
role,
message,
timestamp: Date.now(),
}
setMessages([...messages, newMessage])
}

const updateStoredMessages = () => {
setStoredMessages(messages)
}

const clearMessages = () => {
setMessages([])
updateStoredMessages()
}

return {
messages,
updateAssistantMessage,
addNewMessage,
updateStoredMessages,
clearMessages
}
}
4 changes: 2 additions & 2 deletions src/hooks/useHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ interface ChatHistory {
export const useHistory = () => {
const [history, setHistory] = useStorage<ChatHistory[]>("HISTORY", [])

const createChatHistory = () => {
const createChatHistory = (name: string) => {
const newId = randomUUID()

setHistory(prev => [...prev, {
id: newId,
name: "",
name,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}])
Expand Down

0 comments on commit b73cc3b

Please sign in to comment.