Merge lora name+strength into single key to stay under 32 output limit
Combines separate lora name and strength keys into "name:strength" format, removing 6 strength keys to free output slots for mode. Migration handles legacy <lora:> wrapper, separate strength keys, and already-merged formats. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -209,7 +209,7 @@ class ProjectDB:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _migrate_lora_keys(data: dict) -> dict:
|
def _migrate_lora_keys(data: dict) -> dict:
|
||||||
"""Split legacy <lora:name:strength> values into separate name/strength keys."""
|
"""Merge lora name + strength into single 'name:strength' key, remove separate strength keys."""
|
||||||
for idx in range(1, 4):
|
for idx in range(1, 4):
|
||||||
for tier in ('high', 'low'):
|
for tier in ('high', 'low'):
|
||||||
name_key = f'lora {idx} {tier}'
|
name_key = f'lora {idx} {tier}'
|
||||||
@@ -217,21 +217,16 @@ class ProjectDB:
|
|||||||
raw = str(data.get(name_key, ''))
|
raw = str(data.get(name_key, ''))
|
||||||
if raw.startswith('<lora:'):
|
if raw.startswith('<lora:'):
|
||||||
inner = raw.replace('<lora:', '').replace('>', '')
|
inner = raw.replace('<lora:', '').replace('>', '')
|
||||||
if ':' in inner:
|
|
||||||
parts = inner.rsplit(':', 1)
|
|
||||||
data[name_key] = parts[0]
|
|
||||||
try:
|
|
||||||
data[str_key] = float(parts[1])
|
|
||||||
except ValueError:
|
|
||||||
data.setdefault(str_key, 1.0)
|
|
||||||
else:
|
|
||||||
data[name_key] = inner
|
data[name_key] = inner
|
||||||
data.setdefault(str_key, 1.0)
|
if ':' not in inner:
|
||||||
elif name_key in data and str_key not in data:
|
strength = data.pop(str_key, 1.0)
|
||||||
data[str_key] = 1.0
|
data[name_key] = f'{inner}:{float(strength)}' if inner else ''
|
||||||
# Ensure strength is always a float (JSON may deserialize 1 as int)
|
else:
|
||||||
if str_key in data:
|
data.pop(str_key, None)
|
||||||
data[str_key] = float(data[str_key])
|
elif str_key in data:
|
||||||
|
strength = float(data.pop(str_key))
|
||||||
|
if raw and ':' not in raw:
|
||||||
|
data[name_key] = f'{raw}:{strength}'
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_sequence(self, data_file_id: int, sequence_number: int) -> dict | None:
|
def get_sequence(self, data_file_id: int, sequence_number: int) -> dict | None:
|
||||||
@@ -266,11 +261,6 @@ class ProjectDB:
|
|||||||
return 0
|
return 0
|
||||||
return self.count_sequences(df["id"])
|
return self.count_sequences(df["id"])
|
||||||
|
|
||||||
_FLOAT_KEYS = frozenset(
|
|
||||||
f'lora {idx} {tier} strength'
|
|
||||||
for idx in range(1, 4) for tier in ('high', 'low')
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_sequence_keys(self, data_file_id: int, sequence_number: int) -> tuple[list[str], list[str]]:
|
def get_sequence_keys(self, data_file_id: int, sequence_number: int) -> tuple[list[str], list[str]]:
|
||||||
"""Returns (keys, types) for a sequence's data dict."""
|
"""Returns (keys, types) for a sequence's data dict."""
|
||||||
data = self.get_sequence(data_file_id, sequence_number)
|
data = self.get_sequence(data_file_id, sequence_number)
|
||||||
@@ -280,9 +270,7 @@ class ProjectDB:
|
|||||||
types = []
|
types = []
|
||||||
for k, v in data.items():
|
for k, v in data.items():
|
||||||
keys.append(k)
|
keys.append(k)
|
||||||
if k in self._FLOAT_KEYS:
|
if isinstance(v, bool):
|
||||||
types.append("FLOAT")
|
|
||||||
elif isinstance(v, bool):
|
|
||||||
types.append("STRING")
|
types.append("STRING")
|
||||||
elif isinstance(v, int):
|
elif isinstance(v, int):
|
||||||
types.append("INT")
|
types.append("INT")
|
||||||
|
|||||||
+24
-25
@@ -296,9 +296,9 @@ def render_batch_processor(state: AppState):
|
|||||||
ui.button('From Source', icon='file_download', on_click=add_from_source)
|
ui.button('From Source', icon='file_download', on_click=add_from_source)
|
||||||
|
|
||||||
# --- Standard / LoRA / VACE key sets ---
|
# --- Standard / LoRA / VACE key sets ---
|
||||||
lora_keys = ['lora 1 high', 'lora 1 high strength', 'lora 1 low', 'lora 1 low strength',
|
lora_keys = ['lora 1 high', 'lora 1 low',
|
||||||
'lora 2 high', 'lora 2 high strength', 'lora 2 low', 'lora 2 low strength',
|
'lora 2 high', 'lora 2 low',
|
||||||
'lora 3 high', 'lora 3 high strength', 'lora 3 low', 'lora 3 low strength']
|
'lora 3 high', 'lora 3 low']
|
||||||
standard_keys = {
|
standard_keys = {
|
||||||
'name', 'mode', 'general_prompt', 'general_negative', 'current_prompt', 'negative', 'prompt',
|
'name', 'mode', 'general_prompt', 'general_negative', 'current_prompt', 'negative', 'prompt',
|
||||||
'seed', 'cfg', 'camera', 'flf', KEY_SEQUENCE_NUMBER,
|
'seed', 'cfg', 'camera', 'flf', KEY_SEQUENCE_NUMBER,
|
||||||
@@ -561,28 +561,26 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
|
|||||||
with ui.expansion('LoRA Settings', icon='style').classes('w-full'):
|
with ui.expansion('LoRA Settings', icon='style').classes('w-full'):
|
||||||
for lora_idx in range(1, 4):
|
for lora_idx in range(1, 4):
|
||||||
for tier, tier_label in [('high', 'High'), ('low', 'Low')]:
|
for tier, tier_label in [('high', 'High'), ('low', 'Low')]:
|
||||||
name_key = f'lora {lora_idx} {tier}'
|
lora_key = f'lora {lora_idx} {tier}'
|
||||||
str_key = f'lora {lora_idx} {tier} strength'
|
|
||||||
|
|
||||||
# Migrate legacy <lora:name:strength> format
|
# Parse combined name:strength value
|
||||||
raw = str(seq.get(name_key, ''))
|
raw = str(seq.get(lora_key, ''))
|
||||||
|
# Handle legacy <lora:> wrapper
|
||||||
if raw.startswith('<lora:'):
|
if raw.startswith('<lora:'):
|
||||||
inner = raw.replace('<lora:', '').replace('>', '')
|
raw = raw.replace('<lora:', '').replace('>', '')
|
||||||
if ':' in inner:
|
# Also remove stale separate strength key if present
|
||||||
parts = inner.rsplit(':', 1)
|
seq.pop(f'lora {lora_idx} {tier} strength', None)
|
||||||
seq[name_key] = parts[0]
|
|
||||||
try:
|
|
||||||
seq[str_key] = float(parts[1])
|
|
||||||
except ValueError:
|
|
||||||
seq[str_key] = 1.0
|
|
||||||
else:
|
|
||||||
seq[name_key] = inner
|
|
||||||
seq.setdefault(str_key, 1.0)
|
|
||||||
else:
|
|
||||||
seq.setdefault(str_key, 1.0)
|
|
||||||
|
|
||||||
lora_name = seq.get(name_key, '')
|
if ':' in raw and raw:
|
||||||
lora_strength = seq.get(str_key, 1.0)
|
parts = raw.rsplit(':', 1)
|
||||||
|
lora_name = parts[0]
|
||||||
|
try:
|
||||||
|
lora_strength = float(parts[1])
|
||||||
|
except ValueError:
|
||||||
|
lora_strength = 1.0
|
||||||
|
else:
|
||||||
|
lora_name = raw
|
||||||
|
lora_strength = 1.0
|
||||||
|
|
||||||
with ui.row().classes('w-full items-center q-gutter-sm'):
|
with ui.row().classes('w-full items-center q-gutter-sm'):
|
||||||
ui.label(f'L{lora_idx} {tier_label}').classes(
|
ui.label(f'L{lora_idx} {tier_label}').classes(
|
||||||
@@ -598,9 +596,10 @@ def _render_sequence_card(i, seq, batch_list, data, file_path, state,
|
|||||||
format='%.1f',
|
format='%.1f',
|
||||||
).props('outlined dense').style('max-width: 80px')
|
).props('outlined dense').style('max-width: 80px')
|
||||||
|
|
||||||
def _lora_sync(nk=name_key, sk=str_key, n_inp=name_input, s_inp=strength_input):
|
def _lora_sync(k=lora_key, n_inp=name_input, s_inp=strength_input):
|
||||||
seq[nk] = n_inp.value or ''
|
name = n_inp.value or ''
|
||||||
seq[sk] = s_inp.value if s_inp.value is not None else 1.0
|
strength = s_inp.value if s_inp.value is not None else 1.0
|
||||||
|
seq[k] = f'{name}:{strength}' if name else ''
|
||||||
|
|
||||||
name_input.on('blur', lambda _, s=_lora_sync: s())
|
name_input.on('blur', lambda _, s=_lora_sync: s())
|
||||||
name_input.on('update:model-value', lambda _, s=_lora_sync: s())
|
name_input.on('update:model-value', lambda _, s=_lora_sync: s())
|
||||||
|
|||||||
+1
-5
@@ -626,13 +626,9 @@ def _render_preview_fields(item_data: dict):
|
|||||||
with ui.row().classes('w-full q-gutter-md'):
|
with ui.row().classes('w-full q-gutter-md'):
|
||||||
for lora_idx in range(1, 4):
|
for lora_idx in range(1, 4):
|
||||||
for tier, tier_label in [('high', 'High'), ('low', 'Low')]:
|
for tier, tier_label in [('high', 'High'), ('low', 'Low')]:
|
||||||
with ui.column():
|
ui.input(f'L{lora_idx} {tier_label}',
|
||||||
ui.input(f'L{lora_idx} {tier_label} Name',
|
|
||||||
value=item_data.get(f'lora {lora_idx} {tier}', '')).props(
|
value=item_data.get(f'lora {lora_idx} {tier}', '')).props(
|
||||||
'readonly outlined dense')
|
'readonly outlined dense')
|
||||||
ui.input(f'L{lora_idx} {tier_label} Str',
|
|
||||||
value=str(item_data.get(f'lora {lora_idx} {tier} strength', 1.0))).props(
|
|
||||||
'readonly outlined dense')
|
|
||||||
|
|
||||||
vace_keys = ['frame_to_skip', 'vace schedule', 'video file path']
|
vace_keys = ['frame_to_skip', 'vace schedule', 'video file path']
|
||||||
if any(k in item_data for k in vace_keys):
|
if any(k in item_data for k in vace_keys):
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ DEFAULTS = {
|
|||||||
"reference path": "",
|
"reference path": "",
|
||||||
"flf image path": "",
|
"flf image path": "",
|
||||||
|
|
||||||
# --- LoRAs ---
|
# --- LoRAs (format: "name:strength" or empty) ---
|
||||||
"lora 1 high": "", "lora 1 high strength": 1.0,
|
"lora 1 high": "",
|
||||||
"lora 1 low": "", "lora 1 low strength": 1.0,
|
"lora 1 low": "",
|
||||||
"lora 2 high": "", "lora 2 high strength": 1.0,
|
"lora 2 high": "",
|
||||||
"lora 2 low": "", "lora 2 low strength": 1.0,
|
"lora 2 low": "",
|
||||||
"lora 3 high": "", "lora 3 high strength": 1.0,
|
"lora 3 high": "",
|
||||||
"lora 3 low": "", "lora 3 low strength": 1.0
|
"lora 3 low": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_FILE = Path(".editor_config.json")
|
CONFIG_FILE = Path(".editor_config.json")
|
||||||
@@ -145,7 +145,13 @@ def save_snippets(snippets):
|
|||||||
os.replace(tmp, SNIPPETS_FILE)
|
os.replace(tmp, SNIPPETS_FILE)
|
||||||
|
|
||||||
def _migrate_lora_keys(data: dict) -> None:
|
def _migrate_lora_keys(data: dict) -> None:
|
||||||
"""Split legacy <lora:name:strength> values into separate name/strength keys in-place."""
|
"""Merge lora name + strength into single 'name:strength' key, remove separate strength keys.
|
||||||
|
|
||||||
|
Handles three legacy formats:
|
||||||
|
1. <lora:Name:0.5> → Name:0.5
|
||||||
|
2. Separate name_key + str_key → name:strength (then delete str_key)
|
||||||
|
3. Already merged name:strength → no change
|
||||||
|
"""
|
||||||
for item in data.get(KEY_BATCH_DATA, []):
|
for item in data.get(KEY_BATCH_DATA, []):
|
||||||
if not isinstance(item, dict):
|
if not isinstance(item, dict):
|
||||||
continue
|
continue
|
||||||
@@ -154,23 +160,26 @@ def _migrate_lora_keys(data: dict) -> None:
|
|||||||
name_key = f'lora {idx} {tier}'
|
name_key = f'lora {idx} {tier}'
|
||||||
str_key = f'lora {idx} {tier} strength'
|
str_key = f'lora {idx} {tier} strength'
|
||||||
raw = str(item.get(name_key, ''))
|
raw = str(item.get(name_key, ''))
|
||||||
|
|
||||||
if raw.startswith('<lora:'):
|
if raw.startswith('<lora:'):
|
||||||
|
# Legacy <lora:Name:0.5> format → Name:0.5
|
||||||
inner = raw.replace('<lora:', '').replace('>', '')
|
inner = raw.replace('<lora:', '').replace('>', '')
|
||||||
if ':' in inner:
|
item[name_key] = inner # already name:strength or just name
|
||||||
parts = inner.rsplit(':', 1)
|
if ':' not in inner:
|
||||||
item[name_key] = parts[0]
|
# No strength in the wrapper, check separate key
|
||||||
try:
|
strength = item.pop(str_key, 1.0)
|
||||||
item[str_key] = float(parts[1])
|
item[name_key] = f'{inner}:{float(strength)}' if inner else ''
|
||||||
except ValueError:
|
|
||||||
item.setdefault(str_key, 1.0)
|
|
||||||
else:
|
else:
|
||||||
item[name_key] = inner
|
item.pop(str_key, None)
|
||||||
item.setdefault(str_key, 1.0)
|
elif str_key in item:
|
||||||
elif name_key in item and str_key not in item:
|
# Separate strength key exists → merge into name:strength
|
||||||
item[str_key] = 1.0
|
strength = float(item.pop(str_key))
|
||||||
# Ensure strength is always a float (JSON may deserialize 1 as int)
|
if raw:
|
||||||
if str_key in item:
|
# Avoid double-merging if already has name:strength format
|
||||||
item[str_key] = float(item[str_key])
|
if ':' not in raw:
|
||||||
|
item[name_key] = f'{raw}:{strength}'
|
||||||
|
# else: already merged, just remove the stale strength key
|
||||||
|
# No change needed if already in name:strength format or empty
|
||||||
|
|
||||||
|
|
||||||
def load_json(path: str | Path) -> tuple[dict[str, Any], float]:
|
def load_json(path: str | Path) -> tuple[dict[str, Any], float]:
|
||||||
|
|||||||
Reference in New Issue
Block a user