validate conversaton configuation & handle preset loading in poor network (#624)
This commit is contained in:
@@ -91,7 +91,11 @@
|
||||
"ttsBaseUrlDescription": "leave it blank if you don't have one",
|
||||
"notFound": "Conversation not found",
|
||||
"contentRequired": "Content required",
|
||||
"failedToGenerateResponse": "Failed to generate response, please retry"
|
||||
"failedToGenerateResponse": "Failed to generate response, please retry",
|
||||
"modelIsRequired": "Model is required",
|
||||
"ttsConfigurationsIsRequired": "TTS configurations is required",
|
||||
"ttsModelIsRequired": "TTS model is required",
|
||||
"ttsEngineIsRequired": "TTS engine is required"
|
||||
},
|
||||
"pronunciationAssessment": {
|
||||
"pronunciationScore": "Pronunciation Score",
|
||||
|
||||
@@ -91,7 +91,11 @@
|
||||
"ttsBaseUrlDescription": "留空则使用默认值",
|
||||
"notFound": "未找到对话",
|
||||
"contentRequired": "对话内容不能为空",
|
||||
"failedToGenerateResponse": "生成失败,请重试"
|
||||
"failedToGenerateResponse": "生成失败,请重试",
|
||||
"modelIsRequired": "AI 模型不能为空",
|
||||
"ttsConfigurationsIsRequired": "TTS 配置不能为空",
|
||||
"ttsModelIsRequired": "TTS 模型不能为空",
|
||||
"ttsEngineIsRequired": "TTS 引擎不能为空"
|
||||
},
|
||||
"pronunciationAssessment": {
|
||||
"pronunciationScore": "发音得分",
|
||||
|
||||
@@ -10,10 +10,12 @@ import {
|
||||
HasMany,
|
||||
DataType,
|
||||
AllowNull,
|
||||
BeforeSave,
|
||||
} from "sequelize-typescript";
|
||||
import { Message, Speech } from "@main/db/models";
|
||||
import mainWindow from "@main/window";
|
||||
import log from "@main/logger";
|
||||
import { t } from "i18next";
|
||||
|
||||
const logger = log.scope("db/models/conversation");
|
||||
@Table({
|
||||
@@ -64,6 +66,22 @@ export class Conversation extends Model<Conversation> {
|
||||
@HasMany(() => Message)
|
||||
messages: Message[];
|
||||
|
||||
@BeforeSave
|
||||
static validateConfiguration(conversation: Conversation) {
|
||||
if (!conversation.model) throw new Error(t("models.conversation.modelIsRequired"));
|
||||
if (conversation.type === 'tts') {
|
||||
if (!conversation.configuration.tts) {
|
||||
throw new Error(t("models.conversation.ttsConfigurationIsRequired"))
|
||||
}
|
||||
if (!conversation.configuration.tts.engine) {
|
||||
throw new Error(t("models.conversation.ttsEngineIsRequired"))
|
||||
}
|
||||
if (!conversation.configuration.tts.model) {
|
||||
throw new Error("models.conversation.ttsModelIsRequired")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AfterCreate
|
||||
static notifyForCreate(conversation: Conversation) {
|
||||
this.notify(conversation, "create");
|
||||
|
||||
@@ -74,8 +74,8 @@ export const ConversationForm = (props: {
|
||||
}) => {
|
||||
const { conversation, onFinish } = props;
|
||||
const [submitting, setSubmitting] = useState<boolean>(false);
|
||||
const [gptProviders, setGptProviders] = useState<any>([]);
|
||||
const [ttsProviders, setTtsProviders] = useState<any>([]);
|
||||
const [gptProviders, setGptProviders] = useState<any>(GPT_PROVIDERS);
|
||||
const [ttsProviders, setTtsProviders] = useState<any>(TTS_PROVIDERS);
|
||||
const { EnjoyApp, webApi } = useContext(AppSettingsProviderContext);
|
||||
const { openai } = useContext(AISettingsProviderContext);
|
||||
const navigate = useNavigate();
|
||||
@@ -234,15 +234,6 @@ export const ConversationForm = (props: {
|
||||
}
|
||||
});
|
||||
|
||||
if (configuration.type === "tts") {
|
||||
if (!configuration.tts?.engine) {
|
||||
throw new Error(t("models.conversation.ttsEngineRequired"));
|
||||
}
|
||||
if (!configuration.tts?.model) {
|
||||
throw new Error(t("models.conversation.ttsModelRequired"));
|
||||
}
|
||||
}
|
||||
|
||||
// use default base url if not set
|
||||
if (!configuration.baseUrl) {
|
||||
configuration.baseUrl = gptProviders[engine]?.baseUrl;
|
||||
|
||||
@@ -11,7 +11,11 @@ import {
|
||||
ScrollArea,
|
||||
toast,
|
||||
} from "@renderer/components/ui";
|
||||
import { ConversationCard, ConversationForm } from "@renderer/components";
|
||||
import {
|
||||
ConversationCard,
|
||||
ConversationForm,
|
||||
LoaderSpin,
|
||||
} from "@renderer/components";
|
||||
import { useState, useEffect, useContext, useReducer } from "react";
|
||||
import { ChevronLeftIcon, LoaderIcon } from "lucide-react";
|
||||
import { Link, useNavigate, useSearchParams } from "react-router-dom";
|
||||
@@ -25,13 +29,6 @@ import { CONVERSATION_PRESETS } from "@/constants";
|
||||
|
||||
export default () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [creating, setCreating] = useState<boolean>(false);
|
||||
const [preset, setPreset] = useState<any>({});
|
||||
const [config, setConfig] = useState<any>({
|
||||
gptPresets: [],
|
||||
customPreset: {},
|
||||
ttsPreset: {},
|
||||
});
|
||||
const { addDblistener, removeDbListener } = useContext(DbProviderContext);
|
||||
const { EnjoyApp, webApi } = useContext(AppSettingsProviderContext);
|
||||
const { currentEngine } = useContext(AISettingsProviderContext);
|
||||
@@ -39,6 +36,23 @@ export default () => {
|
||||
conversationsReducer,
|
||||
[]
|
||||
);
|
||||
const [creating, setCreating] = useState<boolean>(false);
|
||||
const [preset, setPreset] = useState<any>({});
|
||||
const [config, setConfig] = useState<any>({
|
||||
gptPresets: [],
|
||||
customPreset: {},
|
||||
ttsPreset: {
|
||||
key: "tts",
|
||||
name: "TTS",
|
||||
engine: currentEngine.name,
|
||||
configuration: {
|
||||
type: "tts",
|
||||
tts: {
|
||||
engine: currentEngine.name,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const [hasMore, setHasMore] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const navigate = useNavigate();
|
||||
@@ -218,6 +232,8 @@ export default () => {
|
||||
{t("chooseFromPresetGpts")}
|
||||
</div>
|
||||
<ScrollArea className="h-64 pr-4">
|
||||
{config.gptPresets.length === 0 && <LoaderSpin />}
|
||||
|
||||
{config.gptPresets.map((preset: any) => (
|
||||
<DialogTrigger
|
||||
key={preset.key}
|
||||
|
||||
Reference in New Issue
Block a user