fix: submit completion by fetch

This commit is contained in:
jialin
2024-06-24 19:14:42 +08:00
parent 4dcd11e119
commit bd6b3dcbe3
5 changed files with 164 additions and 39 deletions
+61
View File
@@ -0,0 +1,61 @@
import qs from 'query-string';
import { useRef } from 'react';
export const createEventSourceURL = (url: string) => {
const { host, protocol } = window.location;
return `${protocol}://${host}${url}`;
};
/*
0: connecting
1: connect successfully
2: closed
*/
export default function useEventSource() {
const eventSourceRef = useRef<any>(null);
const createEventSourceConnection = (query: {
url: string;
params: any;
onmessage?: (data: any) => void;
}) => {
eventSourceRef.current?.close?.();
const { url, params, onmessage = () => {} } = query;
const sseurl = createEventSourceURL(url);
eventSourceRef.current = new EventSource(
`${url}?${qs.stringify({
...params
})}`,
{
withCredentials: true
}
);
eventSourceRef.current.onmessage = (res: any) => {
try {
const data = JSON.parse(res.data);
console.log('event source message: ', { data, resData: res });
onmessage(data);
} catch (error) {
// error
}
};
eventSourceRef.current.onopen = () => {
console.log('event source connected...');
};
eventSourceRef.current.onerror = (error: any) => {
console.log('event source error: ', error);
};
};
return {
eventSourceRef: eventSourceRef,
createEventSourceConnection
};
}
+40
View File
@@ -8,3 +8,43 @@ export async function execChatCompletions(params: any) {
data: params data: params
}); });
} }
export const fetchChatStream = async (params: any) => {
const response = await fetch(`/v1${CHAT_API}`, {
method: 'POST',
body: JSON.stringify(params),
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('Network response was not ok');
return null;
}
const reader = response?.body?.getReader();
const decoder = new TextDecoder('utf-8');
return {
reader,
decoder
};
};
export const receiveChatStream = async (
reader: any,
decoder: TextDecoder,
callback: (data: any) => void
) => {
const { done, value } = await reader.read();
if (done) {
return;
}
let chunk = decoder.decode(value, { stream: true });
if (chunk.startsWith('data: ')) {
chunk = chunk.substring('data: '.length);
}
const item = JSON.parse(chunk?.trim());
callback(item);
await receiveChatStream(reader, decoder, callback);
};
+39 -18
View File
@@ -5,7 +5,7 @@ import { useIntl } from '@umijs/max';
import { Button, Input, Spin } from 'antd'; import { Button, Input, Spin } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { execChatCompletions } from '../apis'; import { fetchChatStream, receiveChatStream } from '../apis';
import { Roles } from '../config'; import { Roles } from '../config';
import '../style/ground-left.less'; import '../style/ground-left.less';
import '../style/system-message-wrap.less'; import '../style/system-message-wrap.less';
@@ -13,7 +13,6 @@ import ChatFooter from './chat-footer';
import MessageItem from './message-item'; import MessageItem from './message-item';
import ReferenceParams from './reference-params'; import ReferenceParams from './reference-params';
import ViewCodeModal from './view-code-modal'; import ViewCodeModal from './view-code-modal';
interface MessageProps { interface MessageProps {
parameters: any; parameters: any;
} }
@@ -42,7 +41,11 @@ const MessageList: React.FC<MessageProps> = (props) => {
const [activeIndex, setActiveIndex] = useState(-1); const [activeIndex, setActiveIndex] = useState(-1);
const [tokenResult, setTokenResult] = useState<any>(null); const [tokenResult, setTokenResult] = useState<any>(null);
const systemRef = useRef<any>(null); const systemRef = useRef<any>(null);
const contentRef = useRef<any>('');
const handleReceiveMessage = (data: any) => {
console.log('event source message: ', { data });
};
const handleSystemMessageChange = (e: any) => { const handleSystemMessageChange = (e: any) => {
setSystemMessage(e.target.value); setSystemMessage(e.target.value);
}; };
@@ -61,10 +64,32 @@ const MessageList: React.FC<MessageProps> = (props) => {
setActiveIndex(messageList.length - 1); setActiveIndex(messageList.length - 1);
}; };
const joinMessage = (chunk: any) => {
if (_.get(chunk, 'choices.0.finish_reason')) {
setTokenResult({
...chunk?.usage
});
return true;
}
contentRef.current =
contentRef.current + _.get(chunk, 'choices.0.delta.content');
setMessageList([
...messageList,
{
role: Roles.Assistant,
content: contentRef.current,
uid: messageId.current
}
]);
return false;
};
const submitMessage = async () => { const submitMessage = async () => {
try { try {
setLoading(true); setLoading(true);
setMessageId();
setTokenResult(null);
contentRef.current = '';
const chatParams = { const chatParams = {
messages: systemMessage messages: systemMessage
? [ ? [
@@ -75,22 +100,19 @@ const MessageList: React.FC<MessageProps> = (props) => {
...messageList ...messageList
] ]
: [...messageList], : [...messageList],
...parameters ...parameters,
stream: true
}; };
const data = await execChatCompletions(chatParams); const result = await fetchChatStream(chatParams);
const assistant = _.get(data, ['choices', '0', 'message']);
setTokenResult({ if (!result) {
...data.usage return;
}
const { reader, decoder } = result;
await receiveChatStream(reader, decoder, (data: any) => {
joinMessage(data);
}); });
setMessageList([
...messageList,
{
role: Roles.Assistant,
content: assistant.content,
uid: messageId.current + 1
}
]);
setMessageId();
setLoading(false); setLoading(false);
} catch (error) { } catch (error) {
setLoading(false); setLoading(false);
@@ -105,7 +127,6 @@ const MessageList: React.FC<MessageProps> = (props) => {
}; };
const handleSubmit = () => { const handleSubmit = () => {
console.log('submit');
submitMessage(); submitMessage();
}; };
@@ -32,28 +32,32 @@ const MessageItem: React.FC<{
} }
}, [isFocus]); }, [isFocus]);
useEffect(() => { // useEffect(() => {
if (isTyping) return; // if (isTyping) return;
let index = 0; // let index = 0;
const text = message.content; // const text = message.content;
if (!text.length) { // if (!text.length) {
return; // return;
} // }
setMessageContent(''); // setMessageContent('');
setIsAnimating(true); // setIsAnimating(true);
const intervalId = setInterval(() => { // const intervalId = setInterval(() => {
setMessageContent((prev) => prev + text[index]); // setMessageContent((prev) => prev + text[index]);
index += 1; // index += 1;
if (index === text.length) { // if (index === text.length) {
setIsAnimating(false); // setIsAnimating(false);
clearInterval(intervalId); // clearInterval(intervalId);
} // }
}, 20); // }, 20);
return () => clearInterval(intervalId); // return () => clearInterval(intervalId);
}, [message.content, isTyping]); // }, [message.content, isTyping]);
useEffect(() => { useEffect(() => {
if (!isAnimating && !isInitialRender.current) { setMessageContent(message.content);
}, [message.content]);
useEffect(() => {
if (!isInitialRender.current) {
updateMessage({ updateMessage({
role: roleType, role: roleType,
content: messageContent, content: messageContent,
-1
View File
@@ -28,7 +28,6 @@ export const requestConfig: RequestConfig = {
], ],
responseInterceptors: [ responseInterceptors: [
(response) => { (response) => {
console.log('responseInterceptors+++++++++++++++', response);
return response; return response;
} }
] ]