const MODEL_FILE_PATTERN = /\.(safetensors|ckpt|pt|bin|pth|sft)$/i; export function inferPickerModelType(node, widget, options = {}) { const widgetName = String(widget?.name || '').toLowerCase(); const nodeType = String(node?.type || '').toLowerCase(); const explicitLabel = String(options.modelTypeLabel || ''); const key = `${widgetName} ${nodeType} ${explicitLabel.toLowerCase()}`; if (widgetName.includes('vae') || nodeType === 'vaeloader' || key.includes('vae loader')) { return { label: 'VAE', folderTypes: ['vae'], isLora: false }; } if (widgetName.includes('lora') || key.includes('lora')) { return { label: 'LoRA', folderTypes: ['loras'], isLora: true }; } if (widgetName.includes('control_net') || widgetName.includes('controlnet') || key.includes('controlnet')) { return { label: 'ControlNet', folderTypes: ['controlnet'], isLora: false }; } if (widgetName.includes('ckpt') || widgetName.includes('checkpoint') || key.includes('checkpoint')) { return { label: 'Checkpoint', folderTypes: ['checkpoints'], isLora: false }; } if (widgetName.includes('unet') || widgetName.includes('diffusion_model') || key.includes('unet loader')) { return { label: 'UNET', folderTypes: ['diffusion_models', 'unet'], isLora: false }; } return { label: explicitLabel || node?.type || widget?.name || 'Model', folderTypes: [], isLora: false, }; } export function getBaseModelFamily(value) { const normalized = String(value || '').trim().toLowerCase().replace(/[^a-z0-9]+/g, ''); if (!normalized) return ''; if (normalized.includes('illustrious')) return 'illustrious'; if (normalized.includes('pony')) return 'pony'; if (normalized.includes('flux')) return 'flux'; if (normalized.includes('sdxl') || normalized.includes('stablediffusionxl')) return 'sdxl'; if (normalized.includes('sd35') || normalized.includes('stablediffusion35')) return 'sd35'; if (normalized.includes('sd3') || normalized.includes('stablediffusion3')) return 'sd3'; if (normalized.includes('sd21') || normalized.includes('stablediffusion21')) return 'sd21'; if (normalized.includes('sd15') || normalized.includes('stablediffusion15')) return 'sd15'; return normalized; } function getGraphNodeById(graph, nodeId) { return graph?.getNodeById?.(nodeId) || graph?._nodes_by_id?.[nodeId] || graph?._nodes?.find?.(candidate => candidate?.id === nodeId) || null; } export function collectMainModelContextRequests(graph, startNode) { if (!graph || !startNode) return []; const queue = [startNode]; const visited = new Set(); let depth = 0; while (queue.length && depth < 12) { const levelSize = queue.length; const found = []; for (let index = 0; index < levelSize; index += 1) { const current = queue.shift(); if (!current || visited.has(current.id)) continue; visited.add(current.id); for (const widget of current.widgets || []) { if (typeof widget?.value !== 'string' || !MODEL_FILE_PATTERN.test(widget.value)) continue; const inferred = inferPickerModelType(current, widget); if (!inferred.isLora) { const request = { path: widget.value }; if (inferred.folderTypes.length) request.folder_types = inferred.folderTypes; found.push(request); } } for (const input of current.inputs || []) { if (!['MODEL', 'CLIP'].includes(input?.type) || input.link == null) continue; const link = graph.links?.[input.link] || graph._links?.[input.link]; const origin = getGraphNodeById(graph, link?.origin_id); if (origin && !visited.has(origin.id)) queue.push(origin); } } if (found.length) { const seen = new Set(); return found.filter(item => { const key = `${(item.folder_types || []).join(',')}|${item.path}`; if (seen.has(key)) return false; seen.add(key); return true; }); } depth += 1; } return []; } export function formatModelTypeLabel(type, fallback = 'Model') { const labels = { loras: 'LoRA', checkpoints: 'Checkpoint', diffusion_models: 'UNET', unet: 'UNET', controlnet: 'ControlNet', vae: 'VAE', }; return labels[String(type || '').toLowerCase()] || fallback; }