This commit is contained in:
Liuweiqing 2024-01-19 15:36:41 +08:00
parent 39245ecd78
commit e4935e4f22
8 changed files with 207 additions and 85 deletions

9
.env.production Normal file
View File

@ -0,0 +1,9 @@
# Update these with your Supabase details from your project settings > API
# https://app.supabase.com/project/_/settings/api
NEXT_PUBLIC_SUPABASE_URL=https://yidfukfbrluizjvfrrsj.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlpZGZ1a2Zicmx1aXpqdmZycnNqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDQ4NjMyNjEsImV4cCI6MjAyMDQzOTI2MX0.EXIXAdNIGLFo5wHmwmY2-9bqLO9vyFYDvMMtCtkxgig
NEXT_PUBLIC_TINYMCE_API_KEY=e983nu390inks6be1wwlsrdxjebot3yc4pld7d44zs6vcrxr
NEXT_PUBLIC_OPENAI_API_KEY=sess-1CQMw0TDL0VZd9C1W3aGAZ6scffG32JJPcap7Re4
NEXT_PUBLIC_SEMANTIC_API_KEY=hEQvK6ARe84dzDPcMnpzX4n9jfoqztkMfaftPWnb
NEXT_PUBLIC_AI_URL=https://chatgpt-api-proxy-private.14790897abc.workers.dev/v1/chat/completions
#"https://api.openai.com/v1/chat/completions"

View File

