Compare commits

...

1 Commits

Author SHA1 Message Date
Deep Mehta
044d4de76a fix: progressive hierarchical fallback and HF-download node dropdowns
1. Fix findProvidersWithFallback to try all intermediate parent paths
   (a/b/c → a/b → a) instead of jumping to top-level only. This
   ensures "Use" on CogVideo/ControlNet/* models creates the correct
   DownloadAndLoadCogVideoControlNet node.

2. Use empty key for DownloadAndLoadCogVideoControlNet and
   DownloadAndLoadCogVideoModel so the asset browser doesn't hijack
   the combo widget — these nodes use HF repo names, not file assets.

3. Add comprehensive tests for 1-4 level nested path fallback.
2026-03-18 09:35:17 -07:00
2 changed files with 77 additions and 10 deletions

View File

@@ -178,6 +178,71 @@ describe('useModelToNodeStore', () => {
).toBeUndefined()
})
describe('progressive hierarchical fallback', () => {
it('should resolve 1-level path via exact match', () => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.quickRegister('level1', 'UNETLoader', 'key1')
const provider = modelToNodeStore.getNodeProvider('level1')
expect(provider?.nodeDef?.name).toBe('UNETLoader')
})
it('should resolve 2-level path to registered parent', () => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.quickRegister('level1', 'UNETLoader', 'key1')
const provider = modelToNodeStore.getNodeProvider('level1/child')
expect(provider?.nodeDef?.name).toBe('UNETLoader')
})
it('should resolve 3-level path to nearest registered ancestor', () => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.quickRegister('level1', 'UNETLoader', 'key1')
modelToNodeStore.quickRegister('level1/level2', 'VAELoader', 'key2')
// 3 levels: should match level1/level2 (nearest), not level1
const provider = modelToNodeStore.getNodeProvider('level1/level2/child')
expect(provider?.nodeDef?.name).toBe('VAELoader')
})
it('should resolve 4-level path to nearest registered ancestor', () => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.quickRegister('a', 'UNETLoader', 'k1')
modelToNodeStore.quickRegister('a/b', 'VAELoader', 'k2')
modelToNodeStore.quickRegister('a/b/c', 'StyleModelLoader', 'k3')
// 4 levels: should match a/b/c (nearest), not a/b or a
const provider = modelToNodeStore.getNodeProvider('a/b/c/d')
expect(provider?.nodeDef?.name).toBe('StyleModelLoader')
})
it('should skip intermediate unregistered levels', () => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.quickRegister('a', 'UNETLoader', 'k1')
// a/b is NOT registered
// 3 levels: a/b not found, falls back to a
const provider = modelToNodeStore.getNodeProvider('a/b/c')
expect(provider?.nodeDef?.name).toBe('UNETLoader')
})
it('should prefer exact match over any fallback', () => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.quickRegister('a', 'UNETLoader', 'k1')
modelToNodeStore.quickRegister('a/b/c', 'VAELoader', 'k2')
const provider = modelToNodeStore.getNodeProvider('a/b/c')
expect(provider?.nodeDef?.name).toBe('VAELoader')
})
it('should return undefined when no ancestor is registered', () => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.quickRegister('x', 'UNETLoader', 'k1')
expect(modelToNodeStore.getNodeProvider('y/z/w')).toBeUndefined()
})
})
it('should return provider for chatterbox nodes with empty key', () => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.registerDefaults()

View File

@@ -75,8 +75,8 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
/**
* Find providers for modelType with hierarchical fallback.
* Tries exact match first, then falls back to top-level segment (e.g., "parent/child" → "parent").
* Note: Only falls back one level; "a/b/c" tries "a/b/c" then "a", not "a/b".
* Tries exact match first, then progressively shorter parent paths.
* e.g., "a/b/c" tries "a/b/c" → "a/b" → "a".
*/
function findProvidersWithFallback(
modelType: string
@@ -88,12 +88,12 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
const exactMatch = modelToNodeMap.value[modelType]
if (exactMatch && exactMatch.length > 0) return exactMatch
const topLevel = modelType.split('/')[0]
if (topLevel === modelType) return undefined
const fallback = modelToNodeMap.value[topLevel]
if (fallback && fallback.length > 0) return fallback
const segments = modelType.split('/')
for (let i = segments.length - 1; i >= 1; i--) {
const parentPath = segments.slice(0, i).join('/')
const fallback = modelToNodeMap.value[parentPath]
if (fallback && fallback.length > 0) return fallback
}
return undefined
}
@@ -361,10 +361,11 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
// CogVideoX models (comfyui-cogvideoxwrapper)
quickRegister('CogVideo/GGUF', 'DownloadAndLoadCogVideoGGUFModel', 'model')
// Empty key: HF-download node — don't activate asset browser for the combo widget
quickRegister(
'CogVideo/ControlNet',
'DownloadAndLoadCogVideoControlNet',
'model'
''
)
// DynamiCrafter models (ComfyUI-DynamiCrafterWrapper)
@@ -386,7 +387,8 @@ export const useModelToNodeStore = defineStore('modelToNode', () => {
quickRegister('lama', 'LaMa', 'lama_model')
// CogVideoX video generation models (comfyui-cogvideoxwrapper)
quickRegister('CogVideo', 'DownloadAndLoadCogVideoModel', 'model')
// Empty key: HF-download node — don't activate asset browser for the combo widget
quickRegister('CogVideo', 'DownloadAndLoadCogVideoModel', '')
// Inpaint models (comfyui-inpaint-nodes)
quickRegister('inpaint', 'INPAINT_LoadInpaintModel', 'model_name')