diff --git a/.env.production b/.env.production index 771e8883..9a7a8b38 100644 --- a/.env.production +++ b/.env.production @@ -5,4 +5,5 @@ NEXT_PUBLIC_OPENAI_API_KEY=sk-ffe19ebe9fa44d00884330ff1c18cf82 NEXT_PUBLIC_PAPER_URL=/api/paper NEXT_PUBLIC_SEMANTIC_API_KEY=hEQvK6ARe84dzDPcMnpzX4n9jfoqztkMfaftPWnb NEXT_PUBLIC_PUBMED_API_KEY=057616e7ce6c722f2ae8679e38a8be9b1a09 -VERCEL_URL=https://www.paperai.life \ No newline at end of file +VERCEL_URL=https://www.paperai.life +NODE_ENV=production diff --git a/app/store/slices/authSlice.ts b/app/store/slices/authSlice.ts index 166fbdf9..7ac3eb6a 100644 --- a/app/store/slices/authSlice.ts +++ b/app/store/slices/authSlice.ts @@ -19,7 +19,7 @@ const initialState: APIState = { 2.文献引用:只引用与主题紧密相关的论文。在引用文献时,文末应使用方括号内的数字来标注引用来源,如 [1]。。请确保每个引用在文章中都有其对应的编号,*无需在文章末尾提供参考文献列表*。*每个文献对应的序号只应该出现一次,比如说引用了第一篇文献文中就只能出现一次[1]*。 3.忽略无关文献:对于与主题无关的论文,请不要包含在您的写作中。只关注对理解和阐述主题有实质性帮助的资料。 4.来源明确:在文章中,清楚地指出每个引用的具体来源。引用的信息应准确无误,确保读者能够追溯到原始文献。 - 5.使用中文完成回答,不超过三百字 + 5.使用用户所说的语言完成回答,不超过五百字 6.只能对给出的文献进行引用,坚决不能虚构文献。 返回格式举例: 在某个方面,某论文实现了以下突破...[1],在另一篇论文中,研究了...[2]`, diff --git a/components/GetArxiv.tsx b/components/GetArxiv.tsx index 458e80ec..01f8c393 100644 --- a/components/GetArxiv.tsx +++ b/components/GetArxiv.tsx @@ -38,12 +38,13 @@ interface Author { async function getArxivPapers( query: string, maxResults = 5, + offset = -1, sortBy = "submittedDate", sortOrder = "descending" ) { - const maxOffset = 30 - maxResults; // 假设总记录数为 100 - const start = getRandomOffset(maxOffset); - const url = `https://export.arxiv.org/api/query?search_query=${query}&start=${start}&max_results=${maxResults}&sortBy=${sortBy}&sortOrder=${sortOrder}`; + const maxOffset = 20 - maxResults; // 假设总记录数为 20 + if (offset === -1) offset = getRandomOffset(maxOffset); + const url = `https://export.arxiv.org/api/query?search_query=${query}&start=${offset}&max_results=${maxResults}&sortBy=${sortBy}&sortOrder=${sortOrder}`; try { const response = await axios.get(url); diff --git a/components/GetPubMed .tsx b/components/GetPubMed .tsx index 9edafb2a..4349e821 100644 --- a/components/GetPubMed .tsx +++ b/components/GetPubMed .tsx @@ -10,14 +10,20 @@ type PubMedID = string; // 定义idList为PubMedID数组 type IDList = PubMedID[]; -async function getPubMedPapers(query: string, year: number, limit = 2) { +async function getPubMedPapers( + query: string, + year: number, + offset = -1, + limit = 2 +) { try { const baseURL = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"; const db = "pubmed"; // 设定搜索的数据库为PubMed const retMax = limit; // 检索的最大记录数 - const retStart = getRandomOffset(20 - limit); // 假设每页最多30条,根据需要随机偏移 - const url = `${baseURL}?db=${db}&term=${query}[Title/Abstract]+AND+2018:3000[Date - Publication]&retMax=${retMax}&retStart=${retStart}&api_key=${process.env.NEXT_PUBLIC_PUBMED_API_KEY}`; + const maxOffset = 20 - limit; // 假设总记录数为 20 + if (offset === -1) offset = getRandomOffset(maxOffset); + const url = `${baseURL}?db=${db}&term=${query}[Title/Abstract]+AND+2018:3000[Date - Publication]&retMax=${retMax}&retStart=${offset}&api_key=${process.env.NEXT_PUBLIC_PUBMED_API_KEY}`; const response = await axios.get(url, { responseType: "text" }); console.log(response.data); // 解析XML数据 @@ -155,9 +161,14 @@ async function getPubMedPaperDetails(idList: IDList) { } // 示例:使用这些函数 -async function fetchPubMedData(query: string, year: number, limit: number) { +async function fetchPubMedData( + query: string, + year: number, + limit: number, + offset: number +) { try { - const idList = await getPubMedPapers(query, year, limit); + const idList = await getPubMedPapers(query, year, offset, limit); if (idList && idList.length > 0) { const paperDetails = await getPubMedPaperDetails(idList); console.log("fetchPubMedData", paperDetails); // 处理或显示文章详情 diff --git a/components/GetSemantic.tsx b/components/GetSemantic.tsx index 5392f2f0..80e8cc6b 100644 --- a/components/GetSemantic.tsx +++ b/components/GetSemantic.tsx @@ -15,10 +15,15 @@ interface Paper { url: string; } -async function getSemanticPapers(query: string, year: string, limit = 2) { +async function getSemanticPapers( + query: string, + year: string, + offset = -1, + limit = 2 +) { try { const maxOffset = 20 - limit; // 假设总记录数为 20 - const offset = getRandomOffset(maxOffset); + if (offset === -1) offset = getRandomOffset(maxOffset); const url = `https://api.semanticscholar.org/graph/v1/paper/search`; const response = await axios.get(url, { headers: { diff --git a/components/QuillEditor.tsx b/components/QuillEditor.tsx index 12d55e4c..33f6967a 100644 --- a/components/QuillEditor.tsx +++ b/components/QuillEditor.tsx @@ -101,6 +101,11 @@ const QEditor = ({ lng }) => { "gpt语言模型", "gpt-4" ); // 默认选项 + const [generatedPaperNumber, setGeneratedPaperNumber] = useLocalStorage( + "生成次数", + 1 + ); // 初始值设为1 + //redux const dispatch = useAppDispatch(); const references = useAppSelector((state) => state.auth.referencesRedux); @@ -231,16 +236,20 @@ const QEditor = ({ lng }) => { const handleInputChange = (event: any) => { setUserInput(event.target.value); }; - + // 处理输入generatedPaperNumber变化的函数 + const handleGeneratedPaperNumberChange = (event: any) => { + const newValue = parseInt(event.target.value, 10); + setGeneratedPaperNumber(newValue); + }; // 处理AI写作 const handleAIWrite = async () => { - quill.setSelection(cursorPosition, 0); // 将光标移动到原来的位置 + quill!.setSelection(cursorPosition!, 0); // 将光标移动到原来的位置 const prompt = "请帮助用户完成论文写作,使用用户所说的语言完成"; await sendMessageToOpenAI( userInput, - quill, - selectedModel, + quill!, + selectedModel!, apiKey, upsreamUrl, prompt @@ -248,132 +257,137 @@ const QEditor = ({ lng }) => { // 清空input内容 setUserInput(""); // 重新获取更新后的内容并更新 Redux store - const updatedContent = quill.root.innerHTML; + const updatedContent = quill!.root.innerHTML; dispatch(setEditorContent(updatedContent)); }; // 处理paper2AI async function paper2AI(topic: string) { - quill.setSelection(cursorPosition, 0); // 将光标移动到原来的位置 - - try { - if (!topic) { - //使用ai提取当前要请求的论文主题 - const prompt = - "As a topic extraction assistant, you can help me extract the current discussion of the paper topic, I will enter the content of the paper, you extract the paper topic , no more than two, Hyphenated query terms yield no matches (replace it with space to find matches) return format is: topic1 topic2"; - const userMessage = getTextBeforeCursor(quill, 2000); - topic = await getTopicFromAI(userMessage, prompt, apiKey); - console.log("topic in AI before removeSpecialCharacters", topic); - topic = removeSpecialCharacters(topic); - topic = topic.split(" ").slice(0, 2).join(" "); - //如果超过十个字符就截断 - if (topic.length > 10) { - topic = topic.slice(0, 10); - } - } - console.log("topic in AI", topic); - let rawData, dataString, newReferences; - if (selectedSource === "arxiv") { - rawData = await getArxivPapers(topic); - console.log("arxiv rawdata:", rawData); - // 将 rawData 转换为引用数组 - newReferences = rawData.map((entry) => ({ - url: entry.id, - title: entry.title, - year: entry.published, - author: entry.authors?.slice(0, 3).join(", "), - })); - dataString = rawData - .map((entry) => { - return `ID: ${entry.id}\nTime: ${entry.published}\nTitle: ${entry.title}\nSummary: ${entry.summary}\n\n`; - }) - .join(""); - } else if (selectedSource === "semanticScholar") { - rawData = await getSemanticPapers(topic, "2015-2023"); - // 将 rawData 转换为引用数组 - newReferences = rawData.map((entry) => ({ - url: entry.url, - title: entry.title, - year: entry.year, - author: entry.authors?.slice(0, 3).join(", "), - venue: entry.venue, - journal: formatJournalReference(entry), - })); - dataString = rawData - .map((entry) => { - return `Time: ${entry.year}\nTitle: ${entry.title}\nSummary: ${entry.abstract}\n\n`; - }) - .join(""); - } else if (selectedSource === "pubmed") { - rawData = await fetchPubMedData(topic, 2020, 2); - if (!rawData) { - throw new Error("未搜索到文献 from PubMed."); + quill!.setSelection(cursorPosition!, 0); // 将光标移动到原来的位置 + let offset = -1; + if (generatedPaperNumber) offset = 0; + for (let i = 0; i < generatedPaperNumber!; i++) { + try { + if (!topic) { + //使用ai提取当前要请求的论文主题 + const prompt = + "As a topic extraction assistant, you can help me extract the current discussion of the paper topic, I will enter the content of the paper, you extract the paper topic , no more than two, Hyphenated query terms yield no matches (replace it with space to find matches) return format is: topic1 topic2"; + const userMessage = getTextBeforeCursor(quill!, 2000); + topic = await getTopicFromAI(userMessage, prompt, apiKey); + console.log("topic in AI before removeSpecialCharacters", topic); + topic = removeSpecialCharacters(topic); + topic = topic.split(" ").slice(0, 2).join(" "); + //如果超过十个字符就截断 + if (topic.length > 10) { + topic = topic.slice(0, 10); + } } - newReferences = rawData.map((entry) => ({ - id: entry.id, // 文章的 PubMed ID - title: entry.title, // 文章的标题 - abstract: entry.abstract, // 文章的摘要 - author: entry.authors?.slice(0, 3).join(", "), // 文章的作者列表,假设为字符串数组 - year: entry.year, // 文章的发表日期 - journal: entry.journal, // 文章的发表杂志 - url: entry.url, // 文章的 URL - source: "PubMed", // 指示这些引用来自 PubMed - })); + console.log("topic in AI", topic); + let rawData, dataString, newReferences; + if (selectedSource === "arxiv") { + rawData = await getArxivPapers(topic); + console.log("arxiv rawdata:", rawData); + // 将 rawData 转换为引用数组 + newReferences = rawData.map((entry: any) => ({ + url: entry.id, + title: entry.title, + year: entry.published, + author: entry.authors?.slice(0, 3).join(", "), + })); + dataString = rawData + .map((entry: any) => { + return `ID: ${entry.id}\nTime: ${entry.published}\nTitle: ${entry.title}\nSummary: ${entry.summary}\n\n`; + }) + .join(""); + } else if (selectedSource === "semanticScholar") { + rawData = await getSemanticPapers(topic, "2015-2023", offset); + // 将 rawData 转换为引用数组 + newReferences = rawData.map((entry: any) => ({ + url: entry.url, + title: entry.title, + year: entry.year, + author: entry.authors?.slice(0, 3).join(", "), + venue: entry.venue, + journal: formatJournalReference(entry), + })); + dataString = rawData + .map((entry: any) => { + return `Time: ${entry.year}\nTitle: ${entry.title}\nSummary: ${entry.abstract}\n\n`; + }) + .join(""); + } else if (selectedSource === "pubmed") { + rawData = await fetchPubMedData(topic, 2020, offset, 2); + if (!rawData) { + throw new Error("未搜索到文献 from PubMed."); + } + newReferences = rawData.map((entry: any) => ({ + id: entry.id, // 文章的 PubMed ID + title: entry.title, // 文章的标题 + abstract: entry.abstract, // 文章的摘要 + author: entry.authors?.slice(0, 3).join(", "), // 文章的作者列表,假设为字符串数组 + year: entry.year, // 文章的发表日期 + journal: entry.journal, // 文章的发表杂志 + url: entry.url, // 文章的 URL + source: "PubMed", // 指示这些引用来自 PubMed + })); - // 打印或进一步处理 newReferences - console.log(newReferences); + // 打印或进一步处理 newReferences + console.log(newReferences); - dataString = rawData - .map((entry) => { - return `Time: ${entry.year}\nTitle: ${entry.title}\nSummary: ${entry.abstract}\n\n`; + dataString = rawData + .map((entry: any) => { + return `Time: ${entry.year}\nTitle: ${entry.title}\nSummary: ${entry.abstract}\n\n`; + }) + .join(""); + } + //在对应的位置添加文献 + const nearestNumber = getNumberBeforeCursor(quill!); + dispatch( + addReferencesRedux({ + references: newReferences, + position: nearestNumber, }) - .join(""); - } - //在对应的位置添加文献 - const nearestNumber = getNumberBeforeCursor(quill); - dispatch( - addReferencesRedux({ - references: newReferences, - position: nearestNumber, - }) - ); + ); - // 确保搜索到的论文不超过 3000 个字符 - const trimmedMessage = - dataString.length > 3000 ? dataString.slice(0, 3000) : dataString; - //slate的方法 - // const content = `需要完成的论文主题:${topic}, 搜索到的论文内容:${trimmedMessage},之前已经完成的内容上下文:${extractText( - // editorValue - // )}`; - const content = `之前用户已经完成的内容上下文:${getTextBeforeCursor( - quill, - 500 - )},搜索到的论文内容:${trimmedMessage},需要完成的论文主题:${topic},请根据搜索到的论文内容完成用户的论文`; - await sendMessageToOpenAI( - content, - quill, - selectedModel, - apiKey, - upsreamUrl, - systemPrompt - ); - setUserInput(""); - // 重新获取更新后的内容并更新 Redux store - const updatedContent = quill.root.innerHTML; - dispatch(setEditorContent(updatedContent)); - if (isVip) { - //在云端同步supabase - const data = await submitPaper( - supabase, - updatedContent, - references, - paperNumberRedux + // 确保搜索到的论文不超过 3000 个字符 + const trimmedMessage = + dataString.length > 3000 ? dataString.slice(0, 3000) : dataString; + //slate的方法 + // const content = `需要完成的论文主题:${topic}, 搜索到的论文内容:${trimmedMessage},之前已经完成的内容上下文:${extractText( + // editorValue + // )}`; + const content = `之前用户已经完成的内容上下文:${getTextBeforeCursor( + quill!, + 900 + )},搜索到的论文内容:${trimmedMessage},需要完成的论文主题:${topic},请根据搜索到的论文内容完成用户的论文`; + await sendMessageToOpenAI( + content, + quill!, + selectedModel!, + apiKey, + upsreamUrl, + systemPrompt ); + setUserInput(""); + // 重新获取更新后的内容并更新 Redux store + const updatedContent = quill!.root.innerHTML; + dispatch(setEditorContent(updatedContent)); + if (isVip) { + //在云端同步supabase + const data = await submitPaper( + supabase, + updatedContent, + references, + paperNumberRedux + ); + } + //修改offset使得按照接下来的顺序进行获取文献 + offset += 2; + } catch (error) { + // console.error("Error fetching data:", error); + // 在处理错误后,再次抛出这个错误 + throw new Error(`Paper2AI出现错误: ${error}`); } - } catch (error) { - // console.error("Error fetching data:", error); - // 在处理错误后,再次抛出这个错误 - throw new Error(`Paper2AI出现错误: ${error}`); } } @@ -414,12 +428,18 @@ const QEditor = ({ lng }) => { +