chatpad/src/components/SettingsModal.tsx
2023-05-08 16:45:50 +03:00

342 lines
11 KiB
TypeScript

import {
Alert,
Anchor,
Button,
Flex,
List,
Modal,
PasswordInput,
TextInput,
Select,
Stack,
Text,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { notifications } from "@mantine/notifications";
import { useLiveQuery } from "dexie-react-hooks";
import { cloneElement, ReactElement, useEffect, useState } from "react";
import { db } from "../db";
import { config } from "../utils/config";
import { checkOpenAIKey } from "../utils/openai";
export function SettingsModal({ children }: { children: ReactElement }) {
const [opened, { open, close }] = useDisclosure(false);
const [submitting, setSubmitting] = useState(false);
const [value, setValue] = useState("");
const [model, setModel] = useState(config.defaultModel);
const [type, setType] = useState(config.defaultType);
const [auth, setAuth] = useState(config.defaultAuth);
const [base, setBase] = useState("");
const [version, setVersion] = useState("");
const settings = useLiveQuery(async () => {
return db.settings.where({ id: "general" }).first();
});
useEffect(() => {
if (settings?.openAiApiKey) {
setValue(settings.openAiApiKey);
}
if (settings?.openAiModel) {
setModel(settings.openAiModel);
}
if (settings?.openAiApiType) {
setType(settings.openAiApiType);
}
if (settings?.openAiApiAuth) {
setAuth(settings.openAiApiAuth);
}
if (settings?.openAiApiBase) {
setBase(settings.openAiApiBase);
}
if (settings?.openAiApiVersion) {
setVersion(settings.openAiApiVersion);
}
}, [settings]);
return (
<>
{cloneElement(children, { onClick: open })}
<Modal opened={opened} onClose={close} title="Settings" size="lg">
<Stack>
<form
onSubmit={async (event) => {
try {
setSubmitting(true);
event.preventDefault();
await checkOpenAIKey(value);
await db.settings.where({ id: "general" }).modify((apiKey) => {
apiKey.openAiApiKey = value;
console.log(apiKey);
});
notifications.show({
title: "Saved",
message: "Your OpenAI Key has been saved.",
});
} catch (error: any) {
if (error.toJSON().message === "Network Error") {
notifications.show({
title: "Error",
color: "red",
message: "No internet connection.",
});
}
const message = error.response?.data?.error?.message;
if (message) {
notifications.show({
title: "Error",
color: "red",
message,
});
}
} finally {
setSubmitting(false);
}
}}
>
<Flex gap="xs" align="end">
<PasswordInput
label="OpenAI API Key"
placeholder="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
sx={{ flex: 1 }}
value={value}
onChange={(event) => setValue(event.currentTarget.value)}
formNoValidate
/>
<Button type="submit" loading={submitting}>
Save
</Button>
</Flex>
</form>
<List withPadding>
<List.Item>
<Text size="sm">
<Anchor
href="https://platform.openai.com/account/api-keys"
target="_blank"
>
Get your OpenAI API key
</Anchor>
</Text>
</List.Item>
<List.Item>
<Text size="sm" color="dimmed">
The API Key is stored locally on your browser and never sent
anywhere else.
</Text>
</List.Item>
</List>
<Select
label="OpenAI Type"
value={type}
onChange={async (value) => {
setSubmitting(true);
try {
await db.settings.update("general", {
openAiApiType: value ?? 'openai',
});
notifications.show({
title: "Saved",
message: "Your OpenAI Type has been saved.",
});
} catch (error: any) {
if (error.toJSON().message === "Network Error") {
notifications.show({
title: "Error",
color: "red",
message: "No internet connection.",
});
}
const message = error.response?.data?.error?.message;
if (message) {
notifications.show({
title: "Error",
color: "red",
message,
});
}
} finally {
setSubmitting(false);
}
}}
withinPortal
data={[{ "value": "openai", "label": "OpenAI"}, { "value": "custom", "label": "Custom (e.g. Azure OpenAI)"}]}
/>
<Select
label="OpenAI Model (OpenAI Only)"
value={model}
onChange={async (value) => {
setSubmitting(true);
try {
await db.settings.update("general", {
openAiModel: value ?? undefined,
});
notifications.show({
title: "Saved",
message: "Your OpenAI Model has been saved.",
});
} catch (error: any) {
if (error.toJSON().message === "Network Error") {
notifications.show({
title: "Error",
color: "red",
message: "No internet connection.",
});
}
const message = error.response?.data?.error?.message;
if (message) {
notifications.show({
title: "Error",
color: "red",
message,
});
}
} finally {
setSubmitting(false);
}
}}
withinPortal
data={config.availableModels}
/>
<Alert color="orange" title="Warning">
The displayed cost was not updated yet to reflect the costs for each
model. Right now it will always show the cost for GPT-3.5 on OpenAI.
</Alert>
<Select
label="OpenAI Auth (Custom Only)"
value={auth}
onChange={async (value) => {
setSubmitting(true);
try {
await db.settings.update("general", {
openAiApiAuth: value ?? 'none',
});
notifications.show({
title: "Saved",
message: "Your OpenAI Auth has been saved.",
});
} catch (error: any) {
if (error.toJSON().message === "Network Error") {
notifications.show({
title: "Error",
color: "red",
message: "No internet connection.",
});
}
const message = error.response?.data?.error?.message;
if (message) {
notifications.show({
title: "Error",
color: "red",
message,
});
}
} finally {
setSubmitting(false);
}
}}
withinPortal
data={[{ "value": "none", "label": "None"}, { "value": "bearer-token", "label": "Bearer Token"}, { "value": "api-key", "label": "API Key"}]}
/>
<form
onSubmit={async (event) => {
try {
setSubmitting(true);
event.preventDefault();
await db.settings.where({ id: "general" }).modify((row) => {
row.openAiApiBase = base;
console.log(row);
});
notifications.show({
title: "Saved",
message: "Your OpenAI Base has been saved.",
});
} catch (error: any) {
if (error.toJSON().message === "Network Error") {
notifications.show({
title: "Error",
color: "red",
message: "No internet connection.",
});
}
const message = error.response?.data?.error?.message;
if (message) {
notifications.show({
title: "Error",
color: "red",
message,
});
}
} finally {
setSubmitting(false);
}
}}
>
<Flex gap="xs" align="end">
<TextInput
label="OpenAI API Base (Custom Only)"
placeholder="https://<resource-name>.openai.azure.com/openai/deployments/<deployment>"
sx={{ flex: 1 }}
value={base}
onChange={(event) => setBase(event.currentTarget.value)}
formNoValidate
/>
<Button type="submit" loading={submitting}>
Save
</Button>
</Flex>
</form>
<form
onSubmit={async (event) => {
try {
setSubmitting(true);
event.preventDefault();
await db.settings.where({ id: "general" }).modify((row) => {
row.openAiApiVersion = version;
console.log(row);
});
notifications.show({
title: "Saved",
message: "Your OpenAI Version has been saved.",
});
} catch (error: any) {
if (error.toJSON().message === "Network Error") {
notifications.show({
title: "Error",
color: "red",
message: "No internet connection.",
});
}
const message = error.response?.data?.error?.message;
if (message) {
notifications.show({
title: "Error",
color: "red",
message,
});
}
} finally {
setSubmitting(false);
}
}}
>
<Flex gap="xs" align="end">
<TextInput
label="OpenAI API Version (Custom Only)"
placeholder="2023-03-15-preview"
sx={{ flex: 1 }}
value={version}
onChange={(event) => setVersion(event.currentTarget.value)}
formNoValidate
/>
<Button type="submit" loading={submitting}>
Save
</Button>
</Flex>
</form>
</Stack>
</Modal>
</>
);
}