|
@@ -1,4 +1,4 @@
|
|
|
-import React, { useCallback, useContext, useEffect, useState } from 'react';
|
|
|
|
|
|
|
+import React, { useCallback, useContext, useEffect, useState, useRef } from 'react';
|
|
|
import { useSearchParams } from 'react-router-dom';
|
|
import { useSearchParams } from 'react-router-dom';
|
|
|
import { UserContext } from '../../context/User/index.js';
|
|
import { UserContext } from '../../context/User/index.js';
|
|
|
import {
|
|
import {
|
|
@@ -100,6 +100,7 @@ const Playground = () => {
|
|
|
const [groups, setGroups] = useState([]);
|
|
const [groups, setGroups] = useState([]);
|
|
|
const [showSettings, setShowSettings] = useState(true);
|
|
const [showSettings, setShowSettings] = useState(true);
|
|
|
const [styleState, styleDispatch] = useContext(StyleContext);
|
|
const [styleState, styleDispatch] = useContext(StyleContext);
|
|
|
|
|
+ const sseSourceRef = useRef(null);
|
|
|
|
|
|
|
|
const handleInputChange = (name, value) => {
|
|
const handleInputChange = (name, value) => {
|
|
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
|
@@ -210,9 +211,13 @@ const Playground = () => {
|
|
|
payload: JSON.stringify(payload),
|
|
payload: JSON.stringify(payload),
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // 保存 source 引用以便后续停止生成
|
|
|
|
|
+ sseSourceRef.current = source;
|
|
|
|
|
+
|
|
|
source.addEventListener('message', (e) => {
|
|
source.addEventListener('message', (e) => {
|
|
|
if (e.data === '[DONE]') {
|
|
if (e.data === '[DONE]') {
|
|
|
source.close();
|
|
source.close();
|
|
|
|
|
+ sseSourceRef.current = null;
|
|
|
completeMessage();
|
|
completeMessage();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -240,6 +245,7 @@ const Playground = () => {
|
|
|
const errorMessage = e.data || t('请求发生错误');
|
|
const errorMessage = e.data || t('请求发生错误');
|
|
|
streamMessageUpdate(errorMessage, 'content');
|
|
streamMessageUpdate(errorMessage, 'content');
|
|
|
completeMessage('error');
|
|
completeMessage('error');
|
|
|
|
|
+ sseSourceRef.current = null;
|
|
|
source.close();
|
|
source.close();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -352,6 +358,51 @@ const Playground = () => {
|
|
|
});
|
|
});
|
|
|
}, [setMessage]);
|
|
}, [setMessage]);
|
|
|
|
|
|
|
|
|
|
+ const onStopGenerator = useCallback(() => {
|
|
|
|
|
+ if (sseSourceRef.current) {
|
|
|
|
|
+ sseSourceRef.current.close();
|
|
|
|
|
+ sseSourceRef.current = null;
|
|
|
|
|
+ setMessage((prevMessage) => {
|
|
|
|
|
+ const lastMessage = prevMessage[prevMessage.length - 1];
|
|
|
|
|
+ if (lastMessage.status === 'loading' || lastMessage.status === 'incomplete') {
|
|
|
|
|
+ let content = lastMessage.content || '';
|
|
|
|
|
+ let reasoningContent = lastMessage.reasoningContent || '';
|
|
|
|
|
+
|
|
|
|
|
+ // 处理 <think> 标签格式的思维链
|
|
|
|
|
+ if (content.includes('<think>')) {
|
|
|
|
|
+ const thinkTagRegex = /<think>([\s\S]*?)(?:<\/think>|$)/g;
|
|
|
|
|
+ let thoughts = [];
|
|
|
|
|
+ let replyParts = [];
|
|
|
|
|
+ let lastIndex = 0;
|
|
|
|
|
+ let match;
|
|
|
|
|
+
|
|
|
|
|
+ while ((match = thinkTagRegex.exec(content)) !== null) {
|
|
|
|
|
+ replyParts.push(content.substring(lastIndex, match.index));
|
|
|
|
|
+ thoughts.push(match[1]);
|
|
|
|
|
+ lastIndex = match.index + match[0].length;
|
|
|
|
|
+ }
|
|
|
|
|
+ replyParts.push(content.substring(lastIndex));
|
|
|
|
|
+
|
|
|
|
|
+ // 更新内容和思维链
|
|
|
|
|
+ content = replyParts.join('').trim();
|
|
|
|
|
+ if (thoughts.length > 0) {
|
|
|
|
|
+ reasoningContent = thoughts.join('\n\n---\n\n');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return [...prevMessage.slice(0, -1), {
|
|
|
|
|
+ ...lastMessage,
|
|
|
|
|
+ status: 'complete',
|
|
|
|
|
+ reasoningContent: reasoningContent,
|
|
|
|
|
+ content: content,
|
|
|
|
|
+ isReasoningExpanded: false // 停止时折叠思维链面板
|
|
|
|
|
+ }];
|
|
|
|
|
+ }
|
|
|
|
|
+ return prevMessage;
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [setMessage]);
|
|
|
|
|
+
|
|
|
const SettingsToggle = () => {
|
|
const SettingsToggle = () => {
|
|
|
if (!styleState.isMobile) return null;
|
|
if (!styleState.isMobile) return null;
|
|
|
return (
|
|
return (
|
|
@@ -664,6 +715,8 @@ const Playground = () => {
|
|
|
chats={message}
|
|
chats={message}
|
|
|
onMessageSend={onMessageSend}
|
|
onMessageSend={onMessageSend}
|
|
|
showClearContext
|
|
showClearContext
|
|
|
|
|
+ showStopGenerate
|
|
|
|
|
+ onStopGenerator={onStopGenerator}
|
|
|
onClear={() => {
|
|
onClear={() => {
|
|
|
setMessage([]);
|
|
setMessage([]);
|
|
|
}}
|
|
}}
|