Show the Qwen LoRA card with qwen-stem picks and apply them on generate.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -171,9 +171,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="form.engine!=='qwen21'">
|
<div v-if="['flux','krea','qwen21'].includes(form.engine)">
|
||||||
<span class="field-label">LoRA</span>
|
<span class="field-label">LoRA</span>
|
||||||
<LoraSelect v-model="form.settings.loraStack" compact :names="pickerLoras" :use="currentLoraUse" :clip-strength="!video" filename-subtitle :aliases="{...loraNames.aliases,...library.loraAliases}" :triggers="{...loraNames.triggers,...library.loraTriggers}" missing-label="None" @insert-trigger="form.promptSections.action += ' '+$event" />
|
<LoraSelect
|
||||||
|
v-model="form.settings.loraStack"
|
||||||
|
compact
|
||||||
|
:names="pickerLoras"
|
||||||
|
:use="currentLoraUse"
|
||||||
|
:clip-strength="form.engine!=='qwen21' && !video"
|
||||||
|
filename-subtitle
|
||||||
|
:aliases="{...loraNames.aliases,...library.loraAliases}"
|
||||||
|
:triggers="{...loraNames.triggers,...library.loraTriggers}"
|
||||||
|
missing-label="None"
|
||||||
|
@insert-trigger="form.promptSections.action += ' '+$event"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -532,7 +543,6 @@ watch(()=>form.engine,(engine,prev)=>{
|
|||||||
form.settings.cfg=1
|
form.settings.cfg=1
|
||||||
form.lockFace=false
|
form.lockFace=false
|
||||||
form.lockOutfit=false
|
form.lockOutfit=false
|
||||||
form.settings.loraStack=[]
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
watch(()=>form.enhancePrompt,on=>writeStoredEnhancePrompt(!!on))
|
watch(()=>form.enhancePrompt,on=>writeStoredEnhancePrompt(!!on))
|
||||||
@@ -548,7 +558,6 @@ watch(()=>[form.mode,form.engine],([mode,engine])=>{
|
|||||||
form.settings.cfg=1
|
form.settings.cfg=1
|
||||||
form.lockFace=false
|
form.lockFace=false
|
||||||
form.lockOutfit=false
|
form.lockOutfit=false
|
||||||
form.settings.loraStack=[]
|
|
||||||
}
|
}
|
||||||
const plan=photosPlan({mode,engine})
|
const plan=photosPlan({mode,engine})
|
||||||
if(!plan.show){form.photos=[];form.imageAId='';form.imageBId='';if(engine==='qwen21')form.identityStillId='';return}
|
if(!plan.show){form.photos=[];form.imageAId='';form.imageBId='';if(engine==='qwen21')form.identityStillId='';return}
|
||||||
|
|||||||
@@ -259,7 +259,7 @@
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<div>
|
<div>
|
||||||
<span class="m-label">LoRA</span>
|
<span class="m-label">LoRA</span>
|
||||||
<LoraSelect v-model="ctx.form.settings.loraStack" compact :names="ctx.pickerLoras" :use="ctx.currentLoraUse" :clip-strength="!ctx.video" filename-subtitle :aliases="ctx.loraAliases" :triggers="ctx.loraTriggers" missing-label="None" @insert-trigger="ctx.form.promptSections.action += ' '+$event" />
|
<LoraSelect v-model="ctx.form.settings.loraStack" compact :names="ctx.pickerLoras" :use="ctx.currentLoraUse" :clip-strength="ctx.form.engine!=='qwen21' && !ctx.video" filename-subtitle :aliases="ctx.loraAliases" :triggers="ctx.loraTriggers" missing-label="None" @insert-trigger="ctx.form.promptSections.action += ' '+$event" />
|
||||||
</div>
|
</div>
|
||||||
<label class="m-label">Seed
|
<label class="m-label">Seed
|
||||||
<div class="m-seed">
|
<div class="m-seed">
|
||||||
|
|||||||
@@ -406,6 +406,8 @@ async function prepareGraph(r: any) {
|
|||||||
r.sampleDenoise = null
|
r.sampleDenoise = null
|
||||||
r.heroReferenceAttached = false
|
r.heroReferenceAttached = false
|
||||||
}
|
}
|
||||||
|
await ensureComfyLoraNames('image')
|
||||||
|
applyUserLoraToGraph(graph, s.loraStack)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const mode = identityPlan?.mode ?? (q.mode === 'iterate' ? (a ? 'edit' : 'generate') : q.mode);
|
const mode = identityPlan?.mode ?? (q.mode === 'iterate' ? (a ? 'edit' : 'generate') : q.mode);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function validateRequest(raw, xaigen = false) {
|
|||||||
photos: Array.isArray(raw.photos) ? raw.photos : [],
|
photos: Array.isArray(raw.photos) ? raw.photos : [],
|
||||||
startFrameSource: raw.startClipId ? {kind:'previous-last-frame',clipId:raw.startClipId} : {kind:'still',stillId:raw.imageAId || ''},
|
startFrameSource: raw.startClipId ? {kind:'previous-last-frame',clipId:raw.startClipId} : {kind:'still',stillId:raw.imageAId || ''},
|
||||||
promptSections, compiledPrompt:compilePrompt(promptSections,video,{...raw,engine,lockFace:qwen?false:raw.lockFace,lockOutfit:qwen?false:raw.lockOutfit}),
|
promptSections, compiledPrompt:compilePrompt(promptSections,video,{...raw,engine,lockFace:qwen?false:raw.lockFace,lockOutfit:qwen?false:raw.lockOutfit}),
|
||||||
settings: { aspect, fps:number('fps',24,1,60), width, height, steps, cfg, seedMode:raw.settings?.seedMode==='locked'?'locked':'random',seed:shotSeeds[0],duration:number('duration',5,1,15),turbo:raw.settings?.turbo !== false, loraStack:qwen?[]:(Array.isArray(raw.settings?.loraStack)?raw.settings.loraStack:[]) },
|
settings: { aspect, fps:number('fps',24,1,60), width, height, steps, cfg, seedMode:raw.settings?.seedMode==='locked'?'locked':'random',seed:shotSeeds[0],duration:number('duration',5,1,15),turbo:raw.settings?.turbo !== false, loraStack:Array.isArray(raw.settings?.loraStack)?raw.settings.loraStack:[] },
|
||||||
guides: (Array.isArray(raw.guides)?raw.guides:[]).slice(0,8).map(g=>{ if (!g.stillId || !Number.isInteger(g.frame) || g.frame<1 || g.frame>=frames) fail('Each guide requires a still and positive frame index.'); return {stillId:String(g.stillId),frame:g.frame} }),
|
guides: (Array.isArray(raw.guides)?raw.guides:[]).slice(0,8).map(g=>{ if (!g.stillId || !Number.isInteger(g.frame) || g.frame<1 || g.frame>=frames) fail('Each guide requires a still and positive frame index.'); return {stillId:String(g.stillId),frame:g.frame} }),
|
||||||
batch: (Array.isArray(raw.batch)?raw.batch:[]).slice(0,30).map(s=>{const next=sectionsFrom({...promptSections,...(typeof s==='string'?{action:s}:s)});if(!next.action.trim())fail('Every shot needs an action prompt.');return video?next:{action:next.action}}) }
|
batch: (Array.isArray(raw.batch)?raw.batch:[]).slice(0,30).map(s=>{const next=sectionsFrom({...promptSections,...(typeof s==='string'?{action:s}:s)});if(!next.action.trim())fail('Every shot needs an action prompt.');return video?next:{action:next.action}}) }
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-5
@@ -305,23 +305,30 @@ export function inferLoraUse(name: string): LoraUse[] | null {
|
|||||||
return uses.length ? uses : null
|
return uses.length ? uses : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Qwen 2.1 user adapters: basename stem starts with qwen or qwen2 (not klein). */
|
||||||
|
export function isQwenImageLora(name: string) {
|
||||||
|
const stem = loraIdentityKey(name).replace(/\.(safetensors|sft|ckpt|pt|bin)$/i, '')
|
||||||
|
return /^(qwen2|qwen)/i.test(stem)
|
||||||
|
}
|
||||||
|
|
||||||
export function loraMatchesUse(name: string, use: LoraUse) {
|
export function loraMatchesUse(name: string, use: LoraUse) {
|
||||||
if (use === 'qwen21') return false
|
if (isSystemGraphLora(name)) return false
|
||||||
return !isSystemGraphLora(name) && !!inferLoraUse(name)?.includes(use)
|
if (use === 'qwen21') return isQwenImageLora(name)
|
||||||
|
return !!inferLoraUse(name)?.includes(use)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Saved stacks may contain unlabelled adapters; never carry a known wrong engine. */
|
/** Saved stacks may contain unlabelled adapters; never carry a known wrong engine. */
|
||||||
export function filterLoraStackForUse(stack: unknown, use: LoraUse, xaigen: boolean) {
|
export function filterLoraStackForUse(stack: unknown, use: LoraUse, xaigen: boolean) {
|
||||||
if (use === 'ltx' && !xaigen) return []
|
if (use === 'ltx' && !xaigen) return []
|
||||||
if (use === 'qwen21') return []
|
|
||||||
return filterLoraStackForStudio(stack, xaigen).filter(item => {
|
return filterLoraStackForStudio(stack, xaigen).filter(item => {
|
||||||
|
if (isSystemGraphLora(item.name)) return false
|
||||||
|
if (use === 'qwen21') return isQwenImageLora(item.name)
|
||||||
const uses = inferLoraUse(item.name)
|
const uses = inferLoraUse(item.name)
|
||||||
return !isSystemGraphLora(item.name) && (!uses || uses.includes(use))
|
return !uses || uses.includes(use)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterLorasForUse(names: string[], use: LoraUse) {
|
export function filterLorasForUse(names: string[], use: LoraUse) {
|
||||||
if (use === 'qwen21') return []
|
|
||||||
return names.filter(name => loraMatchesUse(name, use))
|
return names.filter(name => loraMatchesUse(name, use))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user