mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 17:04:27 +08:00
feat(config): enhance model configuration with support for audio models and improve UI for user preferences
This commit is contained in:
@@ -15,12 +15,17 @@ export type AiConfig = {
|
||||
imageModel: string;
|
||||
videoModel: string;
|
||||
textModel: string;
|
||||
audioModel: string;
|
||||
videoSeconds: string;
|
||||
vquality: string;
|
||||
videoGenerateAudio: string;
|
||||
videoWatermark: string;
|
||||
systemPrompt: string;
|
||||
models: string[];
|
||||
imageModels: string[];
|
||||
videoModels: string[];
|
||||
textModels: string[];
|
||||
audioModels: string[];
|
||||
quality: string;
|
||||
size: string;
|
||||
count: string;
|
||||
@@ -28,7 +33,7 @@ export type AiConfig = {
|
||||
};
|
||||
|
||||
export const CONFIG_STORE_KEY = "infinite-canvas:ai_config_store";
|
||||
export type ModelCapability = "image" | "video" | "text";
|
||||
export type ModelCapability = "image" | "video" | "text" | "audio";
|
||||
|
||||
export const defaultConfig: AiConfig = {
|
||||
channelMode: "local",
|
||||
@@ -38,12 +43,17 @@ export const defaultConfig: AiConfig = {
|
||||
imageModel: "gpt-image-2",
|
||||
videoModel: "grok-imagine-video",
|
||||
textModel: "gpt-5.5",
|
||||
audioModel: "",
|
||||
videoSeconds: "6",
|
||||
vquality: "720",
|
||||
videoGenerateAudio: "true",
|
||||
videoWatermark: "false",
|
||||
systemPrompt: "",
|
||||
models: [],
|
||||
imageModels: [],
|
||||
videoModels: [],
|
||||
textModels: [],
|
||||
audioModels: [],
|
||||
quality: "auto",
|
||||
size: "1:1",
|
||||
count: "1",
|
||||
@@ -71,18 +81,25 @@ function resolveEffectiveConfig(config: AiConfig, modelChannel: AdminPublicSetti
|
||||
const textModels = filterModelsByCapability(models, "text");
|
||||
const imageModels = filterModelsByCapability(models, "image");
|
||||
const videoModels = filterModelsByCapability(models, "video");
|
||||
const audioModels = filterModelsByCapability(models, "audio");
|
||||
const fallbackTextModel = validDefault(modelChannel.defaultTextModel, textModels) || preferredModel(textModels, isTextModelName);
|
||||
const fallbackModel = validDefault(modelChannel.defaultModel, textModels) || fallbackTextModel;
|
||||
const fallbackImageModel = validDefault(modelChannel.defaultImageModel, imageModels) || preferredModel(imageModels, isImageModelName);
|
||||
const fallbackVideoModel = validDefault(modelChannel.defaultVideoModel, videoModels) || preferredModel(videoModels, isVideoModelName);
|
||||
const fallbackAudioModel = preferredModel(audioModels, isAudioModelName);
|
||||
return {
|
||||
...config,
|
||||
channelMode,
|
||||
models,
|
||||
imageModels,
|
||||
videoModels,
|
||||
textModels,
|
||||
audioModels,
|
||||
model: textModels.includes(config.model) ? config.model : fallbackModel,
|
||||
imageModel: imageModels.includes(config.imageModel) ? config.imageModel : fallbackImageModel,
|
||||
videoModel: videoModels.includes(config.videoModel) ? config.videoModel : fallbackVideoModel,
|
||||
textModel: textModels.includes(config.textModel) ? config.textModel : fallbackTextModel || fallbackModel,
|
||||
audioModel: audioModels.includes(config.audioModel) ? config.audioModel : fallbackAudioModel,
|
||||
systemPrompt: modelChannel.systemPrompt,
|
||||
};
|
||||
}
|
||||
@@ -102,17 +119,23 @@ function isVideoModelName(model: string) {
|
||||
|
||||
function isImageModelName(model: string) {
|
||||
const value = model.toLowerCase();
|
||||
return !isVideoModelName(model) && (value.includes("seedream") || value.includes("gpt-image") || value.includes("image") || value.includes("dall-e") || value.includes("dalle") || value.includes("imagen") || value.includes("flux") || value.includes("sdxl") || value.includes("stable-diffusion") || value.includes("midjourney"));
|
||||
return !isVideoModelName(model) && !isAudioModelName(model) && (value.includes("seedream") || value.includes("gpt-image") || value.includes("image") || value.includes("dall-e") || value.includes("dalle") || value.includes("imagen") || value.includes("flux") || value.includes("sdxl") || value.includes("stable-diffusion") || value.includes("midjourney"));
|
||||
}
|
||||
|
||||
function isAudioModelName(model: string) {
|
||||
const value = model.toLowerCase();
|
||||
return value.includes("audio") || value.includes("tts") || value.includes("speech") || value.includes("whisper") || value.includes("transcribe") || value.includes("voice") || value.includes("music") || value.includes("sound");
|
||||
}
|
||||
|
||||
function isTextModelName(model: string) {
|
||||
return !isImageModelName(model) && !isVideoModelName(model);
|
||||
return !isImageModelName(model) && !isVideoModelName(model) && !isAudioModelName(model);
|
||||
}
|
||||
|
||||
export function modelMatchesCapability(model: string, capability?: ModelCapability) {
|
||||
if (!capability) return true;
|
||||
if (capability === "image") return isImageModelName(model);
|
||||
if (capability === "video") return isVideoModelName(model);
|
||||
if (capability === "audio") return isAudioModelName(model);
|
||||
return isTextModelName(model);
|
||||
}
|
||||
|
||||
@@ -120,6 +143,15 @@ export function filterModelsByCapability(models: string[], capability?: ModelCap
|
||||
return capability ? models.filter((model) => modelMatchesCapability(model, capability)) : models;
|
||||
}
|
||||
|
||||
export function selectableModelsByCapability(config: AiConfig, capability?: ModelCapability) {
|
||||
if (!capability) return config.models;
|
||||
return config[modelListKey(capability)];
|
||||
}
|
||||
|
||||
function modelListKey(capability: ModelCapability) {
|
||||
return `${capability}Models` as "imageModels" | "videoModels" | "textModels" | "audioModels";
|
||||
}
|
||||
|
||||
function isAiConfigReady(config: AiConfig, model: string) {
|
||||
return Boolean(model.trim()) && (config.channelMode === "remote" || Boolean(config.baseUrl.trim() && config.apiKey.trim()));
|
||||
}
|
||||
@@ -157,13 +189,37 @@ export const useConfigStore = create<ConfigStore>()(
|
||||
name: CONFIG_STORE_KEY,
|
||||
partialize: (state) => ({ config: state.config }),
|
||||
merge: (persisted, current) => {
|
||||
const config = { ...defaultConfig, ...((persisted as Partial<ConfigStore>).config || {}) };
|
||||
return { ...current, config: { ...config, channelMode: config.channelMode || "remote", imageModel: config.imageModel || config.model, videoModel: config.videoModel || "grok-imagine-video", textModel: config.textModel || config.model, videoSeconds: config.videoSeconds || "6", vquality: config.vquality || "720", videoGenerateAudio: config.videoGenerateAudio || "true", videoWatermark: config.videoWatermark || "false", canvasImageCount: config.canvasImageCount || "3" } };
|
||||
const persistedConfig = ((persisted as Partial<ConfigStore>).config || {}) as Partial<AiConfig>;
|
||||
const config = { ...defaultConfig, ...persistedConfig };
|
||||
return {
|
||||
...current,
|
||||
config: {
|
||||
...config,
|
||||
channelMode: config.channelMode || "remote",
|
||||
imageModel: config.imageModel || config.model,
|
||||
videoModel: config.videoModel || "grok-imagine-video",
|
||||
textModel: config.textModel || config.model,
|
||||
audioModel: config.audioModel || "",
|
||||
videoSeconds: config.videoSeconds || "6",
|
||||
vquality: config.vquality || "720",
|
||||
videoGenerateAudio: config.videoGenerateAudio || "true",
|
||||
videoWatermark: config.videoWatermark || "false",
|
||||
canvasImageCount: config.canvasImageCount || "3",
|
||||
imageModels: Array.isArray(persistedConfig.imageModels) ? normalizeModelList(config.imageModels) : filterModelsByCapability(config.models, "image"),
|
||||
videoModels: Array.isArray(persistedConfig.videoModels) ? normalizeModelList(config.videoModels) : filterModelsByCapability(config.models, "video"),
|
||||
textModels: Array.isArray(persistedConfig.textModels) ? normalizeModelList(config.textModels) : filterModelsByCapability(config.models, "text"),
|
||||
audioModels: Array.isArray(persistedConfig.audioModels) ? normalizeModelList(config.audioModels) : filterModelsByCapability(config.models, "audio"),
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
function normalizeModelList(models: string[]) {
|
||||
return Array.from(new Set((models || []).map((model) => model.trim()).filter(Boolean)));
|
||||
}
|
||||
|
||||
export function useEffectiveConfig() {
|
||||
const config = useConfigStore((state) => state.config);
|
||||
const modelChannel = useConfigStore((state) => state.publicSettings?.modelChannel || null);
|
||||
|
||||
Reference in New Issue
Block a user