@ -41,7 +41,7 @@ async function getArxivPapers(
sortBy = "submittedDate",
sortOrder = "descending"
) {
const maxOffset = 100 - maxResults; // 假设总记录数为 100
const maxOffset = 30 - maxResults; // 假设总记录数为 100
const start = getRandomOffset(maxOffset);
const url = `http://export.arxiv.org/api/query?search_query=${query}&start=${start}&max_results=${maxResults}&sortBy=${sortBy}&sortOrder=${sortOrder}`;

View File

@ -17,7 +17,7 @@ interface Paper {
async function getSemanticPapers(query: string, year: string, limit = 2) {
try {
const maxOffset = 100 - limit; // 假设总记录数为 100
const maxOffset = 30 - limit; // 假设总记录数为 100
const offset = getRandomOffset(maxOffset);
const url = `https://api.semanticscholar.org/graph/v1/paper/search`;
const response = await axios.get(url, {
@ -29,7 +29,7 @@ async function getSemanticPapers(query: string, year: string, limit = 2) {
offset: offset,
limit: 2,
year: year,
fields: "title,year,authors.name,abstract,venue,url",
fields: "title,year,authors.name,abstract,venue,url,journal",
},
});
// 提取并处理论文数据

View File

@ -13,6 +13,7 @@ import {
getTextBeforeCursor,
updateBracketNumbersInDelta,
convertToSuperscript,
removeSpecialCharacters,
} from "@/utils/others/quillutils";
import ReferenceList from "./ReferenceList";
//类型声明
@ -113,35 +114,52 @@ const QEditor = () => {
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 = getTopicFromAI(userMessage, prompt);
topic = await getTopicFromAI(userMessage, prompt);
console.log("topic in AI before removeSpecialCharacters", topic);
topic = removeSpecialCharacters(topic);
topic = topic.split(" ").slice(0, 2).join(" ");
}
console.log("topic in AI", topic);
let rawData, dataString;
if (selectedSource === "arxiv") {
rawData = await getArxivPapers(topic);
dataString = rawData
.map((entry) => {
addReference({
// 将 rawData 转换为引用数组
const newReferences = rawData.map((entry) => ({
url: entry.id,
title: entry.title,
year: entry.published,
author: entry.author?.slice(0, 3).join(", "),
});
}));
// 更新引用列表状态
setReferences((prevReferences) => [
...prevReferences,
...newReferences,
]);
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");
dataString = rawData
.map((entry) => {
addReference({
// 将 rawData 转换为引用数组
const newReferences = rawData.map((entry) => ({
url: entry.url,
title: entry.title,
year: entry.published,
author: entry.authors?.slice(0, 3).join(", "),
venue: entry.venue,
});
journal: entry.journal,
}));
// 更新引用列表状态
setReferences((prevReferences) => [
...prevReferences,
...newReferences,
]);
dataString = rawData
.map((entry) => {
return `Time: ${entry.year}\nTitle: ${entry.title}\nSummary: ${entry.abstract}\n\n`;
})
.join("");
@ -182,13 +200,13 @@ const QEditor = () => {
Insert AI Text
</button>*/}
<button
onClick={() => insertPapers(userInput || "robot")}
onClick={() => insertPapers(userInput)}
className="bg-indigo-500 hover:bg-indigo-700 text-black font-bold py-2 px-4 rounded"
>
Insert Papers
</button>
<button
onClick={() => paper2AI(userInput || "robot")}
onClick={() => paper2AI(userInput)}
className="bg-red-500 hover:bg-red-700 text-black font-bold py-2 px-4 rounded"
>
Paper2AI
@ -202,11 +220,11 @@ const QEditor = () => {
{/* 其他来源网站 */}
</select>
</div>
<div>
<div
id="editor"
style={{
height: "500px",
width: "600px",
width: "calc(100vw - 100px)", // 屏幕宽度减去 100px
minHeight: "150px", // 注意驼峰命名法
maxHeight: "500px",
overflowY: "auto", // overflow-y -> overflowY
@ -220,6 +238,7 @@ const QEditor = () => {
removeReference={removeReference}
/>
</div>
</div>
);
};

View File

@ -1,7 +1,10 @@
import React, { useState } from "react";
import { Reference } from "@/utils/global";
import {
copyToClipboard,
formatReferenceForCopy,
} from "@/utils/others/quillutils";
type ReferenceListProps = {
references: Reference[];
addReference: (reference: Reference) => void;
@ -19,8 +22,8 @@ function ReferenceList({
const [newPublisher, setNewPublisher] = useState("");
const [newUrl, setNewUrl] = useState("");
return (
<div>
{/* 这里可以添加输入界面和添加按钮 */}
<div className="container mx-auto p-4">
{/* 表单区域 */}
<form
onSubmit={(e) => {
e.preventDefault();
@ -31,50 +34,102 @@ function ReferenceList({
venue: newPublisher,
url: newUrl,
});
// 清空表单
setNewTitle("");
setNewAuthor("");
setNewYear(2020);
setNewPublisher("");
setNewUrl("");
}}
className="mb-6"
>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3">
<input
className="border p-2 rounded"
type="text"
value={newTitle}
onChange={(e) => setNewTitle(e.target.value)}
placeholder="Title"
/>
<input
className="border p-2 rounded"
type="text"
value={newAuthor}
onChange={(e) => setNewAuthor(e.target.value)}
placeholder="Author"
/>
<input
className="border p-2 rounded"
type="text"
value={newYear}
onChange={(e) => setNewYear(e.target.value)}
onChange={(e) => setNewYear(parseInt(e.target.value))}
placeholder="Year"
/>
<input
className="border p-2 rounded"
type="text"
value={newPublisher}
onChange={(e) => setNewPublisher(e.target.value)}
placeholder="Publisher"
/>
<input
className="border p-2 rounded"
type="text"
value={newUrl}
onChange={(e) => setNewUrl(e.target.value)}
placeholder="URL"
/>
<button type="submit">Add Reference</button>
</div>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-4"
type="submit"
>
Add Reference
</button>
</form>
{/* 这里可以添加显示界面 */}
{/* 引用列表显示区域 */}
<ul>
{references.map((reference, index) => (
<li key={index}>
{/* 如果存在url则输出(url) */}
{reference.author}. {reference.title}. {reference.year}. {reference.venue}. {reference.url && <>({reference.url})</>}
<button onClick={() => removeReference(index)}>X</button>
<li key={index} className="mb-3 p-2 border-b">
{/* 显示序号 */}
<span className="font-bold mr-2">[{index + 1}].</span>
{reference.author}. {reference.title}.{" "}
{/* 判断 journal 字段是否存在 */}
{reference.journal && reference.journal.name ? (
<span>
{reference.journal.name},{reference.year},
{reference.journal.volume ? ` ${reference.journal.volume}` : ""}
{reference.journal.pages ? `: ${reference.journal.pages}` : ""}.
</span>
) : (
<span>
{reference.venue}, {reference.year}.
</span>
)}
{reference.url && (
<a
href={reference.url}
className="text-blue-500 hover:underline"
target="_blank"
rel="noopener noreferrer"
>
{" "}
({reference.url})
</a>
)}
<button
className="bg-gray-500 hover:bg-gray-700 text-white font-bold py-1 px-2 ml-2 rounded"
onClick={() => copyToClipboard(formatReferenceForCopy(reference))}
>
</button>
<button
className="text-red-500 hover:text-red-700 ml-4"
onClick={() => removeReference(index)}
>
X
</button>
</li>
))}
</ul>

View File

@ -27,7 +27,7 @@ const sendMessageToOpenAI = async (
// const content = `需要完成的论文主题:${topic}, 搜索到的论文内容:${trimmedMessage},之前已经完成的内容上下文:${extractText(
// editorValue
// )}`;
const content = `需要完成的论文主题:${topic}, 搜索到的论文内容:${trimmedMessage},之前已经完成的内容上下文:${editorValue}`;
const content = `之前已经完成的内容上下文:${editorValue},搜索到的论文内容:${trimmedMessage},需要完成的论文主题:${topic}`;
// 设置API请求参数
const requestOptions = {
method: "POST",
@ -43,11 +43,12 @@ const sendMessageToOpenAI = async (
role: "system",
content: `作为论文写作助手,您的主要任务是根据用户提供的研究主题和上下文,以及相关的研究论文,来撰写和完善学术论文。在撰写过程中,请注意以下要点:
1.使
2.使 [1]**
2.使 [1]**
3.
4.
5.使,
6.
...[1],...[2]`,
},
{
@ -101,9 +102,9 @@ const getTopicFromAI = async (userMessage: string, prompt: string) => {
}),
};
const response = await fetch(process.env.NEXT_PUBLIC_AI_URL, requestOptions);
const topic = response.body;
console.log("topic in AI", topic);
return topic;
const data = await response.json();
const topic = data.choices[0].message.content
return topic; // 获取并返回回复
};
async function processResult(reader, decoder, editor) {

7
utils/global.d.ts vendored
View File

@ -1,7 +1,14 @@
export type JournalInfo = {
name: string;
pages: string;
volume: string;
};
export type Reference = {
title: string;
author: string;
year: number;
url: string;
venue?: string;
journal?: JournalInfo;
};

View File

@ -1,3 +1,5 @@
import { Reference } from "@/utils/global";
function getTextBeforeCursor(quill, length = 100) {
const cursorPosition = quill.getSelection().index;
const start = Math.max(0, cursorPosition - length); // 确保开始位置不是负数
@ -52,10 +54,39 @@ function getRandomOffset(max: number) {
return Math.floor(Math.random() * max);
}
function removeSpecialCharacters(str: string): string {
// 正则表达式匹配除了字母、空格和中文之外的所有字符
const regex = /[^\u4e00-\u9fa5a-zA-Z ]/g;
return str.replace(regex, '');
}
function copyToClipboard(text: string) {
navigator.clipboard.writeText(text).then(
() => console.log('文献引用复制到剪贴板'),
(err) => console.error('复制到剪贴板失败:', err)
);
}
function formatReferenceForCopy(reference: Reference): string {
let referenceStr = `${reference.author}. ${reference.title}. `;
if (reference.journal && reference.journal.name) {
referenceStr += `${reference.journal.name}, ${reference.year}, `;
if (reference.journal.volume) referenceStr += `${reference.journal.volume}`;
if (reference.journal.pages) referenceStr += `: ${reference.journal.pages}`;
referenceStr += '.';
} else {
referenceStr += `${reference.venue}, ${reference.year}.`;
}
return referenceStr;
}
export {
getTextBeforeCursor,
updateBracketNumbersInDelta,
updateBracketNumbersInDeltaKeepSelection,
convertToSuperscript,
getRandomOffset
getRandomOffset,
removeSpecialCharacters,
copyToClipboard,
formatReferenceForCopy,
};