Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chat api restructure #12

Merged
merged 10 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading