0% found this document useful (0 votes)
6K views10 pages

Chat Application with GPT-4V Integration

This document defines a React component for a conversational chat interface. It uses the React useState hook to manage component state, including the question prompt, number of responses, retrieval mode, and other settings. It calls an API to submit questions and handle the response, either by streaming updates or returning the full response. State is updated with the question, response, and any errors or loading states.

Uploaded by

kumarinetra369
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6K views10 pages

Chat Application with GPT-4V Integration

This document defines a React component for a conversational chat interface. It uses the React useState hook to manage component state, including the question prompt, number of responses, retrieval mode, and other settings. It calls an API to submit questions and handle the response, either by streaming updates or returning the full response. State is updated with the question, response, and any errors or loading states.

Uploaded by

kumarinetra369
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 10

import { useRef, useState, useEffect } from "react";

import { Checkbox, Panel, DefaultButton, TextField, SpinButton } from


"@fluentui/react";
import { SparkleFilled } from "@fluentui/react-icons";
import readNDJSONStream from "ndjson-readablestream";

import styles from "./Chat.module.css";

import {
chatApi,
configApi,
RetrievalMode,
ChatAppResponse,
ChatAppResponseOrError,
ChatAppRequest,
ResponseMessage,
VectorFieldOptions,
GPT4VInput
} from "../../api";
import { Answer, AnswerError, AnswerLoading } from "../../components/Answer";
import { QuestionInput } from "../../components/QuestionInput";
import { ExampleList } from "../../components/Example";
import { UserChatMessage } from "../../components/UserChatMessage";
import { AnalysisPanel, AnalysisPanelTabs } from "../../components/AnalysisPanel";
import { SettingsButton } from "../../components/SettingsButton";
import { ClearChatButton } from "../../components/ClearChatButton";
import { useLogin, getToken, isLoggedIn, requireAccessControl } from
"../../authConfig";
import { VectorSettings } from "../../components/VectorSettings";
import { useMsal } from "@azure/msal-react";
import { TokenClaimsDisplay } from "../../components/TokenClaimsDisplay";
import { GPT4VSettings } from "../../components/GPT4VSettings";

