Skip to content

Commit

Permalink
Chat api restructure (#12)
Browse files Browse the repository at this point in the history
* progress restructuring

* useMainChatAPI

* rearranged runQuery to show results faster

* edited styling

* copyright, added LLM warning to query history names

* added linkq status type, refactored LinkQChatMessageType

* linkq status checkpoint

* fixed type issues, added status badge

* can edit API key through UI

* separated settings component

---------

Co-authored-by: Harry Li <[email protected]>
  • Loading branch information
harryli0088 and Harry Li authored Jan 22, 2025
1 parent 23de07d commit 8e1edf0
Show file tree
Hide file tree
Showing 28 changed files with 682 additions and 355 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# duplicate this file and rename the new file as .env.local

VITE_BASE_URL=
VITE_OPENAI_API_KEY=
VITE_DEMO_MODE=false

Expand Down
4 changes: 0 additions & 4 deletions src/App.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,5 @@
#results-content {
color: white;
padding: 1em;

#empty-results-message {
text-align: center;
}
}
}
94 changes: 21 additions & 73 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,99 +1,47 @@
// Copyright (c) 2024 Massachusetts Institute of Technology
// SPDX-License-Identifier: MIT
import { Title } from '@mantine/core';

import { ApiKeyWarning } from 'components/ApiKeyWarning';
import { Chat } from 'components/Chat/Chat';
import { DemoModeModal } from 'components/DemoModeModal';
import { ErrorMessage } from 'components/ErrorMessage';
import { IDTableContainer } from 'components/IDTable/IDTable';
import { InfoModal } from 'components/InfoModal';
import { LLMWarning } from 'components/LLMWarning';
import { QueryEditor } from 'components/QueryEditor/QueryEditor'
import { QueryVisualization } from "components/QueryVisualization/QueryVisualization";
import { ResultsTable } from 'components/ResultsTable/ResultsTable';
import { Results } from 'components/Results/Results';

import { useAppSelector } from 'redux/store';

import { useRunQuery, RunQueryProvider } from 'hooks/useRunQuery';
import { MainChatAPIProvider } from 'hooks/useMainChatAPI';
import { RunQueryProvider } from 'hooks/useRunQuery';

import styles from 'App.module.scss'


function App() {
return (
<RunQueryProvider>
<div id={styles["app"]}>
<div id={styles["sidebar"]}>
<Chat/>
</div>

<div id={styles["content"]}>
<QueryEditor/>
<MainChatAPIProvider>
<RunQueryProvider>
<div id={styles["app"]}>
<div id={styles["sidebar"]}>
<Chat/>
</div>

<div id={styles["content"]}>
<QueryEditor/>

<IDTableContainer/>
<IDTableContainer/>

<QueryVisualization/>
<QueryVisualization/>

<div id={styles["results-content"]}>
<Results/>
<div id={styles["results-content"]}>
<Results/>
</div>
</div>
</div>
</div>

<DemoModeModal/>
<ApiKeyWarning/>
</RunQueryProvider>
<DemoModeModal/>
<ApiKeyWarning/>
</RunQueryProvider>
</MainChatAPIProvider>
)
}

export default App


function Results() {
const { runQueryIsPending } = useRunQuery()
const results = useAppSelector(state => state.results.results)

if(runQueryIsPending) {
return <p>Loading...</p>
}
else if(results?.error) {
return (
<>
<p>There was an error running your query</p>
<pre>{results.error}</pre>
</>
)
}
else if(results?.data) {
return (
<>
<Title order={4}>Results Summary from LLM</Title>
{results.summary ? (
<div>
<LLMWarning>
<p>This results summary was generated by an LLM that can make mistakes. Refer below to the Results Table from KG for ground-truth data.</p>
<p>Note that the absence of data does not necessairly mean that there is no data. It is possible that the query did not find what that you are looking for.</p>
</LLMWarning>

<p>{results.summary}</p>
</div>
) : <ErrorMessage>There was an error generating a summary.</ErrorMessage>}

<hr/>

<Title order={4}>
Results Table from KG
<InfoModal title="Results Table from KG">
<p>These are ground-truth results retrieved from the KG using the query you executed.</p>
<p>Note that the absence of data does not necessairly mean that there is no data. It is possible that the query did not find what that you are looking for.</p>
</InfoModal>
</Title>
<ResultsTable data={results.data}/>
</>
)
}
else {
return <p id={styles["empty-results-message"]}><b>Run a query to see results!</b></p>
}
}
6 changes: 5 additions & 1 deletion src/components/ApiKeyWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import { useAppSelector } from "redux/store"

import { Settings } from "./Settings/Settings"

