feat: 可以手动停止AI的输出(左下角按钮)

This commit is contained in:
liuweiqing 2024-02-26 09:57:42 +08:00
parent 1eb3c596f3
commit a72329d4a2
5 changed files with 284 additions and 221 deletions

View File

@ -9,6 +9,7 @@
"生成轮数": "Generation Rounds", "生成轮数": "Generation Rounds",
"时间范围": "Range of literature release dates, from this time to this year", "时间范围": "Range of literature release dates, from this time to this year",
"更新文中的上标,使得数字顺序排列": "Update the superscript in the text to make the numbers in order", "更新文中的上标,使得数字顺序排列": "Update the superscript in the text to make the numbers in order",
"停止生成": "Stop Generation",
"+ Add Paper": "+ Add Paper", "+ Add Paper": "+ Add Paper",
"Buy VIP TO UNLOCK Cloud Sync and Edit Mutiple Papers Simultaneously": "Buy VIP TO UNLOCK Cloud Sync and Edit Mutiple Papers Simultaneously", "Buy VIP TO UNLOCK Cloud Sync and Edit Mutiple Papers Simultaneously": "Buy VIP TO UNLOCK Cloud Sync and Edit Mutiple Papers Simultaneously",
"Paper Management": "Paper Management", "Paper Management": "Paper Management",

View File

@ -9,6 +9,7 @@
"生成轮数": "生成轮数", "生成轮数": "生成轮数",
"时间范围": "文献发布日期范围,从这个时间到今年", "时间范围": "文献发布日期范围,从这个时间到今年",
"更新文中的上标,使得数字顺序排列": "更新文中的上标,使得数字顺序排列", "更新文中的上标,使得数字顺序排列": "更新文中的上标,使得数字顺序排列",
"停止生成": "停止生成",
"+ Add Paper": "+ 添加新论文(会直接替换编辑器里的内容)", "+ Add Paper": "+ 添加新论文(会直接替换编辑器里的内容)",
"Buy VIP TO UNLOCK Cloud Sync and Edit Mutiple Papers Simultaneously": "购买VIP解锁云同步和同时编辑多篇论文", "Buy VIP TO UNLOCK Cloud Sync and Edit Mutiple Papers Simultaneously": "购买VIP解锁云同步和同时编辑多篇论文",
"Paper Management": "论文管理", "Paper Management": "论文管理",

View File

