@@ -21,6 +21,7 @@
|
||||
"added": "Successfully added audio",
|
||||
"removed": "Successfully removed audio",
|
||||
"notFound": "Video not found",
|
||||
"fileTooLarge": "File too large {{ file }}",
|
||||
"failedToAdd": "Failed to add audio, {{ error }}",
|
||||
"fileNotFound": "File not found {{file}}",
|
||||
"fileNotSupported": "File not supported {{file}}",
|
||||
@@ -45,6 +46,7 @@
|
||||
"added": "Successfully added video",
|
||||
"removed": "Successfully removed video",
|
||||
"notFound": "Video not found",
|
||||
"fileTooLarge": "File too large {{ file }}",
|
||||
"failedToAdd": "Failed to add video, {{ error }}",
|
||||
"fileNotFound": "File not found {{file}}",
|
||||
"fileNotSupported": "File not supported {{file}}",
|
||||
@@ -82,6 +84,7 @@
|
||||
"ttsEngine": "TTS engine",
|
||||
"ttsModel": "TTS model",
|
||||
"ttsVoice": "TTS voice",
|
||||
"ttsBaseUrl": "TTS base URL",
|
||||
"notFound": "Conversation not found",
|
||||
"contentRequired": "Content required"
|
||||
},
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"added": "成功添加音频",
|
||||
"removed": "成功删除音频",
|
||||
"notFound": "未找到音频",
|
||||
"fileTooLarge": "文件太大了 {{ file }}",
|
||||
"failedToAdd": "添加音频失败, {{error}}",
|
||||
"fileNotFound": "无法访问文件 {{file}}",
|
||||
"fileNotSupported": "文件不支持 {{file}}",
|
||||
@@ -45,6 +46,7 @@
|
||||
"added": "成功添加视频",
|
||||
"removed": "成功删除视频",
|
||||
"notFound": "未找到视频",
|
||||
"fileTooLarge": "文件太大了 {{ file }}",
|
||||
"failedToAdd": "添加视频失败, {{error}}",
|
||||
"fileNotFound": "无法访问文件 {{file}}",
|
||||
"fileNotSupported": "文件不支持 {{file}}",
|
||||
@@ -82,6 +84,7 @@
|
||||
"ttsEngine": "TTS 引擎",
|
||||
"ttsModel": "TTS 模型",
|
||||
"ttsVoice": "TTS 声音",
|
||||
"ttsBaseUrl": "TTS 请求地址",
|
||||
"notFound": "未找到对话",
|
||||
"contentRequired": "对话内容不能为空"
|
||||
},
|
||||
|
||||
@@ -29,6 +29,8 @@ import webApi from "@main/web-api";
|
||||
import { startCase } from "lodash";
|
||||
import { v5 as uuidv5 } from "uuid";
|
||||
|
||||
const SIZE_LIMIT = 1024 * 1024 * 50; // 50MB
|
||||
|
||||
const logger = log.scope("db/models/audio");
|
||||
@Table({
|
||||
modelName: "Audio",
|
||||
@@ -240,6 +242,11 @@ export class Audio extends Model<Audio> {
|
||||
throw new Error(t("models.audio.fileNotSupported", { file: filePath }));
|
||||
}
|
||||
|
||||
const stats = fs.statSync(filePath);
|
||||
if (stats.size > SIZE_LIMIT) {
|
||||
throw new Error(t("models.audio.fileTooLarge", { file: filePath }));
|
||||
}
|
||||
|
||||
const md5 = await hashFile(filePath, { algo: "md5" });
|
||||
|
||||
// Generate ID
|
||||
|
||||
@@ -161,6 +161,7 @@ export class Speech extends Model<Speech> {
|
||||
engine = "openai",
|
||||
model = "tts-1",
|
||||
voice = "alloy",
|
||||
baseUrl,
|
||||
} = configuration || {};
|
||||
|
||||
logger.debug("Generating speech", { engine, model, voice });
|
||||
@@ -176,7 +177,9 @@ export class Speech extends Model<Speech> {
|
||||
}
|
||||
const openai = new OpenAI({
|
||||
apiKey: key,
|
||||
baseURL: baseUrl,
|
||||
});
|
||||
logger.debug("baseURL", openai.baseURL);
|
||||
|
||||
const file = await openai.audio.speech.create({
|
||||
input: text,
|
||||
|
||||
@@ -29,6 +29,8 @@ import webApi from "@main/web-api";
|
||||
import { startCase } from "lodash";
|
||||
import { v5 as uuidv5 } from "uuid";
|
||||
|
||||
const SIZE_LIMIT = 1024 * 1024 * 100; // 100MB
|
||||
|
||||
const logger = log.scope("db/models/video");
|
||||
@Table({
|
||||
modelName: "Video",
|
||||
@@ -262,6 +264,11 @@ export class Video extends Model<Video> {
|
||||
throw new Error(t("models.video.fileNotSupported", { file: filePath }));
|
||||
}
|
||||
|
||||
const stats = fs.statSync(filePath);
|
||||
if (stats.size > SIZE_LIMIT) {
|
||||
throw new Error(t("models.video.fileTooLarge", { file: filePath }));
|
||||
}
|
||||
|
||||
const md5 = await hashFile(filePath, { algo: "md5" });
|
||||
|
||||
// Generate ID
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Link } from "react-router-dom";
|
||||
import { cn } from "@renderer/lib/utils";
|
||||
|
||||
export const AudioCard = (props: {
|
||||
audio: ResourceType;
|
||||
audio: AudioType;
|
||||
className?: string;
|
||||
}) => {
|
||||
const { audio, className } = props;
|
||||
|
||||
@@ -53,6 +53,7 @@ const conversationFormSchema = z.object({
|
||||
engine: z.enum(["openai"]).default("openai"),
|
||||
model: z.string().default("tts-1"),
|
||||
voice: z.string().optional(),
|
||||
baseUrl: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
@@ -102,6 +103,9 @@ export const ConversationForm = (props: {
|
||||
engine: conversation.engine,
|
||||
configuration: {
|
||||
...conversation.configuration,
|
||||
tts: {
|
||||
...conversation.configuration?.tts,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {
|
||||
@@ -128,6 +132,10 @@ export const ConversationForm = (props: {
|
||||
configuration.baseUrl = LLM_PROVIDERS[engine]?.baseUrl;
|
||||
}
|
||||
|
||||
if (!configuration.tts.baseUrl) {
|
||||
configuration.tts.baseUrl = LLM_PROVIDERS[engine]?.baseUrl;
|
||||
}
|
||||
|
||||
if (conversation?.id) {
|
||||
EnjoyApp.conversations
|
||||
.update(conversation.id, {
|
||||
@@ -547,6 +555,21 @@ export const ConversationForm = (props: {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.tts.baseUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("models.conversation.ttsBaseUrl")}</FormLabel>
|
||||
<Input {...field} />
|
||||
<FormDescription>
|
||||
{t("models.conversation.baseUrl")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
@@ -584,7 +607,9 @@ export const ConversationForm = (props: {
|
||||
)}
|
||||
|
||||
<Button
|
||||
disabled={submitting || !form.formState.isDirty}
|
||||
disabled={
|
||||
submitting || (conversation.id && !form.formState.isDirty)
|
||||
}
|
||||
className="w-full h-12"
|
||||
size="lg"
|
||||
type="submit"
|
||||
@@ -686,8 +711,9 @@ const conversationDefaultConfiguration = {
|
||||
maxTokens: 2048,
|
||||
presencePenalty: 0,
|
||||
frequencyPenalty: 0,
|
||||
historyBufferSize: 10,
|
||||
historyBufferSize: 0,
|
||||
tts: {
|
||||
baseUrl: "",
|
||||
engine: "openai",
|
||||
model: "tts-1",
|
||||
voice: "alloy",
|
||||
|
||||
@@ -54,6 +54,7 @@ export const AssistantMessageComponent = (props: {
|
||||
engine: configuration?.tts?.engine,
|
||||
model: configuration?.tts?.model,
|
||||
voice: configuration?.tts?.voice,
|
||||
baseUrl: configuration?.tts?.baseUrl,
|
||||
})
|
||||
.then((speech) => {
|
||||
setSpeech(speech);
|
||||
|
||||
@@ -104,6 +104,7 @@ export const WhisperModelOptions = () => {
|
||||
}
|
||||
if (state.state === "completed") {
|
||||
model.downloaded = true;
|
||||
setWhisperModel(model.name);
|
||||
EnjoyApp.download.removeAllListeners();
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,15 @@ export default () => {
|
||||
|
||||
{conversations.map((conversation) => (
|
||||
<Link key={conversation.id} to={`/conversations/${conversation.id}`}>
|
||||
<div className="bg-white text-primary rounded-full w-full mb-2 p-4 hover:bg-primary hover:text-white cursor-pointer flex items-center">
|
||||
<div
|
||||
className="bg-white text-primary rounded-full w-full mb-2 p-4 hover:bg-primary hover:text-white cursor-pointer flex items-center"
|
||||
style={{
|
||||
borderLeftColor: `#${conversation.id
|
||||
.replaceAll("-", "")
|
||||
.substr(0, 6)}`,
|
||||
borderLeftWidth: 3,
|
||||
}}
|
||||
>
|
||||
<div className="">
|
||||
<MessageCircleIcon className="mr-2" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user