Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9af3296

Browse files
initial frontend
1 parent a32b1a2 commit 9af3296

File tree

2 files changed

+105
-6
lines changed

2 files changed

+105
-6
lines changed

‎src/frontend/src/api/models.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AIChatCompletion } from "@microsoft/ai-chat-protocol";
1+
import { AIChatCompletion,AIChatCompletionDelta } from "@microsoft/ai-chat-protocol";
22

33
export const enum RetrievalMode {
44
Hybrid = "hybrid",
@@ -29,3 +29,7 @@ export type RAGContext = {
2929
export interface RAGChatCompletion extends AIChatCompletion {
3030
context: RAGContext;
3131
}
32+
33+
export interface RAGChatCompletionDelta extends AIChatCompletionDelta {
34+
context: RAGContext;
35+
}

‎src/frontend/src/pages/chat/Chat.tsx

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { useRef, useState, useEffect } from "react";
22
import { Panel, DefaultButton, TextField, SpinButton, Slider, Checkbox } from "@fluentui/react";
33
import { SparkleFilled } from "@fluentui/react-icons";
4-
import { AIChatMessage, AIChatProtocolClient } from "@microsoft/ai-chat-protocol";
54

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

8-
import {RetrievalMode, RAGChatCompletion} from "../../api";
7+
import {RetrievalMode, RAGChatCompletion,RAGChatCompletionDelta} from "../../api";
98
import { Answer, AnswerError, AnswerLoading } from "../../components/Answer";
109
import { QuestionInput } from "../../components/QuestionInput";
1110
import { ExampleList } from "../../components/Example";
@@ -22,19 +21,77 @@ const Chat = () => {
2221
const [retrieveCount, setRetrieveCount] = useState<number>(3);
2322
const [retrievalMode, setRetrievalMode] = useState<RetrievalMode>(RetrievalMode.Hybrid);
2423
const [useAdvancedFlow, setUseAdvancedFlow] = useState<boolean>(true);
24+
const [shouldStream, setShouldStream] = useState<boolean>(true);
2525

2626
const lastQuestionRef = useRef<string>("");
2727
const chatMessageStreamEnd = useRef<HTMLDivElement | null>(null);
2828

2929
const [isLoading, setIsLoading] = useState<boolean>(false);
30+
const [isStreaming, setIsStreaming] = useState<boolean>(false);
3031
const [error, setError] = useState<unknown>();
3132

3233
const [activeCitation, setActiveCitation] = useState<string>();
3334
const [activeAnalysisPanelTab, setActiveAnalysisPanelTab] = useState<AnalysisPanelTabs | undefined>(undefined);
3435

3536
const [selectedAnswer, setSelectedAnswer] = useState<number>(0);
3637
const [answers, setAnswers] = useState<[user: string, response: RAGChatCompletion][]>([]);
38+
const [streamedAnswers, setStreamedAnswers] = useState<[user: string, response: RAGChatCompletion][]>([]);
3739

40+
const handleAsyncRequest = async (
41+
question: string,
42+
answers: [string, RAGChatCompletionDelta][],
43+
setStreamedAnswers: Function,
44+
result: AsyncIterable<AIChatCompletionDelta>
45+
) => {
46+
let answer = "";
47+
let chatCompletion: RAGChatCompletion = {
48+
context: {
49+
data_points: {},
50+
followup_questions: null,
51+
thoughts: []
52+
},
53+
message: { content: "", role: "assistant" },
54+
};
55+
const updateState = (newContent: string) => {
56+
return new Promise(resolve => {
57+
setTimeout(() => {
58+
answer += newContent;
59+
// We need to create a new object to trigger a re-render
60+
const latestCompletion: RAGChatCompletionDelta = {
61+
...chatCompletion,
62+
delta: { content: answer, role: chatCompletion.message.role }
63+
};
64+
setStreamedAnswers([...answers, [question, latestCompletion]]);
65+
resolve(null);
66+
}, 33);
67+
});
68+
};
69+
try {
70+
setIsStreaming(true);
71+
for await (const response of result) {
72+
if (!response.delta) {
73+
continue;
74+
}
75+
if (response.role) {
76+
chatCompletion.message.role = response.delta.role;
77+
}
78+
if (response.content) {
79+
setIsLoading(false);
80+
await updateState(response.delta.content);
81+
}
82+
if (response.context) {
83+
chatCompletion.context = {
84+
...chatCompletion.context,
85+
...response.context
86+
};
87+
}
88+
}
89+
} finally {
90+
setIsStreaming(false);
91+
}
92+
chatCompletion.message.content = answer;
93+
return chatCompletion;
94+
};
3895
const makeApiRequest = async (question: string) => {
3996
lastQuestionRef.current = question;
4097

@@ -61,8 +118,14 @@ const Chat = () => {
61118
}
62119
};
63120
const chatClient: AIChatProtocolClient = new AIChatProtocolClient("/chat");
64-
const result = await chatClient.getCompletion(allMessages, options) as RAGChatCompletion;
65-
setAnswers([...answers, [question, result]]);
121+
if (shouldStream) {
122+
const result = await chatClient.getStreamedCompletion(allMessages, options);
123+
const parsedResponse = await handleAsyncRequest(question, answers, setStreamedAnswers, result);
124+
setAnswers([...answers, [question, parsedResponse]]);
125+
} else {
126+
const result = await chatClient.getCompletion(allMessages, options) as RAGChatCompletion;
127+
setAnswers([...answers, [question, result]]);
128+
}
66129
} catch (e) {
67130
setError(e);
68131
} finally {
@@ -76,10 +139,13 @@ const Chat = () => {
76139
setActiveCitation(undefined);
77140
setActiveAnalysisPanelTab(undefined);
78141
setAnswers([]);
142+
setStreamedAnswers([]);
79143
setIsLoading(false);
144+
setIsStreaming(false);
80145
};
81146

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

84150
const onPromptTemplateChange = (_ev?: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
85151
setPromptTemplate(newValue || "");
@@ -101,6 +167,10 @@ const Chat = () => {
101167
setUseAdvancedFlow(!!checked);
102168
}
103169

170+
const onShouldStreamChange = (_ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean) => {
171+
setShouldStream(!!checked);
172+
};
173+
104174
const onExampleClicked = (example: string) => {
105175
makeApiRequest(example);
106176
};
@@ -143,7 +213,25 @@ const Chat = () => {
143213
</div>
144214
) : (
145215
<div className={styles.chatMessageStream}>
146-
{answers.map((answer, index) => (
216+
{isStreaming && streamedAnswers.map((streamedAnswer, index) => (
217+
<div key={index}>
218+
<UserChatMessage message={streamedAnswer[0]} />
219+
<div className={styles.chatMessageGpt}>
220+
<Answer
221+
isStreaming={true}
222+
key={index}
223+
answer={streamedAnswer[1]}
224+
isSelected={false}
225+
onCitationClicked={c => onShowCitation(c, index)}
226+
onThoughtProcessClicked={() => onToggleTab(AnalysisPanelTabs.ThoughtProcessTab, index)}
227+
onSupportingContentClicked={() => onToggleTab(AnalysisPanelTabs.SupportingContentTab, index)}
228+
onFollowupQuestionClicked={q => makeApiRequest(q)}
229+
/>
230+
</div>
231+
</div>
232+
))}
233+
{!isStreaming &&
234+
answers.map((answer, index) => (
147235
<div key={index}>
148236
<UserChatMessage message={answer[0]} />
149237
<div className={styles.chatMessageGpt}>
@@ -257,6 +345,13 @@ const Chat = () => {
257345
snapToStep
258346
/>
259347

348+
<Checkbox
349+
className={styles.chatSettingsSeparator}
350+
checked={shouldStream}
351+
label="Stream chat completion responses"
352+
onChange={onShouldStreamChange}
353+
/>
354+
260355
</Panel>
261356
</div>
262357
</div>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /