1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-01 01:18:10 +02:00

chore: adapt UI to server-side Unleash AI chat ownership (#8466)

https://linear.app/unleash/issue/2-2847/adapt-unleash-ai-chat-logic-to-new-server-side-chat-ownership-logic

Adapts the Unleash AI chat logic on the UI to the new server-side chat
ownership logic.
This commit is contained in:
Nuno Góis 2024-10-17 09:50:27 +01:00 committed by GitHub
parent 793221524c
commit f2256423d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 30 deletions

View File

@ -15,6 +15,11 @@ import { AIChatMessage } from './AIChatMessage';
import { AIChatHeader } from './AIChatHeader'; import { AIChatHeader } from './AIChatHeader';
import { Resizable } from 'component/common/Resizable/Resizable'; import { Resizable } from 'component/common/Resizable/Resizable';
const AI_ERROR_MESSAGE = {
role: 'assistant',
content: `I'm sorry, I'm having trouble understanding you right now. I've reported the issue to the team. Please try again later.`,
} as const;
const StyledAIIconContainer = styled('div')(({ theme }) => ({ const StyledAIIconContainer = styled('div')(({ theme }) => ({
position: 'fixed', position: 'fixed',
bottom: 20, bottom: 20,
@ -71,13 +76,6 @@ const StyledChatContent = styled('div')(({ theme }) => ({
overflowX: 'hidden', overflowX: 'hidden',
})); }));
const initialMessages: ChatMessage[] = [
{
role: 'system',
content: `You are an assistant that helps users interact with Unleash. You should ask the user in case you're missing any required information.`,
},
];
export const AIChat = () => { export const AIChat = () => {
const unleashAIEnabled = useUiFlag('unleashAI'); const unleashAIEnabled = useUiFlag('unleashAI');
const { const {
@ -86,9 +84,9 @@ export const AIChat = () => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { setToastApiError } = useToast(); const { setToastApiError } = useToast();
const { chat } = useAIApi(); const { chat, newChat } = useAIApi();
const [messages, setMessages] = useState<ChatMessage[]>(initialMessages); const [messages, setMessages] = useState<ChatMessage[]>([]);
const chatEndRef = useRef<HTMLDivElement | null>(null); const chatEndRef = useRef<HTMLDivElement | null>(null);
@ -106,26 +104,34 @@ export const AIChat = () => {
scrollToEnd(); scrollToEnd();
}, [open]); }, [open]);
const onSend = async (message: string) => { const onSend = async (content: string) => {
if (!message.trim() || loading) return; if (!content.trim() || loading) return;
try { try {
setLoading(true); setLoading(true);
const tempMessages: ChatMessage[] = [ setMessages((currentMessages) => [
...messages, ...currentMessages,
{ role: 'user', content: message }, { role: 'user', content },
{ role: 'assistant', content: '_Unleash AI is typing..._' }, ]);
]; const { messages: newMessages } = await chat(content);
setMessages(tempMessages);
const newMessages = await chat(tempMessages.slice(0, -1));
mutate(() => true); mutate(() => true);
setMessages(newMessages); setMessages(newMessages);
setLoading(false);
} catch (error: unknown) { } catch (error: unknown) {
setMessages((currentMessages) => [
...currentMessages,
AI_ERROR_MESSAGE,
]);
setToastApiError(formatUnknownError(error)); setToastApiError(formatUnknownError(error));
} finally {
setLoading(false);
} }
}; };
const onNewChat = () => {
setMessages([]);
newChat();
};
if (!unleashAIEnabled || !unleashAIAvailable) { if (!unleashAIEnabled || !unleashAIAvailable) {
return null; return null;
} }
@ -151,7 +157,7 @@ export const AIChat = () => {
> >
<StyledChat> <StyledChat>
<AIChatHeader <AIChatHeader
onNew={() => setMessages(initialMessages)} onNew={onNewChat}
onClose={() => setOpen(false)} onClose={() => setOpen(false)}
/> />
<StyledChatContent> <StyledChatContent>
@ -163,6 +169,11 @@ export const AIChat = () => {
{content} {content}
</AIChatMessage> </AIChatMessage>
))} ))}
{loading && (
<AIChatMessage from='assistant'>
_Unleash AI is typing..._
</AIChatMessage>
)}
<div ref={chatEndRef} /> <div ref={chatEndRef} />
</StyledChatContent> </StyledChatContent>
<AIChatInput onSend={onSend} loading={loading} /> <AIChatInput onSend={onSend} loading={loading} />

View File

@ -1,3 +1,4 @@
import { useState } from 'react';
import useAPI from '../useApi/useApi'; import useAPI from '../useApi/useApi';
const ENDPOINT = 'api/admin/ai'; const ENDPOINT = 'api/admin/ai';
@ -7,29 +8,47 @@ export type ChatMessage = {
content: string; content: string;
}; };
type Chat = {
id: string;
userId: number;
createdAt: string;
messages: ChatMessage[];
};
export const useAIApi = () => { export const useAIApi = () => {
const { makeRequest, createRequest, errors, loading } = useAPI({ const { makeRequest, createRequest, errors, loading } = useAPI({
propagateErrors: true, propagateErrors: true,
}); });
const chat = async (messages: ChatMessage[]): Promise<ChatMessage[]> => { const [chatId, setChatId] = useState<string>();
const chat = async (message: string): Promise<Chat> => {
const requestId = 'chat'; const requestId = 'chat';
const req = createRequest(`${ENDPOINT}/chat`, { const req = createRequest(
method: 'POST', `${ENDPOINT}/chat${chatId ? `/${chatId}` : ''}`,
body: JSON.stringify({ {
messages, method: 'POST',
}), body: JSON.stringify({
requestId, message,
}); }),
requestId,
},
);
const response = await makeRequest(req.caller, req.id); const response = await makeRequest(req.caller, req.id);
const { messages: newMessages } = await response.json(); const chat: Chat = await response.json();
return newMessages; setChatId(chat.id);
return chat;
};
const newChat = () => {
setChatId(undefined);
}; };
return { return {
chat, chat,
newChat,
errors, errors,
loading, loading,
}; };