export const ATLASCLOUD_BASE_URL = "https://api.atlascloud.ai"; export const ATLASCLOUD_CATALOG_URL = "https://console.atlascloud.ai/api/v1/models"; const COMMON_RATIO_ORDER = [ "auto", "1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "5:4", "4:5", "21:9", ]; const IMAGE_INPUT_KEYS = [ "images", "image_urls", "input_images", "reference_images", "reference_image_urls", "image", "Image", "image_url", "input_image", "reference_image", ]; const RESOLUTION_KEYS = ["resolution", "image_size", "output_resolution"]; const VIDEO_RESOLUTION_KEYS = [ "resolution", "output_resolution", "target_resolution", ]; const ASPECT_RATIO_KEYS = ["aspect_ratio", "aspectRatio", "ratio"]; const OUTPUT_COUNT_KEYS = ["num_images", "number_of_images", "n", "output_count"]; const VIDEO_DURATION_KEYS = ["duration", "video_duration", "seconds"]; const VIDEO_REQUEST_KEYS = new Set([ "model", "prompt", ...VIDEO_RESOLUTION_KEYS, ...ASPECT_RATIO_KEYS, ...VIDEO_DURATION_KEYS, "seed", "watermark", "camera_fixed", "return_last_frame", ]); export interface AtlasCloudCatalogModel { uuid?: string; model: string; type?: string; displayName?: string; profile?: string; logo?: string; avatar?: string; schema?: string; priority?: number; categories?: string[]; tags?: string[]; display_console?: boolean; } export interface AtlasCloudRuntimeConfig { submitEndpoint: string; pollEndpoint: string; propertyNames: string[]; requiredFields: string[]; imageInputs: Array<{ key: string; mode: "array" | "single"; maxItems: number; required: boolean; }>; imageInputKey?: string; imageInputMode?: "array" | "single"; videoClipInput?: { key: string; maxItems: number; required: boolean; itemDefaults: Record; }; resolutionKey?: string; durationKey?: string; aspectRatioKey?: string; outputCountKey?: string; aspectRatioValueMap: Record; resolutionValueMap?: Record; maxReferenceImages: number; maxVideoReferences: number; maxImages: number; defaults: Record; enums: Record>; numericRanges?: Record; sizeValueMap: Record>; } export interface AtlasCloudImageModelPreset { modelId: string; displayName: string; avatarUrl?: string; description: string; supportsVision: boolean; supportsImageGen: boolean; sortOrder: number; capabilities: Record; defaultParams: Record; imageGenConfig: Record; } type JsonRecord = Record; export type AtlasCloudMediaKind = "image" | "video" | "3d"; export function resolveAtlasCloudDisplayName( existingDisplayName: unknown, catalogDisplayName: unknown, modelId: string ) { const existing = String(existingDisplayName || "").trim(); if (existing) return existing; return String(catalogDisplayName || "").trim() || modelId; } export function shouldInsertMissingAtlasCloudPreset( existingMediaKinds: ReadonlySet, mediaKind: string ) { return !existingMediaKinds.has(mediaKind); } function asRecord(value: unknown): JsonRecord { return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {}; } function uniqueStrings(values: unknown[]) { return Array.from( new Set(values.map((value) => String(value || "").trim()).filter(Boolean)) ); } 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); } function getImageInputs(properties: JsonRecord, requiredFields: string[]) { return Object.entries(properties) .filter(([key, property]) => { const normalizedKey = key.toLowerCase(); const record = asRecord(property); const acceptsMediaValue = record.type === "string" || record.type === "array"; return ( IMAGE_INPUT_KEYS.includes(key) || (acceptsMediaValue && normalizedKey.includes("image") && !/(?:size|count|format|resolution|width|height)/.test(normalizedKey)) || (acceptsMediaValue && String(record["x-format"] || "").toLowerCase() === "image") ); }) .map(([key, property]) => { const record = asRecord(property); const mode = record.type === "array" ? "array" : "single"; return { key, mode, maxItems: mode === "array" ? Math.max(1, Math.min(14, Number(record.maxItems || 14))) : 1, required: requiredFields.includes(key), } as const; }) .sort((a, b) => Number(b.required) - Number(a.required)); } function getVideoClipInput(properties: JsonRecord, requiredFields: string[]) { const entry = Object.entries(properties).find(([key, property]) => { const normalizedKey = key.toLowerCase(); const record = asRecord(property); const itemProperties = asRecord(asRecord(record.items).properties); return ( record.type === "array" && normalizedKey.includes("video") && ("url" in itemProperties || normalizedKey.includes("clip")) ); }); if (!entry) return undefined; const [key, property] = entry; const record = asRecord(property); const itemProperties = asRecord(asRecord(record.items).properties); const itemDefaults: Record = {}; for (const [itemKey, itemProperty] of Object.entries(itemProperties)) { if (itemKey === "url") continue; const value = asRecord(itemProperty).default; if (["string", "number", "boolean"].includes(typeof value)) { itemDefaults[itemKey] = value; } } return { key, maxItems: Math.max(1, Math.min(4, Number(record.maxItems || 1))), required: requiredFields.includes(key), itemDefaults, }; } function getEnumValues(property: unknown) { const record = asRecord(property); const values = Array.isArray(record.enum) ? record.enum : []; return values.filter( (value) => typeof value === "string" || typeof value === "number" ) as Array; } function getPostEndpoint(schema: JsonRecord) { const paths = asRecord(schema.paths); return ( Object.entries(paths).find(([, value]) => asRecord(value).post)?.[0] || "/api/v1/model/generateImage" ); } function getPollEndpoint(schema: JsonRecord) { const paths = asRecord(schema.paths); return ( Object.entries(paths).find( ([path, value]) => path.includes("{") && Boolean(asRecord(value).get) )?.[0] || "/api/v1/model/prediction/{request_id}" ); } function getInputSchema(schema: JsonRecord) { const components = asRecord(schema.components); const schemas = asRecord(components.schemas); return asRecord(schemas.Input || schemas.input); } function normalizeKSize(value: unknown) { const match = String(value || "") .trim() .match(/^(1|2|4)\s*k$/i); return match?.[1] ? `${match[1]}K` : ""; } function gcd(a: number, b: number): number { return b ? gcd(b, a % b) : a; } function parsePixelSize(value: unknown) { const match = String(value || "") .trim() .match(/^(\d+)\s*[x*×]\s*(\d+)$/i); if (!match) return null; const width = Number(match[1]); const height = Number(match[2]); if (!width || !height) return null; const divisor = gcd(width, height); return { value: String(value), width, height, ratio: `${Math.round(width / divisor)}:${Math.round(height / divisor)}`, }; } function getPixelTier(width: number, height: number) { const maxSide = Math.max(width, height); if (maxSide <= 1400) return "1K"; if (maxSide <= 2800) return "2K"; return "4K"; } function parseRatio(value: string) { const match = value.match(/^(\d+):(\d+)$/); if (!match) return null; const width = Number(match[1]); const height = Number(match[2]); return width && height ? width / height : null; } function closestCommonRatio(ratio: string) { const target = parseRatio(ratio); if (!target) return ratio; let winner = ratio; let score = Number.POSITIVE_INFINITY; for (const candidate of COMMON_RATIO_ORDER) { const value = parseRatio(candidate); if (!value) continue; const nextScore = Math.abs(Math.log(target / value)); if (nextScore < score) { winner = candidate; score = nextScore; } } return winner; } function normalizeAspectRatioEnum(value: unknown) { const normalized = String(value || "").trim().toLowerCase(); if (normalized === "auto") return "auto"; if (normalized === "adaptive") return "adaptive"; if (/^\d+:\d+$/.test(normalized)) return normalized; if (normalized.startsWith("square")) return "1:1"; const namedRatio = normalized.match( /^(landscape|portrait)[_-](\d+)[_x:](\d+)$/ ); if (!namedRatio) return ""; const [, orientation, first, second] = namedRatio; return orientation === "portrait" ? `${second}:${first}` : `${first}:${second}`; } function normalizeVideoResolution(value: unknown) { const normalized = String(value || "").trim().toLowerCase(); const match = normalized.match(/(?:^|[^0-9])(480|720|1080)p(?:$|[^0-9])/); return match?.[1] ? `${match[1]}p` : ""; } function buildVideoResolutionValueMap( properties: JsonRecord, resolutionKey?: string ) { if (!resolutionKey) return {}; const map: Record = {}; for (const value of getEnumValues(properties[resolutionKey])) { const normalized = normalizeVideoResolution(value); if (!normalized) continue; const raw = String(value); const current = map[normalized]; if (!current || raw.toLowerCase() === normalized) { map[normalized] = raw; } } return map; } function buildNumericRanges(properties: JsonRecord) { const ranges: Record = {}; for (const [key, property] of Object.entries(properties)) { const record = asRecord(property); const min = Number(record.minimum ?? record.min); const max = Number(record.maximum ?? record.max); const range = { ...(Number.isFinite(min) ? { min } : {}), ...(Number.isFinite(max) ? { max } : {}), }; if (Object.keys(range).length > 0) ranges[key] = range; } return ranges; } function deriveVideoDurationOptions( properties: JsonRecord, durationKey?: string ) { if (!durationKey) return []; return Array.from( new Set( getEnumValues(properties[durationKey]) .map(Number) .filter((value) => Number.isFinite(value) && value > 0) .map((value) => Math.round(value)) ) ).sort((a, b) => a - b); } function deriveAtlasCloudVideoModes(params: { catalogModel: AtlasCloudCatalogModel; properties: JsonRecord; requiredFields: string[]; imageInputs: AtlasCloudRuntimeConfig["imageInputs"]; }) { const { catalogModel, properties, requiredFields, imageInputs } = params; const intentText = uniqueStrings([ catalogModel.model, catalogModel.displayName, ...(catalogModel.categories || []), ...(catalogModel.tags || []), ]) .map(normalizeIntentToken) .join(" "); const imageKeys = new Set(imageInputs.map((input) => input.key)); const hasUnsupportedRequiredInput = requiredFields.some((key) => { if (imageKeys.has(key) || VIDEO_REQUEST_KEYS.has(key)) return false; const defaultValue = asRecord(properties[key]).default; return !["string", "number", "boolean"].includes(typeof defaultValue); }); const requiredImages = imageInputs.filter((input) => input.required); const singleImageInputs = imageInputs.filter((input) => input.mode === "single"); const arrayImageInput = imageInputs.find( (input) => input.mode === "array" && input.maxItems > 1 ); const modes: string[] = []; if ( !hasUnsupportedRequiredInput && requiredImages.length === 0 && ("prompt" in properties || intentText.includes("text-to-video")) ) { modes.push("text-to-video"); } if (!hasUnsupportedRequiredInput && imageInputs.length > 0) { if (requiredImages.length <= 1) modes.push("first-frame"); if ( singleImageInputs.length >= 2 || /(?:first|start).*(?:last|end)|(?:last|end).*(?:first|start)/.test(intentText) ) { modes.push("first-last-frame"); } if ( arrayImageInput || imageInputs.length > 2 || /reference-to-video|reference-images/.test(intentText) ) { modes.push("reference-images"); } } return uniqueStrings(modes); } function buildAspectRatioValueMap(properties: JsonRecord) { for (const key of [...ASPECT_RATIO_KEYS, "image_size"]) { if (!(key in properties)) continue; const map: Record = {}; for (const value of getEnumValues(properties[key])) { const ratio = normalizeAspectRatioEnum(value); if (ratio && !map[ratio]) map[ratio] = String(value); } if (Object.keys(map).length > 0) return { key, map }; } return { key: undefined, map: {} }; } function buildSizeValueMap(sizeValues: Array) { const map: Record> = {}; for (const rawValue of sizeValues) { const parsed = parsePixelSize(rawValue); if (!parsed) continue; const tier = getPixelTier(parsed.width, parsed.height); const ratio = closestCommonRatio(parsed.ratio); map[tier] ||= {}; map[tier][ratio] ||= parsed.value; } return map; } function deriveSupportedImageSizes( properties: JsonRecord, resolutionKey: string | undefined, sizeValueMap: Record> ) { const direct = resolutionKey ? getEnumValues(properties[resolutionKey]).map(normalizeKSize).filter(Boolean) : []; return uniqueStrings([...direct, ...Object.keys(sizeValueMap)]).filter((size) => ["1K", "2K", "4K"].includes(size) ); } function deriveAspectRatios( aspectRatioValueMap: Record, sizeValueMap: Record> ) { const fromSizes = Object.values(sizeValueMap).flatMap((entry) => Object.keys(entry) ); const ratios = uniqueStrings([...Object.keys(aspectRatioValueMap), ...fromSizes]); return ratios.sort((a, b) => { const ai = COMMON_RATIO_ORDER.indexOf(a); const bi = COMMON_RATIO_ORDER.indexOf(b); return (ai < 0 ? 999 : ai) - (bi < 0 ? 999 : bi); }); } function buildScalarDefaults(properties: JsonRecord) { const defaults: Record = {}; for (const [key, property] of Object.entries(properties)) { if (["model", "prompt", ...IMAGE_INPUT_KEYS].includes(key)) continue; const value = asRecord(property).default; if (["string", "number", "boolean"].includes(typeof value)) { defaults[key] = value; } } return defaults; } function buildEnums(properties: JsonRecord) { const enums: Record> = {}; for (const [key, property] of Object.entries(properties)) { const values = getEnumValues(property); if (values.length > 0) enums[key] = values; } return enums; } function deriveMaxImages(properties: JsonRecord, outputCountKey?: string) { if (!outputCountKey) return 1; const property = asRecord(properties[outputCountKey]); const enumValues = getEnumValues(property) .map(Number) .filter((value) => Number.isFinite(value) && value > 0); const configuredMax = Number(property.maximum || property.max || 0); return Math.max(1, Math.min(15, configuredMax || Math.max(1, ...enumValues))); } export function createAtlasCloudImagePreset( catalogModel: AtlasCloudCatalogModel, schemaValue: unknown, index = 0 ): AtlasCloudImageModelPreset { const schema = asRecord(schemaValue); const input = getInputSchema(schema); const properties = asRecord(input.properties); const requiredFields = uniqueStrings(Array.isArray(input.required) ? input.required : []); const imageInputs = getImageInputs(properties, requiredFields); const videoClipInput = getVideoClipInput(properties, requiredFields); const imageInputKey = imageInputs[0]?.key; const mediaKind = inferAtlasCloudMediaKind(catalogModel); const resolutionCandidates = mediaKind === "video" ? VIDEO_RESOLUTION_KEYS : RESOLUTION_KEYS; const resolutionKey = resolutionCandidates.find((key) => { if (!(key in properties)) return false; const values = getEnumValues(properties[key]); return ( values.length === 0 || values.some((value) => mediaKind === "video" ? Boolean(normalizeVideoResolution(value)) : Boolean(normalizeKSize(value)) ) ); }); const durationKey = findFirstKey(properties, VIDEO_DURATION_KEYS); const aspectRatioConfig = buildAspectRatioValueMap(properties); const aspectRatioKey = aspectRatioConfig.key; const outputCountKey = findFirstKey(properties, OUTPUT_COUNT_KEYS); const sizeValueMap = buildSizeValueMap(getEnumValues(properties.size)); const supportedImageSizes = mediaKind === "video" ? [] : deriveSupportedImageSizes(properties, resolutionKey, sizeValueMap); const resolutionValueMap = buildVideoResolutionValueMap( properties, mediaKind === "video" ? resolutionKey : undefined ); const supportedVideoResolutions = Object.keys(resolutionValueMap); const durationOptions = deriveVideoDurationOptions(properties, durationKey); const numericRanges = buildNumericRanges(properties); const supportedAspectRatios = deriveAspectRatios( aspectRatioConfig.map, sizeValueMap ); const imageProperty = imageInputKey ? asRecord(properties[imageInputKey]) : {}; const maxReferenceImages = Math.min( 14, imageInputs.reduce((total, input) => total + input.maxItems, 0) ); const maxVideoReferences = videoClipInput?.maxItems || 0; const maxImages = deriveMaxImages(properties, outputCountKey); const categories = uniqueStrings(catalogModel.categories || []).map((value) => value.toLowerCase() ); const supportedModes = mediaKind === "3d" ? derive3DSupportedModes(catalogModel) : mediaKind === "video" ? deriveAtlasCloudVideoModes({ catalogModel, properties, requiredFields, imageInputs, }) : [ ...(categories.some((value) => value.includes("text-to-image")) ? ["text-to-image"] : []), ...(imageInputKey || categories.some((value) => value.includes("image-to-image")) ? ["image-edit", "reference-images"] : []), ...(videoClipInput ? ["reference-video"] : []), ]; if (mediaKind === "image" && supportedModes.length === 0) { supportedModes.push("text-to-image"); } const runtimeConfig: AtlasCloudRuntimeConfig = { submitEndpoint: getPostEndpoint(schema), pollEndpoint: getPollEndpoint(schema), propertyNames: Object.keys(properties), requiredFields, imageInputs, imageInputKey, imageInputMode: imageProperty.type === "array" ? "array" : "single", videoClipInput, resolutionKey, durationKey, aspectRatioKey, outputCountKey, aspectRatioValueMap: aspectRatioConfig.map, resolutionValueMap, maxReferenceImages, maxVideoReferences, maxImages, defaults: buildScalarDefaults(properties), enums: buildEnums(properties), numericRanges, sizeValueMap, }; const supportsSize = Boolean( resolutionKey || Object.keys(sizeValueMap).length > 0 ); const defaultImageSize = supportedImageSizes.find((value) => value === "2K") || supportedImageSizes[0] || "1K"; const defaultAspectRatio = supportedAspectRatios.find((value) => mediaKind === "video" ? value === "adaptive" : value === "auto" ) || supportedAspectRatios.find((value) => value === "1:1") || supportedAspectRatios[0] || (mediaKind === "video" ? "16:9" : "1:1"); const defaultVideoResolution = supportedVideoResolutions.find((value) => value === "720p") || supportedVideoResolutions[0] || "720p"; const durationRange = numericRanges[durationKey || ""] || {}; const defaultDuration = Number(runtimeConfig.defaults[durationKey || ""]) || durationOptions.find((value) => value === 5) || durationOptions[0] || durationRange.min || 5; return { modelId: catalogModel.model, displayName: catalogModel.displayName || catalogModel.model, avatarUrl: catalogModel.avatar || catalogModel.logo || undefined, description: catalogModel.profile || `Atlas Cloud ${mediaKind === "3d" ? "3D" : mediaKind === "video" ? "视频" : "图片"}模型 ${catalogModel.displayName || catalogModel.model}`, supportsVision: Boolean(imageInputKey), supportsImageGen: mediaKind === "image", sortOrder: Math.max(0, 20_000 - index), capabilities: { kind: mediaKind === "3d" ? "3d" : mediaKind === "video" ? "video" : imageInputKey ? "image-edit" : "text-to-image", mediaType: mediaKind, supportedModes, ...(mediaKind === "video" ? { supportedResolutions: supportedVideoResolutions, supportedRatios: supportedAspectRatios, durationOptions, durationRange: { min: durationOptions[0] || durationRange.min || defaultDuration, max: durationOptions[durationOptions.length - 1] || durationRange.max || defaultDuration, }, supportWatermark: "watermark" in properties, supportCameraFixed: "camera_fixed" in properties, supportSeed: "seed" in properties, supportReturnLastFrame: "return_last_frame" in properties, } : {}), maxReferenceImages, maxVideoReferences, supportsMultiImageReference: maxReferenceImages > 1, supportsVideoReference: maxVideoReferences > 0, requiresVideoReference: videoClipInput?.required === true, provider: "atlascloud", schemaUrl: catalogModel.schema || null, }, defaultParams: { ...(mediaKind === "video" ? { resolution: defaultVideoResolution, ratio: defaultAspectRatio, duration: defaultDuration, } : { imageSize: defaultImageSize, aspectRatio: defaultAspectRatio, imageCount: 1, }), runtimeModelId: catalogModel.model, }, imageGenConfig: { apiFormat: mediaKind === "video" ? "atlascloud-unified-video" : "atlascloud-unified-image", maxImages, maxReferenceImages, maxVideoReferences, supportsSize, supports1K: supportedImageSizes.includes("1K"), supports2K: supportedImageSizes.includes("2K"), supports4K: supportedImageSizes.includes("4K"), supportedImageSizes, supportedAspectRatios, videoDurationOptions: durationOptions, supportsSeed: "seed" in properties, supportsNegativePrompt: "negative_prompt" in properties, supportsWatermark: "watermark" in properties, enableLoadingPlaceholder: true, atlasCloud: runtimeConfig, }, }; } async function mapWithConcurrency( values: T[], limit: number, mapper: (value: T, index: number) => Promise ) { const results = new Array(values.length); let cursor = 0; const workers = Array.from({ length: Math.min(limit, values.length) }, async () => { while (cursor < values.length) { const index = cursor; cursor += 1; results[index] = await mapper(values[index], index); } }); await Promise.all(workers); return results; } async function fetchAtlasCloudPresets(mediaTypes: Array<"image" | "video">) { const catalogResponse = await fetch(ATLASCLOUD_CATALOG_URL, { cache: "no-store", headers: { Accept: "application/json", "X-Source-From": "website" }, signal: AbortSignal.timeout(60_000), }); if (!catalogResponse.ok) { throw new Error(`Atlas Cloud 模型目录请求失败(HTTP ${catalogResponse.status})`); } const catalogPayload = await catalogResponse.json(); const catalogModels: AtlasCloudCatalogModel[] = Array.isArray(catalogPayload?.data) ? catalogPayload.data : []; const models = catalogModels .filter( (model: AtlasCloudCatalogModel) => mediaTypes.includes( String(model?.type || "").toLowerCase() as "image" | "video" ) && model?.display_console !== false && Boolean(model?.model) && Boolean(model?.schema) ) .sort( (a: AtlasCloudCatalogModel, b: AtlasCloudCatalogModel) => Number(b.priority || 0) - Number(a.priority || 0) ); const settled = await mapWithConcurrency(models, 10, async (model, index) => { try { const schemaResponse = await fetch(String(model.schema), { cache: "no-store", headers: { Accept: "application/json" }, signal: AbortSignal.timeout(30_000), }); if (!schemaResponse.ok) { throw new Error(`HTTP ${schemaResponse.status}`); } const preset = createAtlasCloudImagePreset( model, await schemaResponse.json(), index ); const supportedModes = Array.isArray(preset.capabilities.supportedModes) ? preset.capabilities.supportedModes : []; if ( preset.capabilities.mediaType === "video" && supportedModes.length === 0 ) { return { preset: null, error: null, skipped: { modelId: model.model, reason: "当前视频工具不支持该模型要求的视频或音频输入", }, }; } return { preset, error: null, skipped: null }; } catch (error) { return { preset: null, error: { modelId: model.model, error: error instanceof Error ? error.message : String(error), }, skipped: null, }; } }); return { presets: settled.flatMap((result) => (result.preset ? [result.preset] : [])), errors: settled.flatMap((result) => (result.error ? [result.error] : [])), skipped: settled.flatMap((result) => (result.skipped ? [result.skipped] : [])), catalogCount: models.length, }; } export function fetchAtlasCloudImagePresets() { return fetchAtlasCloudPresets(["image"]); } export function fetchAtlasCloudVideoPresets() { return fetchAtlasCloudPresets(["video"]); } export function fetchAtlasCloudModelPresets() { return fetchAtlasCloudPresets(["image", "video"]); }