Remember Studio 2 LoRA stacks for each engine
This commit is contained in:
@@ -41,7 +41,7 @@ test('cached LoRAs survive sleeping GPU and failed explicit refresh',async()=>{
|
||||
test('source/hero actions are independent and full input restores structured settings',()=>{
|
||||
const form={identityStillId:'hero',imageAId:'source',promptSections:{action:'old'},settings:{}}
|
||||
assert.equal(useAsHero(form,{id:'new'}).imageAId,'source');assert.equal(useAsSource(form,{id:'new'}).identityStillId,'hero')
|
||||
const saved={mode:'edit',identityStillId:'h',imageAId:'s',imageStyles:{positive:['Comic'],negative:['Skin pores']},promptSections:{action:'New',cameraBeats:[{moves:['hold']}]},settings:{steps:24,loraStack:[{name:'a',strengthModel:.4,strengthClip:.6}]}}
|
||||
const saved={mode:'edit',identityStillId:'h',imageAId:'s',imageStyles:{positive:['Comic'],negative:['Skin pores']},promptSections:{action:'New',cameraBeats:[{moves:['hold']}]},settings:{steps:24,loraStack:[{name:'example.safetensors',strengthModel:.4,strengthClip:.6}]}}
|
||||
const restored=restoreAll(form,{id:'result',studio2:saved});assert.equal(restored.imageAId,'result');assert.equal(restored.identityStillId,'h');assert.deepEqual(restored.settings.loraStack,saved.settings.loraStack);assert.deepEqual(restored.imageStyles,saved.imageStyles);assert.equal(restored.promptSections.action,'New');const clip=restoreAll(form,{id:'clip',kind:'clip',studio2:{...saved,mode:'video',engine:'minimax'}});assert.equal(clip.startClipId,'clip');assert.equal(clip.mode,'extend');assert.equal(clip.promptSections.action,'New')
|
||||
})
|
||||
test('image chips compile positive and negative independently; video chips do not leak',()=>{
|
||||
|
||||
@@ -21,7 +21,7 @@ test('Use as input restores camera, sound, dialogue, music, LoRAs, aspect and lo
|
||||
})
|
||||
test('extend binds completed clip, inherits settings and leaves delta prompt alone',()=>{
|
||||
const form={...base,lockFace:true,lockOutfit:false,settings:{duration:5,width:1344,height:768},promptSections:{action:'Turn',cameraBeats:beats}}
|
||||
const clip={id:'done',kind:'clip',studio2:{...base,lockFace:true,lockOutfit:false,settings:{duration:8,width:768,height:1344,aspect:'9:16',fps:24,steps:8,cfg:1.5,turbo:false,loraStack:[{name:'a',strengthModel:1,strengthClip:.5}]}}}
|
||||
const clip={id:'done',kind:'clip',studio2:{...base,lockFace:true,lockOutfit:false,settings:{duration:8,width:768,height:1344,aspect:'9:16',fps:24,steps:8,cfg:1.5,turbo:false,loraStack:[{name:'example.safetensors',strengthModel:1,strengthClip:.5}]}}}
|
||||
const bound=bindClip(form,clip);assert.equal(bound.startClipId,'done');assert.deepEqual(bound.settings,clip.studio2.settings);assert.equal(bound.promptSections.action,'Turn');assert.equal(bound.identityStillId,'hero');assert.equal(bound.lockOutfit,false)
|
||||
assert.equal(restoreAsset(form,clip).startClipId,'done');assert.equal(bindClip(form,{id:'legacy'}).lockOutfit,false)
|
||||
const overridden={...bound,lockOutfit:false,settings:{...bound.settings,cfg:2.5}}
|
||||
|
||||
@@ -37,7 +37,7 @@ test('image requests omit video sections while form keeps them',()=>{
|
||||
})
|
||||
test('Extend normalizes engine and inherits clip without replacing action or camera',()=>{
|
||||
const form={mode:'edit',engine:'flux',imageAId:'still',promptSections:{action:'New action',cameraBeats:[{moves:['hold']}]},settings:{loraStack:[]}}
|
||||
const clip={id:'clip',kind:'clip',studio2:{engine:'minimax',identityStillId:'hero',lockOutfit:false,promptSections:{action:'Old action'},settings:{duration:8,aspect:'1:1',width:960,height:960,fps:24,turbo:false,steps:8,cfg:1.5,loraStack:[{name:'a',strengthModel:.6,strengthClip:.3}]}}}
|
||||
const clip={id:'clip',kind:'clip',studio2:{engine:'minimax',identityStillId:'hero',lockOutfit:false,promptSections:{action:'Old action'},settings:{duration:8,aspect:'1:1',width:960,height:960,fps:24,turbo:false,steps:8,cfg:1.5,loraStack:[{name:'example.safetensors',strengthModel:.6,strengthClip:.3}]}}}
|
||||
const next=extendSelected(form,clip);assert.equal(next.engine,'minimax');assert.equal(next.startClipId,'clip');assert.deepEqual(next.settings,clip.studio2.settings)
|
||||
assert.deepEqual(next.promptSections,form.promptSections);assert.equal(next.lockOutfit,false)
|
||||
assert.equal(changeSource({...next,imageAId:'new-still'}).startClipId,'clip');assert.equal(changeSource(next).mode,'extend')
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import {reactive,watch} from 'vue'
|
||||
import {inferLoraUse,filterLorasForUse,filterLorasForStudio,filterLoraStackForUse} from '../utils/loras.ts'
|
||||
import {createLoraMemory,loraUse} from '../shared/studio2/lora-memory.mjs'
|
||||
import {restoreAll,restoreAsset,bindClip,extendSelected,useAsHero,useAsSource} from '../shared/studio2/input.mjs'
|
||||
const stack=name=>[{name:name+'.safetensors',strengthModel:.6,strengthClip:.35}]
|
||||
|
||||
test('engine token matrix uses paths and names, omits unknown and system picker entries',()=>{
|
||||
const cases={'xaigen-klein_car':'flux','klein/style':'flux','XAIgen-flux_test':'flux','Flux2-Klein/test':'flux','MiniMax/car':'minimax','folder/LTX_style':'ltx','xiagen-Krea_look':'krea','xaigen-krea/style':'krea','unlabelled':null}
|
||||
for(const [name,use] of Object.entries(cases)) {
|
||||
assert.deepEqual(inferLoraUse(name+'.safetensors'),use?[use]:null)
|
||||
for(const current of ['flux','krea','minimax','ltx']) assert.equal(filterLorasForUse([name+'.safetensors'],current).length,use===current?1:0)
|
||||
}
|
||||
const restricted=['xaigen-klein.safetensors','XAIgen-MiniMax.safetensors','xiagen-Krea.safetensors']
|
||||
assert.deepEqual(filterLorasForStudio(restricted,false),[])
|
||||
assert.deepEqual(filterLoraStackForUse(stack('xaigen-klein'),'flux',false),[])
|
||||
assert.deepEqual(filterLoraStackForUse(stack('LTX'),'ltx',false),[])
|
||||
for(const [name,use] of [['minimax_h3_fl2v_turbo_8step_v1.0_comfyui_bf16','minimax'],['ltx-2.3-22b-distilled','ltx'],['Flux2-Klein-9B-consistency-V2','flux']]) assert.deepEqual(filterLorasForUse([name+'.safetensors'],use),[])
|
||||
assert.deepEqual(filterLoraStackForUse(stack('unlabelled'),'flux',true),stack('unlabelled'))
|
||||
})
|
||||
|
||||
function bench() {
|
||||
const data=new Map(),storage={getItem:k=>data.get(k),setItem:(k,v)=>data.set(k,v)}
|
||||
const memory=createLoraMemory(true,storage)
|
||||
const form=reactive({mode:'generate',engine:'flux',settings:{loraStack:[]}})
|
||||
const stop=watch(()=>[loraUse(form),form.settings.loraStack],()=>memory.sync(form),{deep:true,flush:'sync'})
|
||||
memory.sync(form)
|
||||
return {form,memory,storage,stop}
|
||||
}
|
||||
test('reactive task switches remember four independent stacks and both strengths',()=>{
|
||||
const {form,memory,storage,stop}=bench()
|
||||
form.settings.loraStack=stack('klein_car_bee')
|
||||
form.settings.loraStack[0].strengthClip=.8
|
||||
form.mode='video';form.engine='minimax'
|
||||
assert.deepEqual(form.settings.loraStack,[])
|
||||
form.settings.loraStack=stack('MiniMax_style')
|
||||
form.engine='ltx';assert.deepEqual(form.settings.loraStack,[])
|
||||
form.settings.loraStack=stack('LTX_style')
|
||||
form.mode='edit';form.engine='flux'
|
||||
assert.equal(form.settings.loraStack[0].name,'klein_car_bee.safetensors')
|
||||
assert.equal(form.settings.loraStack[0].strengthClip,.8)
|
||||
form.engine='krea';assert.deepEqual(form.settings.loraStack,[])
|
||||
form.settings.loraStack=stack('Krea_style')
|
||||
form.mode='video';form.engine='minimax'
|
||||
assert.deepEqual(form.settings.loraStack,stack('MiniMax_style'))
|
||||
Object.assign(form,useAsHero(form,{id:'image'}),useAsSource(form,{id:'source'}))
|
||||
assert.deepEqual(form.settings.loraStack,stack('MiniMax_style'))
|
||||
const fresh=createLoraMemory(true,storage),other={mode:'generate',engine:'krea',settings:{loraStack:[]}}
|
||||
fresh.sync(other);assert.deepEqual(other.settings.loraStack,stack('Krea_style'))
|
||||
stop()
|
||||
})
|
||||
test('Use as input and Extend copy saved stacks, filter wrong engines, and fall back only when saved empty',()=>{
|
||||
const {form,memory,stop}=bench()
|
||||
form.settings.loraStack=stack('klein_remembered')
|
||||
const still={id:'still',kind:'still',studio2:{engine:'flux',mode:'edit',settings:{loraStack:[...stack('klein_saved'),...stack('MiniMax_wrong')]}}}
|
||||
Object.assign(form,restoreAll(form,still,true,memory))
|
||||
assert.deepEqual(form.settings.loraStack,stack('klein_saved'))
|
||||
const clip={id:'clip',kind:'clip',studio2:{engine:'minimax',settings:{loraStack:[...stack('MiniMax_saved'),...stack('klein_wrong')]}}}
|
||||
Object.assign(form,extendSelected(form,clip,true,memory))
|
||||
assert.deepEqual(form.settings.loraStack,stack('MiniMax_saved'))
|
||||
Object.assign(form,restoreAll(form,{...still,studio2:{...still.studio2,settings:{loraStack:[]}}},true,memory))
|
||||
assert.deepEqual(form.settings.loraStack,stack('klein_saved'))
|
||||
Object.assign(form,bindClip(form,{id:'empty',settings:{loraStack:[]}},true,memory))
|
||||
assert.deepEqual(form.settings.loraStack,stack('MiniMax_saved'))
|
||||
const legacy=restoreAsset(form,{id:'old-still',kind:'still',engine:'krea',settings:{loraStack:stack('Krea_saved')}},true,memory)
|
||||
assert.equal(legacy.engine,'krea');assert.deepEqual(legacy.settings.loraStack,stack('Krea_saved'))
|
||||
stop()
|
||||
})
|
||||
Reference in New Issue
Block a user