mirror of
https://github.com/artegoser/AnoPaper.git
synced 2024-11-05 20:43:57 +03:00
feat: openai completion support
This commit is contained in:
parent
8fb91d7ecb
commit
2c07e71349
12 changed files with 155 additions and 17 deletions
59
package-lock.json
generated
59
package-lock.json
generated
|
@ -16,6 +16,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
"js-sha3": "^0.8.0",
|
||||
"openai": "^3.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-contenteditable": "^3.3.7",
|
||||
"react-dom": "^18.2.0",
|
||||
|
@ -889,6 +890,14 @@
|
|||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.26.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
|
||||
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.8"
|
||||
}
|
||||
},
|
||||
"node_modules/bail": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
||||
|
@ -2202,6 +2211,25 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
|
@ -3913,6 +3941,15 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz",
|
||||
"integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==",
|
||||
"dependencies": {
|
||||
"axios": "^0.26.0",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
|
||||
|
@ -6269,6 +6306,14 @@
|
|||
"postcss-value-parser": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.26.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
|
||||
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.8"
|
||||
}
|
||||
},
|
||||
"bail": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
||||
|
@ -7138,6 +7183,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
|
@ -8256,6 +8306,15 @@
|
|||
"ee-first": "1.1.1"
|
||||
}
|
||||
},
|
||||
"openai": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz",
|
||||
"integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==",
|
||||
"requires": {
|
||||
"axios": "^0.26.0",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"optionator": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
"js-sha3": "^0.8.0",
|
||||
"openai": "^3.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-contenteditable": "^3.3.7",
|
||||
"react-dom": "^18.2.0",
|
||||
|
|
|
@ -12,6 +12,7 @@ import PubNoteSafe from "./pages/pubNoteSafe";
|
|||
import RenderMarkdown from "./components/markdown";
|
||||
import socket from "./components/socket";
|
||||
import Settings from "./pages/settings";
|
||||
import Chat from "./pages/chat";
|
||||
import Locales from "./localisation/main";
|
||||
import { useState } from "react";
|
||||
|
||||
|
@ -52,6 +53,7 @@ function App() {
|
|||
<Route path="/pubNotesSafe/:id" element={<PubNoteSafe />} />
|
||||
<Route path="/pubError" element={<PubError />} />
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
<Route path="/chat" element={<Chat />} />
|
||||
<Route
|
||||
path="/about"
|
||||
element={
|
||||
|
|
|
@ -5,7 +5,11 @@ function Button(props) {
|
|||
<Link to={props.href} className={props.className}>
|
||||
<div
|
||||
onClick={props.onClick}
|
||||
className={`transition-transform w-48 ease-[cubic-bezier(.69,.58,.32,1.69)] hover:scale-105 p-2 pl-6 text-lg bg-zinc-100 hover:bg-zinc-300 dark:bg-zinc-600 dark:hover:bg-zinc-800 rounded-2xl ${props.className}`}
|
||||
className={`transition-transform ${
|
||||
props.w ? props.w : "w-48"
|
||||
} ease-[cubic-bezier(.69,.58,.32,1.69)] hover:scale-105 p-2 pl-6 text-lg bg-zinc-100 hover:bg-zinc-300 dark:bg-zinc-600 dark:hover:bg-zinc-800 rounded-2xl ${
|
||||
props.className
|
||||
}`}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
|
@ -23,9 +27,9 @@ function IconWithText(props) {
|
|||
);
|
||||
}
|
||||
return (
|
||||
<div className="grid grid-cols-4">
|
||||
<div className="grid place-content-start grid-cols-4">
|
||||
<div>{props.icon}</div>
|
||||
<div className="grid-span-3">{props.children}</div>
|
||||
<div className="justify-self-start col-span-3">{props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -36,6 +40,7 @@ function ButtonWithIcon(props) {
|
|||
href={props.href}
|
||||
className={props.className}
|
||||
onClick={props.onClick}
|
||||
w={props.w}
|
||||
>
|
||||
<IconWithText
|
||||
reverse={props.reverse}
|
||||
|
|
31
src/components/openai.js
Normal file
31
src/components/openai.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { Configuration, OpenAIApi } from "openai";
|
||||
|
||||
async function Complete(setText) {
|
||||
document.body.style.cursor = "wait";
|
||||
|
||||
let initText = localStorage.getItem("NoteText");
|
||||
|
||||
const configuration = new Configuration({
|
||||
apiKey: settings.openAiKey,
|
||||
});
|
||||
const openai = new OpenAIApi(configuration);
|
||||
const response = await openai.createCompletion({
|
||||
model: "text-davinci-003",
|
||||
prompt: initText,
|
||||
max_tokens: 1000,
|
||||
temperature: 1,
|
||||
top_p: 1,
|
||||
n: 1,
|
||||
stream: false,
|
||||
logprobs: null,
|
||||
});
|
||||
|
||||
let totalText = initText + response.data.choices[0].text;
|
||||
|
||||
localStorage.setItem("NoteText", totalText);
|
||||
setText(totalText);
|
||||
|
||||
document.body.style.cursor = "default";
|
||||
}
|
||||
|
||||
export { Complete };
|
|
@ -78,4 +78,17 @@ function SettingsSelectInput({
|
|||
);
|
||||
}
|
||||
|
||||
export { SettingsCheckBox, SettingsTextInput, SettingsSelectInput };
|
||||
function SettingsPlaceholder({ text }) {
|
||||
return (
|
||||
<h1 className="text-center lg:text-left leading-tight text-xl font-semibold">
|
||||
{text}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
SettingsCheckBox,
|
||||
SettingsTextInput,
|
||||
SettingsSelectInput,
|
||||
SettingsPlaceholder,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
let inputStyle =
|
||||
"form-control block px-3 py-1.5 text-base font-normal text-gray-700 dark:text-white bg-white dark:bg-zinc-900 bg-clip-padding border border-solid border-gray-300 rounded-lg transition ease-in-out focus:border-blue-600 focus:outline-none";
|
||||
"w-full form-control block px-3 py-1.5 text-base font-normal text-gray-700 dark:text-white bg-white dark:bg-zinc-900 bg-clip-padding border border-solid border-gray-300 rounded-lg transition ease-in-out focus:border-blue-600 focus:outline-none";
|
||||
|
||||
export { inputStyle };
|
||||
|
|
|
@ -41,6 +41,8 @@ let en = {
|
|||
Delete: "Delete",
|
||||
Open: "Open",
|
||||
NoNotesYet: "No notes yet",
|
||||
AIComplete: "Continue Note (AI)",
|
||||
AdditionalFeatures: "Additional features",
|
||||
};
|
||||
|
||||
export default en;
|
||||
|
|
|
@ -42,6 +42,8 @@ let ru = {
|
|||
Delete: "Удалить",
|
||||
Open: "Открыть",
|
||||
NoNotesYet: "Заметок пока нет",
|
||||
AIComplete: "Продолжить заметку (ИИ)",
|
||||
AdditionalFeatures: "Дополнительные функции",
|
||||
};
|
||||
|
||||
export default ru;
|
||||
|
|
3
src/pages/chat.jsx
Normal file
3
src/pages/chat.jsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
function Chat() {}
|
||||
|
||||
export default Chat;
|
|
@ -1,5 +1,8 @@
|
|||
import { ButtonWithIcon } from "../components/button";
|
||||
import { ChevronDoubleRightIcon } from "@heroicons/react/24/outline";
|
||||
import {
|
||||
ChevronDoubleRightIcon,
|
||||
DocumentTextIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { CheckBox } from "../components/checkbox";
|
||||
import { useState } from "react";
|
||||
import RenderMarkdown from "../components/markdown";
|
||||
|
@ -12,8 +15,12 @@ import rehypeParse from "rehype-parse";
|
|||
import remarkStringify from "remark-stringify";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import { SettingsCheckBox } from "../components/settingsInputs";
|
||||
import {
|
||||
SettingsCheckBox,
|
||||
SettingsPlaceholder,
|
||||
} from "../components/settingsInputs";
|
||||
import { inputStyle } from "../components/styles";
|
||||
import { Complete } from "../components/openai";
|
||||
|
||||
function CreateNote() {
|
||||
const [preview, setPreview] = useState(false);
|
||||
|
@ -126,6 +133,23 @@ function CreateNote() {
|
|||
className="m-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{settings.additionalFeatures && (
|
||||
<div className="justify-self-start lg:justify-self-start">
|
||||
<SettingsPlaceholder text={locals.AdditionalFeatures} />
|
||||
{!!settings.openAiKey && (
|
||||
<ButtonWithIcon
|
||||
icon={DocumentTextIcon}
|
||||
text={locals.AIComplete}
|
||||
className="m-1"
|
||||
w="w-full"
|
||||
onClick={() => {
|
||||
Complete(setText);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
SettingsCheckBox,
|
||||
SettingsTextInput,
|
||||
SettingsSelectInput,
|
||||
SettingsPlaceholder,
|
||||
} from "../components/settingsInputs";
|
||||
import { reRenderPage } from "../components/utils";
|
||||
import Locales from "../localisation/main";
|
||||
|
@ -38,17 +39,20 @@ function Settings() {
|
|||
<SettingsCheckBox
|
||||
label={locals.EditPreview}
|
||||
title={locals.EditPreviewWarn}
|
||||
checked={settings.editPreview}
|
||||
settingName="editPreview"
|
||||
/>
|
||||
|
||||
<SettingsCheckBox
|
||||
label={locals.PublicNote}
|
||||
title={locals.PublicNoteTitle}
|
||||
checked={settings.publicNote}
|
||||
settingName="publicNote"
|
||||
/>
|
||||
|
||||
<SettingsCheckBox
|
||||
label={locals.AdditionalFeatures}
|
||||
settingName="additionalFeatures"
|
||||
/>
|
||||
|
||||
<SettingsPlaceholder text={locals.Interface} />
|
||||
|
||||
<SettingsSelectInput
|
||||
|
@ -87,12 +91,4 @@ function Settings() {
|
|||
);
|
||||
}
|
||||
|
||||
function SettingsPlaceholder({ text }) {
|
||||
return (
|
||||
<h1 className="text-center lg:text-left leading-tight text-xl font-semibold">
|
||||
{text}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
export default Settings;
|
||||
|
|
Loading…
Reference in a new issue