init
This commit is contained in:
parent
39245ecd78
commit
e4935e4f22
9
.env.production
Normal file
9
.env.production
Normal 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"
|
|
@ -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}`;
|
||||
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
});
|
||||
// 提取并处理论文数据
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
7
utils/global.d.ts
vendored
|
@ -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;
|
||||
};
|
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user