feat: add "Respect Recommended Strength" feature to LoRA Randomizer

Add support for respecting recommended strength values from LoRA usage_tips
when randomizing LoRA selection.

Features:
- New toggle setting to enable/disable recommended strength respect (default off)
- Scale range slider (0-2, default 0.5-1.0) to adjust recommended values
- Uses recommended strength × random(scale) when feature enabled
- Fallbacks to original Model/Clip Strength range when no recommendation exists
- Clip strength recommendations only apply when using Custom Range mode

Backend changes:
- Parse usage_tips JSON string to extract strength/clipStrength
- Apply scale factor to recommended values during randomization
- Pass new parameters through API route and node

Frontend changes:
- Update RandomizerConfig type with new properties
- Add new UI section with toggle and dual-range slider
- Wire up state management and event handlers
- No layout shift (removed description text)

Tests:
- Add tests for enabled/disabled recommended strength in API routes
- Add test verifying config passed to service
- All existing tests pass

Build: Include compiled Vue widgets
This commit is contained in:
Will Miao
2026-01-14 16:34:24 +08:00
parent 4951ff358e
commit fc8240e99e
12 changed files with 441 additions and 85 deletions

View File

@@ -16,6 +16,9 @@
:last-used="state.lastUsed.value"
:current-loras="currentLoras"
:can-reuse-last="canReuseLast"
:use-recommended-strength="state.useRecommendedStrength.value"
:recommended-strength-scale-min="state.recommendedStrengthScaleMin.value"
:recommended-strength-scale-max="state.recommendedStrengthScaleMax.value"
@update:count-mode="state.countMode.value = $event"
@update:count-fixed="state.countFixed.value = $event"
@update:count-min="state.countMin.value = $event"
@@ -26,6 +29,9 @@
@update:clip-strength-min="state.clipStrengthMin.value = $event"
@update:clip-strength-max="state.clipStrengthMax.value = $event"
@update:roll-mode="state.rollMode.value = $event"
@update:use-recommended-strength="state.useRecommendedStrength.value = $event"
@update:recommended-strength-scale-min="state.recommendedStrengthScaleMin.value = $event"
@update:recommended-strength-scale-max="state.recommendedStrengthScaleMax.value = $event"
@generate-fixed="handleGenerateFixed"
@always-randomize="handleAlwaysRandomize"
@reuse-last="handleReuseLast"

View File

@@ -73,6 +73,40 @@
</div>
</div>
<!-- Recommended Strength -->
<div class="setting-section">
<div class="section-header-with-toggle">
<label class="setting-label">
Respect Recommended Strength
</label>
<button
type="button"
class="toggle-switch"
:class="{ 'toggle-switch--active': useRecommendedStrength }"
@click="$emit('update:useRecommendedStrength', !useRecommendedStrength)"
role="switch"
:aria-checked="useRecommendedStrength"
title="Use recommended strength values from usage tips"
>
<span class="toggle-switch__track"></span>
<span class="toggle-switch__thumb"></span>
</button>
</div>
<div class="slider-container" :class="{ 'slider-container--disabled': !useRecommendedStrength }">
<DualRangeSlider
:min="0"
:max="2"
:value-min="recommendedStrengthScaleMin"
:value-max="recommendedStrengthScaleMax"
:step="0.1"
:default-range="{ min: 0.5, max: 1.0 }"
:disabled="!useRecommendedStrength"
@update:value-min="$emit('update:recommendedStrengthScaleMin', $event)"
@update:value-max="$emit('update:recommendedStrengthScaleMax', $event)"
/>
</div>
</div>
<!-- Clip Strength Range -->
<div class="setting-section">
<div class="section-header-with-toggle">
@@ -200,6 +234,9 @@ defineProps<{
lastUsed: LoraEntry[] | null
currentLoras: LoraEntry[]
canReuseLast: boolean
useRecommendedStrength: boolean
recommendedStrengthScaleMin: number
recommendedStrengthScaleMax: number
}>()
defineEmits<{
@@ -213,6 +250,9 @@ defineEmits<{
'update:clipStrengthMin': [value: number]
'update:clipStrengthMax': [value: number]
'update:rollMode': [value: 'fixed' | 'always']
'update:useRecommendedStrength': [value: boolean]
'update:recommendedStrengthScaleMin': [value: number]
'update:recommendedStrengthScaleMax': [value: number]
'generate-fixed': []
'always-randomize': []
'reuse-last': []
@@ -341,6 +381,11 @@ const areLorasEqual = (a: LoraEntry[] | null, b: LoraEntry[] | null): boolean =>
padding: 4px 8px;
}
.slider-container--disabled {
opacity: 0.5;
pointer-events: none;
}
/* Toggle Switch (same style as LicenseSection) */
.toggle-switch {
position: relative;