clean up model metadata and plumbing (#770)

This commit is contained in:
Brandon Thomas authored and GitHub committed 2025-08-20 00:56:58 -04:00
1 parent fbf914e24e
commit 85c4e0edab
19 files changed
+241 -152

No files matched your search

@@ -145,8 +145,8 @@ pub async fn enqueue_image_inpaint_command(
sora_task_queue: State<'_, SoraTaskQueue>,
) -> ResponseOrErrorType<EnqueueImageInpaintSuccessResponse, EnqueueInpaintImageErrorType> {
info!("enqueue_image_inpaint_command called; model: {:?}, image_media_token: {:?}, prompt: {:?}",
&request.model, &request.image_media_token, &request.prompt);
info!("enqueue_image_inpaint_command called; model: {:?}, image_media_token: {:?}, prompt: {:?}, image_count: {:?}",
&request.model, &request.image_media_token, &request.prompt, &request.image_count);
let result = handle_request(
&request,
@@ -124,6 +124,25 @@ export const TopBar = ({ pageName, loginSignUpPressed }: Props) => {
}
};
const getPageTitle = () : string => {
switch (tabStore.activeTabId) {
case "2D":
return "Canvas";
case "3D":
return "3D Editor";
case "IMAGE":
return "Text to Image";
case "VIDEO":
return "Image to Video";
case "EDIT":
return "Edit Image";
default:
return "Artcraft";
}
};
const pageTitle = getPageTitle();
// const { toggleModal } = usePricingModalStore(); - Uncomment for pricing modal - BFlat
return (
@@ -179,11 +198,7 @@ export const TopBar = ({ pageName, loginSignUpPressed }: Props) => {
<SceneTitleInput pageName={pageName} />
) : (
<h1>
{tabStore.activeTabId === "2D"
? "Canvas"
: tabStore.activeTabId === "VIDEO"
? "Generate Video"
: "Generate Image"}
{pageTitle}
</h1>
)}
</div>
@@ -7,24 +7,21 @@ import { AspectRatioType, useSceneStore } from "./stores/SceneState";
import { useUndoRedoHotkeys } from "./hooks/useUndoRedoHotkeys";
import { useDeleteHotkeys } from "./hooks/useDeleteHotkeys";
import { useCopyPasteHotkeys } from "./hooks/useCopyPasteHotkeys";
import { PopoverItem, PopoverMenu } from "@storyteller/ui-popover";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faClock, faImage } from "@fortawesome/pro-solid-svg-icons";
import Konva from "konva";
import { setCanvasRenderBitmap } from "../../signals/canvasRenderBitmap";
import { captureStageImageBitmap } from "./hooks/useUpdateSnapshot";
import { ContextMenuContainer } from "./components/ui/ContextMenu";
import { FalBackgroundRemoval } from "@storyteller/tauri-api";
import { EnqueueImageBgRemoval } from "@storyteller/tauri-api";
import { ModelInfo } from "@storyteller/model-list";
import {
instructiveImageEditModels,
ModelCategory,
CANVAS_2D_PAGE_MODEL_LIST,
ModelPage,
ModelSelector,
useModelSelectorStore,
} from "@storyteller/ui-model-selector";
import { useCanvasBgRemovedEvent } from "@storyteller/tauri-api";
const PAGE_ID : ModelPage = ModelPage.Canvas2D;
export const DecodeBase64ToImage = async (
base64String: string,
): Promise<ImageBitmap> => {
@@ -60,11 +57,11 @@ const PageDraw = () => {
const store = useSceneStore();
const selectedModel =
selectedModels[ModelCategory.Canvas2D] ||
instructiveImageEditModels[0]?.label;
selectedModels[PAGE_ID] ||
CANVAS_2D_PAGE_MODEL_LIST[0]?.label;
const selectedModelInfo: ModelInfo | undefined =
instructiveImageEditModels.find(
CANVAS_2D_PAGE_MODEL_LIST.find(
(m) => m.label === selectedModel,
)?.modelInfo;
@@ -450,8 +447,8 @@ const PageDraw = () => {
</div>
<div className="absolute bottom-6 left-6 z-20 flex items-center gap-2">
<ModelSelector
items={instructiveImageEditModels}
category={ModelCategory.Canvas2D}
items={CANVAS_2D_PAGE_MODEL_LIST}
page={PAGE_ID}
panelTitle="Select Model"
panelClassName="min-w-[280px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-white/80 hover:text-white"
@@ -1,39 +1,44 @@
import { useState, useRef, useEffect } from "react";
// https://github.com/SaladTechnologies/comfyui-api
import { PopoverItem, PopoverMenu } from "@storyteller/ui-popover";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faClock, faImage } from "@fortawesome/pro-solid-svg-icons";
import Konva from "konva"; // just for types
// import { setCanvasRenderBitmap } from "../../signals/canvasRenderBitmap"
import {
EnqueueImageInpaint,
EnqueueImageInpaintModel,
} from "@storyteller/tauri-api";
import { ContextMenuContainer } from "../PageDraw/components/ui/ContextMenu";
// import SideToolbar from "../PageDraw/components/ui/SideToolbar";
import { useCopyPasteHotkeys } from "../PageDraw/hooks/useCopyPasteHotkeys";
import { useDeleteHotkeys } from "../PageDraw/hooks/useDeleteHotkeys";
import { useUndoRedoHotkeys } from "../PageDraw/hooks/useUndoRedoHotkeys";
// import { captureStageImageBitmap } from "../PageDraw/hooks/useUpdateSnapshot";
import PromptEditor from "./PromptEditor/PromptEditor";
// import { AspectRatioType } from "../PageDraw/stores/SceneState";
import { ActiveEditTool, useEditStore } from "./stores/EditState";
import { EditPaintSurface } from "./EditPaintSurface";
import { normalizeCanvas } from "../../Helpers/CanvasHelpers";
import { BaseImageSelector, BaseSelectorImage } from "./BaseImageSelector";
import DrawToolControlBar from "./DrawToolControlBar";
import { IMAGE_EDITOR_PAGE_MODEL_LIST, ModelPage, ModelSelector, useModelSelectorStore } from "@storyteller/ui-model-selector";
import { ModelInfo } from "@storyteller/model-list";
const PAGE_ID : ModelPage = ModelPage.ImageEditor;
const PageEdit = () => {
//useStateSceneLoader();
const { selectedModels } = useModelSelectorStore();
const selectedModel =
selectedModels[PAGE_ID] ||
IMAGE_EDITOR_PAGE_MODEL_LIST[0]?.label;
const selectedModelInfo: ModelInfo | undefined =
IMAGE_EDITOR_PAGE_MODEL_LIST.find(
(m) => m.label === selectedModel,
)?.modelInfo;
// State for canvas dimensions
const canvasWidth = useRef<number>(1024);
const canvasHeight = useRef<number>(1024);
// Add new state to track if user is selecting
const [isSelecting, setIsSelecting] = useState<boolean>(false);
const [selectedModel, setSelectedModel] = useState<string>("GPT-4o");
// Create refs for stage and image
const stageRef = useRef<Konva.Stage>({} as Konva.Stage);
const leftPanelRef = useRef<Konva.Layer>({} as Konva.Layer);
@@ -42,6 +47,7 @@ const PageEdit = () => {
const [isEnqueuing, setIsEnqueuing] = useState<boolean>(false);
const [generationCount, setGenerationCount] = useState<number>(1);
// Use the Zustand store
const store = useEditStore();
@@ -115,27 +121,6 @@ const PageEdit = () => {
};
}, [store]);
const modelList: PopoverItem[] = [
{
label: "GPT-4o",
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
selected: selectedModel === "GPT-4o",
description: "High quality model",
badges: [{ label: "2 min.", icon: <FontAwesomeIcon icon={faClock} /> }],
},
{
label: "FLUX.1 Kontext",
icon: <FontAwesomeIcon icon={faImage} className="h-4 w-4" />,
selected: selectedModel === "FLUX.1 Kontext",
description: "Fast and high-quality model",
badges: [{ label: "20 sec.", icon: <FontAwesomeIcon icon={faClock} /> }],
},
];
const handleModelSelect = (item: PopoverItem) => {
setSelectedModel(item.label);
};
const onFitPressed = async () => {
// Get the stage and its container dimensions
const stage = stageRef.current;
@@ -263,6 +248,7 @@ const PageEdit = () => {
}`}
>
<PromptEditor
modelInfo={selectedModelInfo}
onModeChange={(mode: string) => {
store.setActiveTool(mode as ActiveEditTool);
}}
@@ -328,10 +314,9 @@ const PageEdit = () => {
</ContextMenuContainer>
</div>
<div className="absolute bottom-6 left-6 z-20 flex items-center gap-2">
<PopoverMenu
items={modelList}
onSelect={handleModelSelect}
mode="hoverSelect"
<ModelSelector
items={IMAGE_EDITOR_PAGE_MODEL_LIST}
page={PAGE_ID}
panelTitle="Select Model"
panelClassName="min-w-[280px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-white/80 hover:text-white"
@@ -66,13 +66,10 @@ import {
removeImageDropListener,
} from "@storyteller/ui-gallery-modal";
import {
instructiveImageEditModels,
ModelCategory,
// ModelCategory,
STAGE_3D_PAGE_MODEL_LIST,
ModelPage,
ModelSelector,
useModelSelectorStore,
// videoGenerationModels,
// useModelSelectorStore,
} from "@storyteller/ui-model-selector";
import { LoginModal, useLoginModalStore } from "@storyteller/ui-login-modal";
import PageDraw from "../PageDraw/PageDraw";
@@ -80,6 +77,8 @@ import { useTabStore } from "../Stores/TabState";
import PageEdit from "../PageEdit/PageEdit";
import { ModelInfo } from "@storyteller/model-list";
const PAGE_ID : ModelPage = ModelPage.Stage3D;
export const PageEditor = () => {
useSignals();
const { triggerRecheck } = useLoginModalStore();
@@ -112,11 +111,11 @@ export const PageEditor = () => {
}, []);
const selectedModel =
selectedModels[ModelCategory.Editor3D] ||
instructiveImageEditModels[0]?.label;
selectedModels[PAGE_ID] ||
STAGE_3D_PAGE_MODEL_LIST[0]?.label;
const selectedModelInfo: ModelInfo | undefined =
instructiveImageEditModels.find(
STAGE_3D_PAGE_MODEL_LIST.find(
(m) => m.label === selectedModel,
)?.modelInfo;
@@ -550,8 +549,8 @@ export const PageEditor = () => {
<div className="absolute bottom-6 left-6 z-20 flex items-center gap-2">
<ModelSelector
items={instructiveImageEditModels}
category={ModelCategory.Editor3D}
items={STAGE_3D_PAGE_MODEL_LIST}
page={PAGE_ID}
panelTitle="Select Model"
panelClassName="min-w-[280px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-white/80 hover:text-white"
@@ -3,8 +3,8 @@ import { JobContextType } from "@storyteller/common";
import { PromptBoxImage } from "@storyteller/ui-promptbox";
import BackgroundGallery from "./BackgroundGallery";
import {
imageGenerationModels,
ModelCategory,
TEXT_TO_IMAGE_PAGE_MODEL_LIST,
ModelPage,
ModelSelector,
useModelSelectorStore,
} from "@storyteller/ui-model-selector";
@@ -15,15 +15,17 @@ interface TextToImageProps {
}
import { ModelInfo } from "@storyteller/model-list";
const PAGE_ID : ModelPage = ModelPage.TextToImage;
const TextToImage = ({ imageMediaId, imageUrl }: TextToImageProps) => {
const containerRef = useRef<HTMLDivElement>(null);
const { selectedModels } = useModelSelectorStore();
const selectedModel =
selectedModels[ModelCategory.TextToImage] ||
imageGenerationModels[0]?.label;
selectedModels[PAGE_ID] ||
TEXT_TO_IMAGE_PAGE_MODEL_LIST[0]?.label;
const selectedModelInfo: ModelInfo | undefined = imageGenerationModels.find(
const selectedModelInfo: ModelInfo | undefined = TEXT_TO_IMAGE_PAGE_MODEL_LIST.find(
(m) => m.label === selectedModel,
)?.modelInfo;
@@ -66,8 +68,8 @@ const TextToImage = ({ imageMediaId, imageUrl }: TextToImageProps) => {
<div className="absolute bottom-6 left-6 z-20 flex items-center gap-2">
<ModelSelector
items={imageGenerationModels}
category={ModelCategory.TextToImage}
items={TEXT_TO_IMAGE_PAGE_MODEL_LIST}
page={PAGE_ID}
mode="hoverSelect"
panelTitle="Select Model"
panelClassName="min-w-[280px]"
@@ -3,13 +3,15 @@ import { JobContextType } from "@storyteller/common";
import { PromptBoxVideo } from "@storyteller/ui-promptbox";
import BackgroundGallery from "./BackgroundGallery";
import {
ModelCategory,
ModelPage,
ModelSelector,
useModelSelectorStore,
videoGenerationModels,
IMAGE_TO_VIDEO_PAGE_MODEL_LIST,
} from "@storyteller/ui-model-selector";
import { ModelInfo } from "@storyteller/model-list";
const PAGE_ID : ModelPage = ModelPage.ImageToVideo;
interface ImageToVideoProps {
imageMediaId?: string;
imageUrl?: string;
@@ -20,10 +22,10 @@ const ImageToVideo = ({ imageMediaId, imageUrl }: ImageToVideoProps) => {
const { selectedModels } = useModelSelectorStore();
const selectedModel =
selectedModels[ModelCategory.ImageToVideo] ||
videoGenerationModels[0]?.label;
selectedModels[PAGE_ID] ||
IMAGE_TO_VIDEO_PAGE_MODEL_LIST[0]?.label;
const selectedModelInfo: ModelInfo | undefined = videoGenerationModels.find(
const selectedModelInfo: ModelInfo | undefined = IMAGE_TO_VIDEO_PAGE_MODEL_LIST.find(
(m) => m.label === selectedModel,
)?.modelInfo;
@@ -67,8 +69,8 @@ const ImageToVideo = ({ imageMediaId, imageUrl }: ImageToVideoProps) => {
<div className="absolute bottom-6 left-6 z-20 flex items-center gap-2">
<ModelSelector
items={videoGenerationModels}
category={ModelCategory.ImageToVideo}
items={IMAGE_TO_VIDEO_PAGE_MODEL_LIST}
page={PAGE_ID}
panelTitle="Select Model"
panelClassName="min-w-[280px]"
buttonClassName="bg-transparent p-0 text-lg hover:bg-transparent text-white/80 hover:text-white"
@@ -1,4 +1,4 @@
export * from "./lib/model-selector";
export * from "./lib/model-selector-store";
export * from "./lib/model-lists";
export * from "./lib/model-categories";
export * from "./lib/model-pages";
@@ -1,6 +0,0 @@
export enum ModelCategory {
TextToImage = "text-to-image",
ImageToVideo = "image-to-video",
Canvas2D = "2d-canvas",
Editor3D = "3d-editor",
}
@@ -3,8 +3,12 @@ import { faClock, faFilm, faImage } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
getCreatorIcon,
getModelsByCategory,
getInstructiveImageEditModels,
ModelConfig,
TEXT_TO_IMAGE_PAGE_MODELS,
IMAGE_TO_VIDEO_PAGE_MODELS,
CANVAS_2D_PAGE_MODELS,
STAGE_3D_PAGE_MODELS,
IMAGE_EDITOR_PAGE_MODELS,
} from "@storyteller/model-list";
export type ModelList = Omit<PopoverItem, "selected">[];
@@ -12,45 +16,42 @@ export type ModelList = Omit<PopoverItem, "selected">[];
const withIcon = (creatorIcon: any, fallback: any) => creatorIcon || fallback;
const buildItems = (
models: {
label: string;
description?: string;
badges?: { label: string }[];
info: { creator: any };
}[],
models: ModelConfig[],
fallbackIcon: any
) =>
models.map((m) => ({
label: m.label,
icon: withIcon(getCreatorIcon((m as any).info.creator), fallbackIcon),
description: (m as any).description,
badges: (m as any).badges?.map((b: any) => ({
icon: withIcon(getCreatorIcon(m.info.creator), fallbackIcon),
description: m.description,
badges: m.badges?.map((b) => ({
label: b.label,
icon: <FontAwesomeIcon icon={faClock} />,
})),
modelInfo: (m as any).info,
modelConfig: m, // Access to full object.
modelInfo: m.info,
}));
const imageModels = getModelsByCategory("image");
const videoModels = getModelsByCategory("video");
const instructiveModels = getInstructiveImageEditModels();
export const TEXT_TO_IMAGE_PAGE_MODEL_LIST : ModelList = buildItems(
TEXT_TO_IMAGE_PAGE_MODELS,
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
);
export const allModels = {
video: buildItems(
videoModels as any,
<FontAwesomeIcon icon={faFilm} className="h-4 w-4" />
),
image: buildItems(
imageModels as any,
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
instructiveImageEdits: buildItems(
instructiveModels as any,
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
),
};
export const IMAGE_TO_VIDEO_PAGE_MODEL_LIST : ModelList = buildItems(
IMAGE_TO_VIDEO_PAGE_MODELS,
<FontAwesomeIcon icon={faFilm} className="h-4 w-4" />
);
export const videoGenerationModels: ModelList = allModels.video;
export const imageGenerationModels: ModelList = allModels.image;
export const instructiveImageEditModels: ModelList =
allModels.instructiveImageEdits;
export const CANVAS_2D_PAGE_MODEL_LIST : ModelList = buildItems(
CANVAS_2D_PAGE_MODELS,
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
);
export const STAGE_3D_PAGE_MODEL_LIST : ModelList = buildItems(
STAGE_3D_PAGE_MODELS,
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
);
export const IMAGE_EDITOR_PAGE_MODEL_LIST : ModelList = buildItems(
IMAGE_EDITOR_PAGE_MODELS,
<FontAwesomeIcon icon={faImage} className="h-4 w-4" />
);
@@ -0,0 +1,9 @@
export enum ModelPage {
TextToImage = "text-to-image",
ImageToVideo = "image-to-video",
Canvas2D = "canvas-2d",
Stage3D = "stage-3d",
// The "inpainting + outpainting + text" image editor
ImageEditor = "image-editor",
}
@@ -1,18 +1,18 @@
import { create } from "zustand";
import { ModelCategory } from "./model-categories";
import { ModelPage } from "./model-pages";
interface ModelSelectorState {
selectedModels: { [category in ModelCategory]?: string };
setSelectedModel: (category: ModelCategory, modelLabel: string) => void;
selectedModels: { [page in ModelPage]?: string };
setSelectedModel: (page: ModelPage, modelLabel: string) => void;
}
export const useModelSelectorStore = create<ModelSelectorState>((set) => ({
selectedModels: {},
setSelectedModel: (category, modelLabel) =>
setSelectedModel: (page, modelLabel) =>
set((state) => ({
selectedModels: {
...state.selectedModels,
[category]: modelLabel,
[page]: modelLabel,
},
})),
}));
@@ -1,11 +1,11 @@
import { PopoverMenu, type PopoverItem } from "@storyteller/ui-popover";
import { useModelSelectorStore } from "./model-selector-store";
import { useMemo } from "react";
import { ModelCategory } from "./model-categories";
import { ModelPage } from "./model-pages";
interface ModelSelectorProps {
items: Omit<PopoverItem, "selected">[];
category: ModelCategory;
page: ModelPage;
mode?: "hoverSelect" | "default" | "toggle" | "button";
panelTitle?: string;
buttonClassName?: string;
@@ -16,14 +16,14 @@ interface ModelSelectorProps {
export function ModelSelector({
items,
category,
page,
...popoverProps
}: ModelSelectorProps) {
const { selectedModels, setSelectedModel } = useModelSelectorStore();
const selectedModel = selectedModels[category] || items[0]?.label;
const selectedModel = selectedModels[page] || items[0]?.label;
const handleModelSelect = (item: PopoverItem) => {
setSelectedModel(category, item.label);
setSelectedModel(page, item.label);
};
const modelList = useMemo(
@@ -37,9 +37,11 @@ export const PromptBoxEdit = ({
}: PromptBoxEditProps) => {
const [prompt, setPrompt] = useState("");
const [useSystemPrompt, setUseSystemPrompt] = useState(true);
const [generationCount, setGenerationCount] = useState<number>(
typeof generationCountProp === "number" ? generationCountProp : 1
);
const [generationCountList, setGenerationCountList] = useState<PopoverItem[]>(
[]
);
@@ -53,6 +55,8 @@ export const PromptBoxEdit = ({
}
});
console.log(">>> Model info", modelInfo);
// Sync internal state if a controlled prop is provided
useEffect(() => {
if (typeof generationCountProp === "number") {
@@ -63,6 +67,7 @@ export const PromptBoxEdit = ({
// Build generation count options based on selected model
useEffect(() => {
const caps = getCapabilitiesForModel(modelInfo);
console.log(">>> caps", caps)
const items: PopoverItem[] = Array.from(
{ length: caps.maxGenerationCount },
(_, i) => i + 1
+3 -1
View File
@@ -1,5 +1,7 @@
export * from "./lib/ModelInfo.js";
export * from "./lib/ModelConfig.js";
export * from "./lib/ModelCreator.js";
export * from "./lib/ModelCreatorIcons.js";
export * from "./lib/ModelInfo.js";
export * from "./lib/ModelMapping.js";
export * from "./lib/Models.js";
export * from "./lib/ModelsByPage.js";
@@ -0,0 +1,22 @@
import { ModelInfo } from "./ModelInfo.js";
import { ModelTag } from "./ModelTag.js";
export type ModelCategory = "image" | "video";
// Centralized model capability definition
// For setting options for the model
// TODO: add more capabilities here - BFlat
export interface ModelCapabilities {
maxGenerationCount: number;
}
export interface ModelConfig {
id: string;
label: string; // UI label
description?: string;
badges?: { label: string }[];
category: ModelCategory;
info: ModelInfo;
capabilities: ModelCapabilities;
tags?: (ModelTag|string)[]; // optional tags, e.g. ["instructiveEdit"] - for filtering
}
@@ -0,0 +1,11 @@
export enum ModelTag {
// Models that show up in the 2D and 3D editors
InstructiveEdit = "instructiveEdit",
// Models that show up in the inpainting editor and that use masking.
MaskedInpainting = "maskedInpainting",
// Models that show up in the inpainting editor, but that do not use masking.
NonMaskedInpainting = "nonMaskedInpainting",
}
+32 -22
View File
@@ -1,28 +1,10 @@
import { ModelCreator } from "./ModelCreator.js";
import { ModelInfo } from "./ModelInfo.js";
export type ModelCategory = "image" | "video";
export interface ModelConfig {
id: string;
label: string; // UI label
description?: string;
badges?: { label: string }[];
category: ModelCategory;
info: ModelInfo;
capabilities: ModelCapabilities;
tags?: string[]; // optional tags, e.g. ["instructiveEdit"] - for filtering
}
import { ModelTag } from "./ModelTag.js";
import { ModelConfig, ModelCapabilities, ModelCategory } from "./ModelConfig.js";
const mc = ModelCreator;
// Centralized model capability definition
// For setting options for the model
// TODO: add more capabilities here - BFlat
export interface ModelCapabilities {
maxGenerationCount: number;
}
const DEFAULT_CAPABILITIES: ModelCapabilities = {
maxGenerationCount: 1,
};
@@ -129,8 +111,28 @@ export const ALL_MODELS: ModelConfig[] = [
},
description: "Fast and high-quality model",
badges: [{ label: "20 sec." }],
capabilities: { maxGenerationCount: 4 },
tags: ["instructiveEdit"],
capabilities: {
maxGenerationCount: 4,
},
tags: [
ModelTag.InstructiveEdit,
ModelTag.NonMaskedInpainting,
],
}),
cfg({
id: "flux_pro_inpaint",
category: "image",
info: {
name: "Flux Pro 1 (Inpainting)",
tauri_id: "flux_pro_1",
creator: mc.BlackForestLabs,
},
description: "Fast and high-quality model",
badges: [{ label: "20 sec." }],
capabilities: {
maxGenerationCount: 1 // NB: For some reason Fal only supports ONE image!
},
tags: [ModelTag.MaskedInpainting],
}),
//////////////////////////////
@@ -194,6 +196,9 @@ export const ALL_MODELS: ModelConfig[] = [
}),
];
export const ALL_MODELS_BY_ID : Map<string, ModelConfig> =
new Map(ALL_MODELS.map(model => [model.id, model]));
export const getAllModels = (): ModelConfig[] => ALL_MODELS;
export const getModelsByCategory = (category: ModelCategory): ModelConfig[] =>
@@ -204,6 +209,11 @@ export const getInstructiveImageEditModels = (): ModelConfig[] =>
(m) => m.category === "image" && m.tags?.includes("instructiveEdit")
);
export const getMaskedInpaintModels = (): ModelConfig[] =>
ALL_MODELS.filter(
(m) => m.category === "image" && m.tags?.includes(ModelTag.MaskedInpainting)
);
export const lookupModelByTauriId = (
tauriId: string
): ModelConfig | undefined =>
@@ -0,0 +1,35 @@
import { ALL_MODELS_BY_ID } from "./Models.js";
import { ModelConfig } from "./ModelConfig.js";
import { ModelTag } from "./ModelTag.js";
import { getModelsByCategory } from "./Models.js";
/**
* 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_pro_kontext_max")!,
];