import { IS_DEMO_MODE } from "utils/demoData"

export function ApiKeyWarning() {
Expand All @@ -17,7 +19,9 @@ export function ApiKeyWarning() {
display: "flex", justifyContent: "center", alignItems: "center",
zIndex: 1,
}}>
<p>You need to configure the <code>VITE_OPENAI_API_KEY</code> environment variable in your <code>.env.local</code> file.</p>
<Settings/>

<p>You need to configure the <code>VITE_OPENAI_API_KEY</code> environment variable in your <code>.env.local</code> file or update your API key in the settings menu.</p>
</div>
)
}
Expand Down
22 changes: 9 additions & 13 deletions src/components/Chat/Chat.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,8 @@
height: 100vh;
position: relative;

#chat-settings-button {
position: absolute;
top: 0.5rem;
left: 0.5rem;
z-index: 1;
}

#chat-scroll-container {
height: calc(100% - 35px - 1rem);
height: calc(100% - 35px - 2.5rem);
overflow-y: auto;
margin-bottom: 3px;
padding: 0.5rem;
Expand All @@ -41,8 +34,15 @@
.chat {
background-color: #444;
padding: 0.5rem;
margin-bottom: 0.5rem;
border-radius: 5px;

&:not(:last-child) {
margin-bottom: 0.5rem;
}

button {
margin-top: 0.5rem;
}
}

&.user {
Expand All @@ -55,10 +55,6 @@

&.system {
justify-content: flex-end;

.chat {
// background-color: #154360;
}
}

.copy-query-buttons {
Expand Down
78 changes: 47 additions & 31 deletions src/components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@
import CodeMirror from '@uiw/react-codemirror';
import { StreamLanguage } from '@codemirror/language';
import { sparql } from '@codemirror/legacy-modes/mode/sparql';
import { ActionIcon, Button, Checkbox, Modal, TextInput } from "@mantine/core";
import { useEffect, useMemo, useRef, useState } from "react";
import { Badge, Button, Modal, TextInput } from "@mantine/core";
import { useEffect, useRef, useState } from "react";
import { useMutation } from "@tanstack/react-query";
import { IconCaretRight, IconSettings, IconZoomCode } from '@tabler/icons-react';
import { IconCaretRight, IconZoomCode } from '@tabler/icons-react';

import { ErrorMessage } from 'components/ErrorMessage';
import { LLMWarning } from 'components/LLMWarning';
import { Settings } from 'components/Settings/Settings';

import { useMakeChatGPTAPIInstance } from 'hooks/useMakeChatGPTAPIInstance';
import { useMainChatAPI } from 'hooks/useMainChatAPI';
import { useRunQuery } from 'hooks/useRunQuery';

import { addMessageToSimpleChatHistory, toggleShowFullChatHistory } from 'redux/chatHistorySlice';
import { addMessageToSimpleChatHistory } from 'redux/chatHistorySlice';
import { setQueryValue } from 'redux/queryValueSlice';
import { useAppDispatch, useAppSelector } from 'redux/store';

import { handleUserChat } from 'utils/handleUserChat';
import { INITIAL_SYSTEM_MESSAGE } from 'utils/knowledgeBase/prompts';
import { tryParsingOutQuery } from 'utils/tryParsingOutQuery';

import styles from "./Chat.module.scss"
Expand All @@ -44,19 +44,11 @@ export function Chat() {
}
}, [chatHistory.length])

const [showSettingsModal, setShowSettingsModal] = useState<boolean>(false)
const closeSettingsModal = () => setShowSettingsModal(false)

// const [inputText, setInputText] = useState<string>("Who won the 2023 Formula One Championship?"); // prefill the chat
const [inputText, setInputText] = useState<string>("");

const makeChatGPTAPIInstance = useMakeChatGPTAPIInstance()
const chatGPT = useMemo(() => {
return makeChatGPTAPIInstance({
chatId: 0,
systemMessage: INITIAL_SYSTEM_MESSAGE,
})
},[])
const chatAPI = useMainChatAPI()

