refactor: seperate AppContext and AppChatStoreContext
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
import { PaintBucketIcon } from "lucide-react";
|
import { PaintBucketIcon } from "lucide-react";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -27,7 +27,7 @@ interface ImageResponse {
|
|||||||
revised_prompt: string;
|
revised_prompt: string;
|
||||||
}
|
}
|
||||||
export function ImageGenDrawer({ disableFactor }: Props) {
|
export function ImageGenDrawer({ disableFactor }: Props) {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
const [showGenImage, setShowGenImage] = useState(false);
|
const [showGenImage, setShowGenImage] = useState(false);
|
||||||
const [imageGenPrompt, setImageGenPrompt] = useState("");
|
const [imageGenPrompt, setImageGenPrompt] = useState("");
|
||||||
@@ -42,7 +42,7 @@ export function ImageGenDrawer({ disableFactor }: Props) {
|
|||||||
useState("b64_json");
|
useState("b64_json");
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ctx.chatStore.image_gen_api && ctx.chatStore.image_gen_key ? (
|
{chatStore.image_gen_api && chatStore.image_gen_key ? (
|
||||||
<Drawer open={showGenImage} onOpenChange={setShowGenImage}>
|
<Drawer open={showGenImage} onOpenChange={setShowGenImage}>
|
||||||
<DrawerTrigger>
|
<DrawerTrigger>
|
||||||
<Button
|
<Button
|
||||||
@@ -169,11 +169,11 @@ export function ImageGenDrawer({ disableFactor }: Props) {
|
|||||||
body.style = imageGenStyle;
|
body.style = imageGenStyle;
|
||||||
}
|
}
|
||||||
const resp: ImageResponse[] = (
|
const resp: ImageResponse[] = (
|
||||||
await fetch(ctx.chatStore.image_gen_api, {
|
await fetch(chatStore.image_gen_api, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${ctx.chatStore.image_gen_key}`,
|
Authorization: `Bearer ${chatStore.image_gen_key}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
}).then((resp) => resp.json())
|
}).then((resp) => resp.json())
|
||||||
@@ -187,7 +187,7 @@ export function ImageGenDrawer({ disableFactor }: Props) {
|
|||||||
url = "data:image/png;base64," + image.b64_json;
|
url = "data:image/png;base64," + image.b64_json;
|
||||||
if (!url) continue;
|
if (!url) continue;
|
||||||
|
|
||||||
ctx.chatStore.history.push({
|
chatStore.history.push({
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
@@ -210,7 +210,7 @@ export function ImageGenDrawer({ disableFactor }: Props) {
|
|||||||
response_model_name: imageGenModel,
|
response_model_name: imageGenModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
import {
|
import {
|
||||||
NavigationMenu,
|
NavigationMenu,
|
||||||
NavigationMenuList,
|
NavigationMenuList,
|
||||||
@@ -37,26 +37,26 @@ import { newChatStore } from "@/types/newChatstore";
|
|||||||
interface APITemplateDropdownProps {
|
interface APITemplateDropdownProps {
|
||||||
label: string;
|
label: string;
|
||||||
shortLabel: string;
|
shortLabel: string;
|
||||||
ctx: any;
|
|
||||||
apiField: string;
|
apiField: string;
|
||||||
keyField: string;
|
keyField: string;
|
||||||
}
|
}
|
||||||
function APIsDropdownList({
|
function APIsDropdownList({
|
||||||
label,
|
label,
|
||||||
shortLabel,
|
shortLabel,
|
||||||
ctx,
|
|
||||||
apiField,
|
apiField,
|
||||||
keyField,
|
keyField,
|
||||||
}: APITemplateDropdownProps) {
|
}: APITemplateDropdownProps) {
|
||||||
let API = ctx.templateAPIs;
|
const ctxAppChatStore = useContext(AppChatStoreContext);
|
||||||
|
const ctxApp = useContext(AppContext);
|
||||||
|
let API = ctxApp.templateAPIs;
|
||||||
if (label === "Chat API") {
|
if (label === "Chat API") {
|
||||||
API = ctx.templateAPIs;
|
API = ctxApp.templateAPIs;
|
||||||
} else if (label === "Whisper API") {
|
} else if (label === "Whisper API") {
|
||||||
API = ctx.templateAPIsWhisper;
|
API = ctxApp.templateAPIsWhisper;
|
||||||
} else if (label === "TTS API") {
|
} else if (label === "TTS API") {
|
||||||
API = ctx.templateAPIsTTS;
|
API = ctxApp.templateAPIsTTS;
|
||||||
} else if (label === "Image Gen API") {
|
} else if (label === "Image Gen API") {
|
||||||
API = ctx.templateAPIsImageGen;
|
API = ctxApp.templateAPIsImageGen;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -67,14 +67,17 @@ function APIsDropdownList({
|
|||||||
{label}{" "}
|
{label}{" "}
|
||||||
{API.find(
|
{API.find(
|
||||||
(t: TemplateAPI) =>
|
(t: TemplateAPI) =>
|
||||||
ctx.chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
ctxAppChatStore.chatStore[apiField as keyof ChatStore] ===
|
||||||
ctx.chatStore[keyField as keyof ChatStore] === t.key
|
t.endpoint &&
|
||||||
|
ctxAppChatStore.chatStore[keyField as keyof ChatStore] === t.key
|
||||||
)?.name &&
|
)?.name &&
|
||||||
`: ${
|
`: ${
|
||||||
API.find(
|
API.find(
|
||||||
(t: TemplateAPI) =>
|
(t: TemplateAPI) =>
|
||||||
ctx.chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
ctxAppChatStore.chatStore[apiField as keyof ChatStore] ===
|
||||||
ctx.chatStore[keyField as keyof ChatStore] === t.key
|
t.endpoint &&
|
||||||
|
ctxAppChatStore.chatStore[keyField as keyof ChatStore] ===
|
||||||
|
t.key
|
||||||
)?.name
|
)?.name
|
||||||
}`}
|
}`}
|
||||||
</span>
|
</span>
|
||||||
@@ -87,15 +90,20 @@ function APIsDropdownList({
|
|||||||
<a
|
<a
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
ctx.chatStore[apiField as keyof ChatStore] = t.endpoint;
|
ctxAppChatStore.chatStore[apiField as keyof ChatStore] =
|
||||||
|
t.endpoint;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
ctx.chatStore[keyField] = t.key;
|
ctxAppChatStore.chatStore[keyField] = t.key;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
ctxAppChatStore.setChatStore({
|
||||||
|
...ctxAppChatStore.chatStore,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
className={cn(
|
className={cn(
|
||||||
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
||||||
ctx.chatStore[apiField as keyof ChatStore] === t.endpoint &&
|
ctxAppChatStore.chatStore[apiField as keyof ChatStore] ===
|
||||||
ctx.chatStore[keyField as keyof ChatStore] === t.key
|
t.endpoint &&
|
||||||
|
ctxAppChatStore.chatStore[keyField as keyof ChatStore] ===
|
||||||
|
t.key
|
||||||
? "bg-accent text-accent-foreground"
|
? "bg-accent text-accent-foreground"
|
||||||
: ""
|
: ""
|
||||||
)}
|
)}
|
||||||
@@ -117,11 +125,11 @@ function APIsDropdownList({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ToolsDropdownList() {
|
function ToolsDropdownList() {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
const { chatStore, setChatStore } = ctx;
|
const ctx = useContext(AppContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-4 mx-3">
|
<div className="flex items-center space-x-4 mx-3">
|
||||||
@@ -189,7 +197,8 @@ function ToolsDropdownList() {
|
|||||||
function ChatTemplateDropdownList() {
|
function ChatTemplateDropdownList() {
|
||||||
const ctx = useContext(AppContext);
|
const ctx = useContext(AppContext);
|
||||||
|
|
||||||
const { chatStore, setChatStore, templates } = ctx;
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
const { templates } = useContext(AppContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
@@ -245,7 +254,6 @@ const APIListMenu: React.FC = () => {
|
|||||||
<APIsDropdownList
|
<APIsDropdownList
|
||||||
label="Chat API"
|
label="Chat API"
|
||||||
shortLabel="Chat"
|
shortLabel="Chat"
|
||||||
ctx={ctx}
|
|
||||||
apiField="apiEndpoint"
|
apiField="apiEndpoint"
|
||||||
keyField="apiKey"
|
keyField="apiKey"
|
||||||
/>
|
/>
|
||||||
@@ -254,7 +262,6 @@ const APIListMenu: React.FC = () => {
|
|||||||
<APIsDropdownList
|
<APIsDropdownList
|
||||||
label="Whisper API"
|
label="Whisper API"
|
||||||
shortLabel="Whisper"
|
shortLabel="Whisper"
|
||||||
ctx={ctx}
|
|
||||||
apiField="whisper_api"
|
apiField="whisper_api"
|
||||||
keyField="whisper_key"
|
keyField="whisper_key"
|
||||||
/>
|
/>
|
||||||
@@ -263,7 +270,6 @@ const APIListMenu: React.FC = () => {
|
|||||||
<APIsDropdownList
|
<APIsDropdownList
|
||||||
label="TTS API"
|
label="TTS API"
|
||||||
shortLabel="TTS"
|
shortLabel="TTS"
|
||||||
ctx={ctx}
|
|
||||||
apiField="tts_api"
|
apiField="tts_api"
|
||||||
keyField="tts_key"
|
keyField="tts_key"
|
||||||
/>
|
/>
|
||||||
@@ -272,7 +278,6 @@ const APIListMenu: React.FC = () => {
|
|||||||
<APIsDropdownList
|
<APIsDropdownList
|
||||||
label="Image Gen API"
|
label="Image Gen API"
|
||||||
shortLabel="ImgGen"
|
shortLabel="ImgGen"
|
||||||
ctx={ctx}
|
|
||||||
apiField="image_gen_api"
|
apiField="image_gen_api"
|
||||||
keyField="image_gen_key"
|
keyField="image_gen_key"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
AudioLinesIcon,
|
AudioLinesIcon,
|
||||||
LoaderCircleIcon,
|
LoaderCircleIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
|
|
||||||
interface HideMessageProps {
|
interface HideMessageProps {
|
||||||
chat: ChatStoreMessage;
|
chat: ChatStoreMessage;
|
||||||
@@ -171,27 +171,27 @@ export function TTSPlay(props: TTSPlayProps) {
|
|||||||
}
|
}
|
||||||
function TTSButton(props: TTSProps) {
|
function TTSButton(props: TTSProps) {
|
||||||
const [generating, setGenerating] = useState(false);
|
const [generating, setGenerating] = useState(false);
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const api = ctx.chatStore.tts_api;
|
const api = chatStore.tts_api;
|
||||||
const api_key = ctx.chatStore.tts_key;
|
const api_key = chatStore.tts_key;
|
||||||
const model = "tts-1";
|
const model = "tts-1";
|
||||||
const input = getMessageText(props.chat);
|
const input = getMessageText(props.chat);
|
||||||
const voice = ctx.chatStore.tts_voice;
|
const voice = chatStore.tts_voice;
|
||||||
|
|
||||||
const body: Record<string, any> = {
|
const body: Record<string, any> = {
|
||||||
model,
|
model,
|
||||||
input,
|
input,
|
||||||
voice,
|
voice,
|
||||||
response_format: ctx.chatStore.tts_format || "mp3",
|
response_format: chatStore.tts_format || "mp3",
|
||||||
};
|
};
|
||||||
if (ctx.chatStore.tts_speed_enabled) {
|
if (chatStore.tts_speed_enabled) {
|
||||||
body["speed"] = ctx.chatStore.tts_speed;
|
body["speed"] = chatStore.tts_speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
setGenerating(true);
|
setGenerating(true);
|
||||||
@@ -208,13 +208,13 @@ function TTSButton(props: TTSProps) {
|
|||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
// update price
|
// update price
|
||||||
const cost = (input.length * 0.015) / 1000;
|
const cost = (input.length * 0.015) / 1000;
|
||||||
ctx.chatStore.cost += cost;
|
chatStore.cost += cost;
|
||||||
addTotalCost(cost);
|
addTotalCost(cost);
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
|
|
||||||
// save blob
|
// save blob
|
||||||
props.chat.audio = blob;
|
props.chat.audio = blob;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
|
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const audio = new Audio(url);
|
const audio = new Audio(url);
|
||||||
@@ -235,9 +235,8 @@ function TTSButton(props: TTSProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Message(props: { messageIndex: number }) {
|
export default function Message(props: { messageIndex: number }) {
|
||||||
const ctx = useContext(AppContext);
|
|
||||||
const { messageIndex } = props;
|
const { messageIndex } = props;
|
||||||
const { chatStore, setChatStore } = ctx;
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
const chat = chatStore.history[messageIndex];
|
const chat = chatStore.history[messageIndex];
|
||||||
const [showEdit, setShowEdit] = useState(false);
|
const [showEdit, setShowEdit] = useState(false);
|
||||||
@@ -301,8 +300,7 @@ export default function Message(props: { messageIndex: number }) {
|
|||||||
<Markdown>{getMessageText(chat)}</Markdown>
|
<Markdown>{getMessageText(chat)}</Markdown>
|
||||||
) : (
|
) : (
|
||||||
<div className="message-content">
|
<div className="message-content">
|
||||||
{ctx &&
|
{chat.content &&
|
||||||
chat.content &&
|
|
||||||
(chat.logprobs && renderColor
|
(chat.logprobs && renderColor
|
||||||
? chat.logprobs.content
|
? chat.logprobs.content
|
||||||
.filter((c) => c.token)
|
.filter((c) => c.token)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
} from "@/components/ui/pagination";
|
} from "@/components/ui/pagination";
|
||||||
|
|
||||||
import { Input } from "./ui/input";
|
import { Input } from "./ui/input";
|
||||||
import { AppContext } from "../pages/App";
|
import { App, AppContext } from "../pages/App";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
import { SearchIcon } from "lucide-react";
|
import { SearchIcon } from "lucide-react";
|
||||||
|
|
||||||
@@ -33,8 +33,7 @@ interface ChatStoreSearchResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Search() {
|
export default function Search() {
|
||||||
const ctx = useContext(AppContext);
|
const { setSelectedChatIndex, db } = useContext(AppContext);
|
||||||
const { setSelectedChatIndex, db } = ctx;
|
|
||||||
|
|
||||||
const [searchResult, setSearchResult] = useState<ChatStoreSearchResult[]>([]);
|
const [searchResult, setSearchResult] = useState<ChatStoreSearchResult[]>([]);
|
||||||
const [searching, setSearching] = useState<boolean>(false);
|
const [searching, setSearching] = useState<boolean>(false);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ import {
|
|||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Slider } from "@/components/ui/slider";
|
import { Slider } from "@/components/ui/slider";
|
||||||
import { NonOverflowScrollArea, ScrollArea } from "@/components/ui/scroll-area";
|
import { NonOverflowScrollArea, ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
|
|
||||||
const TTS_VOICES: string[] = [
|
const TTS_VOICES: string[] = [
|
||||||
"alloy",
|
"alloy",
|
||||||
@@ -96,11 +96,11 @@ const Help = (props: { children: any; help: string; field: string }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SelectModel = (props: { help: string }) => {
|
const SelectModel = (props: { help: string }) => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
let shouldIUseCustomModel: boolean = true;
|
let shouldIUseCustomModel: boolean = true;
|
||||||
for (const model in models) {
|
for (const model in models) {
|
||||||
if (ctx.chatStore.model === model) {
|
if (chatStore.model === model) {
|
||||||
shouldIUseCustomModel = false;
|
shouldIUseCustomModel = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,22 +140,22 @@ const SelectModel = (props: { help: string }) => {
|
|||||||
|
|
||||||
{useCustomModel ? (
|
{useCustomModel ? (
|
||||||
<Input
|
<Input
|
||||||
value={ctx.chatStore.model}
|
value={chatStore.model}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
ctx.chatStore.model = e.target.value;
|
chatStore.model = e.target.value;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Select
|
<Select
|
||||||
value={ctx.chatStore.model}
|
value={chatStore.model}
|
||||||
onValueChange={(model: string) => {
|
onValueChange={(model: string) => {
|
||||||
ctx.chatStore.model = model;
|
chatStore.model = model;
|
||||||
ctx.chatStore.maxTokens = getDefaultParams(
|
chatStore.maxTokens = getDefaultParams(
|
||||||
"max",
|
"max",
|
||||||
models[model].maxToken
|
models[model].maxToken
|
||||||
);
|
);
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
@@ -182,7 +182,7 @@ const LongInput = (props: {
|
|||||||
label: string;
|
label: string;
|
||||||
help: string;
|
help: string;
|
||||||
}) => {
|
}) => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="name" className="text-right">
|
<Label htmlFor="name" className="text-right">
|
||||||
@@ -204,10 +204,10 @@ const LongInput = (props: {
|
|||||||
|
|
||||||
<Textarea
|
<Textarea
|
||||||
className="h-24 w-full"
|
className="h-24 w-full"
|
||||||
value={ctx.chatStore[props.field]}
|
value={chatStore[props.field]}
|
||||||
onChange={(event: any) => {
|
onChange={(event: any) => {
|
||||||
ctx.chatStore[props.field] = event.target.value;
|
chatStore[props.field] = event.target.value;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
autoHeight(event.target);
|
autoHeight(event.target);
|
||||||
}}
|
}}
|
||||||
onKeyPress={(event: any) => {
|
onKeyPress={(event: any) => {
|
||||||
@@ -230,7 +230,7 @@ const InputField = (props: {
|
|||||||
| "image_gen_key";
|
| "image_gen_key";
|
||||||
help: string;
|
help: string;
|
||||||
}) => {
|
}) => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
const [hideInput, setHideInput] = useState(true);
|
const [hideInput, setHideInput] = useState(true);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -256,10 +256,10 @@ const InputField = (props: {
|
|||||||
<div className="flex w-full items-center space-x-2">
|
<div className="flex w-full items-center space-x-2">
|
||||||
<Input
|
<Input
|
||||||
type={hideInput ? "password" : "text"}
|
type={hideInput ? "password" : "text"}
|
||||||
value={ctx.chatStore[props.field]}
|
value={chatStore[props.field]}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
ctx.chatStore[props.field] = event.target.value;
|
chatStore[props.field] = event.target.value;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@@ -285,24 +285,24 @@ const Slicer = (props: {
|
|||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
}) => {
|
}) => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
const enable_filed_name: "temperature_enabled" | "top_p_enabled" =
|
const enable_filed_name: "temperature_enabled" | "top_p_enabled" =
|
||||||
`${props.field}_enabled` as any;
|
`${props.field}_enabled` as any;
|
||||||
|
|
||||||
const enabled = ctx.chatStore[enable_filed_name];
|
const enabled = chatStore[enable_filed_name];
|
||||||
|
|
||||||
if (enabled === null || enabled === undefined) {
|
if (enabled === null || enabled === undefined) {
|
||||||
if (props.field === "temperature") {
|
if (props.field === "temperature") {
|
||||||
ctx.chatStore[enable_filed_name] = true;
|
chatStore[enable_filed_name] = true;
|
||||||
}
|
}
|
||||||
if (props.field === "top_p") {
|
if (props.field === "top_p") {
|
||||||
ctx.chatStore[enable_filed_name] = false;
|
chatStore[enable_filed_name] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setEnabled = (state: boolean) => {
|
const setEnabled = (state: boolean) => {
|
||||||
ctx.chatStore[enable_filed_name] = state;
|
chatStore[enable_filed_name] = state;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -323,10 +323,10 @@ const Slicer = (props: {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={ctx.chatStore[enable_filed_name]}
|
checked={chatStore[enable_filed_name]}
|
||||||
onCheckedChange={(checked: boolean) => setEnabled(!!checked)}
|
onCheckedChange={(checked: boolean) => setEnabled(!!checked)}
|
||||||
/>
|
/>
|
||||||
{!ctx.chatStore[enable_filed_name] && (
|
{!chatStore[enable_filed_name] && (
|
||||||
<span className="text-xs text-muted-foreground">disabled</span>
|
<span className="text-xs text-muted-foreground">disabled</span>
|
||||||
)}
|
)}
|
||||||
</Label>
|
</Label>
|
||||||
@@ -339,10 +339,10 @@ const Slicer = (props: {
|
|||||||
min={props.min}
|
min={props.min}
|
||||||
max={props.max}
|
max={props.max}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
value={[ctx.chatStore[props.field]]}
|
value={[chatStore[props.field]]}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
ctx.chatStore[props.field] = value[0];
|
chatStore[props.field] = value[0];
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -350,11 +350,11 @@ const Slicer = (props: {
|
|||||||
type="number"
|
type="number"
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
className="w-24"
|
className="w-24"
|
||||||
value={ctx.chatStore[props.field]}
|
value={chatStore[props.field]}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const value = parseFloat(e.target.value);
|
const value = parseFloat(e.target.value);
|
||||||
ctx.chatStore[props.field] = value;
|
chatStore[props.field] = value;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -375,7 +375,7 @@ const Number = (props: {
|
|||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
help: string;
|
help: string;
|
||||||
}) => {
|
}) => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="flex items-center gap-2">
|
<Label className="flex items-center gap-2">
|
||||||
@@ -397,12 +397,12 @@ const Number = (props: {
|
|||||||
|
|
||||||
{props.field === "maxGenTokens" && (
|
{props.field === "maxGenTokens" && (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={ctx.chatStore.maxGenTokens_enabled}
|
checked={chatStore.maxGenTokens_enabled}
|
||||||
onCheckedChange={() => {
|
onCheckedChange={() => {
|
||||||
const newChatStore = { ...ctx.chatStore };
|
const newChatStore = { ...chatStore };
|
||||||
newChatStore.maxGenTokens_enabled =
|
newChatStore.maxGenTokens_enabled =
|
||||||
!newChatStore.maxGenTokens_enabled;
|
!newChatStore.maxGenTokens_enabled;
|
||||||
ctx.setChatStore({ ...newChatStore });
|
setChatStore({ ...newChatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -412,14 +412,14 @@ const Number = (props: {
|
|||||||
type="number"
|
type="number"
|
||||||
readOnly={props.readOnly}
|
readOnly={props.readOnly}
|
||||||
disabled={
|
disabled={
|
||||||
props.field === "maxGenTokens" && !ctx.chatStore.maxGenTokens_enabled
|
props.field === "maxGenTokens" && !chatStore.maxGenTokens_enabled
|
||||||
}
|
}
|
||||||
value={ctx.chatStore[props.field]}
|
value={chatStore[props.field]}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
let newNumber = parseFloat(event.target.value);
|
let newNumber = parseFloat(event.target.value);
|
||||||
if (newNumber < 0) newNumber = 0;
|
if (newNumber < 0) newNumber = 0;
|
||||||
ctx.chatStore[props.field] = newNumber;
|
chatStore[props.field] = newNumber;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -430,17 +430,17 @@ const Choice = (props: {
|
|||||||
field: "streamMode" | "develop_mode" | "json_mode" | "logprobs";
|
field: "streamMode" | "develop_mode" | "json_mode" | "logprobs";
|
||||||
help: string;
|
help: string;
|
||||||
}) => {
|
}) => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id={`${props.field}-checkbox`}
|
id={`${props.field}-checkbox`}
|
||||||
checked={ctx.chatStore[props.field]}
|
checked={chatStore[props.field]}
|
||||||
onCheckedChange={(checked: boolean) => {
|
onCheckedChange={(checked: boolean) => {
|
||||||
ctx.chatStore[props.field] = checked;
|
chatStore[props.field] = checked;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -468,13 +468,27 @@ const Choice = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const APIShowBlock = (props: {
|
const APIShowBlock = (props: {
|
||||||
ctx: any;
|
|
||||||
index: number;
|
index: number;
|
||||||
label: string;
|
label: string;
|
||||||
type: string;
|
type: string;
|
||||||
apiField: string;
|
apiField: string;
|
||||||
keyField: string;
|
keyField: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const {
|
||||||
|
templates,
|
||||||
|
setTemplates,
|
||||||
|
templateAPIs,
|
||||||
|
setTemplateAPIs,
|
||||||
|
templateAPIsWhisper,
|
||||||
|
setTemplateAPIsWhisper,
|
||||||
|
templateAPIsTTS,
|
||||||
|
setTemplateAPIsTTS,
|
||||||
|
templateAPIsImageGen,
|
||||||
|
setTemplateAPIsImageGen,
|
||||||
|
templateTools,
|
||||||
|
setTemplateTools,
|
||||||
|
selectedChatIndex,
|
||||||
|
} = useContext(AppContext);
|
||||||
return (
|
return (
|
||||||
<div className="border-b border-gray-200 pb-4 pt-4">
|
<div className="border-b border-gray-200 pb-4 pt-4">
|
||||||
<Badge variant="outline">{props.type}</Badge> <Label>{props.label}</Label>
|
<Badge variant="outline">{props.type}</Badge> <Label>{props.label}</Label>
|
||||||
@@ -506,23 +520,17 @@ const APIShowBlock = (props: {
|
|||||||
const name = prompt(`Give template ${props.label} a new name`);
|
const name = prompt(`Give template ${props.label} a new name`);
|
||||||
if (!name) return;
|
if (!name) return;
|
||||||
if (props.type === "Chat") {
|
if (props.type === "Chat") {
|
||||||
props.ctx.templateAPIs[props.index].name = name;
|
templateAPIs[props.index].name = name;
|
||||||
props.ctx.setTemplateAPIs(structuredClone(props.ctx.templateAPIs));
|
setTemplateAPIs(structuredClone(templateAPIs));
|
||||||
} else if (props.type === "Whisper") {
|
} else if (props.type === "Whisper") {
|
||||||
props.ctx.templateAPIsWhisper[props.index].name = name;
|
templateAPIsWhisper[props.index].name = name;
|
||||||
props.ctx.setTemplateAPIsWhisper(
|
setTemplateAPIsWhisper(structuredClone(templateAPIsWhisper));
|
||||||
structuredClone(props.ctx.templateAPIsWhisper)
|
|
||||||
);
|
|
||||||
} else if (props.type === "TTS") {
|
} else if (props.type === "TTS") {
|
||||||
props.ctx.templateAPIsTTS[props.index].name = name;
|
templateAPIsTTS[props.index].name = name;
|
||||||
props.ctx.setTemplateAPIsTTS(
|
setTemplateAPIsTTS(structuredClone(templateAPIsTTS));
|
||||||
structuredClone(props.ctx.templateAPIsTTS)
|
|
||||||
);
|
|
||||||
} else if (props.type === "ImgGen") {
|
} else if (props.type === "ImgGen") {
|
||||||
props.ctx.templateAPIsImageGen[props.index].name = name;
|
templateAPIsImageGen[props.index].name = name;
|
||||||
props.ctx.setTemplateAPIsImageGen(
|
setTemplateAPIsImageGen(structuredClone(templateAPIsImageGen));
|
||||||
structuredClone(props.ctx.templateAPIsImageGen)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -533,7 +541,6 @@ const APIShowBlock = (props: {
|
|||||||
size="sm"
|
size="sm"
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!props.ctx) return;
|
|
||||||
if (
|
if (
|
||||||
!confirm(
|
!confirm(
|
||||||
`Are you sure to delete ${props.label}(${props.type}) API?`
|
`Are you sure to delete ${props.label}(${props.type}) API?`
|
||||||
@@ -542,23 +549,17 @@ const APIShowBlock = (props: {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (props.type === "Chat") {
|
if (props.type === "Chat") {
|
||||||
props.ctx.templateAPIs.splice(props.index, 1);
|
templateAPIs.splice(props.index, 1);
|
||||||
props.ctx.setTemplateAPIs(structuredClone(props.ctx.templateAPIs));
|
setTemplateAPIs(structuredClone(templateAPIs));
|
||||||
} else if (props.type === "Whisper") {
|
} else if (props.type === "Whisper") {
|
||||||
props.ctx.templateAPIsWhisper.splice(props.index, 1);
|
templateAPIsWhisper.splice(props.index, 1);
|
||||||
props.ctx.setTemplateAPIsWhisper(
|
setTemplateAPIsWhisper(structuredClone(templateAPIsWhisper));
|
||||||
structuredClone(props.ctx.templateAPIsWhisper)
|
|
||||||
);
|
|
||||||
} else if (props.type === "TTS") {
|
} else if (props.type === "TTS") {
|
||||||
props.ctx.templateAPIsTTS.splice(props.index, 1);
|
templateAPIsTTS.splice(props.index, 1);
|
||||||
props.ctx.setTemplateAPIsTTS(
|
setTemplateAPIsTTS(structuredClone(templateAPIsTTS));
|
||||||
structuredClone(props.ctx.templateAPIsTTS)
|
|
||||||
);
|
|
||||||
} else if (props.type === "ImgGen") {
|
} else if (props.type === "ImgGen") {
|
||||||
props.ctx.templateAPIsImageGen.splice(props.index, 1);
|
templateAPIsImageGen.splice(props.index, 1);
|
||||||
props.ctx.setTemplateAPIsImageGen(
|
setTemplateAPIsImageGen(structuredClone(templateAPIsImageGen));
|
||||||
structuredClone(props.ctx.templateAPIsImageGen)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -569,11 +570,25 @@ const APIShowBlock = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ToolsShowBlock = (props: {
|
const ToolsShowBlock = (props: {
|
||||||
ctx: any;
|
|
||||||
index: number;
|
index: number;
|
||||||
label: string;
|
label: string;
|
||||||
content: string;
|
content: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const {
|
||||||
|
templates,
|
||||||
|
setTemplates,
|
||||||
|
templateAPIs,
|
||||||
|
setTemplateAPIs,
|
||||||
|
templateAPIsWhisper,
|
||||||
|
setTemplateAPIsWhisper,
|
||||||
|
templateAPIsTTS,
|
||||||
|
setTemplateAPIsTTS,
|
||||||
|
templateAPIsImageGen,
|
||||||
|
setTemplateAPIsImageGen,
|
||||||
|
templateTools,
|
||||||
|
setTemplateTools,
|
||||||
|
selectedChatIndex,
|
||||||
|
} = useContext(AppContext);
|
||||||
return (
|
return (
|
||||||
<div className="border-b border-gray-200 pb-4 pt-4">
|
<div className="border-b border-gray-200 pb-4 pt-4">
|
||||||
<Badge variant="outline">Tool</Badge> <Label>{props.label}</Label>
|
<Badge variant="outline">Tool</Badge> <Label>{props.label}</Label>
|
||||||
@@ -594,8 +609,8 @@ const ToolsShowBlock = (props: {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const name = prompt(`Give the tool ${props.label} a new name`);
|
const name = prompt(`Give the tool ${props.label} a new name`);
|
||||||
if (!name) return;
|
if (!name) return;
|
||||||
props.ctx.templateTools[props.index].name = name;
|
templateTools[props.index].name = name;
|
||||||
props.ctx.setTemplateTools(structuredClone(props.ctx.templateTools));
|
setTemplateTools(structuredClone(templateTools));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
@@ -605,12 +620,11 @@ const ToolsShowBlock = (props: {
|
|||||||
size="sm"
|
size="sm"
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!props.ctx) return;
|
|
||||||
if (!confirm(`Are you sure to delete ${props.label} Tool?`)) {
|
if (!confirm(`Are you sure to delete ${props.label} Tool?`)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
props.ctx.templateTools.splice(props.index, 1);
|
templateTools.splice(props.index, 1);
|
||||||
props.ctx.setTemplateTools(structuredClone(props.ctx.templateTools));
|
setTemplateTools(structuredClone(templateTools));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
@@ -620,19 +634,34 @@ const ToolsShowBlock = (props: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default (props: {}) => {
|
export default (props: {}) => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
const {
|
||||||
|
templates,
|
||||||
|
setTemplates,
|
||||||
|
templateAPIs,
|
||||||
|
setTemplateAPIs,
|
||||||
|
templateAPIsWhisper,
|
||||||
|
setTemplateAPIsWhisper,
|
||||||
|
templateAPIsTTS,
|
||||||
|
setTemplateAPIsTTS,
|
||||||
|
templateAPIsImageGen,
|
||||||
|
setTemplateAPIsImageGen,
|
||||||
|
templateTools,
|
||||||
|
setTemplateTools,
|
||||||
|
selectedChatIndex,
|
||||||
|
} = useContext(AppContext);
|
||||||
|
|
||||||
let link =
|
let link =
|
||||||
location.protocol +
|
location.protocol +
|
||||||
"//" +
|
"//" +
|
||||||
location.host +
|
location.host +
|
||||||
location.pathname +
|
location.pathname +
|
||||||
`?key=${encodeURIComponent(ctx.chatStore.apiKey)}&api=${encodeURIComponent(
|
`?key=${encodeURIComponent(chatStore.apiKey)}&api=${encodeURIComponent(
|
||||||
ctx.chatStore.apiEndpoint
|
chatStore.apiEndpoint
|
||||||
)}&mode=${ctx.chatStore.streamMode ? "stream" : "fetch"}&model=${
|
)}&mode=${chatStore.streamMode ? "stream" : "fetch"}&model=${
|
||||||
ctx.chatStore.model
|
chatStore.model
|
||||||
}&sys=${encodeURIComponent(ctx.chatStore.systemMessageContent)}`;
|
}&sys=${encodeURIComponent(chatStore.systemMessageContent)}`;
|
||||||
if (ctx.chatStore.develop_mode) {
|
if (chatStore.develop_mode) {
|
||||||
link = link + `&dev=true`;
|
link = link + `&dev=true`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,7 +691,7 @@ export default (props: {}) => {
|
|||||||
<SheetTrigger asChild>
|
<SheetTrigger asChild>
|
||||||
<Button variant="outline" className="flex-grow">
|
<Button variant="outline" className="flex-grow">
|
||||||
{Tr("Settings")}
|
{Tr("Settings")}
|
||||||
{(!ctx.chatStore.apiKey || !ctx.chatStore.apiEndpoint) && (
|
{(!chatStore.apiKey || !chatStore.apiEndpoint) && (
|
||||||
<TriangleAlertIcon className="w-4 h-4 ml-1 text-yellow-500" />
|
<TriangleAlertIcon className="w-4 h-4 ml-1 text-yellow-500" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -693,7 +722,7 @@ export default (props: {}) => {
|
|||||||
$ USD
|
$ USD
|
||||||
</span>
|
</span>
|
||||||
<span className="text-lg font-bold leading-none sm:text-3xl">
|
<span className="text-lg font-bold leading-none sm:text-3xl">
|
||||||
{ctx.chatStore.cost?.toFixed(4)}
|
{chatStore.cost?.toFixed(4)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -714,7 +743,7 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
<span className="pt-1">
|
<span className="pt-1">
|
||||||
JSON Check:{" "}
|
JSON Check:{" "}
|
||||||
{isVailedJSON(ctx.chatStore.toolsString) ? (
|
{isVailedJSON(chatStore.toolsString) ? (
|
||||||
<CheckIcon className="inline w-3 h-3" />
|
<CheckIcon className="inline w-3 h-3" />
|
||||||
) : (
|
) : (
|
||||||
<BanIcon className="inline w-3 h-3" />
|
<BanIcon className="inline w-3 h-3" />
|
||||||
@@ -722,7 +751,7 @@ export default (props: {}) => {
|
|||||||
</span>
|
</span>
|
||||||
<div className="box">
|
<div className="box">
|
||||||
<div className="flex justify-evenly flex-wrap">
|
<div className="flex justify-evenly flex-wrap">
|
||||||
{ctx.chatStore.toolsString.trim() && (
|
{chatStore.toolsString.trim() && (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="outline">{Tr(`Save Tools`)}</Button>
|
<Button variant="outline">{Tr(`Save Tools`)}</Button>
|
||||||
@@ -772,10 +801,10 @@ export default (props: {}) => {
|
|||||||
}
|
}
|
||||||
const newToolsTmp: TemplateTools = {
|
const newToolsTmp: TemplateTools = {
|
||||||
name: name.value,
|
name: name.value,
|
||||||
toolsString: ctx.chatStore.toolsString,
|
toolsString: chatStore.toolsString,
|
||||||
};
|
};
|
||||||
ctx.templateTools.push(newToolsTmp);
|
templateTools.push(newToolsTmp);
|
||||||
ctx.setTemplateTools([...ctx.templateTools]);
|
setTemplateTools([...templateTools]);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SaveIcon className="w-4 h-4" /> Save
|
<SaveIcon className="w-4 h-4" /> Save
|
||||||
@@ -884,12 +913,11 @@ export default (props: {}) => {
|
|||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
ctx.chatStore.history =
|
chatStore.history = chatStore.history.filter(
|
||||||
ctx.chatStore.history.filter(
|
(msg) => msg.example && !msg.hide
|
||||||
(msg) => msg.example && !msg.hide
|
);
|
||||||
);
|
chatStore.postBeginIndex = 0;
|
||||||
ctx.chatStore.postBeginIndex = 0;
|
setChatStore({ ...chatStore });
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Yes, clear all history
|
Yes, clear all history
|
||||||
@@ -905,14 +933,14 @@ export default (props: {}) => {
|
|||||||
let dataStr =
|
let dataStr =
|
||||||
"data:text/json;charset=utf-8," +
|
"data:text/json;charset=utf-8," +
|
||||||
encodeURIComponent(
|
encodeURIComponent(
|
||||||
JSON.stringify(ctx.chatStore, null, "\t")
|
JSON.stringify(chatStore, null, "\t")
|
||||||
);
|
);
|
||||||
let downloadAnchorNode =
|
let downloadAnchorNode =
|
||||||
document.createElement("a");
|
document.createElement("a");
|
||||||
downloadAnchorNode.setAttribute("href", dataStr);
|
downloadAnchorNode.setAttribute("href", dataStr);
|
||||||
downloadAnchorNode.setAttribute(
|
downloadAnchorNode.setAttribute(
|
||||||
"download",
|
"download",
|
||||||
`chatgpt-api-web-${ctx.selectedChatIndex}.json`
|
`chatgpt-api-web-${selectedChatIndex}.json`
|
||||||
);
|
);
|
||||||
document.body.appendChild(downloadAnchorNode);
|
document.body.appendChild(downloadAnchorNode);
|
||||||
downloadAnchorNode.click();
|
downloadAnchorNode.click();
|
||||||
@@ -933,14 +961,12 @@ export default (props: {}) => {
|
|||||||
alert(tr("No template name specified", langCode));
|
alert(tr("No template name specified", langCode));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tmp: ChatStore = structuredClone(
|
const tmp: ChatStore = structuredClone(chatStore);
|
||||||
ctx.chatStore
|
|
||||||
);
|
|
||||||
tmp.history = tmp.history.filter((h) => h.example);
|
tmp.history = tmp.history.filter((h) => h.example);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
tmp.name = name;
|
tmp.name = name;
|
||||||
ctx.templates.push(tmp as TemplateChatStore);
|
templates.push(tmp as TemplateChatStore);
|
||||||
ctx.setTemplates([...ctx.templates]);
|
setTemplates([...templates]);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Tr("As template")}
|
{Tr("As template")}
|
||||||
@@ -992,7 +1018,7 @@ export default (props: {}) => {
|
|||||||
langCode
|
langCode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ctx.setChatStore({ ...newChatStore });
|
setChatStore({ ...newChatStore });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(
|
alert(
|
||||||
tr(
|
tr(
|
||||||
@@ -1034,10 +1060,10 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
<SetAPIsTemplate
|
<SetAPIsTemplate
|
||||||
label="Chat API"
|
label="Chat API"
|
||||||
endpoint={ctx.chatStore.apiEndpoint}
|
endpoint={chatStore.apiEndpoint}
|
||||||
APIkey={ctx.chatStore.apiKey}
|
APIkey={chatStore.apiKey}
|
||||||
temps={ctx.templateAPIs}
|
temps={templateAPIs}
|
||||||
setTemps={ctx.setTemplateAPIs}
|
setTemps={setTemplateAPIs}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1139,10 +1165,10 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
<SetAPIsTemplate
|
<SetAPIsTemplate
|
||||||
label="Whisper API"
|
label="Whisper API"
|
||||||
endpoint={ctx.chatStore.whisper_api}
|
endpoint={chatStore.whisper_api}
|
||||||
APIkey={ctx.chatStore.whisper_key}
|
APIkey={chatStore.whisper_key}
|
||||||
temps={ctx.templateAPIsWhisper}
|
temps={templateAPIsWhisper}
|
||||||
setTemps={ctx.setTemplateAPIsWhisper}
|
setTemps={setTemplateAPIsWhisper}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1172,10 +1198,10 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
<SetAPIsTemplate
|
<SetAPIsTemplate
|
||||||
label="TTS API"
|
label="TTS API"
|
||||||
endpoint={ctx.chatStore.tts_api}
|
endpoint={chatStore.tts_api}
|
||||||
APIkey={ctx.chatStore.tts_key}
|
APIkey={chatStore.tts_key}
|
||||||
temps={ctx.templateAPIsTTS}
|
temps={templateAPIsTTS}
|
||||||
setTemps={ctx.setTemplateAPIsTTS}
|
setTemps={setTemplateAPIsTTS}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1200,10 +1226,10 @@ export default (props: {}) => {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
value={ctx.chatStore.tts_voice}
|
value={chatStore.tts_voice}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
ctx.chatStore.tts_voice = value;
|
chatStore.tts_voice = value;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
@@ -1250,10 +1276,10 @@ export default (props: {}) => {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</Label>
|
</Label>
|
||||||
<Select
|
<Select
|
||||||
value={ctx.chatStore.tts_format}
|
value={chatStore.tts_format}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
ctx.chatStore.tts_format = value;
|
chatStore.tts_format = value;
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
@@ -1297,10 +1323,10 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
<SetAPIsTemplate
|
<SetAPIsTemplate
|
||||||
label="Image Gen API"
|
label="Image Gen API"
|
||||||
endpoint={ctx.chatStore.image_gen_api}
|
endpoint={chatStore.image_gen_api}
|
||||||
APIkey={ctx.chatStore.image_gen_key}
|
APIkey={chatStore.image_gen_key}
|
||||||
temps={ctx.templateAPIsImageGen}
|
temps={templateAPIsImageGen}
|
||||||
setTemps={ctx.setTemplateAPIsImageGen}
|
setTemps={setTemplateAPIsImageGen}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1309,10 +1335,9 @@ export default (props: {}) => {
|
|||||||
<AccordionItem value="templates">
|
<AccordionItem value="templates">
|
||||||
<AccordionTrigger>Saved Template</AccordionTrigger>
|
<AccordionTrigger>Saved Template</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
{ctx.templateAPIs.map((template, index) => (
|
{templateAPIs.map((template, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<APIShowBlock
|
<APIShowBlock
|
||||||
ctx={ctx}
|
|
||||||
index={index}
|
index={index}
|
||||||
label={template.name}
|
label={template.name}
|
||||||
type="Chat"
|
type="Chat"
|
||||||
@@ -1321,10 +1346,9 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{ctx.templateAPIsWhisper.map((template, index) => (
|
{templateAPIsWhisper.map((template, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<APIShowBlock
|
<APIShowBlock
|
||||||
ctx={ctx}
|
|
||||||
index={index}
|
index={index}
|
||||||
label={template.name}
|
label={template.name}
|
||||||
type="Whisper"
|
type="Whisper"
|
||||||
@@ -1333,10 +1357,9 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{ctx.templateAPIsTTS.map((template, index) => (
|
{templateAPIsTTS.map((template, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<APIShowBlock
|
<APIShowBlock
|
||||||
ctx={ctx}
|
|
||||||
index={index}
|
index={index}
|
||||||
label={template.name}
|
label={template.name}
|
||||||
type="TTS"
|
type="TTS"
|
||||||
@@ -1345,10 +1368,9 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{ctx.templateAPIsImageGen.map((template, index) => (
|
{templateAPIsImageGen.map((template, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<APIShowBlock
|
<APIShowBlock
|
||||||
ctx={ctx}
|
|
||||||
index={index}
|
index={index}
|
||||||
label={template.name}
|
label={template.name}
|
||||||
type="ImgGen"
|
type="ImgGen"
|
||||||
@@ -1357,10 +1379,9 @@ export default (props: {}) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{ctx.templateTools.map((template, index) => (
|
{templateTools.map((template, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<ToolsShowBlock
|
<ToolsShowBlock
|
||||||
ctx={ctx}
|
|
||||||
index={index}
|
index={index}
|
||||||
label={template.name}
|
label={template.name}
|
||||||
content={template.toolsString}
|
content={template.toolsString}
|
||||||
@@ -1373,7 +1394,7 @@ export default (props: {}) => {
|
|||||||
<div className="pt-4 space-y-2">
|
<div className="pt-4 space-y-2">
|
||||||
<p className="text-sm text-muted-foreground text-center">
|
<p className="text-sm text-muted-foreground text-center">
|
||||||
chatgpt-api-web ChatStore {Tr("Version")}{" "}
|
chatgpt-api-web ChatStore {Tr("Version")}{" "}
|
||||||
{ctx.chatStore.chatgpt_api_web_version}
|
{chatStore.chatgpt_api_web_version}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-muted-foreground text-center">
|
<p className="text-sm text-muted-foreground text-center">
|
||||||
{Tr("Documents and source code are avaliable here")}:{" "}
|
{Tr("Documents and source code are avaliable here")}:{" "}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
import { TemplateChatStore } from "@/types/chatstore";
|
import { TemplateChatStore } from "@/types/chatstore";
|
||||||
import { ChatStore } from "@/types/chatstore";
|
import { ChatStore } from "@/types/chatstore";
|
||||||
import { getDefaultParams } from "@/utils/getDefaultParam";
|
import { getDefaultParams } from "@/utils/getDefaultParam";
|
||||||
@@ -6,7 +6,8 @@ import { useContext } from "react";
|
|||||||
|
|
||||||
const Templates = () => {
|
const Templates = () => {
|
||||||
const ctx = useContext(AppContext);
|
const ctx = useContext(AppContext);
|
||||||
const { templates, chatStore, setChatStore, setTemplates } = ctx;
|
const { templates, setTemplates } = useContext(AppContext);
|
||||||
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { ChatStore } from "@/types/chatstore";
|
import { ChatStore } from "@/types/chatstore";
|
||||||
import { Tr } from "@/translate";
|
import { Tr } from "@/translate";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
|
|
||||||
const VersionHint = () => {
|
const VersionHint = () => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
const { chatStore } = ctx;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{chatStore.chatgpt_api_web_version < "v1.3.0" && (
|
{chatStore.chatgpt_api_web_version < "v1.3.0" && (
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ import { createRef, useContext } from "react";
|
|||||||
import { useState, Dispatch } from "react";
|
import { useState, Dispatch } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { AudioWaveformIcon, CircleStopIcon, MicIcon } from "lucide-react";
|
import { AudioWaveformIcon, CircleStopIcon, MicIcon } from "lucide-react";
|
||||||
import { AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
|
|
||||||
const WhisperButton = (props: {
|
const WhisperButton = (props: {
|
||||||
inputMsg: string;
|
inputMsg: string;
|
||||||
setInputMsg: Dispatch<string>;
|
setInputMsg: Dispatch<string>;
|
||||||
}) => {
|
}) => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore } = useContext(AppChatStoreContext);
|
||||||
const { chatStore } = ctx;
|
|
||||||
|
|
||||||
const { inputMsg, setInputMsg } = props;
|
const { inputMsg, setInputMsg } = props;
|
||||||
const mediaRef = createRef();
|
const mediaRef = createRef();
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
import { AppContext } from "../pages/App";
|
import { AppChatStoreContext, AppContext } from "../pages/App";
|
||||||
|
|
||||||
interface EditMessageProps {
|
interface EditMessageProps {
|
||||||
chat: ChatStoreMessage;
|
chat: ChatStoreMessage;
|
||||||
@@ -21,7 +21,7 @@ interface EditMessageProps {
|
|||||||
setShowEdit: Dispatch<boolean>;
|
setShowEdit: Dispatch<boolean>;
|
||||||
}
|
}
|
||||||
export function EditMessage(props: EditMessageProps) {
|
export function EditMessage(props: EditMessageProps) {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
const { showEdit, setShowEdit, chat } = props;
|
const { showEdit, setShowEdit, chat } = props;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ export function EditMessage(props: EditMessageProps) {
|
|||||||
) : (
|
) : (
|
||||||
<EditMessageDetail chat={chat} setShowEdit={setShowEdit} />
|
<EditMessageDetail chat={chat} setShowEdit={setShowEdit} />
|
||||||
)}
|
)}
|
||||||
{ctx.chatStore.develop_mode && (
|
{chatStore.develop_mode && (
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@@ -57,7 +57,7 @@ export function EditMessage(props: EditMessageProps) {
|
|||||||
} else {
|
} else {
|
||||||
chat.content = "";
|
chat.content = "";
|
||||||
}
|
}
|
||||||
ctx.setChatStore({ ...ctx.chatStore });
|
setChatStore({ ...chatStore });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Switch to{" "}
|
Switch to{" "}
|
||||||
|
|||||||
@@ -15,16 +15,14 @@ import {
|
|||||||
|
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { AppContext } from "../pages/App";
|
import { AppChatStoreContext, AppContext } from "../pages/App";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chat: ChatStoreMessage;
|
chat: ChatStoreMessage;
|
||||||
setShowEdit: (se: boolean) => void;
|
setShowEdit: (se: boolean) => void;
|
||||||
}
|
}
|
||||||
export function EditMessageDetail({ chat, setShowEdit }: Props) {
|
export function EditMessageDetail({ chat, setShowEdit }: Props) {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
const { chatStore, setChatStore } = ctx;
|
|
||||||
|
|
||||||
if (typeof chat.content !== "object") return <div>error</div>;
|
if (typeof chat.content !== "object") return <div>error</div>;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,16 +5,14 @@ import { Tr } from "@/translate";
|
|||||||
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { AppContext } from "../pages/App";
|
import { AppChatStoreContext, AppContext } from "../pages/App";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chat: ChatStoreMessage;
|
chat: ChatStoreMessage;
|
||||||
setShowEdit: (se: boolean) => void;
|
setShowEdit: (se: boolean) => void;
|
||||||
}
|
}
|
||||||
export function EditMessageString({ chat, setShowEdit }: Props) {
|
export function EditMessageString({ chat, setShowEdit }: Props) {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
|
|
||||||
const { chatStore, setChatStore } = ctx;
|
|
||||||
|
|
||||||
if (typeof chat.content !== "string") return <div>error</div>;
|
if (typeof chat.content !== "string") return <div>error</div>;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
ArrowUpDownIcon,
|
ArrowUpDownIcon,
|
||||||
ScissorsIcon,
|
ScissorsIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { AppContext } from "@/pages/App";
|
import { AppChatStoreContext, AppContext } from "@/pages/App";
|
||||||
import { models } from "@/types/models";
|
import { models } from "@/types/models";
|
||||||
import { getTotalCost } from "@/utils/totalCost";
|
import { getTotalCost } from "@/utils/totalCost";
|
||||||
import { Tr } from "@/translate";
|
import { Tr } from "@/translate";
|
||||||
@@ -36,9 +36,7 @@ import Search from "@/components/Search";
|
|||||||
import Settings from "@/components/Settings";
|
import Settings from "@/components/Settings";
|
||||||
|
|
||||||
const Navbar: React.FC = () => {
|
const Navbar: React.FC = () => {
|
||||||
const ctx = useContext(AppContext);
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
if (!ctx) return <div>error</div>;
|
|
||||||
const { chatStore, setChatStore } = ctx;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="flex sticky top-0 bg-background h-14 shrink-0 items-center border-b z-50">
|
<header className="flex sticky top-0 bg-background h-14 shrink-0 items-center border-b z-50">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IDBPDatabase, openDB } from "idb";
|
import { IDBPDatabase, openDB } from "idb";
|
||||||
import { createContext, useEffect, useState } from "react";
|
import { createContext, useContext, useEffect, useState } from "react";
|
||||||
import "@/global.css";
|
import "@/global.css";
|
||||||
|
|
||||||
import { calculate_token_length } from "@/chatgpt";
|
import { calculate_token_length } from "@/chatgpt";
|
||||||
@@ -31,8 +31,6 @@ import {
|
|||||||
|
|
||||||
interface AppContextType {
|
interface AppContextType {
|
||||||
db: Promise<IDBPDatabase<ChatStore>>;
|
db: Promise<IDBPDatabase<ChatStore>>;
|
||||||
chatStore: ChatStore;
|
|
||||||
setChatStore: (cs: ChatStore) => void;
|
|
||||||
selectedChatIndex: number;
|
selectedChatIndex: number;
|
||||||
setSelectedChatIndex: (i: number) => void;
|
setSelectedChatIndex: (i: number) => void;
|
||||||
templates: TemplateChatStore[];
|
templates: TemplateChatStore[];
|
||||||
@@ -49,7 +47,15 @@ interface AppContextType {
|
|||||||
setTemplateTools: (t: TemplateTools[]) => void;
|
setTemplateTools: (t: TemplateTools[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AppChatStoreContextType {
|
||||||
|
chatStore: ChatStore;
|
||||||
|
setChatStore: (cs: ChatStore) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export const AppContext = createContext<AppContextType>(null as any);
|
export const AppContext = createContext<AppContextType>(null as any);
|
||||||
|
export const AppChatStoreContext = createContext<AppChatStoreContextType>(
|
||||||
|
null as any
|
||||||
|
);
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
@@ -123,57 +129,6 @@ export function App() {
|
|||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [chatStore, _setChatStore] = useState(newChatStore({}));
|
|
||||||
const setChatStore = async (chatStore: ChatStore) => {
|
|
||||||
console.log("recalculate postBeginIndex");
|
|
||||||
const max = chatStore.maxTokens - chatStore.tokenMargin;
|
|
||||||
let sum = 0;
|
|
||||||
chatStore.postBeginIndex = chatStore.history.filter(
|
|
||||||
({ hide }) => !hide
|
|
||||||
).length;
|
|
||||||
for (const msg of chatStore.history
|
|
||||||
.filter(({ hide }) => !hide)
|
|
||||||
.slice()
|
|
||||||
.reverse()) {
|
|
||||||
if (sum + msg.token > max) break;
|
|
||||||
sum += msg.token;
|
|
||||||
chatStore.postBeginIndex -= 1;
|
|
||||||
}
|
|
||||||
chatStore.postBeginIndex =
|
|
||||||
chatStore.postBeginIndex < 0 ? 0 : chatStore.postBeginIndex;
|
|
||||||
|
|
||||||
// manually estimate token
|
|
||||||
chatStore.totalTokens = calculate_token_length(
|
|
||||||
chatStore.systemMessageContent
|
|
||||||
);
|
|
||||||
for (const msg of chatStore.history
|
|
||||||
.filter(({ hide }) => !hide)
|
|
||||||
.slice(chatStore.postBeginIndex)) {
|
|
||||||
chatStore.totalTokens += msg.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("saved chat", selectedChatIndex, chatStore);
|
|
||||||
(await db).put(STORAGE_NAME, chatStore, selectedChatIndex);
|
|
||||||
|
|
||||||
// update total tokens
|
|
||||||
chatStore.totalTokens = calculate_token_length(
|
|
||||||
chatStore.systemMessageContent
|
|
||||||
);
|
|
||||||
for (const msg of chatStore.history
|
|
||||||
.filter(({ hide }) => !hide)
|
|
||||||
.slice(chatStore.postBeginIndex)) {
|
|
||||||
chatStore.totalTokens += msg.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setChatStore(chatStore);
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
|
||||||
const run = async () => {
|
|
||||||
_setChatStore(await getChatStoreByIndex(selectedChatIndex));
|
|
||||||
};
|
|
||||||
run();
|
|
||||||
}, [selectedChatIndex]);
|
|
||||||
|
|
||||||
// all chat store indexes
|
// all chat store indexes
|
||||||
const [allChatStoreIndexes, setAllChatStoreIndexes] = useState<IDBValidKey>(
|
const [allChatStoreIndexes, setAllChatStoreIndexes] = useState<IDBValidKey>(
|
||||||
[]
|
[]
|
||||||
@@ -189,7 +144,8 @@ export function App() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleNewChatStore = async () => {
|
const handleNewChatStore = async () => {
|
||||||
return handleNewChatStoreWithOldOne(chatStore);
|
let currentChatStore = await getChatStoreByIndex(selectedChatIndex);
|
||||||
|
return handleNewChatStoreWithOldOne(currentChatStore);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDEL = async () => {
|
const handleDEL = async () => {
|
||||||
@@ -198,7 +154,7 @@ export function App() {
|
|||||||
const newAllChatStoreIndexes = await (await db).getAllKeys(STORAGE_NAME);
|
const newAllChatStoreIndexes = await (await db).getAllKeys(STORAGE_NAME);
|
||||||
|
|
||||||
if (newAllChatStoreIndexes.length === 0) {
|
if (newAllChatStoreIndexes.length === 0) {
|
||||||
handleNewChatStore();
|
await handleNewChatStore();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,8 +289,6 @@ export function App() {
|
|||||||
<AppContext.Provider
|
<AppContext.Provider
|
||||||
value={{
|
value={{
|
||||||
db,
|
db,
|
||||||
chatStore,
|
|
||||||
setChatStore,
|
|
||||||
selectedChatIndex,
|
selectedChatIndex,
|
||||||
setSelectedChatIndex,
|
setSelectedChatIndex,
|
||||||
templates,
|
templates,
|
||||||
@@ -407,19 +361,93 @@ export function App() {
|
|||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
|
|
||||||
{chatStore.develop_mode && (
|
|
||||||
<Button onClick={handleCLS} variant="destructive">
|
|
||||||
<span>{Tr("CLS")}</span>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</SidebarFooter>
|
</SidebarFooter>
|
||||||
<SidebarRail />
|
<SidebarRail />
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
<Navbar />
|
<AppChatStoreProvider
|
||||||
<ChatBOX />
|
selectedChatIndex={selectedChatIndex}
|
||||||
|
getChatStoreByIndex={getChatStoreByIndex}
|
||||||
|
>
|
||||||
|
<Navbar />
|
||||||
|
<ChatBOX />
|
||||||
|
</AppChatStoreProvider>
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
</AppContext.Provider>
|
</AppContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AppChatStoreProvider = ({
|
||||||
|
children,
|
||||||
|
selectedChatIndex,
|
||||||
|
getChatStoreByIndex,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
selectedChatIndex: number;
|
||||||
|
getChatStoreByIndex: (index: number) => Promise<ChatStore>;
|
||||||
|
}) => {
|
||||||
|
console.log("[Render] AppChatStoreProvider");
|
||||||
|
const ctx = useContext(AppContext);
|
||||||
|
|
||||||
|
const [chatStore, _setChatStore] = useState(newChatStore({}));
|
||||||
|
const setChatStore = async (chatStore: ChatStore) => {
|
||||||
|
console.log("recalculate postBeginIndex");
|
||||||
|
const max = chatStore.maxTokens - chatStore.tokenMargin;
|
||||||
|
let sum = 0;
|
||||||
|
chatStore.postBeginIndex = chatStore.history.filter(
|
||||||
|
({ hide }) => !hide
|
||||||
|
).length;
|
||||||
|
for (const msg of chatStore.history
|
||||||
|
.filter(({ hide }) => !hide)
|
||||||
|
.slice()
|
||||||
|
.reverse()) {
|
||||||
|
if (sum + msg.token > max) break;
|
||||||
|
sum += msg.token;
|
||||||
|
chatStore.postBeginIndex -= 1;
|
||||||
|
}
|
||||||
|
chatStore.postBeginIndex =
|
||||||
|
chatStore.postBeginIndex < 0 ? 0 : chatStore.postBeginIndex;
|
||||||
|
|
||||||
|
// manually estimate token
|
||||||
|
chatStore.totalTokens = calculate_token_length(
|
||||||
|
chatStore.systemMessageContent
|
||||||
|
);
|
||||||
|
for (const msg of chatStore.history
|
||||||
|
.filter(({ hide }) => !hide)
|
||||||
|
.slice(chatStore.postBeginIndex)) {
|
||||||
|
chatStore.totalTokens += msg.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("saved chat", selectedChatIndex, chatStore);
|
||||||
|
(await ctx.db).put(STORAGE_NAME, chatStore, selectedChatIndex);
|
||||||
|
|
||||||
|
// update total tokens
|
||||||
|
chatStore.totalTokens = calculate_token_length(
|
||||||
|
chatStore.systemMessageContent
|
||||||
|
);
|
||||||
|
for (const msg of chatStore.history
|
||||||
|
.filter(({ hide }) => !hide)
|
||||||
|
.slice(chatStore.postBeginIndex)) {
|
||||||
|
chatStore.totalTokens += msg.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setChatStore(chatStore);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
const run = async () => {
|
||||||
|
_setChatStore(await getChatStoreByIndex(selectedChatIndex));
|
||||||
|
};
|
||||||
|
run();
|
||||||
|
}, [selectedChatIndex]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppChatStoreContext.Provider
|
||||||
|
value={{
|
||||||
|
chatStore,
|
||||||
|
setChatStore,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AppChatStoreContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -39,19 +39,14 @@ import {
|
|||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||||
|
|
||||||
import { AppContext } from "./App";
|
import { AppChatStoreContext, AppContext } from "./App";
|
||||||
import APIListMenu from "@/components/ListAPI";
|
import APIListMenu from "@/components/ListAPI";
|
||||||
import { ImageGenDrawer } from "@/components/ImageGenDrawer";
|
import { ImageGenDrawer } from "@/components/ImageGenDrawer";
|
||||||
|
|
||||||
export default function ChatBOX() {
|
export default function ChatBOX() {
|
||||||
const ctx = useContext(AppContext);
|
const { db, selectedChatIndex, setSelectedChatIndex } =
|
||||||
const {
|
useContext(AppContext);
|
||||||
db,
|
const { chatStore, setChatStore } = useContext(AppChatStoreContext);
|
||||||
chatStore,
|
|
||||||
setChatStore,
|
|
||||||
selectedChatIndex,
|
|
||||||
setSelectedChatIndex,
|
|
||||||
} = ctx;
|
|
||||||
// prevent error
|
// prevent error
|
||||||
const [inputMsg, setInputMsg] = useState("");
|
const [inputMsg, setInputMsg] = useState("");
|
||||||
const [images, setImages] = useState<MessageDetail[]>([]);
|
const [images, setImages] = useState<MessageDetail[]>([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user