Add LTX-2.3 models and resolved selection alias matching
This commit is contained in:
@@ -5313,6 +5313,127 @@
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2-19b-LoRA-Camera-Control-Static/resolve/main/ltx-2-19b-lora-camera-control-static.safetensors",
|
||||
"size": "2.21GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 22B Dev",
|
||||
"type": "checkpoint",
|
||||
"base": "LTX-2.3",
|
||||
"save_path": "checkpoints/LTX-2.3",
|
||||
"description": "LTX-2.3 22B development model.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3",
|
||||
"filename": "ltx-2.3-22b-dev.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-22b-dev.safetensors",
|
||||
"size": "42.98GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 22B Distilled 1.1",
|
||||
"type": "checkpoint",
|
||||
"base": "LTX-2.3",
|
||||
"save_path": "checkpoints/LTX-2.3",
|
||||
"description": "LTX-2.3 22B distilled model v1.1.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3",
|
||||
"filename": "ltx-2.3-22b-distilled-1.1.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-22b-distilled-1.1.safetensors",
|
||||
"size": "42.98GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 22B Dev FP8",
|
||||
"type": "checkpoint",
|
||||
"base": "LTX-2.3",
|
||||
"save_path": "checkpoints/LTX-2.3",
|
||||
"description": "LTX-2.3 22B Dev FP8 model.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3-fp8",
|
||||
"filename": "ltx-2.3-22b-dev-fp8.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3-fp8/resolve/main/ltx-2.3-22b-dev-fp8.safetensors",
|
||||
"size": "27.14GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 22B Distilled FP8",
|
||||
"type": "checkpoint",
|
||||
"base": "LTX-2.3",
|
||||
"save_path": "checkpoints/LTX-2.3",
|
||||
"description": "LTX-2.3 22B Distilled FP8 model.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3-fp8",
|
||||
"filename": "ltx-2.3-22b-distilled-fp8.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3-fp8/resolve/main/ltx-2.3-22b-distilled-fp8.safetensors",
|
||||
"size": "27.50GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 22B Dev NVFP4",
|
||||
"type": "checkpoint",
|
||||
"base": "LTX-2.3",
|
||||
"save_path": "checkpoints/LTX-2.3",
|
||||
"description": "LTX-2.3 22B Dev NVFP4 model.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3-nvfp4",
|
||||
"filename": "ltx-2.3-22b-dev-nvfp4.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3-nvfp4/resolve/main/ltx-2.3-22b-dev-nvfp4.safetensors",
|
||||
"size": "20.21GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 22B Distilled LoRA 1.1",
|
||||
"type": "lora",
|
||||
"base": "LTX-2.3",
|
||||
"save_path": "loras/ltxv/ltx2",
|
||||
"description": "A LoRA adapter that transforms the standard LTX-2.3 22B model into a distilled version when loaded.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3",
|
||||
"filename": "ltx-2.3-22b-distilled-lora-384-1.1.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-22b-distilled-lora-384-1.1.safetensors",
|
||||
"size": "7.08GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 22B IC-LoRA Union Control",
|
||||
"type": "lora",
|
||||
"base": "LTX-2.3",
|
||||
"save_path": "loras/ltxv/ltx2",
|
||||
"description": "In-Context LoRA (IC LoRA) for unified multi-condition control (canny, depth, pose) guided video generation.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3-22b-IC-LoRA-Union-Control",
|
||||
"filename": "ltx-2.3-22b-ic-lora-union-control-ref0.5.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3-22b-IC-LoRA-Union-Control/resolve/main/ltx-2.3-22b-ic-lora-union-control-ref0.5.safetensors",
|
||||
"size": "0.61GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 22B IC-LoRA Motion Track Control",
|
||||
"type": "lora",
|
||||
"base": "LTX-2.3",
|
||||
"save_path": "loras/ltxv/ltx2",
|
||||
"description": "In-Context LoRA (IC LoRA) for motion track guided video generation.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3-22b-IC-LoRA-Motion-Track-Control",
|
||||
"filename": "ltx-2.3-22b-ic-lora-motion-track-control-ref0.5.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3-22b-IC-LoRA-Motion-Track-Control/resolve/main/ltx-2.3-22b-ic-lora-motion-track-control-ref0.5.safetensors",
|
||||
"size": "0.30GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 Spatial Upscaler x2 1.1",
|
||||
"type": "upscale",
|
||||
"base": "upscale",
|
||||
"save_path": "latent_upscale_models",
|
||||
"description": "Spatial upscaler model for LTX-2.3. This model enhances the spatial resolution of generated videos by 2x.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3",
|
||||
"filename": "ltx-2.3-spatial-upscaler-x2-1.1.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-spatial-upscaler-x2-1.1.safetensors",
|
||||
"size": "0.93GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 Spatial Upscaler x1.5",
|
||||
"type": "upscale",
|
||||
"base": "upscale",
|
||||
"save_path": "latent_upscale_models",
|
||||
"description": "Spatial upscaler model for LTX-2.3. This model enhances the spatial resolution of generated videos by 1.5x.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3",
|
||||
"filename": "ltx-2.3-spatial-upscaler-x1.5-1.0.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-spatial-upscaler-x1.5-1.0.safetensors",
|
||||
"size": "1.02GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-2.3 Temporal Upscaler x2",
|
||||
"type": "upscale",
|
||||
"base": "upscale",
|
||||
"save_path": "latent_upscale_models",
|
||||
"description": "Temporal upscaler model for LTX-2.3. This model enhances the temporal resolution of generated videos.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-2.3",
|
||||
"filename": "ltx-2.3-temporal-upscaler-x2-1.0.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-2.3/resolve/main/ltx-2.3-temporal-upscaler-x2-1.0.safetensors",
|
||||
"size": "0.24GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-Video Spatial Upscaler v0.9.7",
|
||||
"type": "upscale",
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"source": "Comfy-Org/ComfyUI-Manager",
|
||||
"source_url": "https://raw.githubusercontent.com/Comfy-Org/ComfyUI-Manager/main/model-list.json",
|
||||
"github_url": "https://github.com/Comfy-Org/ComfyUI-Manager/blob/main/model-list.json",
|
||||
"sha": "5fcb5f0637f9fbdedaf8d37da9a390f76ae0ded8",
|
||||
"size": 268817,
|
||||
"model_count": 527,
|
||||
"updated_at": "2026-06-05T01:04:57+00:00"
|
||||
"sha": "5d9308f50a08cf3cfb47e6a25cc525425714d3ad",
|
||||
"size": 274526,
|
||||
"model_count": 538,
|
||||
"updated_at": "2026-06-22T01:35:07+00:00"
|
||||
}
|
||||
|
||||
@@ -1815,6 +1815,7 @@ export const queueMethods = {
|
||||
if (data.success) {
|
||||
const optimisticData = this.getOptimisticAnalysisDataAfterApply(appliedSelections);
|
||||
await this.updateWorkflowInComfyUI(data.workflow);
|
||||
this.rememberAppliedResolvedSelections(appliedSelections);
|
||||
|
||||
const appliedKeys = new Set(appliedSelections.map(selection => this.getResolutionQueueKey(selection)));
|
||||
const remainingSelections = clearAll
|
||||
@@ -1950,6 +1951,169 @@ export const queueMethods = {
|
||||
return (this.missingModels || []).filter(missing => activeKeys.has(this.getMissingModelKey(missing)));
|
||||
},
|
||||
|
||||
getResolvedSelectionPathCandidates(selection = {}) {
|
||||
const model = selection.resolved_model || {};
|
||||
return [
|
||||
model.relative_path,
|
||||
model.filename,
|
||||
model.name,
|
||||
model.path,
|
||||
model.resolved_path,
|
||||
selection.resolved_path
|
||||
].filter(Boolean);
|
||||
},
|
||||
|
||||
normalizeResolvedSelectionToken(value = '', { basename = false, stem = false } = {}) {
|
||||
let text = String(value || '').trim().replace(/\\/g, '/').toLowerCase();
|
||||
if (!text) return '';
|
||||
if (basename) {
|
||||
text = text.split('/').pop() || text;
|
||||
}
|
||||
if (stem) {
|
||||
text = text.replace(/\.(safetensors|ckpt|pt|pth|bin|gguf|onnx)$/i, '');
|
||||
}
|
||||
return text;
|
||||
},
|
||||
|
||||
getResolvedSelectionTokenSet(values = []) {
|
||||
const tokens = new Set();
|
||||
for (const value of values) {
|
||||
const normalized = this.normalizeResolvedSelectionToken(value);
|
||||
const basename = this.normalizeResolvedSelectionToken(value, { basename: true });
|
||||
const stem = this.normalizeResolvedSelectionToken(value, { basename: true, stem: true });
|
||||
if (normalized) tokens.add(normalized);
|
||||
if (basename) tokens.add(basename);
|
||||
if (stem) tokens.add(stem);
|
||||
}
|
||||
return tokens;
|
||||
},
|
||||
|
||||
getResolvedSelectionRefs(selection = {}) {
|
||||
const refs = Array.isArray(selection.node_refs) && selection.node_refs.length
|
||||
? selection.node_refs
|
||||
: [selection];
|
||||
return refs.filter(Boolean).map(ref => ({
|
||||
node_id: ref.node_id ?? selection.node_id,
|
||||
widget_index: ref.widget_index ?? selection.widget_index,
|
||||
subgraph_id: ref.subgraph_id ?? selection.subgraph_id ?? '',
|
||||
is_top_level: ref.is_top_level ?? selection.is_top_level,
|
||||
nested_key: ref.nested_key ?? selection.nested_key ?? ''
|
||||
}));
|
||||
},
|
||||
|
||||
rememberAppliedResolvedSelections(appliedSelections = []) {
|
||||
if (!(this.appliedResolvedSelectionAliases instanceof Map)) {
|
||||
this.appliedResolvedSelectionAliases = new Map();
|
||||
}
|
||||
|
||||
for (const selection of Array.isArray(appliedSelections) ? appliedSelections : []) {
|
||||
const key = this.getResolutionQueueKey(selection);
|
||||
if (!key) continue;
|
||||
this.appliedResolvedSelectionAliases.set(key, {
|
||||
key,
|
||||
category: selection.category || '',
|
||||
refs: this.getResolvedSelectionRefs(selection),
|
||||
tokens: this.getResolvedSelectionTokenSet(this.getResolvedSelectionPathCandidates(selection))
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
doesResolvedModelMatchAlias(model = {}, alias = {}) {
|
||||
const refs = Array.isArray(alias.refs) ? alias.refs : [];
|
||||
const modelNodeId = model.node_id ?? '';
|
||||
const modelWidgetIndex = model.widget_index ?? '';
|
||||
const modelSubgraphId = model.subgraph_id ?? '';
|
||||
const modelScope = model.is_top_level !== false;
|
||||
const modelNestedKey = model.nested_key || '';
|
||||
const refMatch = refs.some(ref => (
|
||||
String(ref.node_id ?? '') === String(modelNodeId)
|
||||
&& String(ref.widget_index ?? '') === String(modelWidgetIndex)
|
||||
&& String(ref.subgraph_id ?? '') === String(modelSubgraphId)
|
||||
&& (ref.is_top_level !== false) === modelScope
|
||||
&& String(ref.nested_key || '') === String(modelNestedKey)
|
||||
));
|
||||
if (!refMatch) return false;
|
||||
|
||||
const aliasCategory = String(alias.category || '').toLowerCase();
|
||||
const modelCategory = String(model.category || '').toLowerCase();
|
||||
if (aliasCategory && modelCategory && aliasCategory !== modelCategory) return false;
|
||||
|
||||
const modelTokens = this.getResolvedSelectionTokenSet([
|
||||
model.original_path,
|
||||
model.name,
|
||||
model.filename,
|
||||
model.relative_path,
|
||||
model.full_path,
|
||||
model.path
|
||||
]);
|
||||
for (const token of alias.tokens || []) {
|
||||
if (modelTokens.has(token)) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
applyResolvedSelectionAliasesToAnalysisData(data = null) {
|
||||
if (!data || !(this.appliedResolvedSelectionAliases instanceof Map) || !this.appliedResolvedSelectionAliases.size) {
|
||||
return data;
|
||||
}
|
||||
const resolvedModels = Array.isArray(data.resolved_models) ? data.resolved_models : [];
|
||||
if (!resolvedModels.length) return data;
|
||||
|
||||
const matchedKeys = new Set();
|
||||
for (const model of resolvedModels) {
|
||||
if (model?.missing_key) continue;
|
||||
for (const [key, alias] of this.appliedResolvedSelectionAliases.entries()) {
|
||||
if (matchedKeys.has(key)) continue;
|
||||
if (!this.doesResolvedModelMatchAlias(model, alias)) continue;
|
||||
model.missing_key = key;
|
||||
matchedKeys.add(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of matchedKeys) {
|
||||
this.appliedResolvedSelectionAliases.delete(key);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
buildOptimisticResolvedModel(selection = {}, sourceMissing = null) {
|
||||
const resolvedModel = selection.resolved_model || {};
|
||||
const resolvedRelativePath = resolvedModel.relative_path
|
||||
|| resolvedModel.filename
|
||||
|| selection.resolved_path
|
||||
|| sourceMissing?.original_path
|
||||
|| selection.original_path
|
||||
|| '';
|
||||
|
||||
return {
|
||||
...(sourceMissing || {}),
|
||||
missing_key: this.getResolutionQueueKey(selection),
|
||||
__isOptimisticResolved: true,
|
||||
node_id: selection.node_id ?? sourceMissing?.node_id,
|
||||
widget_index: selection.widget_index ?? sourceMissing?.widget_index,
|
||||
category: selection.category || sourceMissing?.category || resolvedModel.category || 'unknown',
|
||||
original_path: resolvedRelativePath,
|
||||
name: resolvedRelativePath,
|
||||
filename: resolvedModel.filename || resolvedRelativePath.split('/').pop()?.split('\\').pop() || resolvedRelativePath,
|
||||
relative_path: resolvedRelativePath,
|
||||
full_path: resolvedModel.path || selection.resolved_path || sourceMissing?.full_path || '',
|
||||
path: resolvedModel.path || selection.resolved_path || '',
|
||||
matches: [{
|
||||
confidence: 100,
|
||||
match_type: 'exact',
|
||||
filename: resolvedModel.filename || resolvedRelativePath,
|
||||
path: resolvedModel.path || selection.resolved_path || '',
|
||||
model: {
|
||||
...resolvedModel,
|
||||
path: resolvedModel.path || selection.resolved_path || '',
|
||||
relative_path: resolvedRelativePath,
|
||||
filename: resolvedModel.filename || resolvedRelativePath
|
||||
}
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
getOptimisticAnalysisDataAfterApply(appliedSelections = []) {
|
||||
const missingModels = Array.isArray(this.missingModels) ? this.missingModels : [];
|
||||
const appliedKeys = new Set(
|
||||
@@ -1966,10 +2130,25 @@ export const queueMethods = {
|
||||
? sourceData.missing_models
|
||||
: missingModels;
|
||||
const nextMissing = sourceMissing.filter(missing => !appliedKeys.has(this.getMissingModelKey(missing)));
|
||||
const currentResolved = Array.isArray(sourceData.resolved_models) ? sourceData.resolved_models : [];
|
||||
const optimisticResolved = [];
|
||||
for (const selection of Array.isArray(appliedSelections) ? appliedSelections : []) {
|
||||
const key = this.getResolutionQueueKey(selection);
|
||||
const source = sourceMissing.find(missing => this.getMissingModelKey(missing) === key)
|
||||
|| missingModels.find(missing => this.getMissingModelKey(missing) === key)
|
||||
|| null;
|
||||
optimisticResolved.push(this.buildOptimisticResolvedModel(selection, source));
|
||||
}
|
||||
const optimisticKeys = new Set(optimisticResolved.map(model => model.missing_key).filter(Boolean));
|
||||
const nextResolved = [
|
||||
...currentResolved.filter(model => !optimisticKeys.has(this.getMissingModelKey(model))),
|
||||
...optimisticResolved
|
||||
];
|
||||
|
||||
return {
|
||||
...sourceData,
|
||||
missing_models: nextMissing,
|
||||
resolved_models: nextResolved,
|
||||
total_missing: nextMissing.length
|
||||
};
|
||||
},
|
||||
@@ -1977,6 +2156,7 @@ export const queueMethods = {
|
||||
applyOptimisticAnalysisData(data, workflow = null) {
|
||||
if (!data) return false;
|
||||
|
||||
this.applyResolvedSelectionAliasesToAnalysisData(data);
|
||||
const workflowSignature = this.getWorkflowSignature(workflow || this.getCurrentWorkflow());
|
||||
if (workflowSignature) {
|
||||
this.cachedWorkflowSignature = workflowSignature;
|
||||
@@ -2010,6 +2190,7 @@ export const queueMethods = {
|
||||
})
|
||||
.then(data => {
|
||||
if (signature && this.activeWorkflowSignature !== signature) return;
|
||||
this.applyResolvedSelectionAliasesToAnalysisData(data);
|
||||
this.cachedWorkflowSignature = signature || this.cachedWorkflowSignature;
|
||||
this.cachedAnalysisData = data;
|
||||
this.saveAnalysisCacheForActiveWorkflow?.();
|
||||
|
||||
@@ -288,6 +288,32 @@ export const missingBrowserMethods = {
|
||||
this.displayMissingModels(this.contentElement, this.cachedAnalysisData);
|
||||
},
|
||||
|
||||
getMissingListScrollSnapshot(container) {
|
||||
const list = container?.querySelector?.('.mr-missing-list');
|
||||
if (!(list instanceof HTMLElement)) return null;
|
||||
return {
|
||||
top: list.scrollTop,
|
||||
left: list.scrollLeft
|
||||
};
|
||||
},
|
||||
|
||||
restoreMissingListScroll(container, snapshot = null) {
|
||||
if (!snapshot) return;
|
||||
const list = container?.querySelector?.('.mr-missing-list');
|
||||
if (!(list instanceof HTMLElement)) return;
|
||||
|
||||
const applyScroll = () => {
|
||||
if (!list.isConnected) return;
|
||||
const maxTop = Math.max(0, list.scrollHeight - list.clientHeight);
|
||||
const maxLeft = Math.max(0, list.scrollWidth - list.clientWidth);
|
||||
list.scrollTop = Math.min(Number(snapshot.top) || 0, maxTop);
|
||||
list.scrollLeft = Math.min(Number(snapshot.left) || 0, maxLeft);
|
||||
};
|
||||
|
||||
applyScroll();
|
||||
requestAnimationFrame(applyScroll);
|
||||
},
|
||||
|
||||
renderMissingModelsBrowser(missingModels, selectedKey, totalMissing, activeCount, hasAny100Match, options = {}) {
|
||||
const hiddenResolvedCount = Number(options.hiddenResolvedCount || 0);
|
||||
const resolvedCount = Number(options.resolvedCount || 0);
|
||||
@@ -897,6 +923,7 @@ export const missingBrowserMethods = {
|
||||
* Display missing models in the dialog
|
||||
*/
|
||||
displayMissingModels(container, data) {
|
||||
const listScrollSnapshot = this.getMissingListScrollSnapshot(container);
|
||||
const missingModels = (data.missing_models || []).map(missing => (
|
||||
this.restoreDownloadedLocalMatchesForMissing?.(missing) || missing
|
||||
));
|
||||
@@ -1007,6 +1034,7 @@ export const missingBrowserMethods = {
|
||||
{ hiddenResolvedCount, resolvedCount, rawMissingCount, missingCount: unresolvedMissingCount }
|
||||
);
|
||||
this.wireMissingModelsBrowser(container, data, sortedMissingModels);
|
||||
this.restoreMissingListScroll(container, listScrollSnapshot);
|
||||
this.scheduleInitialUrnLocalMatchRefresh(sortedMissingModels, container, data);
|
||||
this.reconnectActiveSearchProgress(sortedMissingModels);
|
||||
this.updateBatchFooterButtons();
|
||||
|
||||
Reference in New Issue
Block a user