Feat: more settings for ai engine & models (#611)

* may setup gpt ai engine & model

* ai models setting works

* update openai setting

* validate engine setting before save

* fail fast

* clean code

* refactor gpt preset
This commit is contained in:
an-lee
2024-05-15 15:52:07 +08:00
committed by GitHub
parent b4d7fb837e
commit 61c76006fd
16 changed files with 449 additions and 148 deletions

View File

@@ -1,5 +1,5 @@
import { ChatOpenAI } from "@langchain/openai";
import { RESPONSE_JSON_FORMAT_MODELS } from "@/constants";
import { NOT_SUPPORTE_JSON_FORMAT_MODELS } from "@/constants";
import { zodToJsonSchema } from "zod-to-json-schema";
export const jsonCommand = async (
@@ -15,7 +15,7 @@ export const jsonCommand = async (
const { key, temperature = 0, baseUrl, schema } = options;
let { modelName = "gpt-4o" } = options;
if (RESPONSE_JSON_FORMAT_MODELS.indexOf(modelName) === -1) {
if (NOT_SUPPORTE_JSON_FORMAT_MODELS.indexOf(modelName) > -1) {
modelName = "gpt-4o";
}

View File

@@ -125,16 +125,10 @@ export const PROCESS_TIMEOUT = 1000 * 60 * 15;
export const AI_GATEWAY_ENDPOINT =
"https://gateway.ai.cloudflare.com/v1/11d43ab275eb7e1b271ba4089ecc3864/enjoy";
export const RESPONSE_JSON_FORMAT_MODELS = [
"gpt-3.5-turbo-0125",
"gpt-3.5-turbo",
"gpt-3.5-turbo-1106",
"gpt-4o",
"gpt-4-turbo",
"gpt-4-turbo-2024-04-09",
"gpt-4-0125-preview",
"gpt-4-turbo-preview",
"gpt-4-1106-preview",
export const NOT_SUPPORTE_JSON_FORMAT_MODELS = [
"gpt-4-vision-preview",
"gpt-4",
"gpt-4-32k",
];
export const CONVERSATION_PRESETS = [

View File

@@ -368,6 +368,12 @@
"whisperModelIsNotWorking": "Whisper model is not working",
"relaunchIsNeededAfterChanged": "Relaunch is needed after changed",
"defaultAiEngine": "Default AI engine",
"aiEngine": "AI engine",
"defaultAiModel": "Default AI model",
"lookupAiModel": "AI Lookup",
"translateAiModel": "AI Translate",
"analyzeAiModel": "AI Analyze",
"extractStoryAiModel": "AI extract vocabulary",
"openAiEngineTips": "Use OpenAI with your own key as default AI engine.",
"enjoyAiEngineTips": "Use EnjoyAI as default AI engine. It is a paid service.",
"openaiKeySaved": "OpenAI key saved",

View File

@@ -368,6 +368,12 @@
"whisperModelIsNotWorking": "Whisper 模型无法正常工作,请尝试更换模型后重试,或联系开发者",
"relaunchIsNeededAfterChanged": "更改后需要重新启动",
"defaultAiEngine": "默认 AI 引擎",
"aiEngine": "AI 引擎",
"defaultAiModel": "默认 AI 模型",
"lookupAiModel": "智能词典模型",
"translateAiModel": "智能翻译模型",
"analyzeAiModel": "句子分析模型",
"extractStoryAiModel": "提取生词模型",
"openAiEngineTips": "使用 OpenAI 作为默认 AI 引擎,需要配置 API 密钥。",
"enjoyAiEngineTips": "使用 EnjoyAI 作为默认 AI 引擎,收费服务。",
"openaiKeySaved": "OpenAI 密钥已保存",

View File

@@ -154,6 +154,14 @@ export default {
return settings.setSync("defaultEngine", engine);
});
ipcMain.handle("settings-get-gpt-engine", (_event) => {
return settings.getSync("engine.gpt");
});
ipcMain.handle("settings-set-gpt-engine", (_event, engine) => {
return settings.setSync("engine.gpt", engine);
});
ipcMain.handle("settings-get-default-hotkeys", (_event) => {
return settings.getSync("defaultHotkeys");
});

View File

@@ -177,6 +177,12 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", {
setDefaultEngine: (engine: "enjoyai" | "openai") => {
return ipcRenderer.invoke("settings-set-default-engine", engine);
},
getGptEngine: () => {
return ipcRenderer.invoke("settings-get-gpt-engine");
},
setGptEngine: (engine: GptEngineSettingType) => {
return ipcRenderer.invoke("settings-set-gpt-engine", engine);
},
getLlm: (provider: string) => {
return ipcRenderer.invoke("settings-get-llm", provider);
},

View File

@@ -1,3 +1,6 @@
import * as z from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { t } from "i18next";
import {
Select,
@@ -6,47 +9,314 @@ import {
SelectItem,
SelectValue,
toast,
Form,
Button,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@renderer/components/ui";
import { AISettingsProviderContext } from "@renderer/context";
import { useContext } from "react";
import {
AISettingsProviderContext,
AppSettingsProviderContext,
} from "@renderer/context";
import { useContext, useEffect, useState } from "react";
import { GPT_PROVIDERS } from "@renderer/components";
export const DefaultEngineSettings = () => {
const { defaultEngine, setDefaultEngine, openai } = useContext(
const { currentEngine, setGptEngine, openai } = useContext(
AISettingsProviderContext
);
const { webApi } = useContext(AppSettingsProviderContext);
const [providers, setProviders] = useState<any>(GPT_PROVIDERS);
const [editing, setEditing] = useState(false);
const gptEngineSchema = z
.object({
name: z.enum(["enjoyai", "openai"]),
models: z.object({
default: z.string(),
lookup: z.string().optional(),
translate: z.string().optional(),
analyze: z.string().optional(),
extractStory: z.string().optional(),
}),
})
.required();
const form = useForm<z.infer<typeof gptEngineSchema>>({
resolver: zodResolver(gptEngineSchema),
values: {
name: currentEngine.name as "enjoyai" | "openai",
models: currentEngine.models || {},
},
});
const onSubmit = async (data: z.infer<typeof gptEngineSchema>) => {
const { name, models } = data;
models.default ||= providers[name].models[0];
Object.keys(models).forEach((key: keyof typeof models) => {
if (!providers[name].models.includes(models[key])) {
delete models[key];
}
});
setGptEngine(data as GptEngineSettingType);
setEditing(false);
};
useEffect(() => {
webApi
.config("gpt_providers")
.then((data) => {
setProviders(data);
})
.catch((error) => {
console.error(error);
});
}, []);
return (
<div className="flex items-start justify-between py-4">
<div className="">
<div className="flex items-center mb-2">
<span>{t("defaultAiEngine")}</span>
</div>
<div className="text-sm text-muted-foreground">
{defaultEngine === "openai" && t("openAiEngineTips")}
{defaultEngine === "enjoyai" && t("enjoyAiEngineTips")}
</div>
</div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="flex items-start justify-between py-4">
<div className="">
<div className="flex items-center mb-2">
<span>{t("defaultAiEngine")}</span>
</div>
<div className="text-sm text-muted-foreground space-y-3">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<div className="flex items-center space-x-2">
<FormLabel className="min-w-max">
{t("aiEngine")}:
</FormLabel>
<Select
value={field.value}
disabled={!editing}
onValueChange={(value) => {
if (value === "openai" && !openai?.key) {
toast.warning(t("openaiKeyRequired"));
} else {
field.onChange(value);
}
}}
>
<SelectTrigger className="min-w-fit">
<SelectValue
placeholder={t("defaultAiEngine")}
></SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="enjoyai">EnjoyAI</SelectItem>
<SelectItem value="openai">OpenAI</SelectItem>
</SelectContent>
</Select>
</div>
<FormMessage />
<div className="text-xs text-muted-foreground">
{form.watch("name") === "openai" && t("openAiEngineTips")}
{form.watch("name") === "enjoyai" &&
t("enjoyAiEngineTips")}
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="models.default"
render={({ field }) => (
<FormItem>
<div className="flex items-center space-x-2">
<FormLabel className="min-w-max">
{t("defaultAiModel")}:
</FormLabel>
<Select
value={field.value}
disabled={!editing}
onValueChange={field.onChange}
>
<SelectTrigger className="min-w-fit">
<SelectValue
placeholder={t("defaultAiModel")}
></SelectValue>
</SelectTrigger>
<SelectContent>
{providers[form.watch("name")].models.map(
(model: string) => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
</FormItem>
)}
/>
{editing && (
<>
<FormField
control={form.control}
name="models.lookup"
render={({ field }) => (
<FormItem>
<div className="flex items-center space-x-2">
<FormLabel className="min-w-max">
{t("lookupAiModel")}:
</FormLabel>
<Select
value={field.value}
disabled={!editing}
onValueChange={field.onChange}
>
<SelectTrigger className="min-w-fit">
<SelectValue
placeholder={t("leaveEmptyToUseDefault")}
></SelectValue>
</SelectTrigger>
<SelectContent>
{providers[form.watch("name")].models.map(
(model: string) => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="models.translate"
render={({ field }) => (
<FormItem>
<div className="flex items-center space-x-2">
<FormLabel className="min-w-max">
{t("translateAiModel")}:
</FormLabel>
<Select
value={field.value}
disabled={!editing}
onValueChange={field.onChange}
>
<SelectTrigger className="min-w-fit">
<SelectValue
placeholder={t("leaveEmptyToUseDefault")}
></SelectValue>
</SelectTrigger>
<SelectContent>
{providers[form.watch("name")].models.map(
(model: string) => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="models.analyze"
render={({ field }) => (
<FormItem>
<div className="flex items-center space-x-2">
<FormLabel className="min-w-max">
{t("analyzeAiModel")}:
</FormLabel>
<Select
value={field.value}
disabled={!editing}
onValueChange={field.onChange}
>
<SelectTrigger className="min-w-fit">
<SelectValue
placeholder={t("leaveEmptyToUseDefault")}
></SelectValue>
</SelectTrigger>
<SelectContent>
{providers[form.watch("name")].models.map(
(model: string) => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
</FormItem>
)}
/>
<FormField
control={form.control}
name="models.extractStory"
render={({ field }) => (
<FormItem>
<div className="flex items-center space-x-2">
<FormLabel className="min-w-max">
{t("extractStoryAiModel")}:
</FormLabel>
<Select
value={field.value}
disabled={!editing}
onValueChange={field.onChange}
>
<SelectTrigger className="min-w-fit">
<SelectValue
placeholder={t("leaveEmptyToUseDefault")}
></SelectValue>
</SelectTrigger>
<SelectContent>
{providers[form.watch("name")].models.map(
(model: string) => (
<SelectItem key={model} value={model}>
{model}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
</FormItem>
)}
/>
</>
)}
</div>
</div>
<div className="flex items-center space-x-2">
<Select
value={defaultEngine}
onValueChange={(value) => {
if (value === "openai" && !openai?.key) {
toast.warning(t("openaiKeyRequired"));
} else {
setDefaultEngine(value);
}
}}
>
<SelectTrigger className="min-w-fit">
<SelectValue placeholder={t("defaultAiEngine")}></SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="enjoyai">EnjoyAI</SelectItem>
<SelectItem value="openai">OpenAI</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="flex items-center space-x-2">
<Button
variant={editing ? "outline" : "secondary"}
size="sm"
type="reset"
onClick={(event) => {
event.preventDefault();
form.reset();
setEditing(!editing);
}}
>
{editing ? t("cancel") : t("edit")}
</Button>
<Button className={editing ? "" : "hidden"} size="sm" type="submit">
{t("save")}
</Button>
</div>
</div>
</form>
</Form>
);
};

View File

@@ -38,7 +38,7 @@ export const GoogleGenerativeAiSettings = () => {
ref={ref}
type="password"
defaultValue={googleGenerativeAi?.key}
placeholder="*********"
placeholder=""
disabled={!editing}
className="focus-visible:outline-0 focus-visible:ring-0 shadow-none"
/>

View File

@@ -8,17 +8,10 @@ import {
Form,
FormItem,
FormLabel,
FormControl,
FormMessage,
Input,
toast,
Select,
SelectTrigger,
SelectItem,
SelectValue,
SelectContent,
} from "@renderer/components/ui";
import { GPT_PROVIDERS } from "@renderer/components";
import { AISettingsProviderContext } from "@renderer/context";
import { useContext, useState } from "react";
@@ -28,7 +21,6 @@ export const OpenaiSettings = () => {
const openAiConfigSchema = z.object({
key: z.string().optional(),
model: z.enum(GPT_PROVIDERS.openai.models),
baseUrl: z.string().optional(),
});
@@ -36,7 +28,6 @@ export const OpenaiSettings = () => {
resolver: zodResolver(openAiConfigSchema),
values: {
key: openai?.key,
model: openai?.model,
baseUrl: openai?.baseUrl,
},
});
@@ -55,7 +46,7 @@ export const OpenaiSettings = () => {
<div className="flex items-start justify-between py-4">
<div className="">
<div className="mb-2">Open AI</div>
<div className="text-sm text-muted-foreground space-y-1">
<div className="text-sm text-muted-foreground space-y-3">
<FormField
control={form.control}
name="key"
@@ -66,7 +57,7 @@ export const OpenaiSettings = () => {
<Input
disabled={!editing}
type="password"
placeholder="*********"
placeholder=""
value={field.value}
onChange={field.onChange}
/>
@@ -75,45 +66,15 @@ export const OpenaiSettings = () => {
</FormItem>
)}
/>
<FormField
control={form.control}
name="model"
render={({ field }) => (
<FormItem>
<div className="flex items-center space-x-2">
<FormLabel className="min-w-max">{t("model")}:</FormLabel>
<Select
disabled={!editing}
onValueChange={field.onChange}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder={t("selectAiModel")} />
</SelectTrigger>
</FormControl>
<SelectContent>
{(GPT_PROVIDERS.openai.models || []).map(
(option: string) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="baseUrl"
render={({ field }) => (
<FormItem>
<div className="flex items-center space-x-2">
<FormLabel className="min-w-max">{t("baseUrl")}:</FormLabel>
<FormLabel className="min-w-max">
{t("baseUrl")}:
</FormLabel>
<Input
disabled={!editing}
placeholder={t("leaveEmptyToUseDefault")}

View File

@@ -29,8 +29,6 @@ export const Preferences = () => {
<div className="font-semibold mb-4 capitilized">
{t("basicSettings")}
</div>
<LibrarySettings />
<Separator />
<WhisperSettings />
<Separator />
<DefaultEngineSettings />
@@ -69,6 +67,8 @@ export const Preferences = () => {
</div>
<UserSettings />
<Separator />
<LibrarySettings />
<Separator />
<EmailSettings />
<Separator />
<BalanceSettings />

View File

@@ -10,9 +10,8 @@ type AISettingsProviderState = {
setOpenai?: (config: LlmProviderType) => void;
googleGenerativeAi?: LlmProviderType;
setGoogleGenerativeAi?: (config: LlmProviderType) => void;
defaultEngine?: string;
setDefaultEngine?: (engine: string) => void;
currentEngine?: LlmProviderType;
setGptEngine?: (engine: GptEngineSettingType) => void;
currentEngine?: GptEngineSettingType;
};
const initialState: AISettingsProviderState = {};
@@ -25,12 +24,17 @@ export const AISettingsProvider = ({
}: {
children: React.ReactNode;
}) => {
const [defaultEngine, setDefaultEngine] = useState<string>("openai");
const [gptEngine, setGptEngine] = useState<GptEngineSettingType>({
name: "enjoyai",
models: {
default: "gpt-4o",
},
});
const [openai, setOpenai] = useState<LlmProviderType>(null);
const [googleGenerativeAi, setGoogleGenerativeAi] =
useState<LlmProviderType>(null);
const [whisperConfig, setWhisperConfig] = useState<WhisperConfigType>(null);
const { EnjoyApp, apiUrl, user, libraryPath } = useContext(
const { EnjoyApp, libraryPath, user, apiUrl } = useContext(
AppSettingsProviderContext
);
@@ -79,15 +83,39 @@ export const AISettingsProvider = ({
}
const _defaultEngine = await EnjoyApp.settings.getDefaultEngine();
if (_defaultEngine) {
setDefaultEngine(_defaultEngine);
const _gptEngine = await EnjoyApp.settings.getGptEngine();
if (_gptEngine) {
setGptEngine(_gptEngine);
} else if (_defaultEngine) {
// Migrate default engine to gpt engine
const engine = {
name: _defaultEngine,
models: {
default: "gpt-4o",
},
};
EnjoyApp.settings.setGptEngine(engine).then(() => {
setGptEngine(engine);
});
} else if (_openai?.key) {
EnjoyApp.settings.setDefaultEngine("openai").then(() => {
setDefaultEngine("openai");
const engine = {
name: "openai",
models: {
default: "gpt-4o",
},
};
EnjoyApp.settings.setGptEngine(engine).then(() => {
setGptEngine(engine);
});
} else {
EnjoyApp.settings.setDefaultEngine("enjoyai").then(() => {
setDefaultEngine("enjoyai");
const engine = {
name: "enjoyai",
models: {
default: "gpt-4o",
},
};
EnjoyApp.settings.setGptEngine(engine).then(() => {
setGptEngine(engine);
});
}
};
@@ -114,20 +142,21 @@ export const AISettingsProvider = ({
return (
<AISettingsProviderContext.Provider
value={{
defaultEngine,
setDefaultEngine: (engine: "openai" | "enjoyai") => {
EnjoyApp.settings.setDefaultEngine(engine).then(() => {
setDefaultEngine(engine);
setGptEngine: (engine: GptEngineSettingType) => {
EnjoyApp.settings.setGptEngine(engine).then(() => {
setGptEngine(engine);
});
},
currentEngine: {
openai: openai,
enjoyai: {
name: "enjoyai" as LlmProviderType["name"],
key: user?.accessToken,
baseUrl: `${apiUrl}/api/ai`,
},
}[defaultEngine],
currentEngine:
gptEngine.name === "openai"
? Object.assign(gptEngine, {
key: openai.key,
baseUrl: openai.baseUrl,
})
: Object.assign(gptEngine, {
key: user?.accessToken,
baseUrl: `${apiUrl}/api/ai`,
}),
openai,
setOpenai: (config: LlmProviderType) => handleSetLlm("openai", config),
googleGenerativeAi,

View File

@@ -38,6 +38,9 @@ export const useAiCommand = () => {
return lookup;
}
const modelName =
currentEngine.models.lookup || currentEngine.models.default;
const res = await lookupCommand(
{
word,
@@ -46,12 +49,13 @@ export const useAiCommand = () => {
},
{
key: currentEngine.key,
modelName: currentEngine.model,
modelName,
baseUrl: currentEngine.baseUrl,
}
);
if (res.context_translation?.trim()) {
// Accept result from gpt-3/4 models
if (modelName.match(/^gpt-(3|4)\S*/i) && res.context_translation?.trim()) {
return webApi.updateLookup(lookup.id, {
meaning: res,
sourceId,
@@ -61,17 +65,17 @@ export const useAiCommand = () => {
};
const extractStory = async (story: StoryType) => {
return extractStoryCommand(story.content, {
const res = await extractStoryCommand(story.content, {
key: currentEngine.key,
modelName: currentEngine.model,
modelName:
currentEngine.models.extractStory || currentEngine.models.default,
baseUrl: currentEngine.baseUrl,
}).then((res) => {
const { words = [], idioms = [] } = res;
});
const { words = [], idioms = [] } = res;
return webApi.extractVocabularyFromStory(story.id, {
words,
idioms,
});
return webApi.extractVocabularyFromStory(story.id, {
words,
idioms,
});
};
@@ -81,7 +85,7 @@ export const useAiCommand = () => {
): Promise<string> => {
return translateCommand(text, {
key: currentEngine.key,
modelName: currentEngine.model,
modelName: currentEngine.models.translate || currentEngine.models.default,
baseUrl: currentEngine.baseUrl,
}).then((res) => {
if (cacheKey) {
@@ -92,22 +96,22 @@ export const useAiCommand = () => {
};
const analyzeText = async (text: string, cacheKey?: string) => {
return analyzeCommand(text, {
const res = await analyzeCommand(text, {
key: currentEngine.key,
modelName: currentEngine.model,
modelName: currentEngine.models.analyze || currentEngine.models.default,
baseUrl: currentEngine.baseUrl,
}).then((res) => {
if (cacheKey) {
EnjoyApp.cacheObjects.set(cacheKey, res);
}
return res;
});
if (cacheKey) {
EnjoyApp.cacheObjects.set(cacheKey, res);
}
return res;
};
const punctuateText = async (text: string) => {
return punctuateCommand(text, {
key: currentEngine.key,
modelName: currentEngine.model,
modelName: currentEngine.models.default,
baseUrl: currentEngine.baseUrl,
});
};
@@ -115,7 +119,7 @@ export const useAiCommand = () => {
const summarizeTopic = async (text: string) => {
return summarizeTopicCommand(text, {
key: currentEngine.key,
modelName: currentEngine.model,
modelName: currentEngine.models.default,
baseUrl: currentEngine.baseUrl,
});
};

View File

@@ -39,7 +39,7 @@ export const useConversation = () => {
configuration: {
baseURL: `${apiUrl}/api/ai`,
},
maxRetries: 2,
maxRetries: 0,
modelName: model,
temperature,
maxTokens,
@@ -55,7 +55,7 @@ export const useConversation = () => {
configuration: {
baseURL: baseUrl || openai.baseUrl,
},
maxRetries: 2,
maxRetries: 0,
modelName: model,
temperature,
maxTokens,

View File

@@ -22,7 +22,6 @@ import {
} from "@renderer/context";
import { conversationsReducer } from "@renderer/reducers";
import { CONVERSATION_PRESETS } from "@/constants";
import { set } from "lodash";
export default () => {
const [searchParams] = useSearchParams();
@@ -124,9 +123,10 @@ export default () => {
name: t("custom"),
configuration: {
type: "gpt",
engine: currentEngine?.name || "enjoyai",
engine: currentEngine.name,
model: currentEngine.models.default,
tts: {
engine: currentEngine?.name || "enjoyai",
engine: currentEngine.name,
},
},
};
@@ -137,7 +137,7 @@ export default () => {
configuration: {
type: "tts",
tts: {
engine: currentEngine?.name || "enjoyai",
engine: currentEngine.name,
},
},
};
@@ -150,12 +150,13 @@ export default () => {
presets = gptPresets;
defaultGpt.key = "custom";
defaultGpt.name = t("custom");
defaultGpt.engine = currentEngine?.name || "enjoyai";
defaultGpt.configuration.tts.engine = currentEngine?.name || "enjoyai";
defaultGpt.engine = currentEngine.name;
defaultGpt.configuration.model = currentEngine.models.default;
defaultGpt.configuration.tts.engine = currentEngine.name;
defaultGptPreset = defaultGpt;
defaultTts.engine = currentEngine?.name || "enjoyai";
defaultTts.configuration.tts.engine = currentEngine?.name || "enjoyai";
defaultTts.engine = currentEngine.name;
defaultTts.configuration.tts.engine = currentEngine.name;
defaultTtsPreset = defaultTts;
} catch (error) {
console.error(error);
@@ -166,9 +167,10 @@ export default () => {
engine: currentEngine?.name,
configuration: {
...preset.configuration,
model: currentEngine.models.default,
tts: {
...preset.configuration.tts,
engine: currentEngine?.name,
engine: currentEngine.name,
},
},
})

View File

@@ -111,7 +111,9 @@ type EnjoyAppType = {
setUser: (user: UserType) => Promise<void>;
getUserDataPath: () => Promise<string>;
getDefaultEngine: () => Promise<string>;
setDefaultEngine: (engine: "enjoyai" | "openai") => Promise<void>;
setDefaultEngine: (string) => Promise<string>;
getGptEngine: () => Promise<GptEngineSettingType>;
setGptEngine: (GptEngineSettingType) => Promise<GptEngineSettingType>;
getLlm: (provider: SupportedLlmProviderType) => Promise<LlmProviderType>;
setLlm: (
provider: SupportedLlmProviderType,

View File

@@ -161,3 +161,16 @@ type YoutubeVideoType = {
videoId: string;
duration: string;
};
type GptEngineSettingType = {
name: string;
models: {
default: string;
lookup?: string;
translate?: string;
analyze?: string;
extractStory?: string;
};
baseUrl?: string;
key?: string;
};