diff --git a/src/app/api/__tests__/popiserverModels.test.ts b/src/app/api/__tests__/popiserverModels.test.ts index 0be78c4e..42e47970 100644 --- a/src/app/api/__tests__/popiserverModels.test.ts +++ b/src/app/api/__tests__/popiserverModels.test.ts @@ -38,14 +38,14 @@ describe("popiserver model helpers", () => { }); it("passes only the model subType filter when listing models", async () => { - const params = new URLSearchParams({ subType: "202,203,204" }); + const params = new URLSearchParams({ subType: "202,203,204,205" }); await fetchPopiModels(makeRequest(), "login-token", undefined, params); expect(global.fetch).toHaveBeenCalledTimes(1); const url = String((global.fetch as unknown as ReturnType).mock.calls[0][0]); const searchParams = new URL(url).searchParams; - expect(searchParams.get("subType")).toBe("202,203,204"); + expect(searchParams.get("subType")).toBe("202,203,204,205"); expect(searchParams.get("origin")).toBe("canvas"); expect(searchParams.has("type")).toBe(false); expect(searchParams.has("subtype")).toBe(false); diff --git a/src/app/api/_popiserverModels.ts b/src/app/api/_popiserverModels.ts index e204f4c4..2ff82f9c 100644 --- a/src/app/api/_popiserverModels.ts +++ b/src/app/api/_popiserverModels.ts @@ -3,6 +3,11 @@ import { ApiError } from "./_errors"; import { logGatewayRequest, logGatewayResponse } from "./_gatewayLogging"; import type { ProviderType } from "@/types"; import type { ModelInput, ModelCapability, ModelParameter, ProviderModel } from "@/lib/providers/types"; +import { + getPopiCapabilitiesForSubType, + getPopiSubTypeFilter, + type PopiModelMediaKind, +} from "@/utils/popiModelClassification"; const POPISERVER_MODEL_LIST_PATH = "/api_client/anime/ai/model/list"; const POPISERVER_MODEL_DETAIL_PATH = "/api_client/anime/ai/model/detail"; @@ -17,13 +22,13 @@ type PopiCategory = { taskSubType?: number | string; }; -export type PopiMediaKind = "image" | "video" | "audio" | "llm"; +export type PopiMediaKind = PopiModelMediaKind; export const POPI_MODEL_FILTERS: Record = { - image: { subType: "102,103,106" }, - video: { subType: "202,203,204" }, - audio: { subType: "301" }, - llm: { subType: "501" }, + image: { subType: getPopiSubTypeFilter("image") }, + video: { subType: getPopiSubTypeFilter("video") }, + audio: { subType: getPopiSubTypeFilter("audio") }, + llm: { subType: getPopiSubTypeFilter("llm") }, }; export type PopiAIModel = { @@ -88,25 +93,6 @@ export type SchemaErrorResponse = { export type SchemaResponse = SchemaSuccessResponse | SchemaErrorResponse; -const SUBTYPE_TO_CAPABILITIES: Record = { - 101: ["text-to-image", "image-to-image"], - 102: ["text-to-image"], - 103: ["image-to-image"], - 104: ["image-to-image"], - 105: ["image-to-image"], - 106: ["image-to-image"], - 107: ["image-to-image"], - 108: ["image-to-image"], - 109: ["image-to-image"], - 201: ["text-to-video"], - 202: ["image-to-video"], - 203: ["image-to-video"], - 204: ["image-to-video"], - 205: ["image-to-video"], - 301: ["text-to-audio"], - 302: ["text-to-audio"], -}; - export function getPopiserverBaseUrl(request: NextRequest): string { const requestOrigin = "nextUrl" in request && request.nextUrl @@ -178,23 +164,10 @@ function getTaskSubTypes(model: PopiAIModel): Set { export function inferPopiModelCapabilities(model: PopiAIModel): ModelCapability[] { const capabilities = new Set(); for (const subType of getTaskSubTypes(model)) { - for (const capability of SUBTYPE_TO_CAPABILITIES[subType] || []) { + for (const capability of getPopiCapabilitiesForSubType(subType)) { capabilities.add(capability); } } - - if (capabilities.size === 0) { - if (model.isSupportVideos) { - capabilities.add("text-to-video"); - if (model.isSupportImages) capabilities.add("image-to-video"); - } else if (model.isSupportAudios) { - capabilities.add("text-to-audio"); - } else { - capabilities.add("text-to-image"); - if (model.isSupportImages) capabilities.add("image-to-image"); - } - } - return [...capabilities]; } @@ -238,6 +211,7 @@ export function popiMediaKindForCapabilities( capabilities: ModelCapability[] | null ): PopiMediaKind | null { if (!capabilities || capabilities.length === 0) return null; + if (capabilities.some((capability) => capability === "text-to-text")) return "llm"; if (capabilities.some((capability) => capability.includes("audio"))) return "audio"; if (capabilities.some((capability) => capability.includes("video"))) return "video"; if (capabilities.some((capability) => capability.includes("image"))) return "image"; diff --git a/src/app/api/models/route.ts b/src/app/api/models/route.ts index 8a69c639..17b47186 100644 --- a/src/app/api/models/route.ts +++ b/src/app/api/models/route.ts @@ -1137,7 +1137,9 @@ export async function GET( } const popiModels = await fetchPopiModels(request, popiLoginToken, searchQuery, popiserverParams); - const providerModels = popiModels.map(popiModelToProviderModel); + const providerModels = popiModels + .map(popiModelToProviderModel) + .filter((model) => model.capabilities.length > 0); const filteredModels = filterProviderModelsByCapabilities(providerModels, capabilitiesFilter); return NextResponse.json({ diff --git a/src/components/FloatingActionBar.tsx b/src/components/FloatingActionBar.tsx index 166aa1e1..8d345a42 100644 --- a/src/components/FloatingActionBar.tsx +++ b/src/components/FloatingActionBar.tsx @@ -17,7 +17,6 @@ const ALL_NODES_CATEGORIES: { labelKey: TranslationKey; nodes: { type: NodeType; { type: "imageInput", labelKey: "node.imageInput" }, { type: "audioInput", labelKey: "node.audioInput" }, { type: "videoInput", labelKey: "node.videoInput" }, - { type: "glbViewer", labelKey: "node.glbViewer" }, ], }, { @@ -33,7 +32,6 @@ const ALL_NODES_CATEGORIES: { labelKey: TranslationKey; nodes: { type: NodeType; nodes: [ { type: "nanoBanana", labelKey: "node.generateImage" }, { type: "generateVideo", labelKey: "node.generateVideo" }, - { type: "generate3d", labelKey: "node.generate3d" }, { type: "generateAudio", labelKey: "node.generateAudio" }, { type: "llmGenerate", labelKey: "node.llmGenerate" }, ], @@ -251,15 +249,17 @@ function GenerateComboButton() { {t("toolbar.video")}