improve chat page
This commit is contained in:
parent
7ba90ae7af
commit
752b72e2c9
@ -10,9 +10,10 @@ def set_torch():
|
|||||||
if torch_path in paths:
|
if torch_path in paths:
|
||||||
print("torch already set")
|
print("torch already set")
|
||||||
else:
|
else:
|
||||||
print("run:")
|
|
||||||
os.environ["PATH"] = paths + os.pathsep + torch_path + os.pathsep
|
os.environ["PATH"] = paths + os.pathsep + torch_path + os.pathsep
|
||||||
print(f"set Path={paths + os.pathsep + torch_path + os.pathsep}")
|
print("torch set")
|
||||||
|
# print("run:")
|
||||||
|
# print(f"set Path={paths + os.pathsep + torch_path + os.pathsep}")
|
||||||
else:
|
else:
|
||||||
print("torch not found")
|
print("torch not found")
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ export const ToolTipButton: FC<{
|
|||||||
desc: string,
|
desc: string,
|
||||||
icon?: ReactElement,
|
icon?: ReactElement,
|
||||||
size?: 'small' | 'medium' | 'large',
|
size?: 'small' | 'medium' | 'large',
|
||||||
|
shape?: 'rounded' | 'circular' | 'square';
|
||||||
|
appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent';
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
onClick?: MouseEventHandler
|
onClick?: MouseEventHandler
|
||||||
}> = ({
|
}> = ({
|
||||||
@ -13,12 +15,15 @@ export const ToolTipButton: FC<{
|
|||||||
desc,
|
desc,
|
||||||
icon,
|
icon,
|
||||||
size,
|
size,
|
||||||
|
shape,
|
||||||
|
appearance,
|
||||||
disabled,
|
disabled,
|
||||||
onClick
|
onClick
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Tooltip content={desc} showDelay={0} hideDelay={0} relationship="label">
|
<Tooltip content={desc} showDelay={0} hideDelay={0} relationship="label">
|
||||||
<Button disabled={disabled} icon={icon} onClick={onClick} size={size}>{text}</Button>
|
<Button disabled={disabled} icon={icon} onClick={onClick} size={size} shape={shape}
|
||||||
|
appearance={appearance}>{text}</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,8 @@ import {fetchEventSource} from '@microsoft/fetch-event-source';
|
|||||||
import {ConversationPair, getConversationPairs, Record} from '../utils/get-conversation-pairs';
|
import {ConversationPair, getConversationPairs, Record} from '../utils/get-conversation-pairs';
|
||||||
import logo from '../../../build/appicon.png';
|
import logo from '../../../build/appicon.png';
|
||||||
import MarkdownRender from '../components/MarkdownRender';
|
import MarkdownRender from '../components/MarkdownRender';
|
||||||
|
import {ToolTipButton} from '../components/ToolTipButton';
|
||||||
|
import {ArrowCircleUp28Regular, Delete28Regular, RecordStop28Regular} from '@fluentui/react-icons';
|
||||||
|
|
||||||
const userName = 'M E';
|
const userName = 'M E';
|
||||||
const botName = 'A I';
|
const botName = 'A I';
|
||||||
@ -48,6 +50,16 @@ const ChatPanel: FC = observer(() => {
|
|||||||
const bodyRef = useRef<HTMLDivElement>(null);
|
const bodyRef = useRef<HTMLDivElement>(null);
|
||||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const port = commonStore.getCurrentModelConfig().apiParameters.apiPort;
|
const port = commonStore.getCurrentModelConfig().apiParameters.apiPort;
|
||||||
|
const sseControllerRef = useRef<AbortController | null>(null);
|
||||||
|
|
||||||
|
let lastMessageId: string;
|
||||||
|
let generating: boolean = false;
|
||||||
|
if (conversationsOrder.length > 0) {
|
||||||
|
lastMessageId = conversationsOrder[conversationsOrder.length - 1];
|
||||||
|
const lastMessage = conversations[lastMessageId];
|
||||||
|
if (lastMessage.sender === botName)
|
||||||
|
generating = !lastMessage.done;
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inputRef.current)
|
if (inputRef.current)
|
||||||
@ -59,103 +71,111 @@ const ChatPanel: FC = observer(() => {
|
|||||||
bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
|
bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (e: React.ChangeEvent<HTMLFormElement>) => {
|
const handleKeyDownOrClick = (e: any) => {
|
||||||
e.preventDefault();
|
e.stopPropagation();
|
||||||
if (message !== '') {
|
if (e.type === 'click' || (e.keyCode === 13 && !e.shiftKey)) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!message) return;
|
||||||
|
onSubmit(message);
|
||||||
setMessage('');
|
setMessage('');
|
||||||
const newId = uuid();
|
}
|
||||||
conversations[newId] = {
|
};
|
||||||
sender: userName,
|
|
||||||
type: MessageType.Normal,
|
|
||||||
color: 'brand',
|
|
||||||
time: new Date().toISOString(),
|
|
||||||
content: message,
|
|
||||||
side: 'right',
|
|
||||||
done: true
|
|
||||||
};
|
|
||||||
setConversations(conversations);
|
|
||||||
conversationsOrder.push(newId);
|
|
||||||
setConversationsOrder(conversationsOrder);
|
|
||||||
|
|
||||||
const records: Record[] = [];
|
const onSubmit = (message: string) => {
|
||||||
conversationsOrder.forEach((uuid, index) => {
|
const newId = uuid();
|
||||||
const conversation = conversations[uuid];
|
conversations[newId] = {
|
||||||
if (conversation.done && conversation.type === MessageType.Normal && conversation.sender === botName) {
|
sender: userName,
|
||||||
if (index > 0) {
|
type: MessageType.Normal,
|
||||||
const questionId = conversationsOrder[index - 1];
|
color: 'brand',
|
||||||
const question = conversations[questionId];
|
time: new Date().toISOString(),
|
||||||
if (question.done && question.type === MessageType.Normal && question.sender === userName) {
|
content: message,
|
||||||
records.push({question: question.content, answer: conversation.content});
|
side: 'right',
|
||||||
}
|
done: true
|
||||||
|
};
|
||||||
|
setConversations(conversations);
|
||||||
|
conversationsOrder.push(newId);
|
||||||
|
setConversationsOrder(conversationsOrder);
|
||||||
|
|
||||||
|
const records: Record[] = [];
|
||||||
|
conversationsOrder.forEach((uuid, index) => {
|
||||||
|
const conversation = conversations[uuid];
|
||||||
|
if (conversation.done && conversation.type === MessageType.Normal && conversation.sender === botName) {
|
||||||
|
if (index > 0) {
|
||||||
|
const questionId = conversationsOrder[index - 1];
|
||||||
|
const question = conversations[questionId];
|
||||||
|
if (question.done && question.type === MessageType.Normal && question.sender === userName) {
|
||||||
|
records.push({question: question.content, answer: conversation.content});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
const messages = getConversationPairs(records, false);
|
});
|
||||||
(messages as ConversationPair[]).push({role: 'user', content: message});
|
const messages = getConversationPairs(records, false);
|
||||||
|
(messages as ConversationPair[]).push({role: 'user', content: message});
|
||||||
|
|
||||||
const answerId = uuid();
|
const answerId = uuid();
|
||||||
conversations[answerId] = {
|
conversations[answerId] = {
|
||||||
sender: botName,
|
sender: botName,
|
||||||
type: MessageType.Normal,
|
type: MessageType.Normal,
|
||||||
color: 'colorful',
|
color: 'colorful',
|
||||||
avatarImg: logo,
|
avatarImg: logo,
|
||||||
time: new Date().toISOString(),
|
time: new Date().toISOString(),
|
||||||
content: '',
|
content: '',
|
||||||
side: 'left',
|
side: 'left',
|
||||||
done: false
|
done: false
|
||||||
};
|
};
|
||||||
setConversations(conversations);
|
setConversations(conversations);
|
||||||
conversationsOrder.push(answerId);
|
conversationsOrder.push(answerId);
|
||||||
setConversationsOrder(conversationsOrder);
|
setConversationsOrder(conversationsOrder);
|
||||||
setTimeout(scrollToBottom);
|
setTimeout(scrollToBottom);
|
||||||
let answer = '';
|
let answer = '';
|
||||||
fetchEventSource(`http://127.0.0.1:${port}/chat/completions`, // https://api.openai.com/v1/chat/completions || http://127.0.0.1:${port}/chat/completions
|
sseControllerRef.current = new AbortController();
|
||||||
{
|
fetchEventSource(`http://127.0.0.1:${port}/chat/completions`, // https://api.openai.com/v1/chat/completions || http://127.0.0.1:${port}/chat/completions
|
||||||
method: 'POST',
|
{
|
||||||
headers: {
|
method: 'POST',
|
||||||
'Content-Type': 'application/json',
|
headers: {
|
||||||
Authorization: `Bearer sk-`
|
'Content-Type': 'application/json',
|
||||||
},
|
Authorization: `Bearer sk-`
|
||||||
body: JSON.stringify({
|
},
|
||||||
messages,
|
body: JSON.stringify({
|
||||||
stream: true,
|
messages,
|
||||||
model: 'gpt-3.5-turbo'
|
stream: true,
|
||||||
}),
|
model: 'gpt-3.5-turbo'
|
||||||
onmessage(e) {
|
}),
|
||||||
console.log('sse message', e);
|
signal: sseControllerRef.current?.signal,
|
||||||
scrollToBottom();
|
onmessage(e) {
|
||||||
if (e.data === '[DONE]') {
|
console.log('sse message', e);
|
||||||
conversations[answerId].done = true;
|
scrollToBottom();
|
||||||
setConversations(conversations);
|
if (e.data === '[DONE]') {
|
||||||
setConversationsOrder([...conversationsOrder]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = JSON.parse(e.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.debug('json error', error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (data.choices && Array.isArray(data.choices) && data.choices.length > 0) {
|
|
||||||
answer += data.choices[0]?.delta?.content || '';
|
|
||||||
conversations[answerId].content = answer;
|
|
||||||
setConversations(conversations);
|
|
||||||
setConversationsOrder([...conversationsOrder]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onclose() {
|
|
||||||
console.log('Connection closed');
|
|
||||||
},
|
|
||||||
onerror(err) {
|
|
||||||
conversations[answerId].type = MessageType.Error;
|
|
||||||
conversations[answerId].done = true;
|
conversations[answerId].done = true;
|
||||||
setConversations(conversations);
|
setConversations(conversations);
|
||||||
setConversationsOrder([...conversationsOrder]);
|
setConversationsOrder([...conversationsOrder]);
|
||||||
throw err;
|
return;
|
||||||
}
|
}
|
||||||
});
|
let data;
|
||||||
}
|
try {
|
||||||
|
data = JSON.parse(e.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.debug('json error', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.choices && Array.isArray(data.choices) && data.choices.length > 0) {
|
||||||
|
answer += data.choices[0]?.delta?.content || '';
|
||||||
|
conversations[answerId].content = answer;
|
||||||
|
setConversations(conversations);
|
||||||
|
setConversationsOrder([...conversationsOrder]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onclose() {
|
||||||
|
console.log('Connection closed');
|
||||||
|
},
|
||||||
|
onerror(err) {
|
||||||
|
conversations[answerId].type = MessageType.Error;
|
||||||
|
conversations[answerId].done = true;
|
||||||
|
setConversations(conversations);
|
||||||
|
setConversationsOrder([...conversationsOrder]);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -192,26 +212,40 @@ const ChatPanel: FC = observer(() => {
|
|||||||
</div>;
|
</div>;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-end">
|
<div className="flex items-end gap-2">
|
||||||
<button className="bg-blue-500 text-white rounded-lg py-2 px-4 mr-2" onClick={() => {
|
<ToolTipButton desc={t('Clear')}
|
||||||
setConversations({});
|
icon={<Delete28Regular/>}
|
||||||
setConversationsOrder([]);
|
size="large" shape="circular" appearance="subtle"
|
||||||
}}>
|
onClick={(e) => {
|
||||||
{t('Clear')}
|
setConversations({});
|
||||||
</button>
|
setConversationsOrder([]);
|
||||||
<form onSubmit={handleSubmit} className="flex items-end grow gap-2">
|
}}
|
||||||
<Textarea
|
/>
|
||||||
ref={inputRef}
|
<Textarea
|
||||||
className="grow"
|
ref={inputRef}
|
||||||
resize="vertical"
|
className="grow"
|
||||||
placeholder={t('Type your message here')!}
|
resize="vertical"
|
||||||
value={message}
|
placeholder={t('Type your message here')!}
|
||||||
onChange={(e) => setMessage(e.target.value)}
|
value={message}
|
||||||
/>
|
onChange={(e) => setMessage(e.target.value)}
|
||||||
<button type="submit" className="bg-blue-500 text-white rounded-lg py-2 px-4">
|
onKeyDown={handleKeyDownOrClick}
|
||||||
{t('Send')}
|
/>
|
||||||
</button>
|
<ToolTipButton desc={generating ? t('Stop') : t('Send')}
|
||||||
</form>
|
icon={generating ? <RecordStop28Regular/> : <ArrowCircleUp28Regular/>}
|
||||||
|
size="large" shape="circular" appearance="subtle"
|
||||||
|
onClick={(e) => {
|
||||||
|
if (generating) {
|
||||||
|
sseControllerRef.current?.abort();
|
||||||
|
if (lastMessageId) {
|
||||||
|
conversations[lastMessageId].type = MessageType.Error;
|
||||||
|
conversations[lastMessageId].done = true;
|
||||||
|
setConversations(conversations);
|
||||||
|
setConversationsOrder([...conversationsOrder]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleKeyDownOrClick(e);
|
||||||
|
}
|
||||||
|
}}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user