fix: submit completion by fetch
This commit is contained in:
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -8,3 +8,43 @@ export async function execChatCompletions(params: any) {
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useIntl } from '@umijs/max';
|
||||
import { Button, Input, Spin } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import { useRef, useState } from 'react';
|
||||
import { execChatCompletions } from '../apis';
|
||||
import { fetchChatStream, receiveChatStream } from '../apis';
|
||||
import { Roles } from '../config';
|
||||
import '../style/ground-left.less';
|
||||
import '../style/system-message-wrap.less';
|
||||
@@ -13,7 +13,6 @@ import ChatFooter from './chat-footer';
|
||||
import MessageItem from './message-item';
|
||||
import ReferenceParams from './reference-params';
|
||||
import ViewCodeModal from './view-code-modal';
|
||||
|
||||
interface MessageProps {
|
||||
parameters: any;
|
||||
}
|
||||
@@ -42,7 +41,11 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
const [activeIndex, setActiveIndex] = useState(-1);
|
||||
const [tokenResult, setTokenResult] = useState<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) => {
|
||||
setSystemMessage(e.target.value);
|
||||
};
|
||||
@@ -61,10 +64,32 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
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 () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
setMessageId();
|
||||
setTokenResult(null);
|
||||
contentRef.current = '';
|
||||
const chatParams = {
|
||||
messages: systemMessage
|
||||
? [
|
||||
@@ -75,22 +100,19 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
...messageList
|
||||
]
|
||||
: [...messageList],
|
||||
...parameters
|
||||
...parameters,
|
||||
stream: true
|
||||
};
|
||||
const data = await execChatCompletions(chatParams);
|
||||
const assistant = _.get(data, ['choices', '0', 'message']);
|
||||
setTokenResult({
|
||||
...data.usage
|
||||
const result = await fetchChatStream(chatParams);
|
||||
|
||||
if (!result) {
|
||||
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);
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
@@ -105,7 +127,6 @@ const MessageList: React.FC<MessageProps> = (props) => {
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
console.log('submit');
|
||||
submitMessage();
|
||||
};
|
||||
|
||||
|
||||
@@ -32,28 +32,32 @@ const MessageItem: React.FC<{
|
||||
}
|
||||
}, [isFocus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isTyping) return;
|
||||
let index = 0;
|
||||
const text = message.content;
|
||||
if (!text.length) {
|
||||
return;
|
||||
}
|
||||
setMessageContent('');
|
||||
setIsAnimating(true);
|
||||
const intervalId = setInterval(() => {
|
||||
setMessageContent((prev) => prev + text[index]);
|
||||
index += 1;
|
||||
if (index === text.length) {
|
||||
setIsAnimating(false);
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}, 20);
|
||||
return () => clearInterval(intervalId);
|
||||
}, [message.content, isTyping]);
|
||||
// useEffect(() => {
|
||||
// if (isTyping) return;
|
||||
// let index = 0;
|
||||
// const text = message.content;
|
||||
// if (!text.length) {
|
||||
// return;
|
||||
// }
|
||||
// setMessageContent('');
|
||||
// setIsAnimating(true);
|
||||
// const intervalId = setInterval(() => {
|
||||
// setMessageContent((prev) => prev + text[index]);
|
||||
// index += 1;
|
||||
// if (index === text.length) {
|
||||
// setIsAnimating(false);
|
||||
// clearInterval(intervalId);
|
||||
// }
|
||||
// }, 20);
|
||||
// return () => clearInterval(intervalId);
|
||||
// }, [message.content, isTyping]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAnimating && !isInitialRender.current) {
|
||||
setMessageContent(message.content);
|
||||
}, [message.content]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isInitialRender.current) {
|
||||
updateMessage({
|
||||
role: roleType,
|
||||
content: messageContent,
|
||||
|
||||
@@ -28,7 +28,6 @@ export const requestConfig: RequestConfig = {
|
||||
],
|
||||
responseInterceptors: [
|
||||
(response) => {
|
||||
console.log('responseInterceptors+++++++++++++++', response);
|
||||
return response;
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user