fix: classify media models and open canvas chat
This commit is contained in:
parent
e8d1aa25aa
commit
fd47e0ae61
@ -173,6 +173,7 @@ import {
|
||||
getImageSizeCreditMultiplier,
|
||||
} from "@/lib/ai/image-model-pricing";
|
||||
import {
|
||||
is3DModelDescriptor,
|
||||
isImageGenerationModelDescriptor,
|
||||
isVideoModelDescriptor,
|
||||
} from "@/lib/ai/model-classification";
|
||||
@ -1586,7 +1587,7 @@ const PROVIDER_TYPES = [
|
||||
{ value: "openai-image", label: "OpenAI 图片兼容" },
|
||||
{ value: "bananaimage", label: "BananaImage 生图" },
|
||||
{ value: "visionary", label: "Visionary 生图" },
|
||||
{ value: "atlascloud", label: "Atlas Cloud 图片模型" },
|
||||
{ value: "atlascloud", label: "Atlas Cloud 图片 / 3D 模型" },
|
||||
{ value: "stepfun-image", label: "阶跃星辰 Step Image Edit" },
|
||||
{ value: "bltcy-video", label: "柏拉图统一视频" },
|
||||
{ value: "grsai-gpt-image", label: "Grsai GPT Image" },
|
||||
@ -2083,10 +2084,23 @@ export function AdminSettingsClient() {
|
||||
|
||||
const detailAgentModelOptions = useMemo(
|
||||
() =>
|
||||
(modelsData?.models || []).filter(
|
||||
(model) => !model.supportsImageGen && model.isEnabled
|
||||
),
|
||||
[modelsData?.models]
|
||||
(modelsData?.models || []).filter((model) => {
|
||||
if (model.supportsImageGen || !model.isEnabled) return false;
|
||||
const provider = (providersData?.providers || []).find(
|
||||
(item) => item.id === model.providerId
|
||||
);
|
||||
const descriptor = {
|
||||
providerType: provider?.type,
|
||||
modelId: model.modelId,
|
||||
capabilities: (model as any).capabilities,
|
||||
supportsImageGen: model.supportsImageGen,
|
||||
};
|
||||
return (
|
||||
!is3DModelDescriptor(descriptor) &&
|
||||
!isVideoModelDescriptor(descriptor)
|
||||
);
|
||||
}),
|
||||
[modelsData?.models, providersData?.providers]
|
||||
);
|
||||
const imageGenerationModelOptions = useMemo(
|
||||
() =>
|
||||
@ -4176,6 +4190,11 @@ export function AdminSettingsClient() {
|
||||
capabilities: modelForm.capabilities,
|
||||
supportsImageGen: modelForm.supportsImageGen,
|
||||
});
|
||||
const is3DProviderForModelConfig = is3DModelDescriptor({
|
||||
providerType: selectedModelProviderType,
|
||||
modelId: modelForm.modelId,
|
||||
capabilities: modelForm.capabilities,
|
||||
});
|
||||
const isImageProviderForModelConfig = isImageGenerationModelDescriptor({
|
||||
providerType: selectedModelProviderType,
|
||||
modelId: modelForm.modelId,
|
||||
@ -4198,7 +4217,9 @@ export function AdminSettingsClient() {
|
||||
? `${
|
||||
selectedModelProviderType === "krea" ? "Krea" : "Runway"
|
||||
} 同时支持图片与视频模型,填写模型 ID 后系统会自动识别标签。`
|
||||
: isVideoProviderForModelConfig
|
||||
: is3DProviderForModelConfig
|
||||
? "当前识别为 3D 模型,将与图片、视频和对话模型分开管理。"
|
||||
: isVideoProviderForModelConfig
|
||||
? "当前识别为视频模型,将使用视频能力、视频积分和视频路由。"
|
||||
: isImageProviderForModelConfig
|
||||
? "当前识别为图片模型,将使用生图能力、图片积分和生图路由。"
|
||||
@ -6452,7 +6473,7 @@ export function AdminSettingsClient() {
|
||||
} catch (modelError) {
|
||||
console.error("[Auto Add Atlas Cloud Models] Error:", modelError);
|
||||
toast.warning(
|
||||
'Atlas Cloud 模型同步失败,请手动点击"同步 Atlas Cloud 全部图片模型"按钮'
|
||||
'Atlas Cloud 模型同步失败,请手动点击"同步 Atlas Cloud 图片 / 3D 模型"按钮'
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -7513,7 +7534,7 @@ export function AdminSettingsClient() {
|
||||
if (
|
||||
!options?.skipConfirm &&
|
||||
!confirm(
|
||||
"将从 Atlas Cloud 官方目录同步全部开放图片模型,并读取每个模型的参数 Schema。同步数量较多,可能需要几十秒。\n\n继续吗?"
|
||||
"将从 Atlas Cloud 官方目录同步全部开放图片与 3D 模型,并读取每个模型的参数 Schema。同步数量较多,可能需要几十秒。\n\n继续吗?"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
@ -7532,7 +7553,7 @@ export function AdminSettingsClient() {
|
||||
const totalSynced =
|
||||
Number(data.addedCount || 0) + Number(data.updatedCount || 0);
|
||||
toast.success(
|
||||
`已同步 ${totalSynced} 个 Atlas Cloud 图片模型(官方目录共 ${Number(data.counts?.total || totalSynced)} 个)`
|
||||
`已同步 ${totalSynced} 个 Atlas Cloud 图片 / 3D 模型(官方目录共 ${Number(data.counts?.total || totalSynced)} 个)`
|
||||
);
|
||||
mutateModels();
|
||||
if (data.errors?.length) {
|
||||
@ -7540,7 +7561,7 @@ export function AdminSettingsClient() {
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("[Quick Add Atlas Cloud Models] Error:", error);
|
||||
toast.error(error.message || "同步 Atlas Cloud 图片模型失败");
|
||||
toast.error(error.message || "同步 Atlas Cloud 图片 / 3D 模型失败");
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@ -15764,7 +15785,7 @@ export function AdminSettingsClient() {
|
||||
{providerForm.type === "atlascloud" && (
|
||||
<p className="text-muted-foreground text-xs">
|
||||
Atlas Cloud 使用 Bearer API Key 认证,默认地址
|
||||
https://api.atlascloud.ai。保存后会从官方目录同步全部开放图片模型,
|
||||
https://api.atlascloud.ai。保存后会从官方目录同步全部开放图片与 3D 模型,
|
||||
并按每个模型自己的 Schema 配置多图数量、比例及 1K/2K/4K 档位。
|
||||
</p>
|
||||
)}
|
||||
@ -15922,7 +15943,7 @@ export function AdminSettingsClient() {
|
||||
variant="secondary"
|
||||
>
|
||||
<ImageIcon className="mr-1 h-4 w-4" />
|
||||
同步 Atlas Cloud 全部图片模型
|
||||
同步 Atlas Cloud 图片 / 3D 模型
|
||||
</Button>
|
||||
)}
|
||||
{provider.type === "bltcy-video" && (
|
||||
@ -17417,6 +17438,11 @@ export function AdminSettingsClient() {
|
||||
capabilities: (model as any).capabilities,
|
||||
supportsImageGen: model.supportsImageGen,
|
||||
});
|
||||
const is3DModel = is3DModelDescriptor({
|
||||
providerType: provider?.type,
|
||||
modelId: model.modelId,
|
||||
capabilities: (model as any).capabilities,
|
||||
});
|
||||
const isImageModel = isImageGenerationModelDescriptor({
|
||||
providerType: provider?.type,
|
||||
modelId: model.modelId,
|
||||
@ -17479,6 +17505,11 @@ export function AdminSettingsClient() {
|
||||
Agent
|
||||
</span>
|
||||
)}
|
||||
{is3DModel && (
|
||||
<span className="rounded bg-amber-100 px-2 py-0.5 text-amber-800 text-xs">
|
||||
3D模型
|
||||
</span>
|
||||
)}
|
||||
{isImageModel && (
|
||||
<span className="rounded bg-sky-100 px-2 py-0.5 text-sky-800 text-xs">
|
||||
图片模型
|
||||
@ -17489,7 +17520,7 @@ export function AdminSettingsClient() {
|
||||
视频模型
|
||||
</span>
|
||||
)}
|
||||
{!isImageModel && !isVideoModel && (
|
||||
{!is3DModel && !isImageModel && !isVideoModel && (
|
||||
<span className="rounded bg-slate-100 px-2 py-0.5 text-slate-700 text-xs">
|
||||
对话模型
|
||||
</span>
|
||||
|
||||
@ -79,7 +79,7 @@ export async function POST(request: Request) {
|
||||
avatarUrl: preset.avatarUrl || null,
|
||||
description: preset.description,
|
||||
supportsVision: preset.supportsVision,
|
||||
supportsImageGen: true,
|
||||
supportsImageGen: preset.supportsImageGen,
|
||||
isEnabled: true,
|
||||
isHidden: false,
|
||||
sortOrder: preset.sortOrder,
|
||||
|
||||
@ -2013,6 +2013,7 @@ export default function CanvasEditorLegacyPage({
|
||||
useState(false);
|
||||
// 面板模式: 'chat' 聊天面板, 'fileList' 文件列表
|
||||
const [panelMode, setPanelMode] = useState<"chat" | "fileList">("chat");
|
||||
const chatPanelCanvasIdRef = useRef<string | null>(null);
|
||||
const [viewportWidth, setViewportWidth] = useState(0);
|
||||
const [horizontalFloatingToolbarWidth, setHorizontalFloatingToolbarWidth] =
|
||||
useState(0);
|
||||
@ -2036,6 +2037,16 @@ export default function CanvasEditorLegacyPage({
|
||||
Partial<Record<AgentWorkflowStepKey, string>>
|
||||
>({});
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!id || isVideoMode || chatPanelCanvasIdRef.current === id) {
|
||||
return;
|
||||
}
|
||||
|
||||
chatPanelCanvasIdRef.current = id;
|
||||
setPanelMode("chat");
|
||||
setIsPanelOpen(true);
|
||||
}, [id, isVideoMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVideoMode) {
|
||||
setVideoCanvasInsertMenu(null);
|
||||
|
||||
@ -77,7 +77,7 @@ export interface AtlasCloudImageModelPreset {
|
||||
avatarUrl?: string;
|
||||
description: string;
|
||||
supportsVision: boolean;
|
||||
supportsImageGen: true;
|
||||
supportsImageGen: boolean;
|
||||
sortOrder: number;
|
||||
capabilities: Record<string, unknown>;
|
||||
defaultParams: Record<string, unknown>;
|
||||
@ -86,6 +86,8 @@ export interface AtlasCloudImageModelPreset {
|
||||
|
||||
type JsonRecord = Record<string, any>;
|
||||
|
||||
export type AtlasCloudMediaKind = "image" | "video" | "3d";
|
||||
|
||||
function asRecord(value: unknown): JsonRecord {
|
||||
return value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as JsonRecord)
|
||||
@ -98,6 +100,67 @@ function uniqueStrings(values: unknown[]) {
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeIntentToken(value: unknown) {
|
||||
return String(value || "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replaceAll("_", "-");
|
||||
}
|
||||
|
||||
export function inferAtlasCloudMediaKind(
|
||||
catalogModel: AtlasCloudCatalogModel
|
||||
): AtlasCloudMediaKind {
|
||||
const catalogType = normalizeIntentToken(catalogModel.type);
|
||||
const intentTokens = uniqueStrings([
|
||||
catalogModel.model,
|
||||
catalogModel.displayName,
|
||||
...(catalogModel.categories || []),
|
||||
...(catalogModel.tags || []),
|
||||
]).map(normalizeIntentToken);
|
||||
const intentText = intentTokens.join(" ");
|
||||
|
||||
if (
|
||||
/(?:^|[\s/.-])(?:text|image)-to-3d(?:$|[\s/.-])/.test(intentText) ||
|
||||
/(?:^|[\s/.-])(?:hunyuan3d|seed3d|tripo-h3(?:\.\d+)?)(?:$|[\s/.-])/.test(
|
||||
intentText
|
||||
)
|
||||
) {
|
||||
return "3d";
|
||||
}
|
||||
|
||||
if (
|
||||
catalogType === "video" ||
|
||||
intentTokens.some((token) =>
|
||||
[
|
||||
"text-to-video",
|
||||
"image-to-video",
|
||||
"video-to-video",
|
||||
"audio-to-video",
|
||||
].includes(token)
|
||||
)
|
||||
) {
|
||||
return "video";
|
||||
}
|
||||
|
||||
return "image";
|
||||
}
|
||||
|
||||
function derive3DSupportedModes(catalogModel: AtlasCloudCatalogModel) {
|
||||
const intentText = uniqueStrings([
|
||||
catalogModel.model,
|
||||
catalogModel.displayName,
|
||||
...(catalogModel.categories || []),
|
||||
...(catalogModel.tags || []),
|
||||
])
|
||||
.map(normalizeIntentToken)
|
||||
.join(" ");
|
||||
const modes = [
|
||||
...(intentText.includes("text-to-3d") ? ["text-to-3d"] : []),
|
||||
...(intentText.includes("image-to-3d") ? ["image-to-3d"] : []),
|
||||
];
|
||||
return modes.length > 0 ? modes : ["3d"];
|
||||
}
|
||||
|
||||
function findFirstKey(properties: JsonRecord, candidates: string[]) {
|
||||
return candidates.find((key) => key in properties);
|
||||
}
|
||||
@ -361,15 +424,22 @@ export function createAtlasCloudImagePreset(
|
||||
const categories = uniqueStrings(catalogModel.categories || []).map((value) =>
|
||||
value.toLowerCase()
|
||||
);
|
||||
const supportedModes = [
|
||||
...(categories.some((value) => value.includes("text-to-image"))
|
||||
? ["text-to-image"]
|
||||
: []),
|
||||
...(imageInputKey || categories.some((value) => value.includes("image-to-image"))
|
||||
? ["image-edit", "reference-images"]
|
||||
: []),
|
||||
];
|
||||
if (supportedModes.length === 0) supportedModes.push("text-to-image");
|
||||
const mediaKind = inferAtlasCloudMediaKind(catalogModel);
|
||||
const supportedModes =
|
||||
mediaKind === "3d"
|
||||
? derive3DSupportedModes(catalogModel)
|
||||
: [
|
||||
...(categories.some((value) => value.includes("text-to-image"))
|
||||
? ["text-to-image"]
|
||||
: []),
|
||||
...(imageInputKey ||
|
||||
categories.some((value) => value.includes("image-to-image"))
|
||||
? ["image-edit", "reference-images"]
|
||||
: []),
|
||||
];
|
||||
if (mediaKind === "image" && supportedModes.length === 0) {
|
||||
supportedModes.push("text-to-image");
|
||||
}
|
||||
|
||||
const runtimeConfig: AtlasCloudRuntimeConfig = {
|
||||
submitEndpoint: getPostEndpoint(schema),
|
||||
@ -409,12 +479,20 @@ export function createAtlasCloudImagePreset(
|
||||
avatarUrl: catalogModel.avatar || catalogModel.logo || undefined,
|
||||
description:
|
||||
catalogModel.profile ||
|
||||
`Atlas Cloud 图片模型 ${catalogModel.displayName || catalogModel.model}`,
|
||||
`Atlas Cloud ${mediaKind === "3d" ? "3D" : mediaKind === "video" ? "视频" : "图片"}模型 ${catalogModel.displayName || catalogModel.model}`,
|
||||
supportsVision: Boolean(imageInputKey),
|
||||
supportsImageGen: true,
|
||||
supportsImageGen: mediaKind === "image",
|
||||
sortOrder: Math.max(0, 20_000 - index),
|
||||
capabilities: {
|
||||
kind: imageInputKey ? "image-edit" : "text-to-image",
|
||||
kind:
|
||||
mediaKind === "3d"
|
||||
? "3d"
|
||||
: mediaKind === "video"
|
||||
? "video"
|
||||
: imageInputKey
|
||||
? "image-edit"
|
||||
: "text-to-image",
|
||||
mediaType: mediaKind,
|
||||
supportedModes,
|
||||
maxReferenceImages,
|
||||
supportsMultiImageReference: maxReferenceImages > 1,
|
||||
|
||||
@ -21,12 +21,17 @@ const STATIC_IMAGE_PROVIDER_TYPES = new Set([
|
||||
"openai-image",
|
||||
"bananaimage",
|
||||
"visionary",
|
||||
"atlascloud",
|
||||
"stepfun-image",
|
||||
"midjourney",
|
||||
"volcano-vision",
|
||||
]);
|
||||
|
||||
const STATIC_3D_PROVIDER_TYPES = new Set([
|
||||
"tripo-3d",
|
||||
"worldlabs-3d",
|
||||
"kittycad-cad",
|
||||
]);
|
||||
|
||||
const RUNWAY_IMAGE_MODEL_IDS = new Set([
|
||||
"gen4_image",
|
||||
"gen4_image_turbo",
|
||||
@ -47,10 +52,42 @@ const RUNWAY_VIDEO_MODEL_PREFIXES = [
|
||||
|
||||
const VIDEO_MODE_KEYS = new Set([
|
||||
"text-to-video",
|
||||
"image-to-video",
|
||||
"video-to-video",
|
||||
"audio-to-video",
|
||||
"first-frame",
|
||||
"first-last-frame",
|
||||
]);
|
||||
|
||||
const VIDEO_KIND_KEYS = new Set([
|
||||
"video",
|
||||
"text-to-video",
|
||||
"image-to-video",
|
||||
"video-to-video",
|
||||
"audio-to-video",
|
||||
]);
|
||||
|
||||
const THREE_D_MODE_KEYS = new Set([
|
||||
"3d",
|
||||
"text-to-3d",
|
||||
"text_to_3d",
|
||||
"image-to-3d",
|
||||
"image_to_3d",
|
||||
"mesh-generation",
|
||||
"mesh_generation",
|
||||
"cad",
|
||||
]);
|
||||
|
||||
const THREE_D_KIND_KEYS = new Set([
|
||||
"3d",
|
||||
"model-3d",
|
||||
"model_3d",
|
||||
"mesh",
|
||||
"mesh-generation",
|
||||
"mesh_generation",
|
||||
"cad",
|
||||
]);
|
||||
|
||||
const TEXT_TO_IMAGE_MODE_KEYS = new Set([
|
||||
"text-to-image",
|
||||
"text2image",
|
||||
@ -107,6 +144,48 @@ function hasVideoModes(capabilities: unknown) {
|
||||
return supportedModes.some((mode) => VIDEO_MODE_KEYS.has(String(mode || "").trim()));
|
||||
}
|
||||
|
||||
function hasVideoKind(capabilities: unknown) {
|
||||
return ["kind", "mediaType", "modelType", "outputType"].some((key) =>
|
||||
VIDEO_KIND_KEYS.has(getStringField(capabilities, key))
|
||||
);
|
||||
}
|
||||
|
||||
function has3DModes(capabilities: unknown) {
|
||||
const supportedModes = getStringArrayField(capabilities, "supportedModes");
|
||||
return supportedModes.some((mode) => THREE_D_MODE_KEYS.has(mode));
|
||||
}
|
||||
|
||||
function has3DKind(capabilities: unknown) {
|
||||
return ["kind", "mediaType", "modelType", "outputType"].some((key) =>
|
||||
THREE_D_KIND_KEYS.has(getStringField(capabilities, key))
|
||||
);
|
||||
}
|
||||
|
||||
function modelIdLooks3D(modelId?: string | null) {
|
||||
const normalized = normalizeText(modelId);
|
||||
if (!normalized) return false;
|
||||
return (
|
||||
/(?:^|[/_.-])(?:text|image)[_-]to[_-]3d(?:$|[/_.-])/.test(normalized) ||
|
||||
/(?:^|[/_.-])(?:hunyuan3d|seed3d|tripo-h3(?:\.\d+)?)(?:$|[/_.-])/.test(
|
||||
normalized
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function is3DModelDescriptor(params: {
|
||||
providerType?: string | null;
|
||||
modelId?: string | null;
|
||||
capabilities?: unknown;
|
||||
}): boolean {
|
||||
const providerType = normalizeText(params.providerType);
|
||||
return (
|
||||
STATIC_3D_PROVIDER_TYPES.has(providerType) ||
|
||||
has3DModes(params.capabilities) ||
|
||||
has3DKind(params.capabilities) ||
|
||||
modelIdLooks3D(params.modelId)
|
||||
);
|
||||
}
|
||||
|
||||
export function inferRunwayModelKind(modelId?: string | null): "image" | "video" | null {
|
||||
const normalized = normalizeText(modelId);
|
||||
if (!normalized) return null;
|
||||
@ -129,11 +208,15 @@ export function isVideoModelDescriptor(params: {
|
||||
}): boolean {
|
||||
const providerType = normalizeText(params.providerType);
|
||||
|
||||
if (is3DModelDescriptor(params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isStaticVideoProviderType(providerType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasVideoModes(params.capabilities)) {
|
||||
if (hasVideoModes(params.capabilities) || hasVideoKind(params.capabilities)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -156,6 +239,10 @@ export function isImageGenerationModelDescriptor(params: {
|
||||
}): boolean {
|
||||
const providerType = normalizeText(params.providerType);
|
||||
|
||||
if (is3DModelDescriptor(params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (providerType === "runway") {
|
||||
const runwayModelKind = inferRunwayModelKind(params.modelId);
|
||||
if (runwayModelKind === "image") return true;
|
||||
|
||||
@ -1,35 +1,95 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import {
|
||||
createAtlasCloudImagePreset,
|
||||
inferAtlasCloudMediaKind,
|
||||
} from "@/lib/ai/atlascloud-models";
|
||||
import {
|
||||
is3DModelDescriptor,
|
||||
isImageGenerationModelDescriptor,
|
||||
isVideoModelDescriptor,
|
||||
} from "@/lib/ai/model-classification";
|
||||
|
||||
test.describe("model classification", () => {
|
||||
test("keeps multi-reference BananaImage models classified as image models", () => {
|
||||
const atlasHunyuan3D = {
|
||||
model: "tencent/hunyuan3d-pro/text-to-3d",
|
||||
type: "Image",
|
||||
displayName: "Hunyuan 3D Pro Text-to-3D",
|
||||
categories: ["TEXT-TO-3D"],
|
||||
tags: ["TEXT-TO-3D", "NEW"],
|
||||
};
|
||||
|
||||
test.describe("AI model media classification", () => {
|
||||
test("keeps Atlas 3D entries out of image and video categories", () => {
|
||||
expect(inferAtlasCloudMediaKind(atlasHunyuan3D)).toBe("3d");
|
||||
|
||||
const descriptor = {
|
||||
providerType: "bananaimage",
|
||||
modelId: "gemini-3-pro-image-preview",
|
||||
providerType: "atlascloud",
|
||||
modelId: atlasHunyuan3D.model,
|
||||
capabilities: {
|
||||
supportedModes: [
|
||||
"text-to-image",
|
||||
"image-to-image",
|
||||
"reference-images",
|
||||
],
|
||||
kind: "3d",
|
||||
mediaType: "3d",
|
||||
supportedModes: ["text-to-3d"],
|
||||
},
|
||||
supportsImageGen: false,
|
||||
};
|
||||
|
||||
expect(is3DModelDescriptor(descriptor)).toBe(true);
|
||||
expect(isImageGenerationModelDescriptor(descriptor)).toBe(false);
|
||||
expect(isVideoModelDescriptor(descriptor)).toBe(false);
|
||||
});
|
||||
|
||||
test("writes 3D Atlas presets with model-level capabilities", () => {
|
||||
const preset = createAtlasCloudImagePreset(
|
||||
atlasHunyuan3D,
|
||||
{
|
||||
components: {
|
||||
schemas: {
|
||||
Input: {
|
||||
type: "object",
|
||||
properties: {
|
||||
model: { type: "string" },
|
||||
prompt: { type: "string" },
|
||||
},
|
||||
required: ["model", "prompt"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
0
|
||||
);
|
||||
|
||||
expect(preset.supportsImageGen).toBe(false);
|
||||
expect(preset.capabilities).toMatchObject({
|
||||
kind: "3d",
|
||||
mediaType: "3d",
|
||||
supportedModes: ["text-to-3d"],
|
||||
});
|
||||
});
|
||||
|
||||
test("still distinguishes Atlas image and video descriptors", () => {
|
||||
const imageDescriptor = {
|
||||
providerType: "atlascloud",
|
||||
modelId: "black-forest-labs/flux-2-pro/text-to-image",
|
||||
capabilities: {
|
||||
kind: "text-to-image",
|
||||
mediaType: "image",
|
||||
supportedModes: ["text-to-image"],
|
||||
},
|
||||
supportsImageGen: true,
|
||||
};
|
||||
const videoDescriptor = {
|
||||
providerType: "atlascloud",
|
||||
modelId: "veed/fabric-1.0/image-to-video",
|
||||
capabilities: {
|
||||
kind: "video",
|
||||
mediaType: "video",
|
||||
supportedModes: ["image-to-video"],
|
||||
},
|
||||
supportsImageGen: false,
|
||||
};
|
||||
|
||||
expect(isVideoModelDescriptor(descriptor)).toBe(false);
|
||||
expect(isImageGenerationModelDescriptor(descriptor)).toBe(true);
|
||||
});
|
||||
|
||||
test("still recognizes explicit video generation modes", () => {
|
||||
expect(
|
||||
isVideoModelDescriptor({
|
||||
providerType: "custom",
|
||||
modelId: "custom-video-model",
|
||||
capabilities: { supportedModes: ["text-to-video"] },
|
||||
})
|
||||
).toBe(true);
|
||||
expect(isImageGenerationModelDescriptor(imageDescriptor)).toBe(true);
|
||||
expect(isVideoModelDescriptor(imageDescriptor)).toBe(false);
|
||||
expect(isVideoModelDescriptor(videoDescriptor)).toBe(true);
|
||||
expect(isImageGenerationModelDescriptor(videoDescriptor)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user