const {error, isPending, mutate:submitChat, reset} = useMutation({
mutationKey: ['submit-chat'],
Expand All @@ -68,13 +60,14 @@ export function Chat() {
mutationFn: async (text:string) => {
//add the user's message to the simple chat history
dispatch(addMessageToSimpleChatHistory({
chatId: chatGPT.chatId,
chatId: chatAPI.chatId,
content: text,
name: "user",
role: "user",
stage: "Question Refinement",
}))

const llmResponse = await handleUserChat(text, chatGPT)
const llmResponse = await handleUserChat(text, chatAPI)

//add the LLM's final response to the simple chat
dispatch(addMessageToSimpleChatHistory(llmResponse))
Expand All @@ -86,17 +79,7 @@ export function Chat() {

return (
<div id={styles["chat-container"]}>
<Modal opened={showSettingsModal} onClose={closeSettingsModal} title="Settings">
<Checkbox
checked={showFullChatHistory}
onChange={() => dispatch(toggleShowFullChatHistory())}
label="Show full chat history"
/>
</Modal>

<ActionIcon id={styles["chat-settings-button"]} size="sm" variant="filled" aria-label="Show Settings" onClick={() => setShowSettingsModal(true)}>
<IconSettings/>
</ActionIcon>
<Settings/>

<div id={styles["chat-scroll-container"]}>
{chatHistory.map((c, i) => {
Expand All @@ -121,7 +104,8 @@ export function Chat() {
<ErrorMessage>{error.message}</ErrorMessage>
</Modal>
)}
{isPending && <p className={styles.loading}>Loading...</p>}
{/* {isPending && <p className={styles.loading}>Loading...</p>} */}
<LinkQStatus chatIsPending={isPending}/>

<form
onSubmit={e => {
Expand Down Expand Up @@ -156,6 +140,7 @@ function RenderLLMResponse({
<LLMWarning>
<p>This was generated by an LLM that can make mistakes.</p>
</LLMWarning>
<br/>

<RenderSparqlQuery
pre={parsedQuery.pre}
Expand Down Expand Up @@ -217,11 +202,42 @@ function RenderSparqlQuery({
</div>
<pre>{post}</pre>
<br/>
<Button onClick={() => setInputText("You identified the wrong data. I was actually looking for: ")} style={{marginTop:"0.5rem"}}>You identified the wrong data</Button>
<Button onClick={() => setInputText("You identified the wrong data. I was actually looking for: ")}>You identified the wrong data</Button>
<br/>
<Button onClick={() => setInputText("You misunderstood my question. I was actually asking about: ")} style={{marginTop:"0.5rem"}}>You misunderstood my question</Button>
<Button onClick={() => setInputText("You misunderstood my question. I was actually asking about: ")}>You misunderstood my question</Button>
<br/>
<Button onClick={() => setInputText("I want to ask something different: ")}>I want to ask something different</Button>
</>
)
}



function LinkQStatus({
chatIsPending,
}:{
chatIsPending: boolean,
}) {
const fullChatHistory = useAppSelector(state => state.chatHistory.fullChatHistory)
const { runQueryIsPending, summarizeResultsIsPending } = useRunQuery()

let color = "blue"
let displayMessage = "Waiting for User Input"
const stage = fullChatHistory.at(-1)?.stage
if(chatIsPending) {
color = "yellow"
displayMessage = stage || ""
}
else if(runQueryIsPending) {
color = "yellow"
displayMessage = "Executing Query"
}
else if(summarizeResultsIsPending) {
color = "yellow"
displayMessage = "Query Summarization"
}

return (
<p className={styles.loading}><Badge color={color}>{displayMessage}</Badge></p>
)
}
2 changes: 1 addition & 1 deletion src/components/LLMWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const LLMWarning = ({
{children}
</Modal>

<ActionIcon size="xs" variant="filled" aria-label="LLM Hallucination Warning" color="yellow" onClick={open} style={{float:"right"}}>
<ActionIcon size="xs" variant="filled" aria-label="LLM Hallucination Warning" color="yellow" onClick={open} style={{float:"right", marginTop: 0}}>
<IconAlertTriangle/>
</ActionIcon>
</>
Expand Down
9 changes: 8 additions & 1 deletion src/components/QueryEditor/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useAppDispatch, useAppSelector } from 'redux/store';
import { useRunQuery } from 'hooks/useRunQuery';

import styles from "./QueryEditor.module.scss"
import { LLMWarning } from 'components/LLMWarning';

export function QueryEditor() {
const dispatch = useAppDispatch()
Expand Down Expand Up @@ -48,7 +49,13 @@ export function QueryEditor() {
</div>

<Modal opened={historyOpened} onClose={closeHistory} size="lg" withCloseButton={false}>
<Title order={2}>Query History</Title>
<Title order={2}>
Query History

<LLMWarning>
<p>These query names are generated by an LLM</p>
</LLMWarning>
</Title>
<Divider/>
{queryHistory.map((record,i) => {
return (
Expand Down
6 changes: 6 additions & 0 deletions src/components/Results/Results.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* Copyright (c) 2024 Massachusetts Institute of Technology */
/* SPDX-License-Identifier: MIT */

#empty-results-message {
text-align: center;
}
Loading

0 comments on commit 8e1edf0

Please sign in to comment.