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
|
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 { 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,
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ export const requestConfig: RequestConfig = {
|
|||||||
],
|
],
|
||||||
responseInterceptors: [
|
responseInterceptors: [
|
||||||
(response) => {
|
(response) => {
|
||||||
console.log('responseInterceptors+++++++++++++++', response);
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user