export const THREED_MODEL_ALIAS_CONFIG_SETTING_KEY = "threed_model_alias_config"; export type ThreeDModelAliasEntry = { id: string; displayName: string; realModelId: string; description: string; enabled: boolean; isDefault: boolean; }; export type ThreeDModelFeatureVisibilityConfig = { pbr: boolean; quad: boolean; smartLowPoly: boolean; }; export type ThreeDModelAliasConfig = { tripoModels: ThreeDModelAliasEntry[]; worldModels: ThreeDModelAliasEntry[]; featureVisibility: ThreeDModelFeatureVisibilityConfig; }; export type PublicThreeDModelAliasEntry = Omit< ThreeDModelAliasEntry, "realModelId" > & { billingModelId?: string; }; export type PublicThreeDModelAliasConfig = { tripoModels: PublicThreeDModelAliasEntry[]; worldModels: PublicThreeDModelAliasEntry[]; featureVisibility: ThreeDModelFeatureVisibilityConfig; }; const DEFAULT_TRIPO_MODEL_ALIASES: ThreeDModelAliasEntry[] = [ { id: "tripo-standard", displayName: "标准模型", realModelId: "Turbo-v1.0-20250506", description: "默认高速高质量模型,适合大多数 3D 生成场景。", enabled: true, isDefault: true, }, { id: "tripo-detail", displayName: "高精模型", realModelId: "v3.1-20260211", description: "偏向细节质量和结构还原,适合高要求模型生成。", enabled: true, isDefault: false, }, { id: "tripo-balance", displayName: "均衡模型", realModelId: "v3.0-20250812", description: "速度与质量更均衡,适合日常生产。", enabled: true, isDefault: false, }, { id: "tripo-classic", displayName: "经典模型", realModelId: "v2.5-20250123", description: "兼顾稳定性与兼容性,适合保守方案。", enabled: true, isDefault: false, }, { id: "tripo-stable", displayName: "稳定模型", realModelId: "v2.0-20240919", description: "老版本稳定模型,适合兼容历史流程。", enabled: true, isDefault: false, }, { id: "tripo-legacy", displayName: "兼容模型", realModelId: "v1.4-20240625", description: "更旧的兼容模型,适合特定老任务回放。", enabled: true, isDefault: false, }, ]; const DEFAULT_WORLD_MODEL_ALIASES: ThreeDModelAliasEntry[] = [ { id: "world-standard", displayName: "标准质量", realModelId: "world-generation", description: "默认世界生成质量,适合大多数空间创建。", enabled: true, isDefault: true, }, { id: "world-fidelity", displayName: "高保真", realModelId: "world-generation", description: "更高保真与细节表现,适合高质量空间生成。", enabled: true, isDefault: false, }, { id: "world-compatible", displayName: "兼容质量", realModelId: "world-generation", description: "兼容旧版世界任务和历史工作流。", enabled: true, isDefault: false, }, { id: "world-draft", displayName: "快速草稿", realModelId: "world-generation", description: "更适合快速预览与草稿生成。", enabled: true, isDefault: false, }, ]; export const DEFAULT_THREED_MODEL_FEATURE_VISIBILITY: ThreeDModelFeatureVisibilityConfig = { pbr: true, quad: true, smartLowPoly: true, }; function sanitizeText(value: unknown) { return String(value || "").trim(); } function cloneEntries(entries: ThreeDModelAliasEntry[]) { return entries.map((entry) => ({ ...entry })); } function normalizeEntry( value: unknown, index: number, defaults: ThreeDModelAliasEntry[], prefix: string, ): ThreeDModelAliasEntry { const fallback = defaults[index] || defaults[0]; const hasRawObject = value && typeof value === "object" && !Array.isArray(value); const raw = hasRawObject ? (value as Record) : {}; return { id: sanitizeText(raw.id) || fallback?.id || `${prefix}-${index + 1}`, displayName: hasRawObject ? sanitizeText(raw.displayName) : fallback?.displayName || "", realModelId: hasRawObject ? sanitizeText(raw.realModelId || raw.modelId) : fallback?.realModelId || "", description: hasRawObject ? sanitizeText(raw.description) : fallback?.description || "", enabled: typeof raw.enabled === "boolean" ? raw.enabled : fallback?.enabled !== false, isDefault: typeof raw.isDefault === "boolean" ? raw.isDefault : fallback?.isDefault === true, }; } function finalizeEntries( entries: ThreeDModelAliasEntry[], defaults: ThreeDModelAliasEntry[], ) { const nextEntries = entries.length > 0 ? cloneEntries(entries) : cloneEntries(defaults); const usedIds = new Set(); nextEntries.forEach((entry, index) => { let nextId = sanitizeText(entry.id) || `${defaults[0]?.id || "model"}-${index + 1}`; while (usedIds.has(nextId)) { nextId = `${nextId}-${index + 1}`; } entry.id = nextId; usedIds.add(nextId); }); const enabledIndex = nextEntries.findIndex((entry) => entry.enabled); const defaultEnabledIndex = nextEntries.findIndex( (entry) => entry.enabled && entry.isDefault, ); const targetDefaultIndex = defaultEnabledIndex >= 0 ? defaultEnabledIndex : enabledIndex >= 0 ? enabledIndex : 0; return nextEntries.map((entry, index) => ({ ...entry, isDefault: index === targetDefaultIndex, })); } function normalizeEntryList( value: unknown, defaults: ThreeDModelAliasEntry[], prefix: string, ) { if (!Array.isArray(value) || value.length === 0) { return cloneEntries(defaults); } return finalizeEntries( value.map((item, index) => normalizeEntry(item, index, defaults, prefix)), defaults, ); } function normalizeFeatureVisibility( value: unknown, ): ThreeDModelFeatureVisibilityConfig { const raw = value && typeof value === "object" && !Array.isArray(value) ? (value as Partial) : {}; return { pbr: typeof raw.pbr === "boolean" ? raw.pbr : DEFAULT_THREED_MODEL_FEATURE_VISIBILITY.pbr, quad: typeof raw.quad === "boolean" ? raw.quad : DEFAULT_THREED_MODEL_FEATURE_VISIBILITY.quad, smartLowPoly: typeof raw.smartLowPoly === "boolean" ? raw.smartLowPoly : DEFAULT_THREED_MODEL_FEATURE_VISIBILITY.smartLowPoly, }; } export function defaultThreeDModelAliasConfig(): ThreeDModelAliasConfig { return { tripoModels: cloneEntries(DEFAULT_TRIPO_MODEL_ALIASES), worldModels: cloneEntries(DEFAULT_WORLD_MODEL_ALIASES), featureVisibility: { ...DEFAULT_THREED_MODEL_FEATURE_VISIBILITY }, }; } export function normalizeThreeDModelAliasConfig( value: unknown, ): ThreeDModelAliasConfig { const raw = value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; return { tripoModels: normalizeEntryList( raw.tripoModels, DEFAULT_TRIPO_MODEL_ALIASES, "tripo-model", ), worldModels: normalizeEntryList( raw.worldModels, DEFAULT_WORLD_MODEL_ALIASES, "world-model", ), featureVisibility: normalizeFeatureVisibility(raw.featureVisibility), }; } export function toPublicThreeDModelAliasConfig( config: ThreeDModelAliasConfig, ): PublicThreeDModelAliasConfig { return { tripoModels: config.tripoModels.map(({ realModelId: _realModelId, ...entry }) => ({ ...entry, })), worldModels: config.worldModels.map(({ realModelId, ...entry }) => ({ ...entry, billingModelId: realModelId, })), featureVisibility: normalizeFeatureVisibility(config.featureVisibility), }; } export const DEFAULT_PUBLIC_THREED_MODEL_ALIAS_CONFIG = toPublicThreeDModelAliasConfig(defaultThreeDModelAliasConfig()); function findEntryByValue< T extends { id: string; displayName: string; realModelId?: string; billingModelId?: string; }, >(entries: T[], value: unknown) { const normalized = sanitizeText(value); if (!normalized) return null; return ( entries.find((entry) => sanitizeText(entry.id) === normalized) || entries.find( (entry) => "realModelId" in entry && sanitizeText(entry.realModelId) === normalized, ) || entries.find( (entry) => "billingModelId" in entry && sanitizeText(entry.billingModelId) === normalized, ) || entries.find((entry) => sanitizeText(entry.displayName) === normalized) || null ); } export function getDefaultThreeDModelAliasId( entries: Array<{ id: string; enabled?: boolean; isDefault?: boolean }>, ) { const enabledEntries = entries.filter((entry) => entry.enabled !== false); const defaultEntry = enabledEntries.find((entry) => entry.isDefault) || enabledEntries[0] || entries[0]; return sanitizeText(defaultEntry?.id) || ""; } export function resolveThreeDModelRealId( value: unknown, entries: ThreeDModelAliasEntry[], ) { const match = findEntryByValue(entries, value); if (match?.realModelId) { return sanitizeText(match.realModelId); } const normalized = sanitizeText(value); if (normalized) { return normalized; } const fallbackEntry = entries.find((entry) => entry.enabled && entry.isDefault) || entries.find((entry) => entry.enabled) || entries[0]; return sanitizeText(fallbackEntry?.realModelId); } export function resolveThreeDModelAliasId( value: unknown, entries: ThreeDModelAliasEntry[], ) { const match = findEntryByValue(entries, value); if (match?.id) { return sanitizeText(match.id); } return sanitizeText(value); } export function resolveThreeDModelBillingId< T extends { id: string; displayName: string; realModelId?: string; billingModelId?: string; }, >(value: unknown, entries: T[]) { const match = findEntryByValue(entries, value); return sanitizeText(match?.billingModelId || match?.realModelId || value); } export function resolveThreeDModelDisplayName< T extends { id: string; displayName: string; realModelId?: string; billingModelId?: string; }, >( value: unknown, entries: T[], fallback = "", ) { const match = findEntryByValue(entries, value); if (match?.displayName) { return sanitizeText(match.displayName); } const normalized = sanitizeText(value); return normalized || fallback; }