Skip to content

Commit

Permalink
feat(chat): integrating chatbot with new API
Browse files Browse the repository at this point in the history
  • Loading branch information
baaalint committed Sep 6, 2024
1 parent ba15e16 commit 27e403c
Show file tree
Hide file tree
Showing 22 changed files with 551 additions and 264 deletions.
2 changes: 1 addition & 1 deletion ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function App() {
element: <UsersTablePage />
},
{
path: '/admin/chat-histories',
path: '/admin/histories',
element: <ChatHistoriesTablePage />
},

Expand Down
22 changes: 4 additions & 18 deletions ui/src/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import Client from '@services/Api';
import { ChatHistory, DataRow, User } from '@shared/types';
import { ChatHistory, User } from '@shared/types';
import { atom } from 'jotai';
import { atomWithStorage } from "jotai/utils";

Expand All @@ -23,26 +22,13 @@ export const adminAtom = atomWithStorage('admin', localStorage.getItem('admin')
export const modalAtom = atom<boolean>(false);
export const asyncAtom = atom<boolean>(false);
export const messagesAtom = atom<ChatHistory[]>([]);
export const conversationsAtom = atom<ChatHistory[]>([]);
export const userAtom = atomWithStorage<User | null>('user', localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user') as string) : null);
export const userWithTokenAtom = atomWithStorage<User | null>('user', localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user') as string) : null);
export const publicUserAtom = atom<User>({});
export const usernameAtom = atom<string>('');
export const selectedUserAtom = atom<User>({ username: '', admin: false, token: '' });
export const comparisonUserAtom = atom<User>({ username: '', admin: false, token: '' });
export const isTypingAtom = atom<boolean>(false);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const selectedRowAtom = atom<any>({});


export const usersAtom = atom<DataRow<User>[]>([]);
export const createUserAtom = atom(
null,
async (get, set, newUser: User) => {
try {
const createdUser = await Client.createUser(newUser);
set(usersAtom, (prev) => [...prev, createdUser]);

} catch (error) {
console.error(error);
}
}
);
47 changes: 47 additions & 0 deletions ui/src/atoms/sessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2024 Iguazio
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Client from '@services/Api';
import { Session } from '@shared/types/session';
import { atom } from 'jotai';

export const sessionsAtom = atom<Session[]>([]);

export const sessionsLoadingAtom = atom<boolean>(false);

export const sessionsErrorAtom = atom<string | null>(null);


export const sessionsWithFetchAtom = atom(
(get) => get(sessionsAtom),
async (_get, set, username) => {
set(sessionsLoadingAtom, true);
set(sessionsErrorAtom, null);
try {
const sessions = await Client.getSessions(username as string);
const sortedSessions = sessions.data.sort((a: Session, b: Session) => {
const dateA = new Date(a.created as string);
const dateB = new Date(b.created as string);
return dateA.getTime() - dateB.getTime();
});
set(sessionsAtom, sortedSessions);
} catch (error) {
set(sessionsErrorAtom, 'Failed to fetch sessions');
} finally {
set(sessionsLoadingAtom, false);
}
}
);

export const selectedSessionAtom = atom<Session>({ name: '', description: '', labels: {} });
29 changes: 20 additions & 9 deletions ui/src/atoms/apiAtoms.ts → ui/src/atoms/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,9 @@ import { User } from '@shared/types';
import { atom } from 'jotai';

export const usersAtom = atom<User[]>([]);

export const usersLoadingAtom = atom<boolean>(false);

export const usersErrorAtom = atom<string | null>(null);

export const fetchUsersAtom = atom(
async (get) => {
get(usersLoadingAtom);
const users = await Client.getUsers();
return users.data;
}
);

export const usersWithFetchAtom = atom(
(get) => get(usersAtom),
Expand All @@ -45,3 +36,23 @@ export const usersWithFetchAtom = atom(
}
}
);

export const publicUserAtom = atom<User>({});
export const userLoadingAtom = atom<boolean>(false);
export const userErrorAtom = atom<string | null>(null);

export const userWithFetchAtom = atom(
(get) => get(publicUserAtom),
async (_get, set, username: string) => {
set(userLoadingAtom, true);
set(userErrorAtom, null);
try {
const user = await Client.getUser(username);
set(publicUserAtom, user.data);
} catch (error) {
set(userErrorAtom, 'Failed to fetch user');
} finally {
set(userLoadingAtom, false);
}
}
);
45 changes: 22 additions & 23 deletions ui/src/components/feature/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,53 @@ import Bubble from '@components/shared/Bubble'
import Message from '@components/shared/Message'
import Client from '@services/Api'
import { ChatHistory } from '@shared/types'
import { messagesAtom, sessionIdAtom } from 'atoms'
import { messagesAtom, sessionIdAtom, usernameAtom } from 'atoms'
import { useAtom } from 'jotai'
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'

const Chat = () => {
const [messages, setMessages] = useAtom<ChatHistory[]>(messagesAtom)
const [sessionId, setSessionId] = useAtom(sessionIdAtom)
const [sessionId] = useAtom(sessionIdAtom)
const [username] = useAtom(usernameAtom)

useEffect(() => {
async function fetchData() {
console.log('getting session:', sessionId)
if (!sessionId) {
setMessages([])
return
}
const chatSession = await Client.getSession(sessionId)
console.log('session resp:', chatSession)
if (chatSession) {
setMessages(chatSession.history)
} else {
setMessages([])
}
await Client.getSession(username, sessionId)
}
fetchData()
}, [sessionId, setMessages])
}, [sessionId, setMessages, username])

const lastMessageRef = useRef<HTMLDivElement>(null)

useEffect(() => {
if (lastMessageRef.current) {
lastMessageRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' })
}
}, [messages, setMessages])

return (
<Flex
marginX={{ sm: '5%', lg: '25%' }}
paddingX={{ xl: '25%', md: '10%' }}
paddingBottom={4}
flexDir={'column'}
justifyContent={'space-between'}
flexGrow={1}
height="calc(100vh - 92px)"
>
<Flex justifyContent="flex-start" flexGrow={1} flexDirection="column" paddingBottom="92px" overflowY="scroll">
{messages?.map((chatHistory, index) => (
{messages?.map((message, index) => (
<Bubble
key={index}
content={chatHistory.content}
bot={chatHistory.role}
sources={chatHistory.sources}
html={chatHistory.html as string}
content={message.content}
bot={message.role}
sources={message.sources}
html={message.html as string}
/>
))}
<Box height={'2px'} ref={lastMessageRef} />
</Flex>
<Box>
<Message setter={setMessages} />
<Message />
</Box>
</Flex>
)
Expand Down
78 changes: 0 additions & 78 deletions ui/src/components/feature/ChatHistoryList.tsx

This file was deleted.

4 changes: 3 additions & 1 deletion ui/src/components/feature/ChatHistoryTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const ChatHistoryTable = () => {
},
{
page: 'Chat Histories',
url: '/chat-histories'
url: '/histories'
}
]}
/>
Expand All @@ -90,6 +90,8 @@ const ChatHistoryTable = () => {
columns={columns as TableColumn<Partial<ChatHistory>>[]}
contextActions={contextActions}
onSelectedRowChange={e => setSelectedRows(e.selectedRows)}
toggleClearRows={false}
onOpenDrawer={() => {}}
/>
</Flex>
)
Expand Down
10 changes: 9 additions & 1 deletion ui/src/components/feature/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { isTypingAtom } from '@atoms/index'
import { useAtom } from 'jotai'
import React from 'react'
import Markdown from 'react-markdown'
import TypingText from './TypingText'

interface ChatMessageProps {
message: string
}

const ChatMessage: React.FC<ChatMessageProps> = ({ message }) => {
return <TypingText text={message} />
const [isTyping] = useAtom(isTypingAtom)

if (isTyping) {
return <TypingText text={message} />
}
return <Markdown>{message}</Markdown>
}

export default ChatMessage
Loading

0 comments on commit 27e403c

Please sign in to comment.