feat: add Chatterbox model support for Cloud asset browser

- Register Chatterbox TTS node mappings in modelToNodeStore:
  - chatterbox/chatterbox → FL_ChatterboxTTS
  - chatterbox/chatterbox_turbo → FL_ChatterboxTurboTTS
  - chatterbox/chatterbox_multilingual → FL_ChatterboxMultilingualTTS
  - chatterbox/chatterbox_vc → FL_ChatterboxVC

- Add hierarchical fallback in findProvidersWithFallback() for model
  type lookups (e.g., 'parent/child' falls back to 'parent')

- Support empty widget key for auto-load nodes (skip widget assignment
  in createModelNodeFromAsset when provider.key is empty)

- Add comprehensive tests for new behavior

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019c0849-5650-7060-b4bd-23ef0c8dfa39
This commit is contained in:
Subagent 5
2026-01-28 23:24:59 -08:00
parent dd3e4d3edc
commit 050868c7d2
4 changed files with 131 additions and 24 deletions

View File

@@ -110,13 +110,19 @@ async function createMockNode(overrides?: {
widgets: { value: [widget], writable: true }
})
}
function createMockNodeProvider() {
function createMockNodeProvider(
overrides: {
nodeDef?: { name: string; display_name: string }
key?: string
} = {}
) {
return {
nodeDef: {
name: 'CheckpointLoaderSimple',
display_name: 'Load Checkpoint'
display_name: 'Load Checkpoint',
...overrides.nodeDef
},
key: 'ckpt_name'
key: overrides.key ?? 'ckpt_name'
}
}
/**
@@ -270,6 +276,24 @@ describe('createModelNodeFromAsset', () => {
expect(mockSubgraph.add).toHaveBeenCalledWith(mockNode)
expect(vi.mocked(app).canvas.graph!.add).not.toHaveBeenCalled()
})
it('should succeed when provider has empty key (auto-load nodes)', async () => {
const asset = createMockAsset({
tags: ['models', 'chatterbox/chatterbox_vc'],
user_metadata: { filename: 'chatterbox_vc_model.pt' }
})
const mockNode = await createMockNode({ hasWidgets: false })
const nodeProvider = createMockNodeProvider({
nodeDef: {
name: 'FL_ChatterboxVC',
display_name: 'FL Chatterbox VC'
},
key: ''
})
await setupMocks({ createdNode: mockNode, nodeProvider })
const result = createModelNodeFromAsset(asset)
expect(result.success).toBe(true)
expect(vi.mocked(app).canvas.graph!.add).toHaveBeenCalledWith(mockNode)
})
})
describe('when asset data is incomplete or invalid', () => {
beforeEach(() => {