Merge branch 'phase/63-krea-only'
# Conflicts: # docs/phases/11-events/README.md
This commit is contained in:
@@ -11,22 +11,22 @@ DreamShaper.
|
||||
|
||||
## Задачи
|
||||
|
||||
- [ ] В `swarmui.json` только Muse GGUF и Realism v2.5 INT8 (8 / CFG 1 / euler / simple).
|
||||
- [x] В `swarmui.json` только Muse GGUF и Realism v2.5 INT8 (8 / CFG 1 / euler / simple).
|
||||
babes, epicrealism, lustify и DreamShaper убрать. `Catalog()` совпадает с файлом
|
||||
- [ ] Пресеты `default` и `child` — Realism v2.5. Стиль и shotType `child` — проза без
|
||||
- [x] Пресеты `default` и `child` — Realism v2.5. Стиль и shotType `child` — проза без
|
||||
`(word:1.4)` и SD-весов
|
||||
- [ ] `ageRules` 6–11 по-прежнему ведут на `child`, но модель у child — v2.5
|
||||
- [ ] Живые школы не лифтить. Сокет не бампить. PNG не перегенерировать
|
||||
- [ ] [`protocol.md`](../../protocol.md) — описание каталога без четырёх SDXL, тем же коммитом
|
||||
- [x] `ageRules` 6–11 по-прежнему ведут на `child`, но модель у child — v2.5
|
||||
- [x] Живые школы не лифтить. Сокет не бампить. PNG не перегенерировать
|
||||
- [x] [`protocol.md`](../../protocol.md) — описание каталога без четырёх SDXL, тем же коммитом
|
||||
(версия сокета не меняется)
|
||||
|
||||
## Тесты, без которых фаза не закрыта
|
||||
|
||||
- [ ] `CreateDefault` содержит ровно два id: Muse и Realism v2.5; нет DreamShaper и babes
|
||||
- [ ] Resolve взрослого и ребёнка (6–11) берут Realism v2.5
|
||||
- [ ] Стиль/негатив child без `(low quality` и без `:1.4)`
|
||||
- [ ] `GET /api/settings/swarmui` отдаёт только Krea-id; create копирует их в сейв новой школы
|
||||
- [ ] Нормализация загрузки не переписывает `models` уже лежащей школы с SDXL в каталоге
|
||||
- [x] `CreateDefault` содержит ровно два id: Muse и Realism v2.5; нет DreamShaper и babes
|
||||
- [x] Resolve взрослого и ребёнка (6–11) берут Realism v2.5
|
||||
- [x] Стиль/негатив child без `(low quality` и без `:1.4)`
|
||||
- [x] `GET /api/settings/swarmui` отдаёт только Krea-id; create копирует их в сейв новой школы
|
||||
- [x] Нормализация загрузки не переписывает `models` уже лежащей школы с SDXL в каталоге
|
||||
|
||||
## Критерий готовности
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
| Фаза | Статус | Зачем |
|
||||
| --- | --- | --- |
|
||||
| [63. Каталог Krea 2](63-krea-only.md) | 🔄 | Muse + v2.5, child на v2.5, без лифта |
|
||||
| [63. Каталог Krea 2](63-krea-only.md) | ✅ | Muse + v2.5, child на v2.5, без лифта |
|
||||
|
||||
**Этап B — лента.** Факт, тост, пауза.
|
||||
|
||||
|
||||
+7
-2
@@ -539,9 +539,14 @@ generation knobs, usually empty base positive/negative, LoRA and embedding lists
|
||||
(model id, `style`, extra negative, optional generation overrides, per-kind size/`shotType`, LoRA
|
||||
and embedding lists), `activePresetId` and `ageRules`. LoRA and embeddings on the model, preset and
|
||||
shot kind concatenate at generate time.
|
||||
The vanilla catalog is Krea 2 only: Muse GGUF (`museByStableYogi_v30TurboGguf.gguf`) and Realism
|
||||
v2.5 INT8 (`realismByStableYogi_v25INT8Turbo.safetensors`), both 8 / CFG 1 / euler / simple. The
|
||||
four SDXL Lightning checkpoints (babes, DreamShaper, epicrealism, lustify) are not in the template.
|
||||
Presets `default` and `child` both use Realism v2.5; `ageRules` still send ages 6–11 to `child`.
|
||||
The model picker in the UI is Swarm's list intersected with `models`. A new school copies this into
|
||||
its save as `portraitSettings`. Living schools generate from that copy, not from this file. Older
|
||||
saves without `models` lift on load (`positive` → `style`, kind `positive` → `shotType`).
|
||||
its save as `portraitSettings`. Living schools generate from that copy, not from this file — a save
|
||||
that still lists SDXL models keeps them. Older saves without `models` lift on load (`positive` →
|
||||
`style`, kind `positive` → `shotType`).
|
||||
|
||||
### `PUT /api/settings/swarmui`
|
||||
|
||||
|
||||
@@ -527,10 +527,6 @@ internal sealed class SwarmUiModelDefinition
|
||||
|
||||
public static List<SwarmUiModelDefinition> Catalog() =>
|
||||
[
|
||||
Lightning(BabesId, "babesByStableYogi v4 XL Lightning", 7, 1.5, 0, "euler", "normal", 0),
|
||||
Lightning(DreamShaperId, "DreamShaper XL Lightning", 4, 2, 2, "dpmpp_sde", "karras", 3346112079),
|
||||
Lightning(EpicRealismId, "epicrealism XL Lightning", 7, 1.5, 0, "euler", "normal", 0),
|
||||
Lightning(LustifyId, "lustify NSFW v40 DMD2", 7, 1.5, 0, "euler", "normal", 0),
|
||||
Lightning(MuseId, "museByStableYogi v3.0 Turbo GGUF", 8, 1, 0, "euler", "simple", 0),
|
||||
Lightning(RealismV25Id, "realismByStableYogi v2.5 INT8 Turbo", 8, 1, 0, "euler", "simple", 0),
|
||||
];
|
||||
@@ -765,23 +761,25 @@ internal sealed class SwarmUiPresetDefinition
|
||||
{
|
||||
Id = "child",
|
||||
Label = "Children",
|
||||
Model = SwarmUiModelDefinition.DreamShaperId,
|
||||
Model = SwarmUiModelDefinition.RealismV25Id,
|
||||
Style =
|
||||
"cinematic photo, child-friendly school portrait, soft natural features, gentle expression, neutral background, natural lighting, realistic, sharp focus",
|
||||
"A cinematic school portrait photograph of a child with natural window light, a neutral background, soft natural features, a gentle expression, realistic skin, and sharp focus",
|
||||
Negative =
|
||||
"(low quality, worst quality:1.4), cgi, text, signature, watermark, extra limbs, nsfw, nude, naked, explicit, blurry, deformed, bad anatomy, logo",
|
||||
"text, signature, watermark, extra limbs, deformed anatomy, extra fingers, cgi, nsfw, nude, naked, explicit",
|
||||
Avatar = new SwarmUiKindPreset
|
||||
{
|
||||
Width = 1024,
|
||||
Height = 1024,
|
||||
ShotType = "close up, head and shoulders portrait of a child, facing the camera, upper body visible, soft features.",
|
||||
ShotType =
|
||||
"Close-up head and shoulders portrait of a child, facing the camera, upper body visible, with soft features",
|
||||
},
|
||||
Custom = new SwarmUiKindPreset { Width = 896, Height = 1152 },
|
||||
FullBody = new SwarmUiKindPreset
|
||||
{
|
||||
Width = 896,
|
||||
Height = 1152,
|
||||
ShotType = "full body standing portrait of a child, head to toe visible, neutral pose, current outfit clearly visible.",
|
||||
ShotType =
|
||||
"Full-body standing portrait of a child from head to toe, neutral pose, current outfit clearly visible",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,62 +1,6 @@
|
||||
{
|
||||
"activePresetId": "default",
|
||||
"models": [
|
||||
{
|
||||
"id": "babesByStableYogi_v4XLLightning.safetensors",
|
||||
"label": "babesByStableYogi v4 XL Lightning",
|
||||
"steps": 7,
|
||||
"cfgScale": 1.5,
|
||||
"clipSkip": 0,
|
||||
"sampler": "euler",
|
||||
"scheduler": "normal",
|
||||
"seed": 0,
|
||||
"positive": "",
|
||||
"negative": "",
|
||||
"positiveLoras": [],
|
||||
"negativeLoras": []
|
||||
},
|
||||
{
|
||||
"id": "DreamShaper_XL_-_Lightning_DPM++_SDE.safetensors",
|
||||
"label": "DreamShaper XL Lightning",
|
||||
"steps": 4,
|
||||
"cfgScale": 2,
|
||||
"clipSkip": 2,
|
||||
"sampler": "dpmpp_sde",
|
||||
"scheduler": "karras",
|
||||
"seed": 3346112079,
|
||||
"positive": "",
|
||||
"negative": "",
|
||||
"positiveLoras": [],
|
||||
"negativeLoras": []
|
||||
},
|
||||
{
|
||||
"id": "epicrealismXL_VXIAbeast4SLightning.safetensors",
|
||||
"label": "epicrealism XL Lightning",
|
||||
"steps": 7,
|
||||
"cfgScale": 1.5,
|
||||
"clipSkip": 0,
|
||||
"sampler": "euler",
|
||||
"scheduler": "normal",
|
||||
"seed": 0,
|
||||
"positive": "",
|
||||
"negative": "",
|
||||
"positiveLoras": [],
|
||||
"negativeLoras": []
|
||||
},
|
||||
{
|
||||
"id": "lustifyNSFWCheckpoint_v40DMD2.safetensors",
|
||||
"label": "lustify NSFW v40 DMD2",
|
||||
"steps": 7,
|
||||
"cfgScale": 1.5,
|
||||
"clipSkip": 0,
|
||||
"sampler": "euler",
|
||||
"scheduler": "normal",
|
||||
"seed": 0,
|
||||
"positive": "",
|
||||
"negative": "",
|
||||
"positiveLoras": [],
|
||||
"negativeLoras": []
|
||||
},
|
||||
{
|
||||
"id": "museByStableYogi_v30TurboGguf.gguf",
|
||||
"label": "museByStableYogi v3.0 Turbo GGUF",
|
||||
@@ -112,13 +56,13 @@
|
||||
{
|
||||
"id": "child",
|
||||
"label": "Children",
|
||||
"model": "DreamShaper_XL_-_Lightning_DPM++_SDE.safetensors",
|
||||
"style": "cinematic photo, child-friendly school portrait, soft natural features, gentle expression, neutral background, natural lighting, realistic, sharp focus",
|
||||
"negative": "(low quality, worst quality:1.4), cgi, text, signature, watermark, extra limbs, nsfw, nude, naked, explicit, blurry, deformed, bad anatomy, logo",
|
||||
"model": "realismByStableYogi_v25INT8Turbo.safetensors",
|
||||
"style": "A cinematic school portrait photograph of a child with natural window light, a neutral background, soft natural features, a gentle expression, realistic skin, and sharp focus",
|
||||
"negative": "text, signature, watermark, extra limbs, deformed anatomy, extra fingers, cgi, nsfw, nude, naked, explicit",
|
||||
"avatar": {
|
||||
"width": 1024,
|
||||
"height": 1024,
|
||||
"shotType": "close up, head and shoulders portrait of a child, facing the camera, upper body visible, soft features."
|
||||
"shotType": "Close-up head and shoulders portrait of a child, facing the camera, upper body visible, with soft features"
|
||||
},
|
||||
"custom": {
|
||||
"width": 896,
|
||||
@@ -128,7 +72,7 @@
|
||||
"fullBody": {
|
||||
"width": 896,
|
||||
"height": 1152,
|
||||
"shotType": "full body standing portrait of a child, head to toe visible, neutral pose, current outfit clearly visible."
|
||||
"shotType": "Full-body standing portrait of a child from head to toe, neutral pose, current outfit clearly visible"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -105,8 +105,14 @@ public class PortraitApiTests(AppHostFixture fixture)
|
||||
Assert.NotNull(settings);
|
||||
Assert.NotEmpty(settings!.Presets);
|
||||
Assert.NotEmpty(settings.Models);
|
||||
Assert.Contains(settings.Models, model => model.Id == "museByStableYogi_v30TurboGguf.gguf");
|
||||
Assert.Contains(settings.Models, model => model.Id == "realismByStableYogi_v25INT8Turbo.safetensors");
|
||||
var ids = settings.Models.Select(model => model.Id).ToList();
|
||||
Assert.Equal(
|
||||
["museByStableYogi_v30TurboGguf.gguf", "realismByStableYogi_v25INT8Turbo.safetensors"],
|
||||
ids);
|
||||
Assert.DoesNotContain("babesByStableYogi_v4XLLightning.safetensors", ids);
|
||||
Assert.DoesNotContain("DreamShaper_XL_-_Lightning_DPM++_SDE.safetensors", ids);
|
||||
Assert.DoesNotContain("epicrealismXL_VXIAbeast4SLightning.safetensors", ids);
|
||||
Assert.DoesNotContain("lustifyNSFWCheckpoint_v40DMD2.safetensors", ids);
|
||||
Assert.False(string.IsNullOrWhiteSpace(settings.ActivePresetId));
|
||||
}
|
||||
|
||||
@@ -127,13 +133,21 @@ public class PortraitApiTests(AppHostFixture fixture)
|
||||
Assert.True(presets.TryGetProperty("models", out var models));
|
||||
Assert.True(models.GetArrayLength() > 0);
|
||||
var ids = models.EnumerateArray().Select(model => model.GetProperty("id").GetString()).ToList();
|
||||
Assert.Contains("museByStableYogi_v30TurboGguf.gguf", ids);
|
||||
Assert.Contains("realismByStableYogi_v25INT8Turbo.safetensors", ids);
|
||||
Assert.Equal(
|
||||
["museByStableYogi_v30TurboGguf.gguf", "realismByStableYogi_v25INT8Turbo.safetensors"],
|
||||
ids);
|
||||
Assert.DoesNotContain("babesByStableYogi_v4XLLightning.safetensors", ids);
|
||||
Assert.DoesNotContain("DreamShaper_XL_-_Lightning_DPM++_SDE.safetensors", ids);
|
||||
var defaultPreset = presets.GetProperty("presets").EnumerateArray()
|
||||
.First(preset => preset.GetProperty("id").GetString() == "default");
|
||||
Assert.Equal(
|
||||
"realismByStableYogi_v25INT8Turbo.safetensors",
|
||||
defaultPreset.GetProperty("model").GetString());
|
||||
var childPreset = presets.GetProperty("presets").EnumerateArray()
|
||||
.First(preset => preset.GetProperty("id").GetString() == "child");
|
||||
Assert.Equal(
|
||||
"realismByStableYogi_v25INT8Turbo.safetensors",
|
||||
childPreset.GetProperty("model").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -12,9 +12,11 @@ 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);
|
||||
Assert.Equal(2, profile.CfgScale);
|
||||
Assert.Equal(SwarmUiModelDefinition.RealismV25Id, profile.Model);
|
||||
Assert.Equal(8, profile.Steps);
|
||||
Assert.Equal(1, profile.CfgScale);
|
||||
Assert.Equal("euler", profile.Sampler);
|
||||
Assert.Equal("simple", profile.Scheduler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -157,8 +159,8 @@ public class SwarmUiSettingsStoreTests
|
||||
true,
|
||||
[
|
||||
SwarmUiModelDefinition.BabesId,
|
||||
SwarmUiModelDefinition.MuseId,
|
||||
"not-in-config.safetensors",
|
||||
SwarmUiModelDefinition.EpicRealismId,
|
||||
],
|
||||
[],
|
||||
[],
|
||||
@@ -167,10 +169,9 @@ public class SwarmUiSettingsStoreTests
|
||||
|
||||
var allowed = config.AllowedModelIds(discovery);
|
||||
|
||||
Assert.Equal(
|
||||
[SwarmUiModelDefinition.BabesId, SwarmUiModelDefinition.EpicRealismId],
|
||||
allowed);
|
||||
Assert.Equal([SwarmUiModelDefinition.MuseId], allowed);
|
||||
Assert.DoesNotContain("not-in-config.safetensors", allowed);
|
||||
Assert.DoesNotContain(SwarmUiModelDefinition.BabesId, allowed);
|
||||
Assert.DoesNotContain(SwarmUiModelDefinition.DreamShaperId, allowed);
|
||||
}
|
||||
|
||||
@@ -204,12 +205,13 @@ public class SwarmUiSettingsStoreTests
|
||||
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);
|
||||
Assert.Contains(SwarmUiModelDefinition.MuseId, ids);
|
||||
Assert.Contains(SwarmUiModelDefinition.RealismV25Id, ids);
|
||||
Assert.Equal(
|
||||
[SwarmUiModelDefinition.MuseId, SwarmUiModelDefinition.RealismV25Id],
|
||||
ids);
|
||||
Assert.DoesNotContain(SwarmUiModelDefinition.BabesId, ids);
|
||||
Assert.DoesNotContain(SwarmUiModelDefinition.DreamShaperId, ids);
|
||||
Assert.DoesNotContain(SwarmUiModelDefinition.EpicRealismId, ids);
|
||||
Assert.DoesNotContain(SwarmUiModelDefinition.LustifyId, ids);
|
||||
Assert.DoesNotContain("realismByStableYogi_v20TurboGGUF.gguf", ids);
|
||||
|
||||
var realism = Assert.Single(config.Models, model => model.Id == SwarmUiModelDefinition.RealismV25Id);
|
||||
@@ -230,7 +232,24 @@ public class SwarmUiSettingsStoreTests
|
||||
Assert.Contains("photograph", adult.Style, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
var child = Assert.Single(config.Presets, preset => preset.Id == "child");
|
||||
Assert.Equal(SwarmUiModelDefinition.DreamShaperId, child.Model);
|
||||
Assert.Equal(SwarmUiModelDefinition.RealismV25Id, child.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateChild_StyleAndNegativeAreProseWithoutSdWeights()
|
||||
{
|
||||
var child = SwarmUiPresetDefinition.CreateChild();
|
||||
Assert.NotNull(child.Avatar);
|
||||
Assert.NotNull(child.FullBody);
|
||||
|
||||
Assert.DoesNotContain("(low quality", child.Style, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(":1.4)", child.Style, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("(low quality", child.Negative, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(":1.4)", child.Negative, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("(low quality", child.Avatar.ShotType, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(":1.4)", child.Avatar.ShotType, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("(low quality", child.FullBody.ShotType, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain(":1.4)", child.FullBody.ShotType, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -267,6 +286,72 @@ public class SwarmUiSettingsStoreTests
|
||||
Assert.Equal(expected.Presets[0].Negative, loaded.Presets[0].Negative);
|
||||
Assert.Equal(expected.Presets[0].Avatar?.ShotType, loaded.Presets[0].Avatar?.ShotType);
|
||||
Assert.Equal(expected.Presets[1].Model, loaded.Presets[1].Model);
|
||||
Assert.Equal(expected.Presets[1].Style, loaded.Presets[1].Style);
|
||||
Assert.Equal(expected.Presets[1].Negative, loaded.Presets[1].Negative);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NormalizeAfterLoad_DoesNotRewriteExistingSdxlCatalog()
|
||||
{
|
||||
var living = new SwarmUiConfigFile
|
||||
{
|
||||
ActivePresetId = "default",
|
||||
Models =
|
||||
[
|
||||
new SwarmUiModelDefinition
|
||||
{
|
||||
Id = SwarmUiModelDefinition.BabesId,
|
||||
Label = "babes",
|
||||
Steps = 7,
|
||||
CfgScale = 1.5,
|
||||
Sampler = "euler",
|
||||
Scheduler = "normal",
|
||||
},
|
||||
new SwarmUiModelDefinition
|
||||
{
|
||||
Id = SwarmUiModelDefinition.DreamShaperId,
|
||||
Label = "DreamShaper",
|
||||
Steps = 4,
|
||||
CfgScale = 2,
|
||||
Sampler = "dpmpp_sde",
|
||||
Scheduler = "karras",
|
||||
},
|
||||
],
|
||||
Presets =
|
||||
[
|
||||
new SwarmUiPresetDefinition
|
||||
{
|
||||
Id = "default",
|
||||
Label = "Default",
|
||||
Model = SwarmUiModelDefinition.BabesId,
|
||||
Avatar = new SwarmUiKindPreset { Width = 1024, Height = 1024 },
|
||||
Custom = new SwarmUiKindPreset { Width = 896, Height = 1152 },
|
||||
FullBody = new SwarmUiKindPreset { Width = 896, Height = 1152 },
|
||||
},
|
||||
new SwarmUiPresetDefinition
|
||||
{
|
||||
Id = "child",
|
||||
Label = "Children",
|
||||
Model = SwarmUiModelDefinition.DreamShaperId,
|
||||
Avatar = new SwarmUiKindPreset { Width = 1024, Height = 1024 },
|
||||
Custom = new SwarmUiKindPreset { Width = 896, Height = 1152 },
|
||||
FullBody = new SwarmUiKindPreset { Width = 896, Height = 1152 },
|
||||
},
|
||||
],
|
||||
AgeRules = [new SwarmUiAgeRule { MinAge = 6, MaxAge = 11, PresetId = "child" }],
|
||||
};
|
||||
|
||||
living.NormalizeAfterLoad();
|
||||
|
||||
Assert.Equal(
|
||||
[SwarmUiModelDefinition.BabesId, SwarmUiModelDefinition.DreamShaperId],
|
||||
living.Models.Select(model => model.Id));
|
||||
Assert.DoesNotContain(living.Models, model => model.Id == SwarmUiModelDefinition.MuseId);
|
||||
Assert.DoesNotContain(living.Models, model => model.Id == SwarmUiModelDefinition.RealismV25Id);
|
||||
Assert.Equal(SwarmUiModelDefinition.BabesId, living.Presets[0].Model);
|
||||
Assert.Equal(SwarmUiModelDefinition.DreamShaperId, living.Presets[1].Model);
|
||||
Assert.Equal("child", living.Resolve(8, PortraitKind.Avatar).PresetId);
|
||||
Assert.Equal(SwarmUiModelDefinition.DreamShaperId, living.Resolve(8, PortraitKind.Avatar).Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user