save whisper / tts / image gen api

This commit is contained in:
2023-11-11 13:16:55 +08:00
parent a72e98ad25
commit f3953693fd
7 changed files with 280 additions and 77 deletions

View File

@@ -24,7 +24,7 @@ export function AddImage({
}: Props) { }: Props) {
const [enableHighResolution, setEnableHighResolution] = useState(true); const [enableHighResolution, setEnableHighResolution] = useState(true);
const [imageGenPrompt, setImageGenPrompt] = useState(""); const [imageGenPrompt, setImageGenPrompt] = useState("");
const [imageGenModel, setImageGenModel] = useState("dall-e-2"); const [imageGenModel, setImageGenModel] = useState("dall-e-3");
const [imageGenN, setImageGenN] = useState(1); const [imageGenN, setImageGenN] = useState(1);
const [imageGenQuality, setImageGEnQuality] = useState("standard"); const [imageGenQuality, setImageGEnQuality] = useState("standard");
const [imageGenResponseFormat, setImageGenResponseFormat] = const [imageGenResponseFormat, setImageGenResponseFormat] =

View File

@@ -118,6 +118,9 @@ const STORAGE_NAME_INDEXES = `${STORAGE_NAME}-indexes`;
const STORAGE_NAME_TOTALCOST = `${STORAGE_NAME}-totalcost`; const STORAGE_NAME_TOTALCOST = `${STORAGE_NAME}-totalcost`;
export const STORAGE_NAME_TEMPLATE = `${STORAGE_NAME}-template`; export const STORAGE_NAME_TEMPLATE = `${STORAGE_NAME}-template`;
export const STORAGE_NAME_TEMPLATE_API = `${STORAGE_NAME_TEMPLATE}-api`; export const STORAGE_NAME_TEMPLATE_API = `${STORAGE_NAME_TEMPLATE}-api`;
export const STORAGE_NAME_TEMPLATE_API_WHISPER = `${STORAGE_NAME_TEMPLATE}-api-whisper`;
export const STORAGE_NAME_TEMPLATE_API_TTS = `${STORAGE_NAME_TEMPLATE}-api-tts`;
export const STORAGE_NAME_TEMPLATE_API_IMAGE_GEN = `${STORAGE_NAME_TEMPLATE}-api-image-gen`;
export function addTotalCost(cost: number) { export function addTotalCost(cost: number) {
let totalCost = getTotalCost(); let totalCost = getTotalCost();

View File

@@ -7,6 +7,9 @@ import {
ChatStoreMessage, ChatStoreMessage,
STORAGE_NAME_TEMPLATE, STORAGE_NAME_TEMPLATE,
STORAGE_NAME_TEMPLATE_API, STORAGE_NAME_TEMPLATE_API,
STORAGE_NAME_TEMPLATE_API_IMAGE_GEN,
STORAGE_NAME_TEMPLATE_API_TTS,
STORAGE_NAME_TEMPLATE_API_WHISPER,
TemplateAPI, TemplateAPI,
addTotalCost, addTotalCost,
} from "./app"; } from "./app";
@@ -23,6 +26,7 @@ import models from "./models";
import Settings from "./settings"; import Settings from "./settings";
import getDefaultParams from "./getDefaultParam"; import getDefaultParams from "./getDefaultParam";
import { AddImage } from "./addImage"; import { AddImage } from "./addImage";
import { ListAPIs } from "./listAPIs";
export interface TemplateChatStore extends ChatStore { export interface TemplateChatStore extends ChatStore {
name: string; name: string;
@@ -319,6 +323,21 @@ export default function ChatBOX(props: {
localStorage.getItem(STORAGE_NAME_TEMPLATE_API) || "[]" localStorage.getItem(STORAGE_NAME_TEMPLATE_API) || "[]"
) as TemplateAPI[] ) as TemplateAPI[]
); );
const [templateAPIsWhisper, _setTemplateAPIsWhisper] = useState(
JSON.parse(
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_WHISPER) || "[]"
) as TemplateAPI[]
);
const [templateAPIsTTS, _setTemplateAPIsTTS] = useState(
JSON.parse(
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_TTS) || "[]"
) as TemplateAPI[]
);
const [templateAPIsImageGen, _setTemplateAPIsImageGen] = useState(
JSON.parse(
localStorage.getItem(STORAGE_NAME_TEMPLATE_API_IMAGE_GEN) || "[]"
) as TemplateAPI[]
);
const setTemplates = (templates: TemplateChatStore[]) => { const setTemplates = (templates: TemplateChatStore[]) => {
localStorage.setItem(STORAGE_NAME_TEMPLATE, JSON.stringify(templates)); localStorage.setItem(STORAGE_NAME_TEMPLATE, JSON.stringify(templates));
_setTemplates(templates); _setTemplates(templates);
@@ -330,6 +349,27 @@ export default function ChatBOX(props: {
); );
_setTemplateAPIs(templateAPIs); _setTemplateAPIs(templateAPIs);
}; };
const setTemplateAPIsWhisper = (templateAPIWhisper: TemplateAPI[]) => {
localStorage.setItem(
STORAGE_NAME_TEMPLATE_API_WHISPER,
JSON.stringify(templateAPIWhisper)
);
_setTemplateAPIsWhisper(templateAPIWhisper);
};
const setTemplateAPIsTTS = (templateAPITTS: TemplateAPI[]) => {
localStorage.setItem(
STORAGE_NAME_TEMPLATE_API_TTS,
JSON.stringify(templateAPITTS)
);
_setTemplateAPIsTTS(templateAPITTS);
};
const setTemplateAPIsImageGen = (templateAPIImageGen: TemplateAPI[]) => {
localStorage.setItem(
STORAGE_NAME_TEMPLATE_API_IMAGE_GEN,
JSON.stringify(templateAPIImageGen)
);
_setTemplateAPIsImageGen(templateAPIImageGen);
};
return ( return (
<div className="grow flex flex-col p-2 dark:text-black"> <div className="grow flex flex-col p-2 dark:text-black">
@@ -343,6 +383,12 @@ export default function ChatBOX(props: {
setTemplates={setTemplates} setTemplates={setTemplates}
templateAPIs={templateAPIs} templateAPIs={templateAPIs}
setTemplateAPIs={setTemplateAPIs} setTemplateAPIs={setTemplateAPIs}
templateAPIsWhisper={templateAPIsWhisper}
setTemplateAPIsWhisper={setTemplateAPIsWhisper}
templateAPIsTTS={templateAPIsTTS}
setTemplateAPIsTTS={setTemplateAPIsTTS}
templateAPIsImageGen={templateAPIsImageGen}
setTemplateAPIsImageGen={setTemplateAPIsImageGen}
/> />
)} )}
<div <div
@@ -399,60 +445,65 @@ export default function ChatBOX(props: {
chatStore.history.filter((msg) => !msg.example).length == 0 || chatStore.history.filter((msg) => !msg.example).length == 0 ||
!chatStore.apiEndpoint || !chatStore.apiEndpoint ||
!chatStore.apiKey) && ( !chatStore.apiKey) && (
<div className="break-all opacity-80 p-3 rounded bg-white my-3 text-left dark:text-black"> <ListAPIs
<h2>{Tr("Saved API templates")}</h2> label="API"
<hr className="my-2" /> tmps={templateAPIs}
<div className="flex flex-wrap"> setTmps={setTemplateAPIs}
{templateAPIs.map((t, index) => ( chatStore={chatStore}
<div setChatStore={setChatStore}
className={`cursor-pointer rounded ${ apiField="apiEndpoint"
chatStore.apiEndpoint === t.endpoint && keyField="apiKey"
chatStore.apiKey === t.key />
? "bg-red-600"
: "bg-red-400"
} w-fit p-2 m-1 flex flex-col`}
onClick={() => {
chatStore.apiEndpoint = t.endpoint;
chatStore.apiKey = t.key;
setChatStore({ ...chatStore });
}}
>
<span className="w-full text-center">{t.name}</span>
<hr className="mt-2" />
<span className="flex justify-between">
<button
onClick={() => {
const name = prompt("Give **API** template a name");
if (!name) {
return;
}
t.name = name;
setTemplateAPIs(structuredClone(templateAPIs));
}}
>
🖋
</button>
<button
onClick={() => {
if (
!confirm(
"Are you sure to delete this **API** template?"
)
) {
return;
}
templateAPIs.splice(index, 1);
setTemplateAPIs(structuredClone(templateAPIs));
}}
>
</button>
</span>
</div>
))}
</div>
</div>
)} )}
{templateAPIsWhisper.length > 0 &&
(chatStore.develop_mode ||
chatStore.history.filter((msg) => !msg.example).length == 0 ||
!chatStore.whisper_api ||
!chatStore.whisper_key) && (
<ListAPIs
label="Whisper API"
tmps={templateAPIsWhisper}
setTmps={setTemplateAPIsWhisper}
chatStore={chatStore}
setChatStore={setChatStore}
apiField="whisper_api"
keyField="whisper_key"
/>
)}
{templateAPIsTTS.length > 0 &&
(chatStore.develop_mode ||
chatStore.history.filter((msg) => !msg.example).length == 0 ||
!chatStore.tts_api ||
!chatStore.tts_key) && (
<ListAPIs
label="TTS API"
tmps={templateAPIsTTS}
setTmps={setTemplateAPIsTTS}
chatStore={chatStore}
setChatStore={setChatStore}
apiField="tts_api"
keyField="tts_key"
/>
)}
{templateAPIsImageGen.length > 0 &&
(chatStore.develop_mode ||
chatStore.history.filter((msg) => !msg.example).length == 0 ||
!chatStore.image_gen_api ||
!chatStore.image_gen_key) && (
<ListAPIs
label="Image Gen API"
tmps={templateAPIsImageGen}
setTmps={setTemplateAPIsImageGen}
chatStore={chatStore}
setChatStore={setChatStore}
apiField="image_gen_api"
keyField="image_gen_key"
/>
)}
{chatStore.history.filter((msg) => !msg.example).length == 0 && ( {chatStore.history.filter((msg) => !msg.example).length == 0 && (
<div className="break-all opacity-80 p-3 rounded bg-white my-3 text-left dark:text-black"> <div className="break-all opacity-80 p-3 rounded bg-white my-3 text-left dark:text-black">
<h2> <h2>
@@ -722,6 +773,7 @@ export default function ChatBOX(props: {
{Tr("Send")} {Tr("Send")}
</button> </button>
{chatStore.whisper_api && {chatStore.whisper_api &&
chatStore.whisper_key &&
(chatStore.whisper_key || chatStore.apiKey) && ( (chatStore.whisper_key || chatStore.apiKey) && (
<button <button
className={`disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 ${ className={`disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 ${

View File

@@ -152,10 +152,7 @@ class Chat {
frequency_penalty = 0, frequency_penalty = 0,
} = {} } = {}
) { ) {
if (OPENAI_API_KEY === undefined) { this.OPENAI_API_KEY = OPENAI_API_KEY ?? "";
throw "OPENAI_API_KEY is undefined";
}
this.OPENAI_API_KEY = OPENAI_API_KEY;
this.messages = []; this.messages = [];
this.total_tokens = calculate_token_length(systemMessage); this.total_tokens = calculate_token_length(systemMessage);
this.max_tokens = max_tokens; this.max_tokens = max_tokens;

81
src/listAPIs.tsx Normal file
View File

@@ -0,0 +1,81 @@
import { ChatStore, TemplateAPI } from "./app";
import { Tr } from "./translate";
interface Props {
chatStore: ChatStore;
setChatStore: (cs: ChatStore) => void;
tmps: TemplateAPI[];
setTmps: (tmps: TemplateAPI[]) => void;
label: string;
apiField: string;
keyField: string;
}
export function ListAPIs({
tmps,
setTmps,
chatStore,
setChatStore,
label,
apiField,
keyField,
}: Props) {
return (
<div className="break-all opacity-80 p-3 rounded bg-white my-3 text-left dark:text-black">
<h2>{Tr(`Saved ${label} templates`)}</h2>
<hr className="my-2" />
<div className="flex flex-wrap">
{tmps.map((t, index) => (
<div
className={`cursor-pointer rounded ${
// @ts-ignore
chatStore[apiField] === t.endpoint &&
// @ts-ignore
chatStore[keyField] === t.key
? "bg-red-600"
: "bg-red-400"
} w-fit p-2 m-1 flex flex-col`}
onClick={() => {
// @ts-ignore
chatStore[apiField] = t.endpoint;
// @ts-ignore
chatStore[keyField] = t.key;
setChatStore({ ...chatStore });
}}
>
<span className="w-full text-center">{t.name}</span>
<hr className="mt-2" />
<span className="flex justify-between">
<button
onClick={() => {
const name = prompt(`Give **${label}** template a name`);
if (!name) {
return;
}
t.name = name;
setTmps(structuredClone(tmps));
}}
>
🖋
</button>
<button
onClick={() => {
if (
!confirm(
`Are you sure to delete this **${label}** template?`
)
) {
return;
}
tmps.splice(index, 1);
setTmps(structuredClone(tmps));
}}
>
</button>
</span>
</div>
))}
</div>
</div>
);
}

39
src/setAPIsTemplate.tsx Normal file
View File

@@ -0,0 +1,39 @@
import { TemplateAPI } from "./app";
import { Tr } from "./translate";
interface Props {
tmps: TemplateAPI[];
setTmps: (tmps: TemplateAPI[]) => void;
label: string;
endpoint: string;
APIkey: string;
}
export function SetAPIsTemplate({
endpoint,
APIkey,
tmps,
setTmps,
label,
}: Props) {
return (
<button
className="p-2 m-2 rounded bg-blue-300"
onClick={() => {
const name = prompt(`Give this **${label}** template a name:`);
if (!name) {
alert("No template name specified");
return;
}
const tmp: TemplateAPI = {
name,
endpoint,
key: APIkey,
};
tmps.push(tmp);
setTmps([...tmps]);
}}
>
{Tr(`Save ${label}`)}
</button>
);
}

View File

@@ -6,6 +6,7 @@ import { TemplateChatStore } from "./chatbox";
import { tr, Tr, langCodeContext, LANG_OPTIONS } from "./translate"; import { tr, Tr, langCodeContext, LANG_OPTIONS } from "./translate";
import p from "preact-markdown"; import p from "preact-markdown";
import { isVailedJSON } from "./message"; import { isVailedJSON } from "./message";
import { SetAPIsTemplate } from "./setAPIsTemplate";
const TTS_VOICES: string[] = [ const TTS_VOICES: string[] = [
"alloy", "alloy",
@@ -267,6 +268,12 @@ export default (props: {
setTemplates: (templates: TemplateChatStore[]) => void; setTemplates: (templates: TemplateChatStore[]) => void;
templateAPIs: TemplateAPI[]; templateAPIs: TemplateAPI[];
setTemplateAPIs: (templateAPIs: TemplateAPI[]) => void; setTemplateAPIs: (templateAPIs: TemplateAPI[]) => void;
templateAPIsWhisper: TemplateAPI[];
setTemplateAPIsWhisper: (templateAPIs: TemplateAPI[]) => void;
templateAPIsTTS: TemplateAPI[];
setTemplateAPIsTTS: (templateAPIs: TemplateAPI[]) => void;
templateAPIsImageGen: TemplateAPI[];
setTemplateAPIsImageGen: (templateAPIs: TemplateAPI[]) => void;
}) => { }) => {
let link = let link =
location.protocol + location.protocol +
@@ -519,6 +526,43 @@ export default (props: {
{Tr("Reset")} {Tr("Reset")}
</button> </button>
</div> </div>
<div className="flex justify-evenly flex-wrap">
<SetAPIsTemplate
label="Chat API"
endpoint={props.chatStore.apiEndpoint}
APIkey={props.chatStore.apiKey}
tmps={props.templateAPIs}
setTmps={props.setTemplateAPIs}
/>
{props.chatStore.whisper_api && props.chatStore.whisper_key && (
<SetAPIsTemplate
label="Whisper API"
endpoint={props.chatStore.whisper_api}
APIkey={props.chatStore.whisper_key}
tmps={props.templateAPIsWhisper}
setTmps={props.setTemplateAPIsWhisper}
/>
)}
{props.chatStore.tts_api && props.chatStore.tts_key && (
<SetAPIsTemplate
label="TTS API"
endpoint={props.chatStore.tts_api}
APIkey={props.chatStore.tts_key}
tmps={props.templateAPIsTTS}
setTmps={props.setTemplateAPIsTTS}
/>
)}
{props.chatStore.image_gen_api && props.chatStore.image_gen_key && (
<SetAPIsTemplate
label="Image Gen API"
endpoint={props.chatStore.image_gen_api}
APIkey={props.chatStore.image_gen_key}
tmps={props.templateAPIsImageGen}
setTmps={props.setTemplateAPIsImageGen}
/>
)}
</div>
<p className="flex justify-evenly"> <p className="flex justify-evenly">
<button <button
className="p-2 m-2 rounded bg-amber-500" className="p-2 m-2 rounded bg-amber-500"
@@ -554,6 +598,12 @@ export default (props: {
// clear api because it is stored in the API template // clear api because it is stored in the API template
tmp.apiEndpoint = ""; tmp.apiEndpoint = "";
tmp.apiKey = ""; tmp.apiKey = "";
tmp.whisper_api = "";
tmp.whisper_key = "";
tmp.tts_api = "";
tmp.tts_key = "";
tmp.image_gen_api = "";
tmp.image_gen_key = "";
// @ts-ignore // @ts-ignore
tmp.name = name; tmp.name = name;
props.templates.push(tmp as TemplateChatStore); props.templates.push(tmp as TemplateChatStore);
@@ -562,25 +612,6 @@ export default (props: {
> >
{Tr("As template")} {Tr("As template")}
</button> </button>
<button
className="p-2 m-2 rounded bg-amber-500"
onClick={() => {
const name = prompt("Give this **API** template a name:");
if (!name) {
alert("No template name specified");
return;
}
const tmp: TemplateAPI = {
name,
endpoint: props.chatStore.apiEndpoint,
key: props.chatStore.apiKey,
};
props.templateAPIs.push(tmp);
props.setTemplateAPIs([...props.templateAPIs]);
}}
>
{Tr("As API Template")}
</button>
<button <button
className="p-2 m-2 rounded bg-amber-500" className="p-2 m-2 rounded bg-amber-500"
onClick={() => { onClick={() => {