mirror of
https://github.com/deiucanta/chatpad.git
synced 2026-03-11 09:04:31 +00:00
wip: use base to store chats and messages
This commit is contained in:
parent
290b0acc93
commit
f163198b82
10 changed files with 310 additions and 70 deletions
|
|
@ -8,3 +8,4 @@ micros:
|
|||
primary: true
|
||||
commands:
|
||||
- NODE_OPTIONS="--max-old-space-size=1024" npm run build
|
||||
dev: npm run dev
|
||||
|
|
|
|||
92
package-lock.json
generated
92
package-lock.json
generated
|
|
@ -43,6 +43,7 @@
|
|||
"@types/react-dom": "^18.0.11",
|
||||
"@vitejs/plugin-react": "^4.0.4",
|
||||
"buffer": "^5.7.1",
|
||||
"deta": "^2.0.0-rc.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"process": "^0.11.10",
|
||||
"vite": "^4.4.9"
|
||||
|
|
@ -2643,6 +2644,15 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/deta": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/deta/-/deta-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-NsNVMB54m2WDZQUIWmH51C8kvtUi/U3M2iuj/WdmL2qCmPhhVxsRP2skloAcjk4pgJRklIIEYPGo808vGBDY8w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
|
|
@ -5723,6 +5733,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
||||
|
|
@ -6927,6 +6957,12 @@
|
|||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/trim-lines": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
||||
|
|
@ -7409,6 +7445,22 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
@ -9262,6 +9314,15 @@
|
|||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="
|
||||
},
|
||||
"deta": {
|
||||
"version": "2.0.0-rc.1",
|
||||
"resolved": "https://registry.npmjs.org/deta/-/deta-2.0.0-rc.1.tgz",
|
||||
"integrity": "sha512-NsNVMB54m2WDZQUIWmH51C8kvtUi/U3M2iuj/WdmL2qCmPhhVxsRP2skloAcjk4pgJRklIIEYPGo808vGBDY8w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
|
|
@ -11316,6 +11377,15 @@
|
|||
"styled-jsx": "5.1.1"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
|
||||
|
|
@ -12151,6 +12221,12 @@
|
|||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"dev": true
|
||||
},
|
||||
"trim-lines": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
||||
|
|
@ -12425,6 +12501,22 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"@types/react-dom": "^18.0.11",
|
||||
"@vitejs/plugin-react": "^4.0.4",
|
||||
"buffer": "^5.7.1",
|
||||
"deta": "^2.0.0-rc.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"process": "^0.11.10",
|
||||
"vite": "^4.4.9"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { ActionIcon, Flex, Menu } from "@mantine/core";
|
||||
import { IconDotsVertical, IconMessages } from "@tabler/icons-react";
|
||||
import { Link } from "@tanstack/react-location";
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { useMemo } from "react";
|
||||
import { db } from "../db";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Chat, detaDB } from "../db";
|
||||
import { useChatId } from "../hooks/useChatId";
|
||||
import { DeleteChatModal } from "./DeleteChatModal";
|
||||
import { EditChatModal } from "./EditChatModal";
|
||||
|
|
@ -11,9 +10,23 @@ import { MainLink } from "./MainLink";
|
|||
|
||||
export function Chats({ search }: { search: string }) {
|
||||
const chatId = useChatId();
|
||||
const chats = useLiveQuery(() =>
|
||||
db.chats.orderBy("createdAt").reverse().toArray()
|
||||
);
|
||||
|
||||
const [chats, setChats] = useState<Chat[]>();
|
||||
|
||||
useEffect(() => {
|
||||
// fetch data
|
||||
const dataFetch = async () => {
|
||||
const { items } = await detaDB.chats.fetch();
|
||||
|
||||
setChats(items as unknown as Chat[]);
|
||||
};
|
||||
|
||||
dataFetch();
|
||||
}, []);
|
||||
|
||||
// const chats = useLiveQuery(() =>
|
||||
// db.chats.orderBy("createdAt").reverse().toArray()
|
||||
// );
|
||||
const filteredChats = useMemo(
|
||||
() =>
|
||||
(chats ?? []).filter((chat) => {
|
||||
|
|
@ -27,8 +40,8 @@ export function Chats({ search }: { search: string }) {
|
|||
<>
|
||||
{filteredChats.map((chat) => (
|
||||
<Flex
|
||||
key={chat.id}
|
||||
className={chatId === chat.id ? "active" : undefined}
|
||||
key={chat.key}
|
||||
className={chatId === chat.key ? "active" : undefined}
|
||||
sx={(theme) => ({
|
||||
marginTop: 1,
|
||||
"&:hover, &.active": {
|
||||
|
|
@ -39,7 +52,7 @@ export function Chats({ search }: { search: string }) {
|
|||
},
|
||||
})}
|
||||
>
|
||||
<Link to={`/chats/${chat.id}`} style={{ flex: 1 }}>
|
||||
<Link to={`/chats/${chat.key}`} style={{ flex: 1 }}>
|
||||
<MainLink
|
||||
icon={<IconMessages size="1rem" />}
|
||||
color="teal"
|
||||
|
|
|
|||
|
|
@ -29,10 +29,8 @@ import {
|
|||
IconX,
|
||||
} from "@tabler/icons-react";
|
||||
import { Link, Outlet, useNavigate, useRouter } from "@tanstack/react-location";
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { nanoid } from "nanoid";
|
||||
import { useEffect, useState } from "react";
|
||||
import { db } from "../db";
|
||||
import { Chat, db, detaDB, generateKey } from "../db";
|
||||
import { useChatId } from "../hooks/useChatId";
|
||||
import { Chats } from "./Chats";
|
||||
import { CreatePromptModal } from "./CreatePromptModal";
|
||||
|
|
@ -58,9 +56,17 @@ export function Layout() {
|
|||
|
||||
const [search, setSearch] = useState("");
|
||||
const chatId = useChatId();
|
||||
const chat = useLiveQuery(async () => {
|
||||
if (!chatId) return null;
|
||||
return db.chats.get(chatId);
|
||||
|
||||
const [chat, setChat] = useState<Chat>();
|
||||
|
||||
useEffect(() => {
|
||||
const dataFetch = async () => {
|
||||
const item = await detaDB.chats.get(chatId!);
|
||||
|
||||
setChat(item as unknown as Chat);
|
||||
};
|
||||
|
||||
dataFetch();
|
||||
}, [chatId]);
|
||||
|
||||
const border = `${rem(1)} solid ${
|
||||
|
|
@ -133,14 +139,21 @@ export function Layout() {
|
|||
<Button
|
||||
fullWidth
|
||||
leftIcon={<IconPlus size={20} />}
|
||||
onClick={() => {
|
||||
const id = nanoid();
|
||||
db.chats.add({
|
||||
id,
|
||||
onClick={async () => {
|
||||
const item = await detaDB.chats.put({
|
||||
description: "New Chat",
|
||||
totalTokens: 0,
|
||||
createdAt: new Date(),
|
||||
});
|
||||
createdAt: new Date().toISOString(),
|
||||
}, generateKey())
|
||||
|
||||
const id = item!.key as string
|
||||
// const id = nanoid();
|
||||
// db.chats.add({
|
||||
// id,
|
||||
// description: "New Chat",
|
||||
// totalTokens: 0,
|
||||
// createdAt: new Date(),
|
||||
// });
|
||||
navigate({ to: `/chats/${id}` });
|
||||
}}
|
||||
>
|
||||
|
|
@ -282,10 +295,7 @@ export function Layout() {
|
|||
height: "100%",
|
||||
}}
|
||||
>
|
||||
{`${chat.description} - ${chat.totalTokens ?? 0} tokens ~ $${(
|
||||
((chat.totalTokens ?? 0) * 0.002) /
|
||||
1000
|
||||
).toFixed(5)}`}
|
||||
{chat.description}
|
||||
</div>
|
||||
</Header>
|
||||
) : undefined
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
import { Deta } from "deta";
|
||||
import Dexie, { Table } from "dexie";
|
||||
import "dexie-export-import";
|
||||
import { config } from "../utils/config";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export interface Chat {
|
||||
id: string;
|
||||
key: string;
|
||||
description: string;
|
||||
totalTokens: number;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
id: string;
|
||||
key: string;
|
||||
chatId: string;
|
||||
role: "system" | "assistant" | "user";
|
||||
content: string;
|
||||
|
|
@ -18,7 +20,7 @@ export interface Message {
|
|||
}
|
||||
|
||||
export interface Prompt {
|
||||
id: string;
|
||||
key: string;
|
||||
title: string;
|
||||
content: string;
|
||||
createdAt: Date;
|
||||
|
|
@ -34,6 +36,24 @@ export interface Settings {
|
|||
openAiApiVersion?: string;
|
||||
}
|
||||
|
||||
export const deta = Deta()
|
||||
|
||||
export const detaDB = {
|
||||
chats: deta.Base("chats"),
|
||||
messages: deta.Base("messages"),
|
||||
prompts: deta.Base("prompts"),
|
||||
settings: deta.Base("settings"),
|
||||
}
|
||||
|
||||
// Used as large number to make sure keys are generated in descending order
|
||||
const maxDateNowValue = 8.64e15
|
||||
|
||||
export const generateKey = (ascending: boolean = true): string => {
|
||||
const timestamp = ascending ? Date.now() : maxDateNowValue - Date.now()
|
||||
|
||||
return `${ timestamp.toString(16) }${ nanoid(5) }`
|
||||
}
|
||||
|
||||
export class Database extends Dexie {
|
||||
chats!: Table<Chat>;
|
||||
messages!: Table<Message>;
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ import {
|
|||
import { notifications } from "@mantine/notifications";
|
||||
import { useLiveQuery } from "dexie-react-hooks";
|
||||
import { nanoid } from "nanoid";
|
||||
import { KeyboardEvent, useState, type ChangeEvent } from "react";
|
||||
import { KeyboardEvent, useState, type ChangeEvent, useEffect } from "react";
|
||||
import { AiOutlineSend } from "react-icons/ai";
|
||||
import { MessageItem } from "../components/MessageItem";
|
||||
import { db } from "../db";
|
||||
import { Chat, Message, db, detaDB, generateKey } from "../db";
|
||||
import { useChatId } from "../hooks/useChatId";
|
||||
import { config } from "../utils/config";
|
||||
import {
|
||||
|
|
@ -30,10 +30,24 @@ export function ChatRoute() {
|
|||
const apiKey = useLiveQuery(async () => {
|
||||
return (await db.settings.where({ id: "general" }).first())?.openAiApiKey;
|
||||
});
|
||||
const messages = useLiveQuery(() => {
|
||||
if (!chatId) return [];
|
||||
return db.messages.where("chatId").equals(chatId).sortBy("createdAt");
|
||||
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
// fetch data
|
||||
const dataFetch = async () => {
|
||||
const { items } = await detaDB.messages.fetch({ chatId }, { desc: false });
|
||||
|
||||
setMessages(items as unknown as Message[]);
|
||||
};
|
||||
|
||||
dataFetch();
|
||||
}, [chatId]);
|
||||
|
||||
// const messages = useLiveQuery(() => {
|
||||
// if (!chatId) return [];
|
||||
// return db.messages.where("chatId").equals(chatId).sortBy("createdAt");
|
||||
// }, [chatId]);
|
||||
const userMessages =
|
||||
messages
|
||||
?.filter((message) => message.role === "user")
|
||||
|
|
@ -43,11 +57,23 @@ export function ChatRoute() {
|
|||
const [contentDraft, setContentDraft] = useState("");
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
const chat = useLiveQuery(async () => {
|
||||
if (!chatId) return null;
|
||||
return db.chats.get(chatId);
|
||||
const [chat, setChat] = useState<Chat>();
|
||||
|
||||
useEffect(() => {
|
||||
const dataFetch = async () => {
|
||||
const item = await detaDB.chats.get(chatId!);
|
||||
|
||||
setChat(item as unknown as Chat);
|
||||
};
|
||||
|
||||
dataFetch();
|
||||
}, [chatId]);
|
||||
|
||||
// const chat = useLiveQuery(async () => {
|
||||
// if (!chatId) return null;
|
||||
// return db.chats.get(chatId);
|
||||
// }, [chatId]);
|
||||
|
||||
const [writingCharacter, setWritingCharacter] = useState<string | null>(null);
|
||||
const [writingTone, setWritingTone] = useState<string | null>(null);
|
||||
const [writingStyle, setWritingStyle] = useState<string | null>(null);
|
||||
|
|
@ -90,23 +116,42 @@ export function ChatRoute() {
|
|||
try {
|
||||
setSubmitting(true);
|
||||
|
||||
await db.messages.add({
|
||||
id: nanoid(),
|
||||
const userMessage = await detaDB.messages.put({
|
||||
chatId,
|
||||
content,
|
||||
role: "user",
|
||||
createdAt: new Date(),
|
||||
});
|
||||
createdAt: new Date().toISOString(),
|
||||
}, generateKey())
|
||||
|
||||
setMessages(current => [...current, userMessage as unknown as Message])
|
||||
|
||||
// await db.messages.add({
|
||||
// id: nanoid(),
|
||||
// chatId,
|
||||
// content,
|
||||
// role: "user",
|
||||
// createdAt: new Date(),
|
||||
// });
|
||||
setContent("");
|
||||
|
||||
const messageId = nanoid();
|
||||
await db.messages.add({
|
||||
id: messageId,
|
||||
const systemMessage = await detaDB.messages.put({
|
||||
chatId,
|
||||
content: "█",
|
||||
role: "assistant",
|
||||
createdAt: new Date(),
|
||||
});
|
||||
createdAt: new Date().toISOString(),
|
||||
}, generateKey())
|
||||
|
||||
setMessages(current => [...current, systemMessage as unknown as Message])
|
||||
|
||||
const messageId = systemMessage!.key as string
|
||||
|
||||
// await db.messages.add({
|
||||
// id: messageId,
|
||||
// chatId,
|
||||
// content: "█",
|
||||
// role: "assistant",
|
||||
// createdAt: new Date(),
|
||||
// });
|
||||
|
||||
await createStreamChatCompletion(
|
||||
apiKey,
|
||||
|
|
@ -122,15 +167,26 @@ export function ChatRoute() {
|
|||
{ role: "user", content },
|
||||
],
|
||||
chatId,
|
||||
messageId
|
||||
messageId,
|
||||
(content) => {
|
||||
setMessages(current => current.map(message => {
|
||||
if (message.key === messageId) {
|
||||
return { ...message, content };
|
||||
}
|
||||
|
||||
return message;
|
||||
}));
|
||||
}
|
||||
);
|
||||
|
||||
setSubmitting(false);
|
||||
|
||||
if (chat?.description === "New Chat") {
|
||||
const messages = await db.messages
|
||||
.where({ chatId })
|
||||
.sortBy("createdAt");
|
||||
const res = await detaDB.messages.fetch({ chatId })
|
||||
const messages = res.items as unknown as Message[]
|
||||
// const messages = await db.messages
|
||||
// .where({ chatId })
|
||||
// .sortBy("createdAt");
|
||||
const createChatDescription = await createChatCompletion(apiKey, [
|
||||
{
|
||||
role: "system",
|
||||
|
|
@ -150,15 +206,24 @@ export function ChatRoute() {
|
|||
createChatDescription.data.choices[0].message?.content;
|
||||
|
||||
if (createChatDescription.data.usage) {
|
||||
await db.chats.where({ id: chatId }).modify((chat) => {
|
||||
chat.description = chatDescription ?? "New Chat";
|
||||
if (chat.totalTokens) {
|
||||
chat.totalTokens +=
|
||||
createChatDescription.data.usage!.total_tokens;
|
||||
} else {
|
||||
chat.totalTokens = createChatDescription.data.usage!.total_tokens;
|
||||
}
|
||||
});
|
||||
const chatUpdates = {
|
||||
description: chatDescription ?? "New Chat",
|
||||
// todo: add to existing count instead of replacing
|
||||
totalTokens: createChatDescription.data.usage!.total_tokens,
|
||||
}
|
||||
await detaDB.chats.update(chatUpdates, chatId)
|
||||
|
||||
setChat(chat => ({ ...chat!, ...chatUpdates }))
|
||||
|
||||
// await db.chats.where({ id: chatId }).modify((chat) => {
|
||||
// chat.description = chatDescription ?? "New Chat";
|
||||
// if (chat.totalTokens) {
|
||||
// chat.totalTokens +=
|
||||
// createChatDescription.data.usage!.total_tokens;
|
||||
// } else {
|
||||
// chat.totalTokens = createChatDescription.data.usage!.total_tokens;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
|
|
@ -221,7 +286,7 @@ export function ChatRoute() {
|
|||
<Container pt="xl" pb={100}>
|
||||
<Stack spacing="xs">
|
||||
{messages?.map((message) => (
|
||||
<MessageItem key={message.id} message={message} />
|
||||
<MessageItem key={message.key} message={message} />
|
||||
))}
|
||||
</Stack>
|
||||
{submitting && (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { encode } from "gpt-token-utils";
|
||||
import { ChatCompletionRequestMessage, Configuration, OpenAIApi } from "openai";
|
||||
import { OpenAIExt } from "openai-ext";
|
||||
import { db } from "../db";
|
||||
import { db, detaDB } from "../db";
|
||||
import { config } from "./config";
|
||||
|
||||
function getClient(
|
||||
|
|
@ -24,7 +24,8 @@ export async function createStreamChatCompletion(
|
|||
apiKey: string,
|
||||
messages: ChatCompletionRequestMessage[],
|
||||
chatId: string,
|
||||
messageId: string
|
||||
messageId: string,
|
||||
onContent: (content: string, isFinal: boolean) => void
|
||||
) {
|
||||
const settings = await db.settings.get("general");
|
||||
const model = settings?.openAiModel ?? config.defaultModel;
|
||||
|
|
@ -42,6 +43,7 @@ export async function createStreamChatCompletion(
|
|||
if (isFinal) {
|
||||
setTotalTokens(chatId, content);
|
||||
}
|
||||
onContent(content, isFinal);
|
||||
},
|
||||
onDone(stream) {},
|
||||
onError(error, stream) {
|
||||
|
|
@ -52,24 +54,25 @@ export async function createStreamChatCompletion(
|
|||
);
|
||||
}
|
||||
|
||||
function setStreamContent(
|
||||
async function setStreamContent(
|
||||
messageId: string,
|
||||
content: string,
|
||||
isFinal: boolean
|
||||
) {
|
||||
content = isFinal ? content : content + "█";
|
||||
db.messages.update(messageId, { content: content });
|
||||
|
||||
await detaDB.messages.update({ content: content }, messageId);
|
||||
}
|
||||
|
||||
function setTotalTokens(chatId: string, content: string) {
|
||||
let total_tokens = encode(content).length;
|
||||
db.chats.where({ id: chatId }).modify((chat) => {
|
||||
if (chat.totalTokens) {
|
||||
chat.totalTokens += total_tokens;
|
||||
} else {
|
||||
chat.totalTokens = total_tokens;
|
||||
}
|
||||
});
|
||||
// db.chats.where({ id: chatId }).modify((chat) => {
|
||||
// if (chat.totalTokens) {
|
||||
// chat.totalTokens += total_tokens;
|
||||
// } else {
|
||||
// chat.totalTokens = total_tokens;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
export async function createChatCompletion(
|
||||
|
|
|
|||
|
|
@ -4,4 +4,7 @@ import react from '@vitejs/plugin-react'
|
|||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
32
yarn.lock
32
yarn.lock
|
|
@ -1260,6 +1260,13 @@
|
|||
"resolved" "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
|
||||
"version" "2.0.3"
|
||||
|
||||
"deta@^2.0.0-rc.1":
|
||||
"integrity" "sha512-NsNVMB54m2WDZQUIWmH51C8kvtUi/U3M2iuj/WdmL2qCmPhhVxsRP2skloAcjk4pgJRklIIEYPGo808vGBDY8w=="
|
||||
"resolved" "https://registry.npmjs.org/deta/-/deta-2.0.0-rc.1.tgz"
|
||||
"version" "2.0.0-rc.1"
|
||||
dependencies:
|
||||
"node-fetch" "^2.6.7"
|
||||
|
||||
"detect-libc@^1.0.3":
|
||||
"integrity" "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="
|
||||
"resolved" "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz"
|
||||
|
|
@ -3042,6 +3049,13 @@
|
|||
"@next/swc-win32-ia32-msvc" "13.2.4"
|
||||
"@next/swc-win32-x64-msvc" "13.2.4"
|
||||
|
||||
"node-fetch@^2.6.7":
|
||||
"integrity" "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="
|
||||
"resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
||||
"version" "2.7.0"
|
||||
dependencies:
|
||||
"whatwg-url" "^5.0.0"
|
||||
|
||||
"node-releases@^2.0.13":
|
||||
"integrity" "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
|
||||
"resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz"
|
||||
|
|
@ -3832,6 +3846,11 @@
|
|||
dependencies:
|
||||
"is-number" "^7.0.0"
|
||||
|
||||
"tr46@~0.0.3":
|
||||
"integrity" "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
"resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
|
||||
"version" "0.0.3"
|
||||
|
||||
"trim-lines@^3.0.0":
|
||||
"integrity" "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="
|
||||
"resolved" "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz"
|
||||
|
|
@ -4052,6 +4071,19 @@
|
|||
optionalDependencies:
|
||||
"fsevents" "~2.3.2"
|
||||
|
||||
"webidl-conversions@^3.0.0":
|
||||
"integrity" "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
"resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
|
||||
"version" "3.0.1"
|
||||
|
||||
"whatwg-url@^5.0.0":
|
||||
"integrity" "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="
|
||||
"resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
|
||||
"version" "5.0.0"
|
||||
dependencies:
|
||||
"tr46" "~0.0.3"
|
||||
"webidl-conversions" "^3.0.0"
|
||||
|
||||
"which-boxed-primitive@^1.0.2":
|
||||
"integrity" "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg=="
|
||||
"resolved" "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz"
|
||||
|
|
|
|||
Loading…
Reference in a new issue