@ -116,11 +116,12 @@ const QEditor = ({ lng }) => {
//选择时间范围 //选择时间范围
const [timeRange, setTimeRange] = useLocalStorage("时间范围", "2019"); const [timeRange, setTimeRange] = useLocalStorage("时间范围", "2019");
const [generateNumber, setGenerateNumber] = useState(0); //当前任务的进行数 const [generateNumber, setGenerateNumber] = useState(0); //当前任务的进行数
const [openProgressBar, setOpenProgressBar] = useState(false); const [openProgressBar, setOpenProgressBar] = useState(false); //设置进度条是否打开
const [showAnnouncement, setShowAnnouncement] = useLocalStorage( const [showAnnouncement, setShowAnnouncement] = useLocalStorage(
"显示公告", "显示公告",
true true
); // 是否显示公告 ); // 是否显示公告
const [controller, setController] = useState<AbortController | null>(null); // 创建 AbortController 的状态
//redux //redux
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -287,6 +288,11 @@ const QEditor = ({ lng }) => {
}; };
// 处理AI写作 // 处理AI写作
const handleAIWrite = async () => { const handleAIWrite = async () => {
try {
setOpenProgressBar(true); //开启进度条
// 创建一个新的 AbortController 实例
const newController = new AbortController();
setController(newController);
quill!.setSelection(cursorPosition!, 0); // 将光标移动到原来的位置 quill!.setSelection(cursorPosition!, 0); // 将光标移动到原来的位置
const prompt = "请帮助用户完成论文写作,使用用户所说的语言完成"; const prompt = "请帮助用户完成论文写作,使用用户所说的语言完成";
@ -297,7 +303,9 @@ const QEditor = ({ lng }) => {
apiKey, apiKey,
upsreamUrl, upsreamUrl,
prompt, prompt,
cursorPosition! cursorPosition!,
true,
newController.signal // 传递 AbortSignal
); );
// 清空input内容 // 清空input内容
setUserInput(""); setUserInput("");
@ -309,10 +317,23 @@ const QEditor = ({ lng }) => {
autoClose: 2000, autoClose: 2000,
pauseOnHover: true, pauseOnHover: true,
}); });
} catch (error) {
toast.error(`AI写作出现错误: ${error}`, {
position: "top-center",
autoClose: 3000,
pauseOnHover: true,
});
} finally {
setOpenProgressBar(false); //关闭进度条
}
}; };
// 处理paper2AI // 处理paper2AI
async function paper2AI(topic: string) { async function paper2AI(topic: string) {
try {
// 创建一个新的 AbortController 实例
const newController = new AbortController();
setController(newController);
quill!.setSelection(cursorPosition!, 0); // 将光标移动到原来的位置 quill!.setSelection(cursorPosition!, 0); // 将光标移动到原来的位置
let offset = -1; let offset = -1;
if (generatedPaperNumber != 1) offset = 0; //如果生成的数量不为1则从0开始 if (generatedPaperNumber != 1) offset = 0; //如果生成的数量不为1则从0开始
@ -338,7 +359,8 @@ const QEditor = ({ lng }) => {
upsreamUrl, upsreamUrl,
prompt, prompt,
null, null,
false false,
newController.signal // 传递 AbortSignal
); );
console.log("topic in AI before removeSpecialCharacters", topic); console.log("topic in AI before removeSpecialCharacters", topic);
topic = removeSpecialCharacters(topic); topic = removeSpecialCharacters(topic);
@ -362,7 +384,8 @@ const QEditor = ({ lng }) => {
apiKey, apiKey,
upsreamUrl, upsreamUrl,
selectedModel!, selectedModel!,
topic topic,
newController.signal
); );
rawData = relevantPapers; rawData = relevantPapers;
} }
@ -394,7 +417,8 @@ const QEditor = ({ lng }) => {
apiKey, apiKey,
upsreamUrl, upsreamUrl,
selectedModel!, selectedModel!,
topic topic,
newController.signal
); );
rawData = relevantPapers; rawData = relevantPapers;
} }
@ -431,7 +455,8 @@ const QEditor = ({ lng }) => {
apiKey, apiKey,
upsreamUrl, upsreamUrl,
selectedModel!, selectedModel!,
topic topic,
newController.signal
); );
rawData = relevantPapers; rawData = relevantPapers;
} }
@ -475,7 +500,9 @@ const QEditor = ({ lng }) => {
apiKey, apiKey,
upsreamUrl, upsreamUrl,
systemPrompt, systemPrompt,
cursorPosition! cursorPosition!,
true,
newController.signal // 传递 AbortSignal
); );
setUserInput(""); setUserInput("");
// 重新获取更新后的内容并更新 Redux store // 重新获取更新后的内容并更新 Redux store
@ -517,10 +544,24 @@ const QEditor = ({ lng }) => {
}); });
} }
} }
} catch (error) {
toast.error(`Paper2AI出现错误: ${error}`, {
position: "top-center",
autoClose: 3000,
pauseOnHover: true,
});
} finally {
setOpenProgressBar(false); setOpenProgressBar(false);
setGenerateNumber(0); //总的已经生成的数量 setGenerateNumber(0); //总的已经生成的数量
} }
}
const handleStop = () => {
if (controller) {
controller.abort(); // 取消请求
setController(null); // 重置 controller 状态
}
};
return ( return (
<div className="flex flex-col "> <div className="flex flex-col ">
<div id="Qtoolbar" className="space-y-2 flex justify-between"> <div id="Qtoolbar" className="space-y-2 flex justify-between">
@ -601,6 +642,16 @@ const QEditor = ({ lng }) => {
<ReferenceList editor={quill} lng={lng} /> <ReferenceList editor={quill} lng={lng} />
<ExportDocx editor={quill} /> <ExportDocx editor={quill} />
</div> </div>
{/* 停止生成的按钮只有在开始对话之后才会出现 */}
{openProgressBar ? (
<button
onClick={handleStop}
className="fixed bottom-4 left-4 bg-red-500 hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50 active:bg-red-700 text-white font-bold py-2 px-4 rounded transition ease-in-out duration-150 shadow-lg hover:shadow-xl"
>
{t("停止生成")}
</button>
) : null}
<ToastContainer /> <ToastContainer />
</div> </div>
); );

View File

@ -8,6 +8,7 @@ import {
convertToSuperscript, convertToSuperscript,
deleteSameBracketNumber, deleteSameBracketNumber,
} from "@/utils/others/quillutils"; } from "@/utils/others/quillutils";
import { faSignal } from "@fortawesome/free-solid-svg-icons";
//redux不能在普通函数使用 //redux不能在普通函数使用
interface ChatData { interface ChatData {
@ -29,7 +30,8 @@ const sendMessageToOpenAI = async (
upsreamUrl: string, upsreamUrl: string,
prompt: string, prompt: string,
cursorPosition: number | null, cursorPosition: number | null,
useEditorFlag = true // 新增的标志,用于决定操作 useEditorFlag = true, // 新增的标志,用于决定操作
signal: AbortSignal
) => { ) => {
//识别应该使用的模型 //识别应该使用的模型
let model = selectedModel; let model = selectedModel;
@ -37,6 +39,7 @@ const sendMessageToOpenAI = async (
// 设置API请求参数 // 设置API请求参数
const requestOptions = { const requestOptions = {
method: "POST", method: "POST",
signal: signal,
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
// "Upstream-Url": upsreamUrl, // "Upstream-Url": upsreamUrl,
@ -106,7 +109,12 @@ const sendMessageToOpenAI = async (
const content = data.choices[0].message.content; const content = data.choices[0].message.content;
return content; // 或根据需要处理并返回数据 return content; // 或根据需要处理并返回数据
} }
} catch (error) { } catch (error: any) {
if (error.name === "AbortError") {
console.log("Fetch operation was aborted");
//这里不用产生报错因为是手动停止
return;
}
console.error("Error:", error); console.error("Error:", error);
// 如果有响应,返回响应的原始内容 // 如果有响应,返回响应的原始内容
if (response) { if (response) {

View File

@ -74,7 +74,8 @@ export async function evaluateTopicMatch(
apiKey: string, apiKey: string,
upsreamUrl: string, upsreamUrl: string,
selectedModel: string, selectedModel: string,
topic: string topic: string,
signal: AbortSignal
): Promise<{ relevantPapers: string[]; nonRelevantPapers: string[] }> { ): Promise<{ relevantPapers: string[]; nonRelevantPapers: string[] }> {
const prompt = const prompt =
"请判断文献是否跟用户输入的主题相关,只需要返回true或false的数组"; "请判断文献是否跟用户输入的主题相关,只需要返回true或false的数组";
@ -117,7 +118,8 @@ export async function evaluateTopicMatch(
upsreamUrl, upsreamUrl,
prompt, prompt,
null, null,
false false,
signal
); );
console.log("isrelevantResults in 相关性检查", isRelevantResults); console.log("isrelevantResults in 相关性检查", isRelevantResults);
// 处理每篇文献的相关性结果 // 处理每篇文献的相关性结果