paper-ai-release-24-07-21/components/chatAI.tsx

183 lines
6.6 KiB
TypeScript
Raw Normal View History

2024-02-18 12:10:53 +08:00
import Quill from "quill";
2024-01-18 15:46:18 +08:00
import {
updateBracketNumbersInDeltaKeepSelection,
convertToSuperscript,
deleteSameBracketNumber,
2024-01-18 15:46:18 +08:00
} from "@/utils/others/quillutils";
//redux不能在普通函数使用
2024-01-18 15:46:18 +08:00
interface ChatData {
choices: Array<{
delta: {
content?: string;
};
}>;
}
2024-01-21 23:08:25 +08:00
function isValidApiKey(apiKey: string) {
return apiKey && apiKey.trim() !== "";
}
2024-01-18 15:46:18 +08:00
const sendMessageToOpenAI = async (
2024-01-20 13:43:31 +08:00
content: string,
editor: Quill | null,
2024-02-18 12:10:53 +08:00
selectedModel: string,
2024-01-21 23:08:25 +08:00
apiKey: string,
upsreamUrl: string,
prompt: string,
cursorPosition: number | null,
useEditorFlag = true, // 新增的标志,用于决定操作
signal: AbortSignal
2024-01-18 15:46:18 +08:00
) => {
2024-01-20 13:43:31 +08:00
//识别应该使用的模型
2024-02-04 12:55:09 +08:00
let model = selectedModel;
console.log("upstreamUrl", upsreamUrl);
2024-01-18 15:46:18 +08:00
// 设置API请求参数
const requestOptions = {
method: "POST",
signal: signal,
2024-01-18 15:46:18 +08:00
headers: {
"Content-Type": "application/json",
2024-01-28 14:58:46 +08:00
// "Upstream-Url": upsreamUrl,
2024-01-21 23:08:25 +08:00
Authorization:
"Bearer " +
(isValidApiKey(apiKey)
? apiKey
: process.env.NEXT_PUBLIC_OPENAI_API_KEY),
2024-01-18 15:46:18 +08:00
},
body: JSON.stringify({
2024-01-20 13:43:31 +08:00
model: model,
stream: useEditorFlag, // 根据标志确定是否使用streaming
2024-01-18 15:46:18 +08:00
messages: [
{
role: "system",
2024-01-21 23:08:25 +08:00
content:
prompt ||
`作为论文写作助手,您的主要任务是根据用户提供的研究主题和上下文,以及相关的研究论文,来撰写和完善学术论文。在撰写过程中,请注意以下要点:
2024-01-18 15:46:18 +08:00
1.使
2024-01-28 19:01:11 +08:00
2.使 [1]***[1]*
2024-01-18 15:46:18 +08:00
3.
4.
2024-02-18 12:10:53 +08:00
5.使
2024-01-19 15:36:41 +08:00
6.
2024-01-18 23:22:23 +08:00
...[1],...[2]`,
2024-01-18 15:46:18 +08:00
},
{
role: "user",
content: content,
},
],
}),
};
console.log("请求的内容\n", content);
// 发送API请求
let response;
let responseClone = null; // 用于保存响应内容的变量
2024-01-18 15:46:18 +08:00
try {
2024-01-28 14:58:46 +08:00
response = await fetch(
2024-01-28 15:14:53 +08:00
(upsreamUrl || process.env.NEXT_PUBLIC_AI_URL) + "/v1/chat/completions",
2024-01-28 14:58:46 +08:00
requestOptions
);
2024-02-24 11:20:15 +08:00
// 检查响应状态码是否为429
if (response.status === 429) {
// 可以在这里处理429错误例如通过UI通知用户
throw new Error("请求过于频繁,请稍后再试。");
} else if (!response.ok) {
// 处理其他类型的HTTP错误
throw new Error(`HTTP错误状态码${response.status}`);
}
// 克隆响应以备后用
responseClone = response.clone();
2024-03-06 21:41:44 +08:00
if (useEditorFlag && editor && cursorPosition !== null) {
2024-02-24 11:20:15 +08:00
const reader = response.body!.getReader();
const decoder = new TextDecoder();
//开始前先进行换行
// editor.focus();
editor.insertText(editor.getSelection(true).index, "\n");
await processResult(reader, decoder, editor);
//搜索是否有相同的括号编号,如果有相同的则删除到只剩一个
convertToSuperscript(editor);
deleteSameBracketNumber(editor, cursorPosition);
updateBracketNumbersInDeltaKeepSelection(editor);
} else {
// 直接返回结果的逻辑
const data = await response.json();
const content = data.choices[0].message.content;
return content; // 或根据需要处理并返回数据
}
} catch (error: any) {
if (error.name === "AbortError") {
console.log("Fetch operation was aborted");
//这里不用产生报错因为是手动停止
return;
}
2024-02-13 13:15:38 +08:00
console.error("Error:", error);
// 根据是否成功读取响应体来抛出错误
if (responseClone) {
const textResponse = await responseClone.text(); // 从克隆的响应中读取数据
2024-02-24 11:20:15 +08:00
throw new Error(
`请求发生错误: ${error.message}, Response: ${textResponse}`
2024-02-24 11:20:15 +08:00
);
} else {
throw new Error(`请求发生错误: ${error.message}`);
}
2024-01-18 15:46:18 +08:00
}
};
2024-01-28 11:45:15 +08:00
async function processResult(reader, decoder, editor) {
let buffer = "";
2024-01-18 15:46:18 +08:00
while (true) {
const { done, value } = await reader.read();
if (done) {
console.log("Stream finished");
break;
}
2024-01-28 11:45:15 +08:00
buffer += decoder.decode(value, { stream: true });
2024-01-28 19:01:11 +08:00
// console.log("buffer", buffer);
2024-01-28 11:45:15 +08:00
// 处理缓冲区中的所有完整的 JSON 对象
let boundary;
2024-01-28 14:58:46 +08:00
try {
while ((boundary = buffer.indexOf("}\n")) !== -1) {
// 找到一个完整的 JSON 对象的边界
let jsonStr = buffer.substring(0, boundary + 1);
buffer = buffer.substring(boundary + 2);
2024-01-28 19:01:11 +08:00
// console.log("jsonStr", jsonStr);
2024-01-18 15:46:18 +08:00
2024-01-28 14:58:46 +08:00
// 尝试解析 JSON 对象
try {
// 如果 jsonStr 以 "data: " 开头,就移除这个前缀
// 移除字符串首尾的空白字符
jsonStr = jsonStr.trim();
// jsonStr = jsonStr.substring(6);
jsonStr = jsonStr.replace("data:", "");
2024-01-28 14:58:46 +08:00
let dataObject = JSON.parse(jsonStr);
2024-01-29 13:35:24 +08:00
// console.log("dataObject", dataObject);
2024-01-28 14:58:46 +08:00
// 处理 dataObject 中的 content
if (dataObject.choices && dataObject.choices.length > 0) {
let content =
2024-02-26 20:33:25 +08:00
dataObject.choices[0].delta?.content ??
dataObject.choices[0].message?.content;
2024-01-28 14:58:46 +08:00
if (content) {
// 在当前光标位置插入文本
2024-02-13 13:15:38 +08:00
// editor.focus();
editor.insertText(editor.getSelection(true).index, content);
2024-01-29 13:35:24 +08:00
// console.log("成功插入:", content);
2024-01-28 14:58:46 +08:00
}
2024-01-18 15:46:18 +08:00
}
2024-01-28 14:58:46 +08:00
} catch (error) {
throw new Error(`
there is a error in parse JSON object: ${jsonStr},
error reason: ${error}`);
2024-01-18 15:46:18 +08:00
}
2024-01-28 11:45:15 +08:00
}
2024-01-28 14:58:46 +08:00
} catch (error) {
break;
2024-01-18 15:46:18 +08:00
}
}
}
2024-02-27 22:45:40 +08:00
export { sendMessageToOpenAI };