diff --git a/frontend/libs/components/model-selector/src/index.ts b/frontend/libs/components/model-selector/src/index.ts index f1fad278af..0a3c7c8b82 100644 --- a/frontend/libs/components/model-selector/src/index.ts +++ b/frontend/libs/components/model-selector/src/index.ts @@ -2,5 +2,6 @@ export * from "./lib/model-selectors-for-pages"; export * from "./lib/model-pages"; export * from "./lib/classy-model-selector"; export * from "./lib/classy-model-selector-store"; +export * from "./lib/defaultModelForPage"; export * from "./lib/provider-selector"; export * from "./lib/provider-icons"; diff --git a/frontend/libs/components/model-selector/src/lib/classy-model-selector.tsx b/frontend/libs/components/model-selector/src/lib/classy-model-selector.tsx index e40998b454..fed783e1b3 100644 --- a/frontend/libs/components/model-selector/src/lib/classy-model-selector.tsx +++ b/frontend/libs/components/model-selector/src/lib/classy-model-selector.tsx @@ -11,6 +11,7 @@ import { Model } from "@storyteller/model-list"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCircleCheck, faChevronUp } from "@fortawesome/pro-solid-svg-icons"; import { GenerationProvider } from "@storyteller/api-enums"; +import { defaultModelForPage } from "./defaultModelForPage"; interface ClassyModelSelectorProps { items: Omit[]; @@ -106,7 +107,10 @@ export function ClassyModelSelector({ }: ClassyModelSelectorProps) { const { selectedModels, setSelectedModel, setSelectedProvider } = useClassyModelSelectorStore(); - const selectedModel = selectedModels[page] || items[0]?.model; + const itemModels: Model[] = items + .map(item => item.model) + .filter(model => model !== undefined); + const selectedModel = selectedModels[page] || defaultModelForPage(itemModels, page); const selectedProvider = useSelectedProviderForModel(page, selectedModel?.id); const selectedProvidersByModel = useClassyModelSelectorStore( (s) => s.selectedProviders[page] ?? {}, @@ -116,7 +120,7 @@ export function ClassyModelSelector({ useEffect(() => { // Initialize selected model if not set if (!selectedModels[page] && items[0]) { - setSelectedModel(page, items[0].model!); + setSelectedModel(page, defaultModelForPage(itemModels, page)); } }, []); diff --git a/frontend/libs/components/model-selector/src/lib/defaultModelForPage.ts b/frontend/libs/components/model-selector/src/lib/defaultModelForPage.ts new file mode 100644 index 0000000000..dd99f3f696 --- /dev/null +++ b/frontend/libs/components/model-selector/src/lib/defaultModelForPage.ts @@ -0,0 +1,26 @@ +import { IMAGE_MODELS_BY_ID, Model, VIDEO_MODELS_BY_ID } from "@storyteller/model-list"; +import { ModelPage } from "./model-pages"; + +export const defaultModelForPage = (models: Model[], page: ModelPage): Model => { + let imageModel: Model | undefined; + + switch (page) { + case ModelPage.TextToImage: + imageModel = IMAGE_MODELS_BY_ID.get("nano_banana_pro"); + break; + case ModelPage.ImageToVideo: + imageModel = VIDEO_MODELS_BY_ID.get("seedance_1_0_lite"); + break; + case ModelPage.Canvas2D: + imageModel = IMAGE_MODELS_BY_ID.get("gpt_image_1p5"); + break; + case ModelPage.Stage3D: + imageModel = IMAGE_MODELS_BY_ID.get("gpt_image_1p5"); + break; + case ModelPage.ImageEditor: + imageModel = IMAGE_MODELS_BY_ID.get("nano_banana_pro"); + break; + } + + return imageModel || models[0]; +} diff --git a/frontend/libs/components/model-selector/src/lib/model-selectors-for-pages.tsx b/frontend/libs/components/model-selector/src/lib/model-selectors-for-pages.tsx index 4ae0332039..7a0913db0b 100644 --- a/frontend/libs/components/model-selector/src/lib/model-selectors-for-pages.tsx +++ b/frontend/libs/components/model-selector/src/lib/model-selectors-for-pages.tsx @@ -41,13 +41,12 @@ export const TEXT_TO_IMAGE_PAGE_MODEL_LIST : ModelList = buildItems( (function() : Model[] { const set : Set = new Set(); - set.add(IMAGE_MODELS_BY_ID.get("flux_pro_1_1")!); // Don't put Midjourney first! IMAGE_MODELS .filter((model) => model.canTextToImage) - //.filter((model) => !model.usesInpaintingMask) // We can't use masked inpainting models here - //.filter((model) => model.id !== "flux_pro_kontext_max") // NB: Flux Pro Kontext Max requires an input image .forEach((m) => set.add(m)); - return Array.from(set); + const list = Array.from(set); + list.sort((a, b) => a.selectorName?.localeCompare(b.selectorName)); + return list; })(), ); @@ -56,11 +55,12 @@ export const CANVAS_2D_PAGE_MODEL_LIST : ModelList = buildItems( (function() : Model[] { const set : Set = new Set(); - set.add(IMAGE_MODELS_BY_ID.get("gpt_image_1")!); // Place gpt_image_1 first. IMAGE_MODELS .filter((m) => m.tags?.includes(ModelTag.InstructiveEdit)) .forEach((m) => set.add(m)); - return Array.from(set); + const list = Array.from(set); + list.sort((a, b) => a.selectorName?.localeCompare(b.selectorName)); + return list; })(), ); @@ -69,11 +69,12 @@ export const STAGE_3D_PAGE_MODEL_LIST : ModelList = buildItems( (function() : Model[] { const set : Set = new Set(); - set.add(IMAGE_MODELS_BY_ID.get("gpt_image_1")!); // Place gpt_image_1 first. IMAGE_MODELS .filter((m) => m.tags?.includes(ModelTag.InstructiveEdit)) .forEach((m) => set.add(m)); - return Array.from(set); + const list = Array.from(set); + list.sort((a, b) => a.selectorName?.localeCompare(b.selectorName)); + return list; })(), ); @@ -91,13 +92,21 @@ export const IMAGE_EDITOR_PAGE_MODEL_LIST : ModelList = IMAGE_MODELS .filter((m) => m.canEditImages) .forEach((m) => set.add(m)); - return Array.from(set); + const list = Array.from(set); + list.sort((a, b) => a.selectorName?.localeCompare(b.selectorName)); + return list; })(), ); export const IMAGE_TO_VIDEO_PAGE_MODEL_LIST : ModelList = buildItems( - VIDEO_MODELS, + (function() : Model[] { + const set : Set = new Set(); + VIDEO_MODELS.forEach((m) => set.add(m)); + const list = Array.from(set); + list.sort((a, b) => a.selectorName?.localeCompare(b.selectorName)); + return list; + })(), ); \ No newline at end of file diff --git a/frontend/libs/model-list/src/index.ts b/frontend/libs/model-list/src/index.ts index c0279ad633..c5bac936dd 100644 --- a/frontend/libs/model-list/src/index.ts +++ b/frontend/libs/model-list/src/index.ts @@ -10,7 +10,6 @@ export * from "./lib/classes/metadata/SizeOption.js"; export * from "./lib/legacy/ModelConfig.js"; export * from "./lib/legacy/ModelInfo.js"; export * from "./lib/legacy/Models.js"; -export * from "./lib/legacy/ModelsByPage.js"; export * from "./lib/lists/AllModels.js"; export * from "./lib/lists/AllModelsById.js"; export * from "./lib/lists/ImageModels.js"; diff --git a/frontend/libs/model-list/src/lib/legacy/ModelsByPage.ts b/frontend/libs/model-list/src/lib/legacy/ModelsByPage.ts deleted file mode 100644 index 08c65a1275..0000000000 --- a/frontend/libs/model-list/src/lib/legacy/ModelsByPage.ts +++ /dev/null @@ -1,36 +0,0 @@ - -/** - * We might not want to always programatically build the list of models, because - * we likely want to control the order of models in the UI to prioritize - * more popular models. We may also want to selectively prune non-useful models. - */ - -// export const TEXT_TO_IMAGE_PAGE_MODELS : ModelConfig[] = -// getModelsByCategory("image") -// // Remove inpainting and editing models. -// .filter((m) => m.id !== "flux_pro_inpaint") -// .filter((m) => m.id !== "flux_pro_kontext_max"); - -// export const IMAGE_TO_VIDEO_PAGE_MODELS : ModelConfig[] = -// getModelsByCategory("video"); - -// export const CANVAS_2D_PAGE_MODELS : ModelConfig[] = -// getModelsByCategory("image") -// // Remove non-instructive models. -// .filter((m) => m.tags?.includes(ModelTag.InstructiveEdit)) - -// export const STAGE_3D_PAGE_MODELS : ModelConfig[] = -// getModelsByCategory("image") -// // Remove non-instructive models. -// .filter((m) => m.tags?.includes(ModelTag.InstructiveEdit)) - -// export const IMAGE_EDITOR_PAGE_MODELS : ModelConfig[] = [ -// // Explicit list for now. -// ALL_MODELS_BY_ID.get("flux_pro_inpaint")!, -// ALL_MODELS_BY_ID.get("flux_dev_juggernaut_inpaint")!, -// ALL_MODELS_BY_ID.get("flux_pro_kontext_max")!, -// ]; - -// export const CANVAS_2D_PAGE_MODELS_NEW : ImageModel[] = IMAGE_MODELS -// // Remove non-instructive models. -// .filter((m) => m.tags?.includes(ModelTag.InstructiveEdit)) \ No newline at end of file diff --git a/frontend/libs/model-list/src/lib/lists/ImageModels.ts b/frontend/libs/model-list/src/lib/lists/ImageModels.ts index 2e9e6a5f24..e6d16ee184 100644 --- a/frontend/libs/model-list/src/lib/lists/ImageModels.ts +++ b/frontend/libs/model-list/src/lib/lists/ImageModels.ts @@ -273,7 +273,7 @@ export const IMAGE_MODELS: ImageModel[] = [ tauriId: "seedream_4", fullName: "Seedream 4", category: "image", - creator: ModelCreator.OpenAi, + creator: ModelCreator.Bytedance, selectorName: "Seedream 4", selectorDescription: "Fast", selectorBadges: ["60 sec."], @@ -302,7 +302,7 @@ export const IMAGE_MODELS: ImageModel[] = [ tauriId: "seedream_4p5", fullName: "Seedream 4.5", category: "image", - creator: ModelCreator.OpenAi, + creator: ModelCreator.Bytedance, selectorName: "Seedream 4.5", selectorDescription: "Fast", selectorBadges: ["60 sec."], diff --git a/frontend/libs/model-list/src/lib/lists/VideoModels.ts b/frontend/libs/model-list/src/lib/lists/VideoModels.ts index 5c56b23baa..db7d3dc7b1 100644 --- a/frontend/libs/model-list/src/lib/lists/VideoModels.ts +++ b/frontend/libs/model-list/src/lib/lists/VideoModels.ts @@ -259,3 +259,11 @@ export const VIDEO_MODELS: VideoModel[] = [ progressBarTime: 100000, }), ]; + +export const VIDEO_MODELS_BY_ID: Map = new Map( + VIDEO_MODELS.map((model) => [model.id, model]), +); + +if (VIDEO_MODELS_BY_ID.size !== VIDEO_MODELS.length) { + throw new Error("All video models must have unique IDs"); +}