Split SwarmUI portraits into a model catalog and layered prompts.

Picker is Swarm intersect config, preset style/shot inherit model defaults, and living saves lift the old preset shape.
This commit is contained in:
Leonid Pershin
2026-08-20 14:08:11 +03:00
parent cc9ea18480
commit 81fd3f6181
20 changed files with 1142 additions and 179 deletions
@@ -11,6 +11,8 @@ public class SwarmUiSettingsStoreTests
var profile = config.Resolve(8, PortraitKind.Avatar);
Assert.Equal("child", profile.PresetId);
Assert.Equal(SwarmUiModelDefinition.DreamShaperId, profile.Model);
Assert.Equal(4, profile.Steps);
}
[Fact]
@@ -20,6 +22,23 @@ public class SwarmUiSettingsStoreTests
var profile = config.Resolve(34, PortraitKind.Full);
Assert.Equal("default", profile.PresetId);
Assert.Equal(SwarmUiModelDefinition.BabesId, profile.Model);
Assert.Equal(7, profile.Steps);
}
[Fact]
public void Resolve_PresetOverrideBeatsModelDefaults()
{
var config = SwarmUiConfigFile.CreateDefault();
var preset = config.Presets[0];
preset.Steps = 12;
preset.CfgScale = 3.5;
var profile = config.Resolve(34, PortraitKind.Avatar);
Assert.Equal(12, profile.Steps);
Assert.Equal(3.5, profile.CfgScale);
Assert.Equal(0, profile.ClipSkip);
}
[Fact]
@@ -30,7 +49,7 @@ public class SwarmUiSettingsStoreTests
Model = "legacy.safetensors",
Steps = 12,
Positive = "legacy prompt",
Avatar = new SwarmUiKindPreset { Width = 640, Height = 640 },
Avatar = new SwarmUiKindPreset { Width = 640, Height = 640, Positive = "close up." },
};
config.NormalizeAfterLoad();
@@ -38,7 +57,77 @@ public class SwarmUiSettingsStoreTests
Assert.Single(config.Presets);
Assert.Equal("default", config.Presets[0].Id);
Assert.Equal("legacy.safetensors", config.Presets[0].Model);
Assert.Equal(12, config.Presets[0].Steps);
Assert.Equal("legacy prompt", config.Presets[0].Style);
Assert.Null(config.Presets[0].Positive);
Assert.Null(config.Presets[0].Steps);
var model = Assert.Single(config.Models, entry => entry.Id == "legacy.safetensors");
Assert.Equal(12, model.Steps);
Assert.Equal("close up.", config.Presets[0].Avatar!.ShotType);
Assert.Null(config.Presets[0].Avatar!.Positive);
}
[Fact]
public void NormalizeAfterLoad_LiftsLegacyPresetPositiveIntoStyleAndShotType()
{
var config = new SwarmUiConfigFile
{
Presets =
[
new SwarmUiPresetDefinition
{
Id = "default",
Label = "Default",
Model = "a.safetensors",
Steps = 6,
CfgScale = 2,
Positive = "was base prompt",
Negative = "old neg",
Avatar = new SwarmUiKindPreset { Width = 512, Height = 512, Positive = "was kind" },
Custom = new SwarmUiKindPreset { Width = 512, Height = 512 },
FullBody = new SwarmUiKindPreset { Width = 512, Height = 768 },
},
],
};
config.NormalizeAfterLoad();
Assert.Equal("was base prompt", config.Presets[0].Style);
Assert.Equal("was kind", config.Presets[0].Avatar!.ShotType);
Assert.Contains(config.Models, model => model.Id == "a.safetensors");
Assert.Equal("", Assert.Single(config.Models).Positive);
}
[Fact]
public void AllowedModelIds_IntersectsSwarmWithCatalog()
{
var config = SwarmUiConfigFile.CreateDefault();
var discovery = new SwarmUiDiscovery(
true,
[
SwarmUiModelDefinition.BabesId,
"not-in-config.safetensors",
SwarmUiModelDefinition.EpicRealismId,
],
[],
[],
[]);
var allowed = config.AllowedModelIds(discovery);
Assert.Equal(
[SwarmUiModelDefinition.BabesId, SwarmUiModelDefinition.EpicRealismId],
allowed);
Assert.DoesNotContain("not-in-config.safetensors", allowed);
Assert.DoesNotContain(SwarmUiModelDefinition.DreamShaperId, allowed);
}
[Fact]
public void AllowedModelIds_WhenSwarmOffline_ReturnsCatalog()
{
var config = SwarmUiConfigFile.CreateDefault();
var allowed = config.AllowedModelIds(SwarmUiDiscovery.Offline);
Assert.Equal(config.Models.Select(model => model.Id), allowed);
}
[Fact]
@@ -46,6 +135,7 @@ public class SwarmUiSettingsStoreTests
{
var source = SwarmUiConfigFile.CreateDefault();
source.Presets[0].Model = "mutated.safetensors";
source.Models[0].Id = "mutated.safetensors";
var copy = SwarmUiConfigFile.Clone(source);
copy.Presets[0].Model = "other.safetensors";
@@ -54,4 +144,16 @@ public class SwarmUiSettingsStoreTests
Assert.Equal("other.safetensors", copy.Presets[0].Model);
Assert.Equal(source.ActivePresetId, copy.ActivePresetId);
}
[Fact]
public void CreateDefault_SeedsEveryKnownSwarmModel()
{
var config = SwarmUiConfigFile.CreateDefault();
var ids = config.Models.Select(model => model.Id).ToList();
Assert.Contains(SwarmUiModelDefinition.BabesId, ids);
Assert.Contains(SwarmUiModelDefinition.DreamShaperId, ids);
Assert.Contains(SwarmUiModelDefinition.EpicRealismId, ids);
Assert.Contains(SwarmUiModelDefinition.LustifyId, ids);
}
}