Skip to content

Commit

Permalink
[ENH] Query tool ai UI integration (#261)
Browse files Browse the repository at this point in the history
* Query Tool AI UI integration

* Made the backend url an evironment var

* solved dependency issue

* solving dependency issue

* solving dependency issue

* Added NB_ENABLE_CHATBOT as an env variable

---------

Co-authored-by: Raya679 <[email protected]>
  • Loading branch information
Raya679 and Raya679 authored Aug 26, 2024
1 parent beb0e4e commit 8dd18bd
Show file tree
Hide file tree
Showing 7 changed files with 2,238 additions and 224 deletions.
2,300 changes: 2,078 additions & 222 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
"notistack": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.3.1",
"react-draggable": "^4.4.6",
"react-icons": "^5.3.0",
"react-router-dom": "^6.24.1",
"react-simple-chatbot": "^0.6.1",
"styled-components": "^4.4.1",
"uuid": "^10.0.0"
},
"devDependencies": {
Expand Down
5 changes: 4 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import CloseIcon from '@mui/icons-material/Close';
import { SnackbarKey, SnackbarProvider, closeSnackbar, enqueueSnackbar } from 'notistack';
import { jwtDecode } from 'jwt-decode';
import { googleLogout } from '@react-oauth/google';
import { queryURL, attributesURL, nodesURL, enableAuth } from './utils/constants';
import { queryURL, attributesURL, nodesURL, enableAuth, enableChatbot } from './utils/constants';
import {
RetrievedAttributeOption,
AttributeOption,
Expand All @@ -21,6 +21,7 @@ import QueryForm from './components/QueryForm';
import ResultContainer from './components/ResultContainer';
import Navbar from './components/Navbar';
import AuthDialog from './components/AuthDialog';
import ChatbotFeature from './components/Chatbot';
import './App.css';

function App() {
Expand Down Expand Up @@ -400,6 +401,8 @@ function App() {
</>
)}

<div>{enableChatbot && <ChatbotFeature setResult={setResult} />}</div>

<div className="grid grid-cols-4 gap-4">
<div>
<QueryForm
Expand Down
144 changes: 144 additions & 0 deletions src/components/Chatbot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { useState, useEffect } from 'react';
import axios from 'axios';
import ChatBot from 'react-simple-chatbot';
import { ThemeProvider } from 'styled-components';
import { FaRobot } from 'react-icons/fa';
import Draggable from 'react-draggable';
import { QueryResponse } from '../utils/types';

const theme = {
background: '#f5f8fb',
fontFamily: 'Roboto, sans-serif',
headerBgColor: '#4169E1',
headerFontColor: '#fff',
headerFontSize: '100%',
botBubbleColor: '#4169E1',
botFontColor: '#fff',
userBubbleColor: '#fff',
userFontColor: '#4a4a4a',
};

interface Steps {
waiting2?: {
value: string;
};
}

interface FetchAnswerProps {
steps: Steps;
triggerNextStep: (step: { trigger: string }) => void;
setResult: (result: QueryResponse | null) => void;
}

function FetchAnswer({ steps, triggerNextStep, setResult }: FetchAnswerProps) {
const [answer, setAnswer] = useState<string>('');
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [isFetching, setIsFetching] = useState<boolean>(false);

useEffect(() => {
const userQuery = steps.waiting2?.value;

if (userQuery && !isFetching && !answer && !errorMessage) {
const fetchAnswerFromModel = async (question: string) => {
setIsFetching(true);
try {
const response = await axios.post(
`${import.meta.env.NB_API_QUERY_AI_URL}/generate_url/`,
{
query: question,
}
);

const { data } = response;

if (response.status === 200) {
const urlPattern = /^https?:\/\/[^\s/$.?#].[^\s]*$/;
if (urlPattern.test(data.response)) {
const urlResponse = await axios.get(data.response);
setAnswer('Query successful.');
setResult(urlResponse.data);
} else {
setAnswer(data.response);
setResult(null);
}
triggerNextStep({ trigger: 'waiting2' });
} else {
setErrorMessage(data.detail || 'An error occurred.');
}
} catch {
setErrorMessage('An error occurred while fetching the answer.');
} finally {
setIsFetching(false);
}
};

fetchAnswerFromModel(userQuery);
}
}, [steps.waiting2?.value, answer, errorMessage, isFetching, setResult, triggerNextStep]);

if (isFetching) {
return <p>Loading...</p>;
}

if (errorMessage) {
return <p>Error: {errorMessage}</p>;
}

return <p>{answer}</p>;
}

interface ChatbotFeatureProps {
setResult: (result: QueryResponse | null) => void;
}

function ChatbotFeature({ setResult }: ChatbotFeatureProps) {
const [showChatbot, setShowChatbot] = useState<boolean>(false);

const steps = [
{
id: 'Welcome',
message: 'Hello, welcome to Neurobagel! Please enter your query.',
trigger: 'waiting2',
},
{
id: 'waiting2',
user: true,
trigger: 'FetchAnswer',
},
{
id: 'FetchAnswer',
component: <FetchAnswer setResult={setResult} triggerNextStep={() => {}} steps={{}} />,
asMessage: true,
},
];

return (
<div className="fixed bottom-5 right-9 z-40 flex flex-col items-end">
<button
type="button"
aria-label="Toggle chatbot"
className="relative mb-4 rounded-full border-none bg-blue-500 p-3 text-white outline-none hover:bg-blue-600 focus:outline-none"
onClick={() => setShowChatbot(!showChatbot)}
>
<FaRobot className="text-2xl" />
<span className="absolute right-8 top-0 block h-3 w-3 rounded-full bg-red-500 ring-2 ring-white" />
</button>
{showChatbot && (
<Draggable>
<div className="fixed bottom-12 right-32 z-50 w-80 rounded-lg border border-gray-300 bg-white shadow-lg">
<ThemeProvider theme={theme}>
<ChatBot
headerTitle="Neurobagel Chat"
steps={steps}
placeholder="Enter your query"
recognitionEnable
/>
</ThemeProvider>
</div>
</Draggable>
)}
</div>
);
}

export default ChatbotFeature;
2 changes: 2 additions & 0 deletions src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare module 'react-simple-chatbot';
declare module 'styled-components';
5 changes: 5 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export const enableAuth: boolean =
? false
: import.meta.env.NB_ENABLE_AUTH.toLowerCase() === 'true';

export const enableChatbot: boolean =
import.meta.env.NB_ENABLE_CHATBOT === undefined
? false
: import.meta.env.NB_ENABLE_CHATBOT.toLowerCase() === 'true';

export const clientID: string = import.meta.env.NB_QUERY_CLIENT_ID ?? '';

export const sexes: { [key: string]: string } = {
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src", "cypress.d.ts", "cypress.config.ts", "cypress"],
"include": ["src", "cypress.d.ts", "cypress.config.ts", "cypress", "./src/declarations.d.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}

0 comments on commit 8dd18bd

Please sign in to comment.