From b24d3e897fb1aa1be63d803d99f0bbc577bd075e Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 16 Aug 2026 23:00:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20=E6=A8=A1=E5=9E=8B=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=20-=20=E7=A6=81=E6=AD=A2=E7=AB=AF=E7=82=B9=E5=85=83?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=BC=AA=E9=80=A0=E8=A7=86=E9=A2=91=E5=88=86?= =?UTF-8?q?=E8=BE=A8=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/lib/channel-model-catalog.ts | 13 ++----------- web/test/channel-model-catalog.test.ts | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/web/src/lib/channel-model-catalog.ts b/web/src/lib/channel-model-catalog.ts index 49b96bda6..d79da97ff 100644 --- a/web/src/lib/channel-model-catalog.ts +++ b/web/src/lib/channel-model-catalog.ts @@ -80,9 +80,7 @@ export function mergeFetchedChannelModelCosts(channel: ModelChannel, catalog: Ch if (!protocol || !capability) continue; const protocolChanged = Boolean(existing && inferredProtocol && existing.protocol !== inferredProtocol); const replaceCapabilityConfig = !existing || catalogBackedCapability || protocolChanged; - const capabilityConfig = replaceCapabilityConfig && (capability === "image" || capability === "video") - ? (catalogBackedCapability ? catalogCapabilityConfig(item, protocol, capability) : defaultModelCapabilityConfig(protocol, item.id)) - : undefined; + const capabilityConfig = replaceCapabilityConfig && (capability === "image" || capability === "video") ? (catalogBackedCapability ? catalogCapabilityConfig(item, protocol, capability) : defaultModelCapabilityConfig(protocol, item.id)) : undefined; if (existing) { next.push({ ...existing, @@ -107,14 +105,7 @@ export function mergeFetchedChannelModelCosts(channel: ModelChannel, catalog: Ch } function hasCatalogCapabilityMetadata(item: ChannelModelCatalogItem) { - return Boolean( - item.modelType || - item.defaultParameters || - item.options || - item.supportsImages !== undefined || - item.minImages !== undefined || - item.maxImages !== undefined, - ); + return Boolean(item.modelType || item.supportedEndpointTypes?.length || item.defaultParameters || item.options || item.supportsImages !== undefined || item.minImages !== undefined || item.maxImages !== undefined); } function catalogCapabilityConfig(item: ChannelModelCatalogItem, protocol: ModelProtocol, capability: "image" | "video"): ModelCapabilityConfig { diff --git a/web/test/channel-model-catalog.test.ts b/web/test/channel-model-catalog.test.ts index a4cd11ac1..1e2faba33 100644 --- a/web/test/channel-model-catalog.test.ts +++ b/web/test/channel-model-catalog.test.ts @@ -105,6 +105,30 @@ describe("public channel model catalog", () => { }); }); + test("treats endpoint-only video metadata as authoritative without inventing 720P", async () => { + const catalog: ChannelModelCatalogItem = { + id: "endpoint-video", + supportedEndpointTypes: ["openai-video"], + }; + const config = configForCatalog([catalog], { videoSeconds: "6", size: "16:9", vquality: "720" }); + const model = "flow::endpoint-video"; + const cost = config.channels[0]!.modelCosts![0]!; + + expect(cost.capability).toBe("video"); + expect(cost.protocol).toBe("newapi-channel-2"); + expect(cost.capabilityConfig?.video?.resolutions).toEqual([]); + expect(cost.capabilityConfig?.video?.defaultResolution).toBe(""); + expect(selectableModelsByCapability(config, "video")).toEqual([model]); + expect(resolveModelRequestConfig(config, model).interfaceType).toBe("newapi-channel-2"); + + const canvasHtml = renderToStaticMarkup(React.createElement(CanvasVideoSettingsPopover, { config, onConfigChange: () => undefined })); + expect(canvasHtml).not.toContain("720P"); + + const createSource = await Bun.file(new URL("../src/pages/create/index.tsx", import.meta.url)).text(); + expect(createSource).toContain('const videoResolutionSupported = props.mode === "video" && resolutions.length > 0;'); + expect(createSource).toContain("...(videoResolutionSupported ? [videoResolutionLabel(props.videoQuality)] : [])"); + }); + test("preserves a manually configured capability profile when a catalog only returns an ID", () => { const channel = createModelChannel({ id: "manual",