mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 17:58:55 +00:00
some "optimization"? not convinced this is better
This commit is contained in:
1 parent
5ca570e8af
commit
690d00aa18
8 files changed
+1236
-1221
No files matched your search
@@ -14,6 +14,12 @@ import { GenerationProvider } from "@storyteller/api-enums";
|
||||
import { Tooltip } from "@storyteller/ui-tooltip";
|
||||
import { defaultModelForPage } from "./defaultModelForPage";
|
||||
|
||||
// Model instances are rebuilt when the backend listing hydrates, so compare by
|
||||
// tauriId rather than object identity.
|
||||
const isSameModel = (a: Model | undefined, b: Model | undefined): boolean =>
|
||||
a !== undefined && b !== undefined && a.tauriId === b.tauriId;
|
||||
|
||||
|
||||
interface ClassyModelSelectorProps {
|
||||
items: Omit<PopoverItem, "selected">[];
|
||||
page: ModelPage;
|
||||
@@ -175,7 +181,7 @@ export function ClassyModelSelector({
|
||||
|
||||
return {
|
||||
...item,
|
||||
selected: item.model === selectedModel,
|
||||
selected: isSameModel(item.model as Model | undefined, selectedModel),
|
||||
hoverTooltip: hasMultipleProviders
|
||||
? (close: () => void) => (
|
||||
<ProviderTooltipContent
|
||||
@@ -190,7 +196,7 @@ export function ClassyModelSelector({
|
||||
: undefined,
|
||||
tooltipDelayMs: providerTooltipDelayMs,
|
||||
trailing:
|
||||
item.model !== selectedModel && hasMultipleProviders
|
||||
!isSameModel(item.model as Model | undefined, selectedModel) && hasMultipleProviders
|
||||
? (() => {
|
||||
const prov = modelId
|
||||
? selectedProvidersByModel[modelId]
|
||||
@@ -206,7 +212,7 @@ export function ClassyModelSelector({
|
||||
})()
|
||||
: undefined,
|
||||
selectedRight:
|
||||
item.model === selectedModel &&
|
||||
isSameModel(item.model as Model | undefined, selectedModel) &&
|
||||
selectedProvider &&
|
||||
hasMultipleProviders ? (
|
||||
<div className="mr-1 rounded-md p-1.5 bg-primary/60 group-hover:bg-primary/80 transition-colors">
|
||||
@@ -265,7 +271,7 @@ export function ClassyModelSelector({
|
||||
{...popoverProps}
|
||||
buttonClassName="rounded-xl bg-ui-controls/90 hover:bg-ui-controls text-left shadow-sm px-3 py-1 gap-3 border border-ui-controls-border"
|
||||
renderTrigger={(selectedItem) => {
|
||||
const modelTitle = selectedItem?.label ?? "";
|
||||
const modelTitle = selectedItem?.label ?? selectedModel?.selectorName ?? "";
|
||||
const providerIcon = selectedProvider
|
||||
? getProviderIcon(selectedProvider)
|
||||
: null;
|
||||
|
||||
@@ -24,3 +24,5 @@ export * from "./lib/classes/Object3DModel.js";
|
||||
export * from "./lib/lists/Object3DModels.js";
|
||||
export * from "./lib/loader/modelReconciliation.js";
|
||||
export * from "./lib/loader/buildModelsFromListing.js";
|
||||
export * from "./lib/loader/imageModelFeatures.js";
|
||||
export * from "./lib/loader/videoModelFeatures.js";
|
||||
@@ -1,594 +1,89 @@
|
||||
import { GenerationProvider } from "@storyteller/api-enums";
|
||||
// Static image model overlay, assembled from the per-feature lookup
|
||||
// functions in `../loader/imageModelFeatures.ts`. At runtime the backend
|
||||
// omni listing drives membership + capabilities (`buildModelsFromListing.ts`);
|
||||
// these instances only contribute frontend-only data (presentation, page
|
||||
// flags, providers) and capability fallbacks.
|
||||
|
||||
import { ImageModel } from "../classes/ImageModel.js";
|
||||
import {
|
||||
KNOWN_IMAGE_TAURI_IDS,
|
||||
imageAspectRatios,
|
||||
imageCanEditAngles,
|
||||
imageCanEditImages,
|
||||
imageCanTextToImage,
|
||||
imageCanUseImagePrompt,
|
||||
imageCreator,
|
||||
imageDefaultAspectRatio,
|
||||
imageDefaultGenerationCount,
|
||||
imageDefaultQuality,
|
||||
imageDefaultResolution,
|
||||
imageEditingIsInpainting,
|
||||
imageFullName,
|
||||
imageLegacyId,
|
||||
imageMaxGenerationCount,
|
||||
imageMaxImagePromptCount,
|
||||
imageMaxPromptLength,
|
||||
imagePredefinedGenerationCounts,
|
||||
imageProgressBarTime,
|
||||
imageProviders,
|
||||
imageQualityOptions,
|
||||
imageResolutions,
|
||||
imageSelectorBadges,
|
||||
imageSelectorDescription,
|
||||
imageSelectorName,
|
||||
imageTags,
|
||||
imageUsesInpaintingMask,
|
||||
} from "../loader/imageModelFeatures.js";
|
||||
import { ModelCreator } from "../classes/metadata/ModelCreator.js";
|
||||
import { ModelTag } from "../classes/metadata/ModelTag.js";
|
||||
import { CommonAspectRatio } from "../classes/properties/CommonAspectRatio.js";
|
||||
import { CommonResolution } from "../classes/properties/CommonResolution.js";
|
||||
import { CommonQuality } from "../classes/properties/CommonQuality.js";
|
||||
|
||||
// TODO: Some of the model configs, such as generation counts, are authoritatively controlled in `legacy/Models.ts`
|
||||
const buildStaticImageModel = (tauriId: string): ImageModel =>
|
||||
new ImageModel({
|
||||
// Identity.
|
||||
id: imageLegacyId(tauriId) ?? tauriId,
|
||||
tauriId,
|
||||
fullName: imageFullName(tauriId) ?? tauriId,
|
||||
category: "image",
|
||||
creator: imageCreator(tauriId) ?? ModelCreator.ArtCraft,
|
||||
|
||||
export const IMAGE_MODELS: ImageModel[] = [
|
||||
new ImageModel({
|
||||
id: "grok_image",
|
||||
tauriId: "grok_imagine_image",
|
||||
fullName: "Grok",
|
||||
category: "image",
|
||||
creator: ModelCreator.Grok,
|
||||
providers: [GenerationProvider.Grok],
|
||||
selectorName: "Grok",
|
||||
selectorDescription: "Fast af",
|
||||
selectorBadges: ["10 sec."],
|
||||
// NB: Grok only supports 6 images at a time
|
||||
defaultGenerationCount: 6,
|
||||
maxGenerationCount: 6,
|
||||
predefinedGenerationCounts: [6],
|
||||
canUseImagePrompt: false,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 10000,
|
||||
maxPromptLength: 8000,
|
||||
// Presentation.
|
||||
selectorName:
|
||||
imageSelectorName(tauriId) ?? imageFullName(tauriId) ?? tauriId,
|
||||
selectorDescription: imageSelectorDescription(tauriId) ?? "",
|
||||
selectorBadges: imageSelectorBadges(tauriId) ?? [],
|
||||
tags: imageTags(tauriId),
|
||||
progressBarTime: imageProgressBarTime(tauriId),
|
||||
|
||||
// Desktop-native provider knowledge.
|
||||
providers: imageProviders(tauriId),
|
||||
|
||||
// Page-subsetting flags.
|
||||
canTextToImage: imageCanTextToImage(tauriId),
|
||||
canEditImages: imageCanEditImages(tauriId),
|
||||
usesInpaintingMask: imageUsesInpaintingMask(tauriId),
|
||||
editingIsInpainting: imageEditingIsInpainting(tauriId),
|
||||
canEditAngles: imageCanEditAngles(tauriId),
|
||||
|
||||
// Capability fallbacks (the API overrides these when it knows better).
|
||||
maxPromptLength: imageMaxPromptLength(tauriId),
|
||||
maxGenerationCount: imageMaxGenerationCount(tauriId) ?? 4,
|
||||
defaultGenerationCount: imageDefaultGenerationCount(tauriId) ?? 1,
|
||||
predefinedGenerationCounts: imagePredefinedGenerationCounts(tauriId),
|
||||
canUseImagePrompt: imageCanUseImagePrompt(tauriId),
|
||||
maxImagePromptCount: imageMaxImagePromptCount(tauriId),
|
||||
// Every current overlay entry enables aspect ratio switching.
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.Square,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.WideThreeByTwo,
|
||||
CommonAspectRatio.TallTwoByThree,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "midjourney",
|
||||
tauriId: "midjourney",
|
||||
fullName: "Midjourney",
|
||||
category: "image",
|
||||
creator: ModelCreator.Midjourney,
|
||||
providers: [GenerationProvider.Midjourney],
|
||||
selectorName: "Midjourney",
|
||||
selectorDescription: "Stunning style and quality",
|
||||
selectorBadges: ["45 sec."],
|
||||
// NB: Midjourney only supports 4 images at a time
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 4,
|
||||
predefinedGenerationCounts: [4],
|
||||
canUseImagePrompt: false,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 45000,
|
||||
maxPromptLength: 6000,
|
||||
canChangeAspectRatio: true,
|
||||
// TODO: Rust client doesn't support aspect ratios for MidJourney yet.
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "flux_pro_1_1_ultra",
|
||||
tauriId: "flux_pro_1p1_ultra",
|
||||
fullName: "Flux Pro 1.1 Ultra",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
selectorName: "Flux Pro 1.1 Ultra",
|
||||
selectorDescription: "Higher quality model",
|
||||
selectorBadges: ["35 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 4,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 35000,
|
||||
maxPromptLength: 4000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.Square,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.WideThreeByTwo,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.WideTwentyOneByNine,
|
||||
CommonAspectRatio.TallTwoByThree,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
CommonAspectRatio.TallNineByTwentyOne,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "flux_pro_1_1",
|
||||
tauriId: "flux_pro_1p1",
|
||||
fullName: "Flux Pro 1.1",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
selectorName: "Flux Pro 1.1",
|
||||
selectorDescription: "High quality model",
|
||||
selectorBadges: ["10 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 4,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 10000,
|
||||
maxPromptLength: 4000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.Square,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "flux_1_dev",
|
||||
tauriId: "flux_1_dev",
|
||||
fullName: "Flux 1 Dev",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
providers: [
|
||||
GenerationProvider.Artcraft,
|
||||
GenerationProvider.Fal,
|
||||
],
|
||||
selectorName: "Flux 1 Dev",
|
||||
selectorDescription: "Fast, but lower quality",
|
||||
selectorBadges: ["10 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 4,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 10000,
|
||||
maxPromptLength: 4000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.Square,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "flux_1_schnell",
|
||||
tauriId: "flux_1_schnell",
|
||||
fullName: "Flux 1 Schnell",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
providers: [
|
||||
GenerationProvider.Artcraft,
|
||||
GenerationProvider.Fal,
|
||||
],
|
||||
selectorName: "Flux 1 Schnell",
|
||||
selectorDescription: "Fastest image gen, but lowest quality",
|
||||
selectorBadges: ["10 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 4,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 10000,
|
||||
maxPromptLength: 4000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.Square,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "nano_banana",
|
||||
tauriId: "nano_banana",
|
||||
fullName: "Nano Banana",
|
||||
category: "image",
|
||||
creator: ModelCreator.Google,
|
||||
selectorName: "Nano Banana",
|
||||
selectorDescription: "Fast instructive editing",
|
||||
selectorBadges: ["25 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
canTextToImage: true,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
progressBarTime: 25000,
|
||||
maxPromptLength: Infinity,
|
||||
// canChangeResolution: true,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Auto,
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.WideFiveByFour,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideThreeByTwo,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.WideTwentyOneByNine,
|
||||
CommonAspectRatio.TallFourByFive,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallTwoByThree,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "nano_banana_2",
|
||||
tauriId: "nano_banana_2",
|
||||
fullName: "Nano Banana 2",
|
||||
category: "image",
|
||||
creator: ModelCreator.Google,
|
||||
providers: [
|
||||
GenerationProvider.Artcraft,
|
||||
GenerationProvider.Fal,
|
||||
],
|
||||
selectorName: "Nano Banana 2",
|
||||
selectorDescription: "Fast instructive editing",
|
||||
selectorBadges: ["25 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
canTextToImage: true,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
progressBarTime: 25000,
|
||||
maxPromptLength: Infinity,
|
||||
canChangeResolution: true,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Auto,
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.WideFiveByFour,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideThreeByTwo,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.WideTwentyOneByNine,
|
||||
CommonAspectRatio.TallFourByFive,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallTwoByThree,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
resolutions: [
|
||||
CommonResolution.OneK,
|
||||
CommonResolution.TwoK,
|
||||
CommonResolution.FourK,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "nano_banana_pro",
|
||||
tauriId: "nano_banana_pro",
|
||||
fullName: "Nano Banana Pro",
|
||||
category: "image",
|
||||
creator: ModelCreator.Google,
|
||||
providers: [
|
||||
GenerationProvider.Artcraft,
|
||||
GenerationProvider.Fal,
|
||||
],
|
||||
selectorName: "Nano Banana Pro",
|
||||
selectorDescription: "Powerful instructive editing",
|
||||
selectorBadges: ["30 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 4,
|
||||
canTextToImage: true,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
progressBarTime: 25000,
|
||||
maxPromptLength: Infinity,
|
||||
canChangeResolution: true,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Auto,
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.WideFiveByFour,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideThreeByTwo,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.WideTwentyOneByNine,
|
||||
CommonAspectRatio.TallFourByFive,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallTwoByThree,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
resolutions: [
|
||||
CommonResolution.OneK,
|
||||
CommonResolution.TwoK,
|
||||
CommonResolution.FourK,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "gpt_image_1",
|
||||
tauriId: "gpt_image_1",
|
||||
fullName: "GPT Image 1 (GPT-4o)",
|
||||
category: "image",
|
||||
creator: ModelCreator.OpenAi,
|
||||
providers: [GenerationProvider.Artcraft, GenerationProvider.Sora],
|
||||
selectorName: "GPT Image 1 (GPT-4o)",
|
||||
selectorDescription: "Slow, but super smart",
|
||||
selectorBadges: ["60 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 60000,
|
||||
maxPromptLength: Infinity,
|
||||
canChangeAspectRatio: true,
|
||||
defaultQuality: CommonQuality.High,
|
||||
qualityOptions: [
|
||||
CommonQuality.High,
|
||||
CommonQuality.Medium,
|
||||
CommonQuality.Low,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "gpt_image_1p5",
|
||||
tauriId: "gpt_image_1p5",
|
||||
fullName: "GPT Image 1.5",
|
||||
category: "image",
|
||||
creator: ModelCreator.OpenAi,
|
||||
selectorName: "GPT Image 1.5",
|
||||
selectorDescription: "Faster, improved",
|
||||
selectorBadges: ["60 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 60000,
|
||||
maxPromptLength: Infinity,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.Square,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.Wide,
|
||||
CommonAspectRatio.Tall,
|
||||
],
|
||||
defaultQuality: CommonQuality.High,
|
||||
qualityOptions: [
|
||||
CommonQuality.High,
|
||||
CommonQuality.Medium,
|
||||
CommonQuality.Low,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "gpt_image_2",
|
||||
tauriId: "gpt_image_2",
|
||||
fullName: "GPT Image 2",
|
||||
category: "image",
|
||||
creator: ModelCreator.OpenAi,
|
||||
selectorName: "GPT Image 2",
|
||||
selectorDescription: "Smart with great text support",
|
||||
selectorBadges: ["2 min."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 120000,
|
||||
maxPromptLength: Infinity,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Auto,
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
],
|
||||
defaultQuality: CommonQuality.High,
|
||||
qualityOptions: [
|
||||
CommonQuality.High,
|
||||
CommonQuality.Medium,
|
||||
CommonQuality.Low,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "seedream_4",
|
||||
tauriId: "seedream_4",
|
||||
fullName: "Seedream 4",
|
||||
category: "image",
|
||||
creator: ModelCreator.Bytedance,
|
||||
selectorName: "Seedream 4",
|
||||
selectorDescription: "Fast",
|
||||
selectorBadges: ["60 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 60000,
|
||||
maxPromptLength: 4000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Auto2k,
|
||||
CommonAspectRatio.Auto4k,
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "seedream_4p5",
|
||||
tauriId: "seedream_4p5",
|
||||
fullName: "Seedream 4.5",
|
||||
category: "image",
|
||||
creator: ModelCreator.Bytedance,
|
||||
selectorName: "Seedream 4.5",
|
||||
selectorDescription: "Fast",
|
||||
selectorBadges: ["60 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 60000,
|
||||
maxPromptLength: 4000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Auto2k,
|
||||
CommonAspectRatio.Auto4k,
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "seedream_5_lite",
|
||||
tauriId: "seedream_5_lite",
|
||||
fullName: "Seedream 5 Lite",
|
||||
category: "image",
|
||||
creator: ModelCreator.Bytedance,
|
||||
selectorName: "Seedream 5 Lite",
|
||||
selectorDescription: "Fast",
|
||||
selectorBadges: ["60 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
canEditImages: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 6,
|
||||
progressBarTime: 60000,
|
||||
maxPromptLength: 4000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.WideSixteenByNine,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Auto2k,
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "qwen_edit_2511_angles",
|
||||
tauriId: "qwen_edit_2511_angles",
|
||||
fullName: "Qwen Edit 2511 Angles",
|
||||
category: "image",
|
||||
creator: ModelCreator.Alibaba,
|
||||
selectorName: "Qwen Edit 2511 Angles",
|
||||
selectorDescription: "Angle manipulation with optional prompt",
|
||||
selectorBadges: ["30 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
maxPromptLength: 800,
|
||||
canTextToImage: false,
|
||||
canEditImages: false,
|
||||
canEditAngles: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 1,
|
||||
progressBarTime: 30000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.SquareHd,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "flux_2_lora_angles",
|
||||
tauriId: "flux_2_lora_angles",
|
||||
fullName: "Flux 2 LoRA Angles",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
selectorName: "Flux 2 LoRA Angles",
|
||||
selectorDescription: "Angle manipulation",
|
||||
selectorBadges: ["30 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 1,
|
||||
maxPromptLength: 4000,
|
||||
canTextToImage: false,
|
||||
canEditImages: false,
|
||||
canEditAngles: true,
|
||||
canUseImagePrompt: true,
|
||||
maxImagePromptCount: 1,
|
||||
progressBarTime: 30000,
|
||||
canChangeAspectRatio: true,
|
||||
defaultAspectRatio: CommonAspectRatio.SquareHd,
|
||||
aspectRatios: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "flux_pro_kontext_max",
|
||||
tauriId: "flux_pro_kontext_max",
|
||||
fullName: "Flux Pro Kontext Max",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
selectorName: "Flux Pro Kontext Max",
|
||||
selectorDescription: "Fast instructive editing",
|
||||
selectorBadges: ["20 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 4,
|
||||
maxPromptLength: 4000,
|
||||
canEditImages: true,
|
||||
canTextToImage: false,
|
||||
tags: [ModelTag.InstructiveEdit],
|
||||
progressBarTime: 20000,
|
||||
canChangeAspectRatio: true,
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "flux_pro_inpaint",
|
||||
tauriId: "flux_pro_1",
|
||||
fullName: "Flux Pro Inpaint",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
selectorName: "Flux Pro (Inpainting)",
|
||||
selectorDescription: "Fast inpainting",
|
||||
selectorBadges: ["30 sec."],
|
||||
maxGenerationCount: 1, // NB: Fal only allows one image for some reason!
|
||||
maxPromptLength: 4000,
|
||||
defaultGenerationCount: 1, // NB: Fal only allows one image for some reason!
|
||||
canEditImages: true,
|
||||
usesInpaintingMask: true,
|
||||
editingIsInpainting: true,
|
||||
canTextToImage: false,
|
||||
progressBarTime: 30000,
|
||||
canChangeAspectRatio: true,
|
||||
}),
|
||||
new ImageModel({
|
||||
id: "flux_dev_juggernaut_inpaint",
|
||||
tauriId: "flux_dev_juggernaut",
|
||||
fullName: "Flux Dev Juggernaut Inpaint",
|
||||
category: "image",
|
||||
creator: ModelCreator.BlackForestLabs,
|
||||
selectorName: "Flux Dev Juggernaut",
|
||||
selectorDescription: "Fast inpainting, low quality",
|
||||
selectorBadges: ["10 sec."],
|
||||
maxGenerationCount: 4,
|
||||
defaultGenerationCount: 4,
|
||||
maxPromptLength: 4000,
|
||||
canEditImages: true,
|
||||
usesInpaintingMask: true,
|
||||
editingIsInpainting: true,
|
||||
canTextToImage: false,
|
||||
progressBarTime: 10000,
|
||||
canChangeAspectRatio: true,
|
||||
}),
|
||||
];
|
||||
aspectRatios: imageAspectRatios(tauriId),
|
||||
defaultAspectRatio: imageDefaultAspectRatio(tauriId),
|
||||
canChangeResolution: imageResolutions(tauriId) !== undefined,
|
||||
resolutions: imageResolutions(tauriId),
|
||||
defaultResolution: imageDefaultResolution(tauriId),
|
||||
qualityOptions: imageQualityOptions(tauriId),
|
||||
defaultQuality: imageDefaultQuality(tauriId),
|
||||
});
|
||||
|
||||
export const IMAGE_MODELS: ImageModel[] = KNOWN_IMAGE_TAURI_IDS.map(
|
||||
buildStaticImageModel,
|
||||
);
|
||||
|
||||
export const IMAGE_MODELS_BY_ID: Map<string, ImageModel> = new Map(
|
||||
IMAGE_MODELS.map((model) => [model.id, model]),
|
||||
|
||||
@@ -1,616 +1,96 @@
|
||||
import { GenerationProvider } from "@storyteller/api-enums";
|
||||
import { VideoModel } from "../classes/VideoModel.js";
|
||||
import { ModelCreator } from "../classes/metadata/ModelCreator.js";
|
||||
import { SizeIconOption } from "../classes/metadata/SizeOption.js";
|
||||
// Static video model overlay, assembled from the per-feature lookup
|
||||
// functions in `../loader/videoModelFeatures.ts`. At runtime the backend
|
||||
// omni listing drives membership + capabilities (`buildModelsFromListing.ts`);
|
||||
// these instances only contribute frontend-only data (presentation, page
|
||||
// flags, providers) and capability fallbacks.
|
||||
|
||||
export const VIDEO_MODELS: VideoModel[] = [
|
||||
import { VideoModel } from "../classes/VideoModel.js";
|
||||
import {
|
||||
KNOWN_VIDEO_TAURI_IDS,
|
||||
videoCreator,
|
||||
videoDefaultDuration,
|
||||
videoDefaultResolution,
|
||||
videoDurationOptions,
|
||||
videoEndFrame,
|
||||
videoFullName,
|
||||
videoGenerateWithSound,
|
||||
videoLegacyId,
|
||||
videoMaxAudioRefDuration,
|
||||
videoMaxPromptLength,
|
||||
videoMaxReferenceAudios,
|
||||
videoMaxReferenceImages,
|
||||
videoMaxReferenceVideos,
|
||||
videoMaxVideoRefDuration,
|
||||
videoProgressBarTime,
|
||||
videoProviders,
|
||||
videoRequiresImage,
|
||||
videoResolutionOptions,
|
||||
videoSelectorBadges,
|
||||
videoSelectorDescription,
|
||||
videoSelectorName,
|
||||
videoSizeOptions,
|
||||
videoStartFrame,
|
||||
videoSupportsCommonAspectRatio,
|
||||
videoSupportsReferenceMode,
|
||||
videoSupportsSystemPrompt,
|
||||
videoTags,
|
||||
videoTextToVideoSupported,
|
||||
} from "../loader/videoModelFeatures.js";
|
||||
import { ModelCreator } from "../classes/metadata/ModelCreator.js";
|
||||
|
||||
const buildStaticVideoModel = (tauriId: string): VideoModel =>
|
||||
new VideoModel({
|
||||
id: "grok_video",
|
||||
tauriId: "grok_imagine_video",
|
||||
fullName: "Grok Video",
|
||||
// Identity.
|
||||
id: videoLegacyId(tauriId) ?? tauriId,
|
||||
tauriId,
|
||||
fullName: videoFullName(tauriId) ?? tauriId,
|
||||
category: "video",
|
||||
creator: ModelCreator.Grok,
|
||||
providers: [GenerationProvider.Grok],
|
||||
selectorName: "Grok Video",
|
||||
selectorDescription: "Fastest video model",
|
||||
selectorBadges: ["20 sec."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
progressBarTime: 50000,
|
||||
maxPromptLength: 4096,
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "landscape",
|
||||
textLabel: "Landscape",
|
||||
icon: SizeIconOption.Landscape,
|
||||
},
|
||||
{
|
||||
tauriValue: "portrait",
|
||||
textLabel: "Portrait",
|
||||
icon: SizeIconOption.Portrait,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "Square",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
],
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "grok_imagine_video_1p5",
|
||||
tauriId: "grok_imagine_video_1p5",
|
||||
fullName: "Grok Imagine 1.5",
|
||||
category: "video",
|
||||
creator: ModelCreator.Grok,
|
||||
// Served via Artcraft (storyteller-web / artcraft_router) for now, even
|
||||
// though the model is made by xAI. See generate_video_command.rs.
|
||||
providers: [GenerationProvider.Artcraft],
|
||||
selectorName: "Grok Imagine 1.5",
|
||||
selectorDescription: "Image-to-video by xAI",
|
||||
selectorBadges: ["Preview"],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
textToVideoSupported: false,
|
||||
progressBarTime: 50000,
|
||||
maxPromptLength: 4096,
|
||||
resolutionOptions: ["480p", "720p"],
|
||||
defaultResolution: "720p",
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "landscape",
|
||||
textLabel: "Landscape",
|
||||
icon: SizeIconOption.Landscape,
|
||||
},
|
||||
{
|
||||
tauriValue: "portrait",
|
||||
textLabel: "Portrait",
|
||||
icon: SizeIconOption.Portrait,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "Square",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
],
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "kling_1_6_pro",
|
||||
tauriId: "kling_1p6_pro",
|
||||
fullName: "Kling 1.6 Pro",
|
||||
category: "video",
|
||||
creator: ModelCreator.Kling,
|
||||
selectorName: "Kling 1.6 Pro",
|
||||
selectorDescription: "Fast video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: true,
|
||||
requiresImage: true,
|
||||
progressBarTime: 300000,
|
||||
maxPromptLength: 2500,
|
||||
generateWithSound: false,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "kling_2_1_pro",
|
||||
tauriId: "kling_2p1_pro",
|
||||
fullName: "Kling 2.1 Pro",
|
||||
category: "video",
|
||||
creator: ModelCreator.Kling,
|
||||
selectorName: "Kling 2.1 Pro",
|
||||
selectorDescription: "Fast video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: true,
|
||||
requiresImage: true,
|
||||
progressBarTime: 300000,
|
||||
maxPromptLength: 2500,
|
||||
generateWithSound: false,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "kling_2_1_master",
|
||||
tauriId: "kling_2p1_master",
|
||||
fullName: "Kling 2.1 Master",
|
||||
category: "video",
|
||||
creator: ModelCreator.Kling,
|
||||
selectorName: "Kling 2.1 Master",
|
||||
selectorDescription: "Fast video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
progressBarTime: 300000,
|
||||
maxPromptLength: 2500,
|
||||
generateWithSound: false,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "kling_2p5_turbo_pro",
|
||||
tauriId: "kling_2p5_turbo_pro",
|
||||
fullName: "Kling 2.5 Turbo Pro",
|
||||
category: "video",
|
||||
creator: ModelCreator.Kling,
|
||||
selectorName: "Kling 2.5 Turbo Pro",
|
||||
selectorDescription: "Fast video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: true,
|
||||
requiresImage: false,
|
||||
progressBarTime: 300000,
|
||||
maxPromptLength: 2500,
|
||||
generateWithSound: false,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "kling_2p6_pro",
|
||||
tauriId: "kling_2p6_pro",
|
||||
fullName: "Kling 2.6 Pro",
|
||||
category: "video",
|
||||
creator: ModelCreator.Kling,
|
||||
selectorName: "Kling 2.6 Pro",
|
||||
selectorDescription: "Fast video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
progressBarTime: 300000,
|
||||
maxPromptLength: 2500,
|
||||
generateWithSound: false,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "kling_3p0_standard",
|
||||
tauriId: "kling_3p0_standard",
|
||||
fullName: "Kling 3.0 Standard",
|
||||
category: "video",
|
||||
creator: ModelCreator.Kling,
|
||||
selectorName: "Kling 3.0 Standard",
|
||||
selectorDescription: "Standard quality video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: true,
|
||||
requiresImage: false,
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "wide_sixteen_by_nine",
|
||||
textLabel: "16:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "1:1",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_nine_by_sixteen",
|
||||
textLabel: "9:16",
|
||||
icon: SizeIconOption.Portrait9x16,
|
||||
},
|
||||
],
|
||||
supportsCommonAspectRatio: true,
|
||||
durationOptions: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
defaultDuration: 5,
|
||||
progressBarTime: 300000,
|
||||
maxPromptLength: 2500,
|
||||
generateWithSound: true,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "kling_3p0_pro",
|
||||
tauriId: "kling_3p0_pro",
|
||||
fullName: "Kling 3.0 Pro",
|
||||
category: "video",
|
||||
creator: ModelCreator.Kling,
|
||||
selectorName: "Kling 3.0 Pro",
|
||||
selectorDescription: "High quality video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: true,
|
||||
requiresImage: false,
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "wide_sixteen_by_nine",
|
||||
textLabel: "16:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "1:1",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_nine_by_sixteen",
|
||||
textLabel: "9:16",
|
||||
icon: SizeIconOption.Portrait9x16,
|
||||
},
|
||||
],
|
||||
supportsCommonAspectRatio: true,
|
||||
durationOptions: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
defaultDuration: 5,
|
||||
progressBarTime: 300000,
|
||||
maxPromptLength: 2500,
|
||||
generateWithSound: true,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "happy_horse_1p0",
|
||||
tauriId: "happy_horse_1p0",
|
||||
fullName: "Happy Horse 1.0",
|
||||
category: "video",
|
||||
creator: ModelCreator.Alibaba,
|
||||
selectorName: "Happy Horse 1.0",
|
||||
selectorDescription: "High quality video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: false,
|
||||
resolutionOptions: ["720p", "1080p"],
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "wide_sixteen_by_nine",
|
||||
textLabel: "16:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "wide_four_by_three",
|
||||
textLabel: "4:3",
|
||||
icon: SizeIconOption.Standard4x3,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "1:1",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_three_by_four",
|
||||
textLabel: "3:4",
|
||||
icon: SizeIconOption.Portrait3x4,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_nine_by_sixteen",
|
||||
textLabel: "9:16",
|
||||
icon: SizeIconOption.Portrait9x16,
|
||||
},
|
||||
],
|
||||
supportsCommonAspectRatio: true,
|
||||
durationOptions: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
defaultDuration: 5,
|
||||
progressBarTime: 300000,
|
||||
maxPromptLength: 2500,
|
||||
generateWithSound: true,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "seedance_1_0_lite",
|
||||
tauriId: "seedance_1p0_lite",
|
||||
fullName: "Seedance 1.0 Lite",
|
||||
category: "video",
|
||||
creator: ModelCreator.Bytedance,
|
||||
selectorName: "Seedance 1.0 Lite",
|
||||
selectorDescription: "Fast video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
progressBarTime: 100000,
|
||||
maxPromptLength: 3000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "seedance_1p5_pro",
|
||||
tauriId: "seedance_1p5_pro",
|
||||
fullName: "Seedance 1.5 Pro",
|
||||
category: "video",
|
||||
creator: ModelCreator.Bytedance,
|
||||
selectorName: "Seedance 1.5 Pro",
|
||||
selectorDescription: "High quality video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: true,
|
||||
requiresImage: false,
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "wide_sixteen_by_nine",
|
||||
textLabel: "16:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "1:1",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_three_by_four",
|
||||
textLabel: "3:4",
|
||||
icon: SizeIconOption.Portrait3x4,
|
||||
},
|
||||
{
|
||||
tauriValue: "wide_four_by_three",
|
||||
textLabel: "4:3",
|
||||
icon: SizeIconOption.Standard4x3,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_nine_by_sixteen",
|
||||
textLabel: "9:16",
|
||||
icon: SizeIconOption.Portrait9x16,
|
||||
},
|
||||
{
|
||||
tauriValue: "wide_twenty_one_by_nine",
|
||||
textLabel: "21:9",
|
||||
icon: SizeIconOption.Landscape,
|
||||
},
|
||||
],
|
||||
durationOptions: [4, 5, 6, 7, 8, 9, 10, 11, 12],
|
||||
defaultDuration: 5,
|
||||
resolutionOptions: ["480p", "720p", "1080p"],
|
||||
defaultResolution: "720p",
|
||||
supportsSystemPrompt: false,
|
||||
generateWithSound: true,
|
||||
progressBarTime: 180000,
|
||||
maxPromptLength: 3000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "seedance_2p0",
|
||||
tauriId: "seedance_2p0",
|
||||
fullName: "Seedance 2.0",
|
||||
category: "video",
|
||||
creator: ModelCreator.Bytedance,
|
||||
selectorName: "Seedance 2.0",
|
||||
selectorDescription: "High quality model",
|
||||
selectorBadges: ["~15 min."],
|
||||
startFrame: true,
|
||||
endFrame: true,
|
||||
requiresImage: false,
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "wide_twenty_one_by_nine",
|
||||
textLabel: "21:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "wide_sixteen_by_nine",
|
||||
textLabel: "16:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "1:1",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_three_by_four",
|
||||
textLabel: "3:4",
|
||||
icon: SizeIconOption.Portrait3x4,
|
||||
},
|
||||
{
|
||||
tauriValue: "wide_four_by_three",
|
||||
textLabel: "4:3",
|
||||
icon: SizeIconOption.Standard4x3,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_nine_by_sixteen",
|
||||
textLabel: "9:16",
|
||||
icon: SizeIconOption.Portrait9x16,
|
||||
},
|
||||
],
|
||||
supportsCommonAspectRatio: true,
|
||||
durationOptions: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
defaultDuration: 5,
|
||||
supportsReferenceMode: true,
|
||||
maxReferenceImages: 9,
|
||||
maxReferenceVideos: 3,
|
||||
maxVideoRefDuration: 15,
|
||||
maxReferenceAudios: 3,
|
||||
maxAudioRefDuration: 15,
|
||||
resolutionOptions: ["480p", "720p", "1080p"],
|
||||
defaultResolution: "720p",
|
||||
supportsSystemPrompt: false,
|
||||
progressBarTime: 900000, // ~15 minutes
|
||||
maxPromptLength: Infinity,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "seedance_2p0_fast",
|
||||
tauriId: "seedance_2p0_fast",
|
||||
fullName: "Seedance 2.0 Fast",
|
||||
category: "video",
|
||||
creator: ModelCreator.Bytedance,
|
||||
selectorName: "Seedance 2.0 Fast",
|
||||
selectorDescription: "Fast and high quality",
|
||||
selectorBadges: ["~5 min."],
|
||||
startFrame: true,
|
||||
endFrame: true,
|
||||
requiresImage: false,
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "wide_twenty_one_by_nine",
|
||||
textLabel: "21:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "wide_sixteen_by_nine",
|
||||
textLabel: "16:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "1:1",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_three_by_four",
|
||||
textLabel: "3:4",
|
||||
icon: SizeIconOption.Portrait3x4,
|
||||
},
|
||||
{
|
||||
tauriValue: "wide_four_by_three",
|
||||
textLabel: "4:3",
|
||||
icon: SizeIconOption.Standard4x3,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_nine_by_sixteen",
|
||||
textLabel: "9:16",
|
||||
icon: SizeIconOption.Portrait9x16,
|
||||
},
|
||||
],
|
||||
supportsCommonAspectRatio: true,
|
||||
durationOptions: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
defaultDuration: 5,
|
||||
supportsReferenceMode: true,
|
||||
maxReferenceImages: 9,
|
||||
maxReferenceVideos: 3,
|
||||
maxVideoRefDuration: 15,
|
||||
maxReferenceAudios: 3,
|
||||
maxAudioRefDuration: 15,
|
||||
resolutionOptions: ["480p", "720p"],
|
||||
defaultResolution: "720p",
|
||||
supportsSystemPrompt: false,
|
||||
progressBarTime: 300000, // ~5 minutes
|
||||
maxPromptLength: Infinity,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "sora_2",
|
||||
tauriId: "sora_2",
|
||||
fullName: "Sora 2",
|
||||
category: "video",
|
||||
creator: ModelCreator.OpenAi,
|
||||
providers: [GenerationProvider.Artcraft, GenerationProvider.Sora],
|
||||
selectorName: "Sora 2",
|
||||
selectorDescription: "Smart video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: false,
|
||||
endFrame: false,
|
||||
requiresImage: false,
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "landscape",
|
||||
textLabel: "Landscape",
|
||||
icon: SizeIconOption.Landscape,
|
||||
},
|
||||
{
|
||||
tauriValue: "portrait",
|
||||
textLabel: "Portrait",
|
||||
icon: SizeIconOption.Portrait,
|
||||
},
|
||||
],
|
||||
progressBarTime: 100000,
|
||||
maxPromptLength: 2000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "sora_2_pro",
|
||||
tauriId: "sora_2_pro",
|
||||
fullName: "Sora 2 Pro",
|
||||
category: "video",
|
||||
creator: ModelCreator.OpenAi,
|
||||
selectorName: "Sora 2 Pro",
|
||||
selectorDescription: "Smart video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: false,
|
||||
endFrame: false,
|
||||
requiresImage: false,
|
||||
sizeOptions: [
|
||||
{
|
||||
tauriValue: "landscape",
|
||||
textLabel: "Landscape",
|
||||
icon: SizeIconOption.Landscape,
|
||||
},
|
||||
{
|
||||
tauriValue: "portrait",
|
||||
textLabel: "Portrait",
|
||||
icon: SizeIconOption.Portrait,
|
||||
},
|
||||
],
|
||||
progressBarTime: 100000,
|
||||
maxPromptLength: 2000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "veo_2",
|
||||
tauriId: "veo_2",
|
||||
fullName: "Google Veo 2",
|
||||
category: "video",
|
||||
creator: ModelCreator.Google,
|
||||
selectorName: "Google Veo 2",
|
||||
selectorDescription: "Fast video model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
generateWithSound: false,
|
||||
progressBarTime: 100000,
|
||||
maxPromptLength: 5000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "veo_3",
|
||||
tauriId: "veo_3",
|
||||
fullName: "Google Veo 3",
|
||||
category: "video",
|
||||
creator: ModelCreator.Google,
|
||||
selectorName: "Google Veo 3",
|
||||
selectorDescription: "Slow, high-quality model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
generateWithSound: true,
|
||||
progressBarTime: 100000,
|
||||
maxPromptLength: 5000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "veo_3_fast",
|
||||
tauriId: "veo_3_fast",
|
||||
fullName: "Google Veo 3 Fast",
|
||||
category: "video",
|
||||
creator: ModelCreator.Google,
|
||||
selectorName: "Google Veo 3 Fast",
|
||||
selectorDescription: "High-quality model (faster)",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
generateWithSound: true,
|
||||
progressBarTime: 100000,
|
||||
maxPromptLength: 5000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "veo_3p1",
|
||||
tauriId: "veo_3p1",
|
||||
fullName: "Google Veo 3.1",
|
||||
category: "video",
|
||||
creator: ModelCreator.Google,
|
||||
selectorName: "Google Veo 3.1",
|
||||
selectorDescription: "Slow, high-quality model",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
generateWithSound: true,
|
||||
progressBarTime: 100000,
|
||||
maxPromptLength: 5000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "veo_3p1_fast",
|
||||
tauriId: "veo_3p1_fast",
|
||||
fullName: "Google Veo 3.1 Fast",
|
||||
category: "video",
|
||||
creator: ModelCreator.Google,
|
||||
selectorName: "Google Veo 3.1 Fast",
|
||||
selectorDescription: "High-quality model (faster)",
|
||||
selectorBadges: ["2 min."],
|
||||
startFrame: true,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
generateWithSound: true,
|
||||
progressBarTime: 100000,
|
||||
maxPromptLength: 5000,
|
||||
}),
|
||||
new VideoModel({
|
||||
id: "switch_x",
|
||||
tauriId: "switch_x",
|
||||
fullName: "Beeble SwitchX",
|
||||
category: "video",
|
||||
creator: ModelCreator.Beeble,
|
||||
selectorName: "Beeble SwitchX",
|
||||
selectorDescription: "Relight, change location, swap objects.",
|
||||
selectorBadges: ["5 min."],
|
||||
startFrame: false,
|
||||
endFrame: false,
|
||||
requiresImage: true,
|
||||
progressBarTime: 5 * 60 * 1000,
|
||||
maxPromptLength: 2000,
|
||||
}),
|
||||
];
|
||||
creator: videoCreator(tauriId) ?? ModelCreator.ArtCraft,
|
||||
|
||||
// Presentation.
|
||||
selectorName:
|
||||
videoSelectorName(tauriId) ?? videoFullName(tauriId) ?? tauriId,
|
||||
selectorDescription: videoSelectorDescription(tauriId) ?? "",
|
||||
selectorBadges: videoSelectorBadges(tauriId) ?? [],
|
||||
tags: videoTags(tauriId),
|
||||
progressBarTime: videoProgressBarTime(tauriId),
|
||||
|
||||
// Desktop-native provider knowledge.
|
||||
providers: videoProviders(tauriId),
|
||||
|
||||
// Prompting.
|
||||
maxPromptLength: videoMaxPromptLength(tauriId),
|
||||
supportsSystemPrompt: videoSupportsSystemPrompt(tauriId),
|
||||
textToVideoSupported: videoTextToVideoSupported(tauriId),
|
||||
generateWithSound: videoGenerateWithSound(tauriId),
|
||||
|
||||
// Keyframes / input images.
|
||||
startFrame: videoStartFrame(tauriId) ?? false,
|
||||
endFrame: videoEndFrame(tauriId) ?? false,
|
||||
requiresImage: videoRequiresImage(tauriId) ?? false,
|
||||
|
||||
// Size / aspect ratio.
|
||||
sizeOptions: videoSizeOptions(tauriId),
|
||||
supportsCommonAspectRatio: videoSupportsCommonAspectRatio(tauriId),
|
||||
|
||||
// Duration / resolution.
|
||||
durationOptions: videoDurationOptions(tauriId),
|
||||
defaultDuration: videoDefaultDuration(tauriId),
|
||||
resolutionOptions: videoResolutionOptions(tauriId),
|
||||
defaultResolution: videoDefaultResolution(tauriId),
|
||||
|
||||
// Reference mode.
|
||||
supportsReferenceMode: videoSupportsReferenceMode(tauriId),
|
||||
maxReferenceImages: videoMaxReferenceImages(tauriId),
|
||||
maxReferenceVideos: videoMaxReferenceVideos(tauriId),
|
||||
maxVideoRefDuration: videoMaxVideoRefDuration(tauriId),
|
||||
maxReferenceAudios: videoMaxReferenceAudios(tauriId),
|
||||
maxAudioRefDuration: videoMaxAudioRefDuration(tauriId),
|
||||
});
|
||||
|
||||
export const VIDEO_MODELS: VideoModel[] = KNOWN_VIDEO_TAURI_IDS.map(
|
||||
buildStaticVideoModel,
|
||||
);
|
||||
|
||||
export const VIDEO_MODELS_BY_ID: Map<string, VideoModel> = new Map(
|
||||
VIDEO_MODELS.map((model) => [model.id, model]),
|
||||
|
||||
@@ -22,6 +22,8 @@ import { SizeIconOption, SizeOption } from "../classes/metadata/SizeOption.js";
|
||||
import { CommonAspectRatio } from "../classes/properties/CommonAspectRatio.js";
|
||||
import { CommonResolution } from "../classes/properties/CommonResolution.js";
|
||||
import { CommonQuality } from "../classes/properties/CommonQuality.js";
|
||||
import { imageSelectorName } from "./imageModelFeatures.js";
|
||||
import { videoSelectorName } from "./videoModelFeatures.js";
|
||||
import {
|
||||
BACKEND_TO_TAURI_IMAGE_ID,
|
||||
BACKEND_TO_TAURI_VIDEO_ID,
|
||||
@@ -76,43 +78,84 @@ export interface ListingVideoModel extends ListingModelBase {
|
||||
export const buildImageModelsFromListing = (
|
||||
overlay: ImageModel[],
|
||||
listing: ListingImageModel[],
|
||||
offeredModelIds: string[] = [],
|
||||
): ImageModel[] =>
|
||||
build(overlay, listing, BACKEND_TO_TAURI_IMAGE_ID, mergedImageModel);
|
||||
build(overlay, listing, offeredModelIds, BACKEND_TO_TAURI_IMAGE_ID, mergedImageModel);
|
||||
|
||||
export const buildVideoModelsFromListing = (
|
||||
overlay: VideoModel[],
|
||||
listing: ListingVideoModel[],
|
||||
offeredModelIds: string[] = [],
|
||||
): VideoModel[] =>
|
||||
build(overlay, listing, BACKEND_TO_TAURI_VIDEO_ID, mergedVideoModel);
|
||||
build(overlay, listing, offeredModelIds, BACKEND_TO_TAURI_VIDEO_ID, mergedVideoModel);
|
||||
|
||||
// ── Core assembly ──────────────────────────────────────────────────────────
|
||||
|
||||
// Membership: a model appears in the picker when it is
|
||||
// 1. OFFERED by a provider in the response's `providers[]` (the backend's
|
||||
// publish switch — `models[]` details also contain disabled entries and
|
||||
// internal variants that must NOT surface), or
|
||||
// 2. an ENABLED `models[]` detail that the frontend knows (overlay entry) —
|
||||
// covers desktop-native models like grok video that no backend provider
|
||||
// offers yet, or
|
||||
// 3. a frontend-only overlay model the backend has never heard of
|
||||
// (switch_x, inpaint models, …).
|
||||
// `models[]` details are the capability source either way (the enabled entry
|
||||
// wins when a model is detailed twice).
|
||||
const build = <T extends { tauriId: string }, L extends ListingModelBase>(
|
||||
overlay: T[],
|
||||
listing: L[],
|
||||
offeredModelIds: string[],
|
||||
alias: Record<string, string>,
|
||||
merge: (m: L, tauriId: string, overlayEntry: T | undefined) => T,
|
||||
): T[] => {
|
||||
const toTauriId = (backendId: string) => alias[backendId] ?? backendId;
|
||||
const overlayByTauriId = new Map(overlay.map((m) => [m.tauriId, m]));
|
||||
const knownTauriIds = new Set(listing.map((m) => alias[m.model] ?? m.model));
|
||||
const knownTauriIds = new Set(listing.map((m) => toTauriId(m.model)));
|
||||
const offeredTauriIds = new Set(offeredModelIds.map(toTauriId));
|
||||
|
||||
// Capability lookup — prefer the enabled detail entry when duplicated.
|
||||
const detailsByTauriId = new Map<string, L>();
|
||||
for (const m of listing) {
|
||||
const tauriId = toTauriId(m.model);
|
||||
const existing = detailsByTauriId.get(tauriId);
|
||||
if (existing === undefined || existing.is_disabled === true) {
|
||||
detailsByTauriId.set(tauriId, m);
|
||||
}
|
||||
}
|
||||
|
||||
const result: T[] = [];
|
||||
const seenTauriIds = new Set<string>();
|
||||
// Backend drives membership + order; every entry is rebuilt with API-first
|
||||
// capabilities.
|
||||
// Backend details order drives ordering.
|
||||
for (const m of listing) {
|
||||
if (m.is_disabled === true) continue;
|
||||
const tauriId = alias[m.model] ?? m.model;
|
||||
// Guard: several backend ids aliasing to one frontend model would render
|
||||
// duplicates — only surface the first.
|
||||
const tauriId = toTauriId(m.model);
|
||||
if (seenTauriIds.has(tauriId)) continue;
|
||||
|
||||
const overlayEntry = overlayByTauriId.get(tauriId);
|
||||
const detail = detailsByTauriId.get(tauriId) ?? m;
|
||||
const enabled = detail.is_disabled !== true;
|
||||
const show =
|
||||
offeredTauriIds.has(tauriId) || (enabled && overlayEntry !== undefined);
|
||||
if (!show) continue;
|
||||
|
||||
seenTauriIds.add(tauriId);
|
||||
result.push(merge(detail, tauriId, overlayEntry));
|
||||
}
|
||||
// Offered models with no detail entry at all (unusual, but the publish
|
||||
// switch wins): surface minimally.
|
||||
for (const backendId of offeredModelIds) {
|
||||
const tauriId = toTauriId(backendId);
|
||||
if (seenTauriIds.has(tauriId)) continue;
|
||||
seenTauriIds.add(tauriId);
|
||||
result.push(merge(m, tauriId, overlayByTauriId.get(tauriId)));
|
||||
result.push(
|
||||
merge({ model: backendId } as L, tauriId, overlayByTauriId.get(tauriId)),
|
||||
);
|
||||
}
|
||||
// Append frontend-only models the backend has never heard of (switch_x,
|
||||
// inpaint models, …).
|
||||
// Append frontend-only models the backend has never heard of.
|
||||
for (const m of overlay) {
|
||||
if (!knownTauriIds.has(m.tauriId)) result.push(m);
|
||||
if (!knownTauriIds.has(m.tauriId) && !seenTauriIds.has(m.tauriId)) {
|
||||
result.push(m);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -138,7 +181,7 @@ const mergedImageModel = (
|
||||
creator: creatorFor(m.model_creator, m.model, o?.creator),
|
||||
|
||||
// Presentation — the omni image API has no display fields yet.
|
||||
selectorName: o?.selectorName ?? fullName,
|
||||
selectorName: imageSelectorName(tauriId) ?? fullName,
|
||||
selectorDescription: o?.selectorDescription ?? "",
|
||||
selectorBadges: o?.selectorBadges ?? [],
|
||||
tags: o?.tags ?? [],
|
||||
@@ -205,7 +248,7 @@ const mergedVideoModel = (
|
||||
|
||||
// Presentation. The video API DOES have descriptions (extra_info_short /
|
||||
// extra_info) — prefer them so backend copy reaches the picker.
|
||||
selectorName: o?.selectorName ?? fullName,
|
||||
selectorName: videoSelectorName(tauriId) ?? fullName,
|
||||
selectorDescription:
|
||||
m.extra_info_short ?? m.extra_info ?? o?.selectorDescription ?? "",
|
||||
selectorBadges: o?.selectorBadges ?? [],
|
||||
|
||||
@@ -0,0 +1,476 @@
|
||||
// Frontend-only per-feature data for image models, keyed by `tauriId`.
|
||||
//
|
||||
// Each exported function is a sparse lookup: a model has an entry ONLY when
|
||||
// its value differs from the class default (see `ImageModel` / `Model`), so
|
||||
// every feature is individually inspectable and deletable once the backend
|
||||
// omni listing serves it. `buildModelsFromListing.ts` merges the API
|
||||
// capability fields over these values at runtime.
|
||||
|
||||
import { GenerationProvider } from "@storyteller/api-enums";
|
||||
import { ModelCreator } from "../classes/metadata/ModelCreator.js";
|
||||
import { ModelTag } from "../classes/metadata/ModelTag.js";
|
||||
import { CommonAspectRatio } from "../classes/properties/CommonAspectRatio.js";
|
||||
import { CommonResolution } from "../classes/properties/CommonResolution.js";
|
||||
import { CommonQuality } from "../classes/properties/CommonQuality.js";
|
||||
|
||||
// All image model tauriIds, in picker (list) order.
|
||||
export const KNOWN_IMAGE_TAURI_IDS: string[] = [
|
||||
"grok_imagine_image",
|
||||
"midjourney",
|
||||
"flux_pro_1p1_ultra",
|
||||
"flux_pro_1p1",
|
||||
"flux_1_dev",
|
||||
"flux_1_schnell",
|
||||
"nano_banana",
|
||||
"nano_banana_2",
|
||||
"nano_banana_pro",
|
||||
"gpt_image_1",
|
||||
"gpt_image_1p5",
|
||||
"gpt_image_2",
|
||||
"seedream_4",
|
||||
"seedream_4p5",
|
||||
"seedream_5_lite",
|
||||
"qwen_edit_2511_angles",
|
||||
"flux_2_lora_angles",
|
||||
"flux_pro_kontext_max",
|
||||
"flux_pro_1",
|
||||
"flux_dev_juggernaut",
|
||||
];
|
||||
|
||||
// Models the backend omni listing does not know about; the loader appends
|
||||
// these after the backend-driven entries.
|
||||
export const FRONTEND_ONLY_IMAGE_TAURI_IDS: string[] = [
|
||||
"midjourney",
|
||||
"grok_imagine_image",
|
||||
"qwen_edit_2511_angles",
|
||||
"flux_2_lora_angles",
|
||||
"flux_pro_kontext_max",
|
||||
"flux_pro_1",
|
||||
"flux_dev_juggernaut",
|
||||
];
|
||||
|
||||
// ── Identity ────────────────────────────────────────────────────────────────
|
||||
|
||||
// Frontend `id` where it historically diverged from `tauriId` (BY_ID lookups
|
||||
// and generation history depend on these).
|
||||
export const imageLegacyId = (tauriId: string): string | undefined => {
|
||||
const values: Record<string, string> = {
|
||||
grok_imagine_image: "grok_image",
|
||||
flux_pro_1p1_ultra: "flux_pro_1_1_ultra",
|
||||
flux_pro_1p1: "flux_pro_1_1",
|
||||
flux_pro_1: "flux_pro_inpaint",
|
||||
flux_dev_juggernaut: "flux_dev_juggernaut_inpaint",
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const imageCreator = (tauriId: string): ModelCreator | undefined => {
|
||||
const values: Record<string, ModelCreator> = {
|
||||
grok_imagine_image: ModelCreator.Grok,
|
||||
midjourney: ModelCreator.Midjourney,
|
||||
flux_pro_1p1_ultra: ModelCreator.BlackForestLabs,
|
||||
flux_pro_1p1: ModelCreator.BlackForestLabs,
|
||||
flux_1_dev: ModelCreator.BlackForestLabs,
|
||||
flux_1_schnell: ModelCreator.BlackForestLabs,
|
||||
nano_banana: ModelCreator.Google,
|
||||
nano_banana_2: ModelCreator.Google,
|
||||
nano_banana_pro: ModelCreator.Google,
|
||||
gpt_image_1: ModelCreator.OpenAi,
|
||||
gpt_image_1p5: ModelCreator.OpenAi,
|
||||
gpt_image_2: ModelCreator.OpenAi,
|
||||
seedream_4: ModelCreator.Bytedance,
|
||||
seedream_4p5: ModelCreator.Bytedance,
|
||||
seedream_5_lite: ModelCreator.Bytedance,
|
||||
qwen_edit_2511_angles: ModelCreator.Alibaba,
|
||||
flux_2_lora_angles: ModelCreator.BlackForestLabs,
|
||||
flux_pro_kontext_max: ModelCreator.BlackForestLabs,
|
||||
flux_pro_1: ModelCreator.BlackForestLabs,
|
||||
flux_dev_juggernaut: ModelCreator.BlackForestLabs,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Presentation ────────────────────────────────────────────────────────────
|
||||
|
||||
export const imageFullName = (tauriId: string): string | undefined => {
|
||||
const values: Record<string, string> = {
|
||||
grok_imagine_image: "Grok",
|
||||
midjourney: "Midjourney",
|
||||
qwen_edit_2511_angles: "Qwen Edit 2511 Angles",
|
||||
flux_2_lora_angles: "Flux 2 LoRA Angles",
|
||||
flux_pro_kontext_max: "Flux Pro Kontext Max",
|
||||
flux_pro_1: "Flux Pro Inpaint",
|
||||
flux_dev_juggernaut: "Flux Dev Juggernaut Inpaint",
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only where the picker name differs from `fullName` (the caller falls back
|
||||
// to `imageFullName`).
|
||||
export const imageSelectorName = (tauriId: string): string | undefined => {
|
||||
const values: Record<string, string> = {
|
||||
flux_pro_1: "Flux Pro (Inpainting)",
|
||||
flux_dev_juggernaut: "Flux Dev Juggernaut",
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const imageSelectorDescription = (
|
||||
tauriId: string,
|
||||
): string | undefined => {
|
||||
const values: Record<string, string> = {
|
||||
grok_imagine_image: "Fast af",
|
||||
midjourney: "Stunning style and quality",
|
||||
flux_pro_1p1_ultra: "Higher quality model",
|
||||
flux_pro_1p1: "High quality model",
|
||||
flux_1_dev: "Fast, but lower quality",
|
||||
flux_1_schnell: "Fastest image gen, but lowest quality",
|
||||
nano_banana: "Fast instructive editing",
|
||||
nano_banana_2: "Fast instructive editing",
|
||||
nano_banana_pro: "Powerful instructive editing",
|
||||
gpt_image_1: "Slow, but super smart",
|
||||
gpt_image_1p5: "Faster, improved",
|
||||
gpt_image_2: "Smart with great text support",
|
||||
seedream_4: "Fast",
|
||||
seedream_4p5: "Fast",
|
||||
seedream_5_lite: "Fast",
|
||||
qwen_edit_2511_angles: "Angle manipulation with optional prompt",
|
||||
flux_2_lora_angles: "Angle manipulation",
|
||||
flux_pro_kontext_max: "Fast instructive editing",
|
||||
flux_pro_1: "Fast inpainting",
|
||||
flux_dev_juggernaut: "Fast inpainting, low quality",
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const imageSelectorBadges = (tauriId: string): string[] | undefined => {
|
||||
const values: Record<string, string[]> = {
|
||||
grok_imagine_image: ["10 sec."],
|
||||
midjourney: ["45 sec."],
|
||||
flux_pro_1p1_ultra: ["35 sec."],
|
||||
flux_pro_1p1: ["10 sec."],
|
||||
flux_1_dev: ["10 sec."],
|
||||
flux_1_schnell: ["10 sec."],
|
||||
nano_banana: ["25 sec."],
|
||||
nano_banana_2: ["25 sec."],
|
||||
nano_banana_pro: ["30 sec."],
|
||||
gpt_image_1: ["60 sec."],
|
||||
gpt_image_1p5: ["60 sec."],
|
||||
gpt_image_2: ["2 min."],
|
||||
seedream_4: ["60 sec."],
|
||||
seedream_4p5: ["60 sec."],
|
||||
seedream_5_lite: ["60 sec."],
|
||||
qwen_edit_2511_angles: ["30 sec."],
|
||||
flux_2_lora_angles: ["30 sec."],
|
||||
flux_pro_kontext_max: ["20 sec."],
|
||||
flux_pro_1: ["30 sec."],
|
||||
flux_dev_juggernaut: ["10 sec."],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const imageTags = (tauriId: string): ModelTag[] | undefined => {
|
||||
const values: Record<string, ModelTag[]> = {
|
||||
nano_banana: [ModelTag.InstructiveEdit],
|
||||
nano_banana_2: [ModelTag.InstructiveEdit],
|
||||
nano_banana_pro: [ModelTag.InstructiveEdit],
|
||||
gpt_image_1: [ModelTag.InstructiveEdit],
|
||||
gpt_image_1p5: [ModelTag.InstructiveEdit],
|
||||
gpt_image_2: [ModelTag.InstructiveEdit],
|
||||
seedream_4: [ModelTag.InstructiveEdit],
|
||||
seedream_4p5: [ModelTag.InstructiveEdit],
|
||||
seedream_5_lite: [ModelTag.InstructiveEdit],
|
||||
flux_pro_kontext_max: [ModelTag.InstructiveEdit],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Milliseconds; only where it differs from the `Model` default (20000).
|
||||
export const imageProgressBarTime = (tauriId: string): number | undefined => {
|
||||
const values: Record<string, number> = {
|
||||
grok_imagine_image: 10000,
|
||||
midjourney: 45000,
|
||||
flux_pro_1p1_ultra: 35000,
|
||||
flux_pro_1p1: 10000,
|
||||
flux_1_dev: 10000,
|
||||
flux_1_schnell: 10000,
|
||||
nano_banana: 25000,
|
||||
nano_banana_2: 25000,
|
||||
nano_banana_pro: 25000,
|
||||
gpt_image_1: 60000,
|
||||
gpt_image_1p5: 60000,
|
||||
gpt_image_2: 120000,
|
||||
seedream_4: 60000,
|
||||
seedream_4p5: 60000,
|
||||
seedream_5_lite: 60000,
|
||||
qwen_edit_2511_angles: 30000,
|
||||
flux_2_lora_angles: 30000,
|
||||
flux_pro_1: 30000,
|
||||
flux_dev_juggernaut: 10000,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Desktop-native provider knowledge ───────────────────────────────────────
|
||||
|
||||
// Only models whose overlay entry explicitly set `providers:`; everything
|
||||
// else falls back to `[GenerationProvider.Artcraft]` via `getProviders()`.
|
||||
export const imageProviders = (
|
||||
tauriId: string,
|
||||
): GenerationProvider[] | undefined => {
|
||||
const values: Record<string, GenerationProvider[]> = {
|
||||
grok_imagine_image: [GenerationProvider.Grok],
|
||||
midjourney: [GenerationProvider.Midjourney],
|
||||
flux_1_dev: [GenerationProvider.Artcraft, GenerationProvider.Fal],
|
||||
flux_1_schnell: [GenerationProvider.Artcraft, GenerationProvider.Fal],
|
||||
nano_banana_2: [GenerationProvider.Artcraft, GenerationProvider.Fal],
|
||||
nano_banana_pro: [GenerationProvider.Artcraft, GenerationProvider.Fal],
|
||||
gpt_image_1: [GenerationProvider.Artcraft, GenerationProvider.Sora],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Page-subsetting flags ───────────────────────────────────────────────────
|
||||
|
||||
// Only entries that explicitly opt OUT of text-to-image (default is true).
|
||||
export const imageCanTextToImage = (tauriId: string): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
qwen_edit_2511_angles: false,
|
||||
flux_2_lora_angles: false,
|
||||
flux_pro_kontext_max: false,
|
||||
flux_pro_1: false,
|
||||
flux_dev_juggernaut: false,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries (default is false).
|
||||
export const imageCanEditImages = (tauriId: string): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
nano_banana: true,
|
||||
nano_banana_2: true,
|
||||
nano_banana_pro: true,
|
||||
gpt_image_1: true,
|
||||
gpt_image_1p5: true,
|
||||
gpt_image_2: true,
|
||||
seedream_4: true,
|
||||
seedream_4p5: true,
|
||||
seedream_5_lite: true,
|
||||
flux_pro_kontext_max: true,
|
||||
flux_pro_1: true,
|
||||
flux_dev_juggernaut: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries (default is false).
|
||||
export const imageUsesInpaintingMask = (
|
||||
tauriId: string,
|
||||
): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
flux_pro_1: true,
|
||||
flux_dev_juggernaut: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries (default is false).
|
||||
export const imageEditingIsInpainting = (
|
||||
tauriId: string,
|
||||
): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
flux_pro_1: true,
|
||||
flux_dev_juggernaut: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries (default is false).
|
||||
export const imageCanEditAngles = (tauriId: string): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
qwen_edit_2511_angles: true,
|
||||
flux_2_lora_angles: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Finite values only; only where it differs from the `Model` default (3000).
|
||||
export const imageMaxPromptLength = (tauriId: string): number | undefined => {
|
||||
const values: Record<string, number> = {
|
||||
grok_imagine_image: 8000,
|
||||
// No practical prompt limit for these models.
|
||||
nano_banana: Infinity,
|
||||
nano_banana_2: Infinity,
|
||||
nano_banana_pro: Infinity,
|
||||
gpt_image_1: Infinity,
|
||||
gpt_image_1p5: Infinity,
|
||||
gpt_image_2: Infinity,
|
||||
midjourney: 6000,
|
||||
flux_pro_1p1_ultra: 4000,
|
||||
flux_pro_1p1: 4000,
|
||||
flux_1_dev: 4000,
|
||||
flux_1_schnell: 4000,
|
||||
seedream_4: 4000,
|
||||
seedream_4p5: 4000,
|
||||
seedream_5_lite: 4000,
|
||||
qwen_edit_2511_angles: 800,
|
||||
flux_2_lora_angles: 4000,
|
||||
flux_pro_kontext_max: 4000,
|
||||
flux_pro_1: 4000,
|
||||
flux_dev_juggernaut: 4000,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Capability fallbacks (the API may not provide these per-model) ──────────
|
||||
|
||||
// Only where it differs from the fallback (4).
|
||||
export const imageMaxGenerationCount = (
|
||||
tauriId: string,
|
||||
): number | undefined => {
|
||||
const values: Record<string, number> = {
|
||||
// NB: Grok only supports 6 images at a time
|
||||
grok_imagine_image: 6,
|
||||
// NB: Fal only allows one image for some reason!
|
||||
flux_pro_1: 1,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only where it differs from the fallback (1).
|
||||
export const imageDefaultGenerationCount = (
|
||||
tauriId: string,
|
||||
): number | undefined => {
|
||||
const values: Record<string, number> = {
|
||||
grok_imagine_image: 6,
|
||||
midjourney: 4,
|
||||
flux_pro_kontext_max: 4,
|
||||
flux_dev_juggernaut: 4,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const imagePredefinedGenerationCounts = (
|
||||
tauriId: string,
|
||||
): number[] | undefined => {
|
||||
const values: Record<string, number[]> = {
|
||||
grok_imagine_image: [6],
|
||||
midjourney: [4],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries (default is false).
|
||||
export const imageCanUseImagePrompt = (
|
||||
tauriId: string,
|
||||
): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
flux_pro_1p1_ultra: true,
|
||||
flux_pro_1p1: true,
|
||||
flux_1_dev: true,
|
||||
flux_1_schnell: true,
|
||||
qwen_edit_2511_angles: true,
|
||||
flux_2_lora_angles: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only where it differs from the default (1).
|
||||
export const imageMaxImagePromptCount = (
|
||||
tauriId: string,
|
||||
): number | undefined => {
|
||||
const values: Record<string, number> = {
|
||||
grok_imagine_image: 6,
|
||||
midjourney: 6,
|
||||
flux_pro_1p1_ultra: 6,
|
||||
flux_pro_1p1: 6,
|
||||
flux_1_dev: 6,
|
||||
flux_1_schnell: 6,
|
||||
nano_banana: 6,
|
||||
nano_banana_2: 6,
|
||||
nano_banana_pro: 4,
|
||||
gpt_image_1: 6,
|
||||
gpt_image_1p5: 6,
|
||||
gpt_image_2: 6,
|
||||
seedream_4: 6,
|
||||
seedream_4p5: 6,
|
||||
seedream_5_lite: 6,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const imageAspectRatios = (
|
||||
tauriId: string,
|
||||
): CommonAspectRatio[] | undefined => {
|
||||
const values: Record<string, CommonAspectRatio[]> = {
|
||||
grok_imagine_image: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.WideThreeByTwo,
|
||||
CommonAspectRatio.TallTwoByThree,
|
||||
],
|
||||
qwen_edit_2511_angles: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
flux_2_lora_angles: [
|
||||
CommonAspectRatio.Square,
|
||||
CommonAspectRatio.SquareHd,
|
||||
CommonAspectRatio.WideFourByThree,
|
||||
CommonAspectRatio.WideSixteenByNine,
|
||||
CommonAspectRatio.TallThreeByFour,
|
||||
CommonAspectRatio.TallNineBySixteen,
|
||||
],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const imageDefaultAspectRatio = (
|
||||
tauriId: string,
|
||||
): CommonAspectRatio | undefined => {
|
||||
const values: Record<string, CommonAspectRatio> = {
|
||||
grok_imagine_image: CommonAspectRatio.Square,
|
||||
qwen_edit_2511_angles: CommonAspectRatio.SquareHd,
|
||||
flux_2_lora_angles: CommonAspectRatio.SquareHd,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const imageResolutions = (
|
||||
tauriId: string,
|
||||
): CommonResolution[] | undefined => {
|
||||
const values: Record<string, CommonResolution[]> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// No overlay entry set an explicit default; `ImageModel` falls back to
|
||||
// `resolutions[0]`.
|
||||
export const imageDefaultResolution = (
|
||||
tauriId: string,
|
||||
): CommonResolution | undefined => {
|
||||
const values: Record<string, CommonResolution> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const imageQualityOptions = (
|
||||
tauriId: string,
|
||||
): CommonQuality[] | undefined => {
|
||||
const values: Record<string, CommonQuality[]> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const imageDefaultQuality = (
|
||||
tauriId: string,
|
||||
): CommonQuality | undefined => {
|
||||
const values: Record<string, CommonQuality> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
@@ -0,0 +1,511 @@
|
||||
// Frontend-only per-feature data for video models, keyed by `tauriId`.
|
||||
//
|
||||
// Each exported function is a sparse lookup: a model has an entry ONLY when
|
||||
// its value differs from the class default (see `VideoModel` / `Model`), so
|
||||
// every feature is individually inspectable and deletable once the backend
|
||||
// omni listing serves it. `buildModelsFromListing.ts` merges the API
|
||||
// capability fields over these values at runtime.
|
||||
|
||||
import { GenerationProvider } from "@storyteller/api-enums";
|
||||
import { ModelCreator } from "../classes/metadata/ModelCreator.js";
|
||||
import { ModelTag } from "../classes/metadata/ModelTag.js";
|
||||
import { SizeIconOption, SizeOption } from "../classes/metadata/SizeOption.js";
|
||||
|
||||
// All video model tauriIds, in picker (list) order.
|
||||
export const KNOWN_VIDEO_TAURI_IDS: string[] = [
|
||||
"grok_imagine_video",
|
||||
"grok_imagine_video_1p5",
|
||||
"kling_1p6_pro",
|
||||
"kling_2p1_pro",
|
||||
"kling_2p1_master",
|
||||
"kling_2p5_turbo_pro",
|
||||
"kling_2p6_pro",
|
||||
"kling_3p0_standard",
|
||||
"kling_3p0_pro",
|
||||
"happy_horse_1p0",
|
||||
"seedance_1p0_lite",
|
||||
"seedance_1p5_pro",
|
||||
"seedance_2p0",
|
||||
"seedance_2p0_fast",
|
||||
"sora_2",
|
||||
"sora_2_pro",
|
||||
"veo_2",
|
||||
"veo_3",
|
||||
"veo_3_fast",
|
||||
"veo_3p1",
|
||||
"veo_3p1_fast",
|
||||
"switch_x",
|
||||
];
|
||||
|
||||
// Models the backend omni listing does not know about; the loader appends
|
||||
// these after the backend-driven entries.
|
||||
export const FRONTEND_ONLY_VIDEO_TAURI_IDS: string[] = ["switch_x"];
|
||||
|
||||
// ── Identity ────────────────────────────────────────────────────────────────
|
||||
|
||||
// Frontend `id` where it historically diverged from `tauriId` (BY_ID lookups
|
||||
// and generation history depend on these).
|
||||
export const videoLegacyId = (tauriId: string): string | undefined => {
|
||||
const values: Record<string, string> = {
|
||||
grok_imagine_video: "grok_video",
|
||||
kling_1p6_pro: "kling_1_6_pro",
|
||||
kling_2p1_pro: "kling_2_1_pro",
|
||||
kling_2p1_master: "kling_2_1_master",
|
||||
seedance_1p0_lite: "seedance_1_0_lite",
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const videoCreator = (tauriId: string): ModelCreator | undefined => {
|
||||
const values: Record<string, ModelCreator> = {
|
||||
grok_imagine_video: ModelCreator.Grok,
|
||||
grok_imagine_video_1p5: ModelCreator.Grok,
|
||||
kling_1p6_pro: ModelCreator.Kling,
|
||||
kling_2p1_pro: ModelCreator.Kling,
|
||||
kling_2p1_master: ModelCreator.Kling,
|
||||
kling_2p5_turbo_pro: ModelCreator.Kling,
|
||||
kling_2p6_pro: ModelCreator.Kling,
|
||||
kling_3p0_standard: ModelCreator.Kling,
|
||||
kling_3p0_pro: ModelCreator.Kling,
|
||||
happy_horse_1p0: ModelCreator.Alibaba,
|
||||
seedance_1p0_lite: ModelCreator.Bytedance,
|
||||
seedance_1p5_pro: ModelCreator.Bytedance,
|
||||
seedance_2p0: ModelCreator.Bytedance,
|
||||
seedance_2p0_fast: ModelCreator.Bytedance,
|
||||
sora_2: ModelCreator.OpenAi,
|
||||
sora_2_pro: ModelCreator.OpenAi,
|
||||
veo_2: ModelCreator.Google,
|
||||
veo_3: ModelCreator.Google,
|
||||
veo_3_fast: ModelCreator.Google,
|
||||
veo_3p1: ModelCreator.Google,
|
||||
veo_3p1_fast: ModelCreator.Google,
|
||||
switch_x: ModelCreator.Beeble,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Presentation ────────────────────────────────────────────────────────────
|
||||
|
||||
export const videoFullName = (tauriId: string): string | undefined => {
|
||||
const values: Record<string, string> = {
|
||||
kling_2p1_pro: "Kling 2.1 Pro",
|
||||
kling_2p1_master: "Kling 2.1 Master",
|
||||
kling_3p0_standard: "Kling 3.0 Standard",
|
||||
kling_3p0_pro: "Kling 3.0 Pro",
|
||||
seedance_1p0_lite: "Seedance 1.0 Lite",
|
||||
sora_2: "Sora 2",
|
||||
sora_2_pro: "Sora 2 Pro",
|
||||
veo_2: "Google Veo 2",
|
||||
veo_3: "Google Veo 3",
|
||||
veo_3p1: "Google Veo 3.1",
|
||||
veo_3p1_fast: "Google Veo 3.1 Fast",
|
||||
switch_x: "Beeble SwitchX",
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only where the picker name differs from `fullName` (the caller falls back
|
||||
// to `videoFullName`). Currently every video model uses its full name.
|
||||
export const videoSelectorName = (tauriId: string): string | undefined => {
|
||||
const values: Record<string, string> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const videoSelectorDescription = (
|
||||
tauriId: string,
|
||||
): string | undefined => {
|
||||
const values: Record<string, string> = {
|
||||
grok_imagine_video: "Fastest video model",
|
||||
kling_1p6_pro: "Fast video model",
|
||||
kling_2p1_pro: "Fast video model",
|
||||
kling_2p1_master: "Fast video model",
|
||||
kling_2p5_turbo_pro: "Fast video model",
|
||||
kling_2p6_pro: "Fast video model",
|
||||
kling_3p0_standard: "Standard quality video model",
|
||||
kling_3p0_pro: "High quality video model",
|
||||
happy_horse_1p0: "High quality video model",
|
||||
seedance_1p0_lite: "Fast video model",
|
||||
seedance_1p5_pro: "High quality video model",
|
||||
sora_2: "Smart video model",
|
||||
sora_2_pro: "Smart video model",
|
||||
veo_2: "Fast video model",
|
||||
veo_3: "Slow, high-quality model",
|
||||
veo_3_fast: "High-quality model (faster)",
|
||||
veo_3p1: "Slow, high-quality model",
|
||||
veo_3p1_fast: "High-quality model (faster)",
|
||||
switch_x: "Relight, change location, swap objects.",
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const videoSelectorBadges = (tauriId: string): string[] | undefined => {
|
||||
const values: Record<string, string[]> = {
|
||||
grok_imagine_video: ["20 sec."],
|
||||
grok_imagine_video_1p5: ["Preview"],
|
||||
kling_1p6_pro: ["2 min."],
|
||||
kling_2p1_pro: ["2 min."],
|
||||
kling_2p1_master: ["2 min."],
|
||||
kling_2p5_turbo_pro: ["2 min."],
|
||||
kling_2p6_pro: ["2 min."],
|
||||
kling_3p0_standard: ["2 min."],
|
||||
kling_3p0_pro: ["2 min."],
|
||||
happy_horse_1p0: ["2 min."],
|
||||
seedance_1p0_lite: ["2 min."],
|
||||
seedance_1p5_pro: ["2 min."],
|
||||
seedance_2p0: ["~15 min."],
|
||||
seedance_2p0_fast: ["~5 min."],
|
||||
sora_2: ["2 min."],
|
||||
sora_2_pro: ["2 min."],
|
||||
veo_2: ["2 min."],
|
||||
veo_3: ["2 min."],
|
||||
veo_3_fast: ["2 min."],
|
||||
veo_3p1: ["2 min."],
|
||||
veo_3p1_fast: ["2 min."],
|
||||
switch_x: ["5 min."],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Currently no video model declares tags.
|
||||
export const videoTags = (tauriId: string): ModelTag[] | undefined => {
|
||||
const values: Record<string, ModelTag[]> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Milliseconds; only where it differs from the `Model` default (20000).
|
||||
export const videoProgressBarTime = (tauriId: string): number | undefined => {
|
||||
const values: Record<string, number> = {
|
||||
grok_imagine_video: 50000,
|
||||
grok_imagine_video_1p5: 50000,
|
||||
kling_1p6_pro: 300000,
|
||||
kling_2p1_pro: 300000,
|
||||
kling_2p1_master: 300000,
|
||||
kling_2p5_turbo_pro: 300000,
|
||||
kling_2p6_pro: 300000,
|
||||
kling_3p0_standard: 300000,
|
||||
kling_3p0_pro: 300000,
|
||||
happy_horse_1p0: 300000,
|
||||
seedance_1p0_lite: 100000,
|
||||
seedance_1p5_pro: 180000,
|
||||
seedance_2p0: 900000, // ~15 minutes
|
||||
seedance_2p0_fast: 300000, // ~5 minutes
|
||||
sora_2: 100000,
|
||||
sora_2_pro: 100000,
|
||||
veo_2: 100000,
|
||||
veo_3: 100000,
|
||||
veo_3_fast: 100000,
|
||||
veo_3p1: 100000,
|
||||
veo_3p1_fast: 100000,
|
||||
switch_x: 5 * 60 * 1000,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Desktop-native provider knowledge ───────────────────────────────────────
|
||||
|
||||
// Only models whose overlay entry explicitly set `providers:`; everything
|
||||
// else falls back to `[GenerationProvider.Artcraft]` via `getProviders()`.
|
||||
export const videoProviders = (
|
||||
tauriId: string,
|
||||
): GenerationProvider[] | undefined => {
|
||||
const values: Record<string, GenerationProvider[]> = {
|
||||
grok_imagine_video: [GenerationProvider.Grok],
|
||||
// Served via Artcraft (storyteller-web / artcraft_router) for now, even
|
||||
// though the model is made by xAI. See generate_video_command.rs.
|
||||
grok_imagine_video_1p5: [GenerationProvider.Artcraft],
|
||||
sora_2: [GenerationProvider.Artcraft, GenerationProvider.Sora],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Prompting ───────────────────────────────────────────────────────────────
|
||||
|
||||
// Finite values only; only where it differs from the `Model` default (3000).
|
||||
export const videoMaxPromptLength = (tauriId: string): number | undefined => {
|
||||
const values: Record<string, number> = {
|
||||
grok_imagine_video: 4096,
|
||||
// No practical prompt limit for these models.
|
||||
seedance_2p0: Infinity,
|
||||
seedance_2p0_fast: Infinity,
|
||||
kling_1p6_pro: 2500,
|
||||
kling_2p1_pro: 2500,
|
||||
kling_2p1_master: 2500,
|
||||
kling_2p5_turbo_pro: 2500,
|
||||
kling_2p6_pro: 2500,
|
||||
kling_3p0_standard: 2500,
|
||||
kling_3p0_pro: 2500,
|
||||
happy_horse_1p0: 2500,
|
||||
sora_2: 2000,
|
||||
sora_2_pro: 2000,
|
||||
veo_2: 5000,
|
||||
veo_3: 5000,
|
||||
veo_3_fast: 5000,
|
||||
veo_3p1: 5000,
|
||||
veo_3p1_fast: 5000,
|
||||
switch_x: 2000,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only entries that explicitly opt OUT (default is true).
|
||||
export const videoSupportsSystemPrompt = (
|
||||
tauriId: string,
|
||||
): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
seedance_1p5_pro: false,
|
||||
seedance_2p0: false,
|
||||
seedance_2p0_fast: false,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries (default is false).
|
||||
export const videoGenerateWithSound = (
|
||||
tauriId: string,
|
||||
): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
kling_3p0_standard: true,
|
||||
kling_3p0_pro: true,
|
||||
happy_horse_1p0: true,
|
||||
veo_3: true,
|
||||
veo_3p1: true,
|
||||
veo_3p1_fast: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only entries that explicitly opt OUT of text-to-video (default is true).
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoTextToVideoSupported = (
|
||||
tauriId: string,
|
||||
): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Keyframes / input images ────────────────────────────────────────────────
|
||||
|
||||
// Only `true` entries.
|
||||
export const videoStartFrame = (tauriId: string): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
kling_2p1_pro: true,
|
||||
kling_2p1_master: true,
|
||||
kling_3p0_standard: true,
|
||||
kling_3p0_pro: true,
|
||||
seedance_1p0_lite: true,
|
||||
veo_2: true,
|
||||
veo_3: true,
|
||||
veo_3p1: true,
|
||||
veo_3p1_fast: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries.
|
||||
export const videoEndFrame = (tauriId: string): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
kling_2p1_pro: true,
|
||||
kling_3p0_standard: true,
|
||||
kling_3p0_pro: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries.
|
||||
export const videoRequiresImage = (tauriId: string): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
grok_imagine_video: true,
|
||||
grok_imagine_video_1p5: true,
|
||||
kling_1p6_pro: true,
|
||||
kling_2p1_pro: true,
|
||||
kling_2p1_master: true,
|
||||
kling_2p6_pro: true,
|
||||
seedance_1p0_lite: true,
|
||||
veo_2: true,
|
||||
veo_3: true,
|
||||
veo_3_fast: true,
|
||||
veo_3p1: true,
|
||||
veo_3p1_fast: true,
|
||||
switch_x: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Size / aspect ratio ─────────────────────────────────────────────────────
|
||||
|
||||
export const videoSizeOptions = (
|
||||
tauriId: string,
|
||||
): SizeOption[] | undefined => {
|
||||
const values: Record<string, SizeOption[]> = {
|
||||
kling_3p0_standard: [
|
||||
{
|
||||
tauriValue: "wide_sixteen_by_nine",
|
||||
textLabel: "16:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "1:1",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_nine_by_sixteen",
|
||||
textLabel: "9:16",
|
||||
icon: SizeIconOption.Portrait9x16,
|
||||
},
|
||||
],
|
||||
kling_3p0_pro: [
|
||||
{
|
||||
tauriValue: "wide_sixteen_by_nine",
|
||||
textLabel: "16:9",
|
||||
icon: SizeIconOption.Landscape16x9,
|
||||
},
|
||||
{
|
||||
tauriValue: "square",
|
||||
textLabel: "1:1",
|
||||
icon: SizeIconOption.Square,
|
||||
},
|
||||
{
|
||||
tauriValue: "tall_nine_by_sixteen",
|
||||
textLabel: "9:16",
|
||||
icon: SizeIconOption.Portrait9x16,
|
||||
},
|
||||
],
|
||||
sora_2: [
|
||||
{
|
||||
tauriValue: "landscape",
|
||||
textLabel: "Landscape",
|
||||
icon: SizeIconOption.Landscape,
|
||||
},
|
||||
{
|
||||
tauriValue: "portrait",
|
||||
textLabel: "Portrait",
|
||||
icon: SizeIconOption.Portrait,
|
||||
},
|
||||
],
|
||||
sora_2_pro: [
|
||||
{
|
||||
tauriValue: "landscape",
|
||||
textLabel: "Landscape",
|
||||
icon: SizeIconOption.Landscape,
|
||||
},
|
||||
{
|
||||
tauriValue: "portrait",
|
||||
textLabel: "Portrait",
|
||||
icon: SizeIconOption.Portrait,
|
||||
},
|
||||
],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Only `true` entries (default is false).
|
||||
export const videoSupportsCommonAspectRatio = (
|
||||
tauriId: string,
|
||||
): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {
|
||||
kling_3p0_standard: true,
|
||||
kling_3p0_pro: true,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Duration / resolution ───────────────────────────────────────────────────
|
||||
|
||||
export const videoDurationOptions = (
|
||||
tauriId: string,
|
||||
): number[] | undefined => {
|
||||
const values: Record<string, number[]> = {
|
||||
kling_3p0_standard: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
kling_3p0_pro: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
happy_horse_1p0: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
seedance_1p5_pro: [4, 5, 6, 7, 8, 9, 10, 11, 12],
|
||||
seedance_2p0: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
seedance_2p0_fast: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
export const videoDefaultDuration = (tauriId: string): number | undefined => {
|
||||
const values: Record<string, number> = {
|
||||
kling_3p0_standard: 5,
|
||||
kling_3p0_pro: 5,
|
||||
};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoResolutionOptions = (
|
||||
tauriId: string,
|
||||
): string[] | undefined => {
|
||||
const values: Record<string, string[]> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoDefaultResolution = (
|
||||
tauriId: string,
|
||||
): string | undefined => {
|
||||
const values: Record<string, string> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// ── Reference mode ──────────────────────────────────────────────────────────
|
||||
|
||||
// Only `true` entries.
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoSupportsReferenceMode = (
|
||||
tauriId: string,
|
||||
): boolean | undefined => {
|
||||
const values: Record<string, boolean> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoMaxReferenceImages = (
|
||||
tauriId: string,
|
||||
): number | undefined => {
|
||||
const values: Record<string, number> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoMaxReferenceVideos = (
|
||||
tauriId: string,
|
||||
): number | undefined => {
|
||||
const values: Record<string, number> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoMaxVideoRefDuration = (
|
||||
tauriId: string,
|
||||
): number | undefined => {
|
||||
const values: Record<string, number> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoMaxReferenceAudios = (
|
||||
tauriId: string,
|
||||
): number | undefined => {
|
||||
const values: Record<string, number> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
|
||||
// Fully served by the omni API — delete this function once static fallbacks
|
||||
// are no longer needed.
|
||||
export const videoMaxAudioRefDuration = (
|
||||
tauriId: string,
|
||||
): number | undefined => {
|
||||
const values: Record<string, number> = {};
|
||||
return values[tauriId];
|
||||
};
|
||||
@@ -47,11 +47,12 @@ export const useModelsStore = create<ModelsStoreState>((set, get) => ({
|
||||
const next: Partial<ModelsStoreState> = { isLoading: false, loaded: true };
|
||||
|
||||
if (imageResult.status === "fulfilled") {
|
||||
const { models } = imageResult.value.payload;
|
||||
const built = buildImageModelsFromListing(IMAGE_MODELS, models);
|
||||
const { models, providers } = imageResult.value.payload;
|
||||
const offered = providers.flatMap((p) => p.models.map((pm) => pm.model));
|
||||
const built = buildImageModelsFromListing(IMAGE_MODELS, models, offered);
|
||||
next.imageModels = built;
|
||||
console.log(
|
||||
`[models] image: backend returned ${models.length} → ${built.length} models shown`,
|
||||
`[models] image: backend returned ${models.length} details, ${offered.length} offered → ${built.length} models shown`,
|
||||
built.map((m) => m.tauriId),
|
||||
);
|
||||
} else {
|
||||
@@ -59,11 +60,12 @@ export const useModelsStore = create<ModelsStoreState>((set, get) => ({
|
||||
}
|
||||
|
||||
if (videoResult.status === "fulfilled") {
|
||||
const { models } = videoResult.value.payload;
|
||||
const built = buildVideoModelsFromListing(VIDEO_MODELS, models);
|
||||
const { models, providers } = videoResult.value.payload;
|
||||
const offered = providers.flatMap((p) => p.models.map((pm) => pm.model));
|
||||
const built = buildVideoModelsFromListing(VIDEO_MODELS, models, offered);
|
||||
next.videoModels = built;
|
||||
console.log(
|
||||
`[models] video: backend returned ${models.length} → ${built.length} models shown`,
|
||||
`[models] video: backend returned ${models.length} details, ${offered.length} offered → ${built.length} models shown`,
|
||||
built.map((m) => m.tauriId),
|
||||
);
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user