const Chat = () => {


const [isConfigPanelOpen, setIsConfigPanelOpen] = useState(false);
const [promptTemplate, setPromptTemplate] = useState<string>("");
const [retrieveCount, setRetrieveCount] = useState<number>(3);
const [retrievalMode, setRetrievalMode] =
useState<RetrievalMode>(RetrievalMode.Hybrid);
const [useSemanticRanker, setUseSemanticRanker] = useState<boolean>(true);
const [shouldStream, setShouldStream] = useState<boolean>(true);
const [useSemanticCaptions, setUseSemanticCaptions] = useState<boolean>(false);
const [excludeCategory, setExcludeCategory] = useState<string>("");
const [useSuggestFollowupQuestions, setUseSuggestFollowupQuestions] =
useState<boolean>(false);
const [vectorFieldList, setVectorFieldList] =
useState<VectorFieldOptions[]>([VectorFieldOptions.Embedding]);
const [useOidSecurityFilter, setUseOidSecurityFilter] =
useState<boolean>(false);
const [useGroupsSecurityFilter, setUseGroupsSecurityFilter] =
useState<boolean>(false);
const [gpt4vInput, setGPT4VInput] =
useState<GPT4VInput>(GPT4VInput.TextAndImages);
const [useGPT4V, setUseGPT4V] = useState<boolean>(false);

const lastQuestionRef = useRef<string>("");


const chatMessageStreamEnd = useRef<HTMLDivElement | null>(null);

const [isLoading, setIsLoading] = useState<boolean>(false);


const [isStreaming, setIsStreaming] = useState<boolean>(false);
const [error, setError] = useState<unknown>();

const [activeCitation, setActiveCitation] = useState<string>();


const [activeAnalysisPanelTab, setActiveAnalysisPanelTab] =
useState<AnalysisPanelTabs | undefined>(undefined);

const [selectedAnswer, setSelectedAnswer] = useState<number>(0);


const [answers, setAnswers] = useState<[user: string, response:
ChatAppResponse][]>([]);
console.log('Arun Printing Answers:', answers);
console.log('Arun Printing setAnswers:', setAnswers);
const [streamedAnswers, setStreamedAnswers] = useState<[user: string, response:
ChatAppResponse][]>([]);
console.log('Arun Printing Streamed Answers:', streamedAnswers);
console.log('Arun Printing setStreamedAnswers:', setStreamedAnswers);

const [showGPT4VOptions, setShowGPT4VOptions] = useState<boolean>(false);

const getConfig = async () => {


const token = client ? await getToken(client) : undefined;

configApi(token).then(config => {
setShowGPT4VOptions(config.showGPT4VOptions);
});
};

const handleAsyncRequest = async (question: string, answers: [string,


ChatAppResponse][], setAnswers: Function, responseBody: ReadableStream<any>) => {
console.log("\n Arun inside handleAsyncRequest Printing Question:",
question);
console.log("\n Arun inside handleAsyncRequest Printing Answers:",
answers);
console.log("\n Arun inside handleAsyncRequest Printing setAnswers
Function:", setAnswers);
console.log("\n Arun inside handleAsyncRequest Printing Response
Body:", responseBody);
let answer: string = "";
let askResponse: ChatAppResponse = {} as ChatAppResponse;

const updateState = (newContent: string) => {


return new Promise(resolve => {
setTimeout(() => {
answer += newContent;
const latestResponse: ChatAppResponse = {
...askResponse,
choices: [{ ...askResponse.choices[0], message: { content:
answer, role: askResponse.choices[0].message.role } }]
};
console.log("\n Arun Printing latestResponse:",
latestResponse);
setStreamedAnswers([...answers, [question,
latestResponse]]);
resolve(null);
}, 33);
});
};
try {
setIsStreaming(true);
for await (const event of readNDJSONStream(responseBody)) {
console.log('\n Arun Printing event:', event );
console.log('\n Arun Printing readNDJSONStream:',
readNDJSONStream(responseBody) );
if (event["choices"] && event["choices"][0]["context"] &&
event["choices"][0]["context"]["data_points"]) {
event["choices"][0]["message"] = event["choices"][0]["delta"];
askResponse = event;
console.log('\n Arun Printing askResponse1:',
askResponse);
} else if (event["choices"] && event["choices"][0]["delta"]
["content"]) {
setIsLoading(false);
await updateState(event["choices"][0]["delta"]["content"]);
} else if (event["choices"] && event["choices"][0]["context"]) {
// Update context with new keys from latest event
askResponse.choices[0].context =
{ ...askResponse.choices[0].context, ...event["choices"][0]["context"] };
console.log('\n Arun Printing
askResponse.choices.context:', askResponse.choices[0].context);
} else if (event["error"]) {
throw Error(event["error"]);
}
}
} finally {
setIsStreaming(false);
}
const fullResponse: ChatAppResponse = {
...askResponse,
choices: [{ ...askResponse.choices[0], message: { content: answer,
role: askResponse.choices[0].message.role } }]
};

console.log('\n Arun Printing Full Response:', fullResponse);


return fullResponse;
};

const client = useLogin ? useMsal().instance : undefined;

const makeApiRequest = async (question: string) => {


lastQuestionRef.current = question;

error && setError(undefined);


setIsLoading(true);
setActiveCitation(undefined);
setActiveAnalysisPanelTab(undefined);

const token = client ? await getToken(client) : undefined;

try {
const messages: ResponseMessage[] = answers.flatMap(a => [
{ content: a[0], role: "user" },
{ content: a[1].choices[0].message.content, role: "assistant" }
]);

const request: ChatAppRequest = {


messages: [...messages, { content: question, role: "user" }],
stream: shouldStream,
context: {
overrides: {
prompt_template: promptTemplate.length === 0 ? undefined :
promptTemplate,
exclude_category: excludeCategory.length === 0 ?
undefined : excludeCategory,
top: retrieveCount,
retrieval_mode: retrievalMode,
semantic_ranker: useSemanticRanker,
semantic_captions: useSemanticCaptions,
suggest_followup_questions: useSuggestFollowupQuestions,
use_oid_security_filter: useOidSecurityFilter,
use_groups_security_filter: useGroupsSecurityFilter,
vector_fields: vectorFieldList,
use_gpt4v: useGPT4V,
gpt4v_input: gpt4vInput
}
},
// ChatAppProtocol: Client must pass on any session state received
from the server
session_state: answers.length ? answers[answers.length - 1]
[1].choices[0].session_state : null
};

const response = await chatApi(request, token);


console.log('\n Arun inside Chat.tsx Printing request sent to
chatApi', request);
console.log('\n Arun inside Chat.tsx Printing token sent to
chatApi', token);
console.log('\n Arun inside Chat.tsx Printing response from
chatApi', response);
console.log('\n Arun inside Chat.tsx Printing response body from
chatApi', response.body);
if (!response.body) {
throw Error("No response body");
}
if (shouldStream) {
const parsedResponse: ChatAppResponse = await
handleAsyncRequest(question, answers, setAnswers, response.body);
setAnswers([...answers, [question, parsedResponse]]);
console.log('\n Arun inside Chat.tsx Printing parsed
Response', parsedResponse);
} else {
const parsedResponse: ChatAppResponseOrError = await
response.json();
if (response.status > 299 || !response.ok) {
throw Error(parsedResponse.error || "Unknown error");
}
setAnswers([...answers, [question, parsedResponse as
ChatAppResponse]]);
}
} catch (e) {
setError(e);
} finally {
setIsLoading(false);
}
};

const clearChat = () => {


lastQuestionRef.current = "";
error && setError(undefined);
setActiveCitation(undefined);
setActiveAnalysisPanelTab(undefined);
setAnswers([]);
setStreamedAnswers([]);
setIsLoading(false);
setIsStreaming(false);
};

useEffect(() => chatMessageStreamEnd.current?.scrollIntoView({ behavior:


"smooth" }), [isLoading]);
useEffect(() => chatMessageStreamEnd.current?.scrollIntoView({ behavior: "auto"
}), [streamedAnswers]);
useEffect(() => {
getConfig();
}, []);

const onPromptTemplateChange = (_ev?: React.FormEvent<HTMLInputElement |


HTMLTextAreaElement>, newValue?: string) => {
setPromptTemplate(newValue || "");
};

const onRetrieveCountChange = (_ev?: React.SyntheticEvent<HTMLElement, Event>,


newValue?: string) => {
setRetrieveCount(parseInt(newValue || "3"));
};

const onUseSemanticRankerChange = (_ev?: React.FormEvent<HTMLElement |


HTMLInputElement>, checked?: boolean) => {
setUseSemanticRanker(!!checked);
};

const onUseSemanticCaptionsChange = (_ev?: React.FormEvent<HTMLElement |


HTMLInputElement>, checked?: boolean) => {
setUseSemanticCaptions(!!checked);
};

const onShouldStreamChange = (_ev?: React.FormEvent<HTMLElement |


HTMLInputElement>, checked?: boolean) => {
setShouldStream(!!checked);
};

const onExcludeCategoryChanged = (_ev?: React.FormEvent, newValue?: string) =>


{
setExcludeCategory(newValue || "");
};

const onUseSuggestFollowupQuestionsChange = (_ev?: React.FormEvent<HTMLElement


| HTMLInputElement>, checked?: boolean) => {
setUseSuggestFollowupQuestions(!!checked);
};

const onUseOidSecurityFilterChange = (_ev?: React.FormEvent<HTMLElement |


HTMLInputElement>, checked?: boolean) => {
setUseOidSecurityFilter(!!checked);
};

const onUseGroupsSecurityFilterChange = (_ev?: React.FormEvent<HTMLElement |


HTMLInputElement>, checked?: boolean) => {
setUseGroupsSecurityFilter(!!checked);
};

const onExampleClicked = (example: string) => {


makeApiRequest(example);
};

const onShowCitation = (citation: string, index: number) => {


if (activeCitation === citation && activeAnalysisPanelTab ===
AnalysisPanelTabs.CitationTab && selectedAnswer === index) {
setActiveAnalysisPanelTab(undefined);
} else {
setActiveCitation(citation);
setActiveAnalysisPanelTab(AnalysisPanelTabs.CitationTab);
}

setSelectedAnswer(index);
};

const onToggleTab = (tab: AnalysisPanelTabs, index: number) => {


if (activeAnalysisPanelTab === tab && selectedAnswer === index) {
setActiveAnalysisPanelTab(undefined);
} else {
setActiveAnalysisPanelTab(tab);
}

setSelectedAnswer(index);
};

return (
<div className={styles.container}>
<div className={styles.commandsContainer}>
<ClearChatButton className={styles.commandButton}
onClick={clearChat} disabled={!lastQuestionRef.current || isLoading} />
<SettingsButton className={styles.commandButton} onClick={() =>
setIsConfigPanelOpen(!isConfigPanelOpen)} />
</div>
<div className={styles.chatRoot}>
<div className={styles.chatContainer}>
{!lastQuestionRef.current ? (
<div className={styles.chatEmptyState}>
<SparkleFilled fontSize={"120px"}
primaryFill={"rgba(115, 118, 225, 1)"} aria-hidden="true" aria-label="Chat logo" />
<h1 className={styles.chatEmptyStateTitle}>Chat with
your data</h1>
<h2 className={styles.chatEmptyStateSubtitle}>Ask
anything or try an example</h2>
<ExampleList onExampleClicked={onExampleClicked}
useGPT4V={useGPT4V} />
</div>
) : (
<div className={styles.chatMessageStream}>
{isStreaming &&

streamedAnswers.map((streamedAnswer, index) => {


const userChatMessageContent =
streamedAnswer[0]; // Content of UserChatMessage
const answerContent =
streamedAnswer[1]; // Content of Answer
// Log the content of
UserChatMessage and Answer to the console
console.log('UserChatMessage
content:', userChatMessageContent);
console.log('Answer content:',
answerContent);
return (
<div key={index}>
<UserChatMessage
message={userChatMessageContent} />
<div
className={styles.chatMessageGpt}>
<Answer
isStreaming={true}
key={index}

answer={answerContent}
isSelected={false}

onCitationClicked={c => onShowCitation(c, index)}

onThoughtProcessClicked={() =>
onToggleTab(AnalysisPanelTabs.ThoughtProcessTab, index)}

onSupportingContentClicked={() =>
onToggleTab(AnalysisPanelTabs.SupportingContentTab, index)}

onFollowupQuestionClicked={q => makeApiRequest(q)}

showFollowupQuestions={useSuggestFollowupQuestions && answers.length - 1 ===


index}
/>
</div>
</div>
);
})
}

{!isStreaming &&
answers.map((answer, index) => {
const userChatMessageContent =
answer[0]; // Content of UserChatMessage
const answerContent = answer[1]; //
Content of Answer

// Log the content of


UserChatMessage and Answer to the console
console.log('UserChatMessage
content:', userChatMessageContent);
console.log('Answer content:',
answerContent);

return (
<div key={index}>
<UserChatMessage
message={userChatMessageContent} />
<div
className={styles.chatMessageGpt}>
<Answer

isStreaming={false}
key={index}

answer={answerContent}

isSelected={selectedAnswer === index && activeAnalysisPanelTab !== undefined}

onCitationClicked={c => onShowCitation(c, index)}

onThoughtProcessClicked={() =>
onToggleTab(AnalysisPanelTabs.ThoughtProcessTab, index)}

onSupportingContentClicked={() =>
onToggleTab(AnalysisPanelTabs.SupportingContentTab, index)}

onFollowupQuestionClicked={q => makeApiRequest(q)}

showFollowupQuestions={useSuggestFollowupQuestions && answers.length - 1 ===


index}
/>
</div>
</div>
);
})
}

{isLoading && (
<>
<UserChatMessage
message={lastQuestionRef.current} />
<div className={styles.chatMessageGptMinWidth}>
<AnswerLoading />
</div>
</>
)}
{error ? (
<>
<UserChatMessage
message={lastQuestionRef.current} />
<div className={styles.chatMessageGptMinWidth}>
<AnswerError error={error.toString()}
onRetry={() => makeApiRequest(lastQuestionRef.current)} />
</div>
</>
) : null}
<div ref={chatMessageStreamEnd} />
</div>
)}

<div className={styles.chatInput}>
<QuestionInput
clearOnSend
placeholder="Type a new question (e.g. does my plan
cover annual eye exams?)"
disabled={isLoading}
onSend={question => makeApiRequest(question)}
/>
</div>
</div>

{answers.length > 0 && activeAnalysisPanelTab && (


<AnalysisPanel
className={styles.chatAnalysisPanel}
activeCitation={activeCitation}
onActiveTabChanged={x => onToggleTab(x, selectedAnswer)}
citationHeight="810px"
answer={answers[selectedAnswer][1]}
activeTab={activeAnalysisPanelTab}
/>
)}

<Panel
headerText="Configure answer generation"
isOpen={isConfigPanelOpen}
isBlocking={false}
onDismiss={() => setIsConfigPanelOpen(false)}
closeButtonAriaLabel="Close"
onRenderFooterContent={() => <DefaultButton onClick={() =>
setIsConfigPanelOpen(false)}>Close</DefaultButton>}
isFooterAtBottom={true}
>
<TextField
className={styles.chatSettingsSeparator}
defaultValue={promptTemplate}
label="Override prompt template"
multiline
autoAdjustHeight
onChange={onPromptTemplateChange}
/>

<SpinButton
className={styles.chatSettingsSeparator}
label="Retrieve this many search results:"
min={1}
max={50}
defaultValue={retrieveCount.toString()}
onChange={onRetrieveCountChange}
/>
<TextField className={styles.chatSettingsSeparator}
label="Exclude category" onChange={onExcludeCategoryChanged} />
<Checkbox
className={styles.chatSettingsSeparator}
checked={useSemanticRanker}
label="Use semantic ranker for retrieval"
onChange={onUseSemanticRankerChange}
/>
<Checkbox
className={styles.chatSettingsSeparator}
checked={useSemanticCaptions}
label="Use query-contextual summaries instead of whole
documents"
onChange={onUseSemanticCaptionsChange}
disabled={!useSemanticRanker}
/>
<Checkbox
className={styles.chatSettingsSeparator}
checked={useSuggestFollowupQuestions}
label="Suggest follow-up questions"
onChange={onUseSuggestFollowupQuestionsChange}
/>

{showGPT4VOptions && (
<GPT4VSettings
gpt4vInputs={gpt4vInput}
isUseGPT4V={useGPT4V}
updateUseGPT4V={useGPT4V => {
setUseGPT4V(useGPT4V);
}}
updateGPT4VInputs={inputs => setGPT4VInput(inputs)}
/>
)}

<VectorSettings
showImageOptions={useGPT4V && showGPT4VOptions}
updateVectorFields={(options: VectorFieldOptions[]) =>
setVectorFieldList(options)}
updateRetrievalMode={(retrievalMode: RetrievalMode) =>
setRetrievalMode(retrievalMode)}
/>

{useLogin && (
<Checkbox
className={styles.chatSettingsSeparator}
checked={useOidSecurityFilter || requireAccessControl}
label="Use oid security filter"
disabled={!isLoggedIn(client) || requireAccessControl}
onChange={onUseOidSecurityFilterChange}
/>
)}
{useLogin && (
<Checkbox
className={styles.chatSettingsSeparator}
checked={useGroupsSecurityFilter ||
requireAccessControl}
label="Use groups security filter"
disabled={!isLoggedIn(client) || requireAccessControl}
onChange={onUseGroupsSecurityFilterChange}
/>
)}

<Checkbox
className={styles.chatSettingsSeparator}
checked={shouldStream}
label="Stream chat completion responses"
onChange={onShouldStreamChange}
/>
{useLogin && <TokenClaimsDisplay />}
</Panel>
</div>
</div>
);
};

export default Chat;

You might also like