diff --git a/docs/design/off-queue/README.md b/docs/design/off-queue/README.md
index 82673d1..3c10a73 100644
--- a/docs/design/off-queue/README.md
+++ b/docs/design/off-queue/README.md
@@ -11,6 +11,7 @@
| [Состояние экрана](view-state.md) | 55 |
| [Модели портретов](portrait-models.md) | 60 |
| [LoRA и embeddings](portrait-loras-embeds.md) | 61 |
+| [Промпт и Krea-модели](portrait-prompt-craft.md) | 62 |
Люди: [`../02-people/people.md`](../02-people/people.md). Штат: [`../03-staffing/staffing.md`](../03-staffing/staffing.md).
Расписание: [`../04-schedule/schedule.md`](../04-schedule/schedule.md). Присутствие: [`../05-ai/ai.md`](../05-ai/ai.md).
diff --git a/docs/design/off-queue/portrait-prompt-craft.md b/docs/design/off-queue/portrait-prompt-craft.md
new file mode 100644
index 0000000..3268d34
--- /dev/null
+++ b/docs/design/off-queue/portrait-prompt-craft.md
@@ -0,0 +1,32 @@
+Часть [Вне очереди](README.md). Соседей по папке не читать, если задача не про них.
+
+# Каталог Krea и проза портрета
+
+### Зачем
+
+В Swarm появились Krea 2 чекпоинты, а пикер по-прежнему показывает только whitelist.
+Дефолт новой школы должен считать картинку как Krea turbo. Внешность и одежда в промпте —
+связный английский, а не список тегов: иначе Krea рисует хуже, чем SDXL Lightning.
+
+### Было / Стало / Почему
+
+**Было.** В `swarmui.json` четыре SDXL. Пресет `default` — babes XL Lightning.
+Сборка: `age 12`, `hair colour black`, `wearing shirt in white`. Живая школа держит копию
+шаблона.
+
+**Стало.** В каталог добавлены `museByStableYogi_v30TurboGguf.gguf` и
+`realismByStableYogi_v25INT8Turbo.safetensors` (8 шагов, CFG 1, euler, simple). Пресет
+`default` ссылается на Realism v2.5; стиль и тип кадра — короткая проза; negative без
+SD-весов. Child остаётся DreamShaper. Слои промпта те же (модель → стиль → кадр → extra →
+поза → внешность → одежда). Внешность: *who*, *N years old*, *with black hair, brown eyes…*.
+Одежда: *wearing a white shirt*. `Realism v2.0` GGUF в каталог не входит. Живые школы не
+поднимаем.
+
+**Почему.** Пикер = Swarm ∩ каталог, без строки в JSON модели нет. Krea 2 — не SDXL:
+другие knobs и проза вместо тегов. Копия в сейве не должна менять уже созданные школы.
+
+### Что не входит
+
+`realismByStableYogi_v20TurboGGUF.gguf`. Лифт живых `portraitSettings`. Раздельная сборка
+SDXL vs Krea. Редактор «добавить из Swarm». Поза в UI. ControlNet. Бамп сокета.
+Перегенерация PNG.
diff --git a/docs/phases/off-queue/62-portrait-prompt-craft.md b/docs/phases/off-queue/62-portrait-prompt-craft.md
new file mode 100644
index 0000000..12efa7a
--- /dev/null
+++ b/docs/phases/off-queue/62-portrait-prompt-craft.md
@@ -0,0 +1,41 @@
+# Фаза 62. Промпт и Krea-модели
+
+## Зависимости
+
+Фазы 60 и 61 (каталог моделей, слои, LoRA/embeddings).
+
+## Зачем
+
+Новые Krea-чекпоинты видны в пикере. Новая школа по умолчанию рисует Realism v2.5.
+Внешность и одежда в промпте — проза, не теги.
+
+## Задачи
+
+- [x] Каталог: Muse GGUF и Realism v2.5 INT8, knobs 8 / CFG 1 / euler / simple. Четыре
+ SDXL остаются. v2.0 GGUF не добавлять. `swarmui.json` совпадает с `Catalog()`
+- [x] Пресет `default` — Realism v2.5; стиль и shotType прозой; negative без
+ `(word:1.4)`. Child — DreamShaper, как сейчас
+- [x] Сборка внешности и одежды: `N years old`, `with black hair`, `wearing a white shirt`.
+ Не писать `age N`, `hair colour …`, `wearing X in Y`. Слои и custom extra не трогать
+- [x] Живые школы не лифтить. Сокет не бампить
+
+## Тесты, без которых фаза не закрыта
+
+- [x] `CreateDefault` содержит Muse и Realism v2.5 с 8 / 1 / euler / simple
+- [x] Взрослый resolve берёт Realism v2.5; ребёнок — DreamShaper
+- [x] Промпт: `years old`, `black hair`, `wearing a white shirt`; нет `age 12`, нет
+ `Hair colour`, нет `wearing shirt in white`
+- [x] Порядок слоёв прежний; custom держит кадр и `promptExtra`
+- [x] `GET /api/settings/swarmui` отдаёт оба новых id; create копирует их в сейв
+
+## Критерий готовности
+
+- В диалоге создания школы в пикере есть Muse и Realism v2.5 (если Swarm их отдал)
+- Новая школа без правок пресета генерирует с Realism v2.5
+- «Показать промпт»: связная внешность и одежда, не список статов карточки
+- Уже созданная школа не меняет каталог и дефолтную модель
+
+## Стоп
+
+Не бампить сокет. Не лифтить живые сейвы. Не тащить Realism v2.0. Не писать позу в UI.
+Не перегенерировать PNG. Не зеркалить все параметры Swarm.
diff --git a/src/HSchool.Server/Game/PortraitPromptBuilder.cs b/src/HSchool.Server/Game/PortraitPromptBuilder.cs
index 1cc36f6..4e72fc8 100644
--- a/src/HSchool.Server/Game/PortraitPromptBuilder.cs
+++ b/src/HSchool.Server/Game/PortraitPromptBuilder.cs
@@ -1,10 +1,14 @@
+using HSchool.Content;
using HSchool.Server.Api;
namespace HSchool.Server.Game;
-/// Turns a person card into a Flux-style English prompt for SwarmUI.
+/// Turns a person card into an English prompt for SwarmUI.
internal static class PortraitPromptBuilder
{
+ private const string HairColorId = "HairColor";
+ private const string EyeColorId = "EyeColor";
+
public static (string Positive, string Negative) Build(
PersonCardResponse card,
SwarmUiResolvedProfile profile,
@@ -22,26 +26,8 @@ internal static class PortraitPromptBuilder
}
Add(parts, profile.Pose);
- Add(parts, DescribeSubject(card));
- parts.Add($"age {card.Age}");
-
- foreach (var row in card.Body)
- {
- Add(parts, $"{row.Label.ToLowerInvariant()} {row.Value.ToLowerInvariant()}");
- }
-
- foreach (var item in PortraitVisibleWorn.Filter(card.Worn))
- {
- var color = item.ColorLabel ?? item.Color;
- if (!string.IsNullOrWhiteSpace(color))
- {
- Add(parts, $"wearing {item.Label.ToLowerInvariant()} in {color.ToLowerInvariant()}");
- }
- else
- {
- Add(parts, $"wearing {item.Label.ToLowerInvariant()}");
- }
- }
+ Add(parts, DescribeAppearance(card));
+ Add(parts, DescribeClothing(card));
var positive = SwarmUiLoraFormatter.AppendEmbedTags(
string.Join(", ", parts),
@@ -63,6 +49,83 @@ internal static class PortraitPromptBuilder
parts.Add(value.Trim());
}
+ private static string DescribeAppearance(PersonCardResponse card)
+ {
+ var parts = new List { DescribeSubject(card), $"{card.Age} years old" };
+
+ var hair = BodyValue(card, HairColorId);
+ if (hair is not null)
+ {
+ parts.Add($"{hair.ToLowerInvariant()} hair");
+ }
+
+ var eyes = BodyValue(card, EyeColorId);
+ if (eyes is not null)
+ {
+ parts.Add($"{eyes.ToLowerInvariant()} eyes");
+ }
+
+ var build = BodyValue(card, BodyBuilds.Attribute);
+ if (build is not null && !build.Equals(BodyBuilds.Average, StringComparison.OrdinalIgnoreCase))
+ {
+ parts.Add($"a {build.ToLowerInvariant()} build");
+ }
+
+ var height = BodyValue(card, BodyBuilds.HeightAttribute);
+ if (height is not null)
+ {
+ parts.Add($"{height} cm tall");
+ }
+
+ if (parts.Count == 2)
+ {
+ return $"{parts[0]}, {parts[1]}";
+ }
+
+ return $"{parts[0]}, {parts[1]}, with {string.Join(", ", parts.Skip(2))}";
+ }
+
+ private static string? DescribeClothing(PersonCardResponse card)
+ {
+ var items = PortraitVisibleWorn.Filter(card.Worn)
+ .Select(DescribeWornItem)
+ .Where(item => !string.IsNullOrWhiteSpace(item))
+ .ToList();
+ if (items.Count == 0)
+ {
+ return null;
+ }
+
+ return $"wearing {JoinList(items)}";
+ }
+
+ private static string DescribeWornItem(WornItemResponse item)
+ {
+ var noun = item.Label.Trim();
+ if (string.IsNullOrWhiteSpace(noun))
+ {
+ noun = item.DefName.Trim();
+ }
+
+ noun = noun.ToLowerInvariant();
+ var color = (item.ColorLabel ?? item.Color)?.Trim();
+ if (!string.IsNullOrWhiteSpace(color))
+ {
+ noun = $"{color.ToLowerInvariant()} {noun}";
+ }
+
+ var condition = item.ConditionLabel?.Trim().ToLowerInvariant();
+ noun = condition switch
+ {
+ "torn" => $"torn {noun}",
+ "worn" => $"worn {noun}",
+ "in rags" => $"{noun} in rags",
+ _ => noun,
+ };
+
+ return WithArticle(noun);
+ }
+
private static string DescribeSubject(PersonCardResponse card)
{
if (card.Age <= 11)
@@ -77,4 +140,36 @@ internal static class PortraitPromptBuilder
return card.Female ? "A young woman" : "A young man";
}
+
+ private static string? BodyValue(PersonCardResponse card, string id)
+ {
+ foreach (var row in card.Body)
+ {
+ if (row.Id.Equals(id, StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(row.Value))
+ {
+ return row.Value.Trim();
+ }
+ }
+
+ return null;
+ }
+
+ private static string WithArticle(string phrase)
+ {
+ if (phrase.Length == 0)
+ {
+ return phrase;
+ }
+
+ var first = char.ToLowerInvariant(phrase[0]);
+ var article = first is 'a' or 'e' or 'i' or 'o' or 'u' ? "an" : "a";
+ return $"{article} {phrase}";
+ }
+
+ private static string JoinList(IReadOnlyList items) => items.Count switch
+ {
+ 1 => items[0],
+ 2 => $"{items[0]} and {items[1]}",
+ _ => $"{string.Join(", ", items.Take(items.Count - 1))}, and {items[^1]}",
+ };
}
diff --git a/src/HSchool.Server/Game/SwarmUiSettingsStore.cs b/src/HSchool.Server/Game/SwarmUiSettingsStore.cs
index 52fd36d..295a439 100644
--- a/src/HSchool.Server/Game/SwarmUiSettingsStore.cs
+++ b/src/HSchool.Server/Game/SwarmUiSettingsStore.cs
@@ -471,6 +471,8 @@ internal sealed class SwarmUiModelDefinition
public const string DreamShaperId = "DreamShaper_XL_-_Lightning_DPM++_SDE.safetensors";
public const string EpicRealismId = "epicrealismXL_VXIAbeast4SLightning.safetensors";
public const string LustifyId = "lustifyNSFWCheckpoint_v40DMD2.safetensors";
+ public const string MuseId = "museByStableYogi_v30TurboGguf.gguf";
+ public const string RealismV25Id = "realismByStableYogi_v25INT8Turbo.safetensors";
public string Id { get; set; } = "";
@@ -529,6 +531,8 @@ internal sealed class SwarmUiModelDefinition
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),
];
private static SwarmUiModelDefinition Lightning(
@@ -736,23 +740,23 @@ internal sealed class SwarmUiPresetDefinition
{
Id = "default",
Label = "Default",
- Model = SwarmUiModelDefinition.BabesId,
+ Model = SwarmUiModelDefinition.RealismV25Id,
Style =
- "cinematic photo, realist detail, detailed character expressions, amazing quality, analog film grain, school portrait photograph, neutral background, natural lighting, realistic, sharp focus",
+ "A cinematic school portrait photograph with natural window light, a neutral background, 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",
Avatar = new SwarmUiKindPreset
{
Width = 1024,
Height = 1024,
- ShotType = "close up, head and shoulders portrait, facing the camera, upper body visible.",
+ ShotType = "Close-up head and shoulders portrait, facing the camera, upper body visible",
},
Custom = new SwarmUiKindPreset { Width = 896, Height = 1152 },
FullBody = new SwarmUiKindPreset
{
Width = 896,
Height = 1152,
- ShotType = "full body standing portrait, head to toe visible, neutral pose, current outfit clearly visible.",
+ ShotType = "Full-body standing portrait from head to toe, neutral pose, current outfit clearly visible",
},
};
diff --git a/src/HSchool.Server/swarmui.json b/src/HSchool.Server/swarmui.json
index c785a20..6b165e1 100644
--- a/src/HSchool.Server/swarmui.json
+++ b/src/HSchool.Server/swarmui.json
@@ -56,19 +56,47 @@
"negative": "",
"positiveLoras": [],
"negativeLoras": []
+ },
+ {
+ "id": "museByStableYogi_v30TurboGguf.gguf",
+ "label": "museByStableYogi v3.0 Turbo GGUF",
+ "steps": 8,
+ "cfgScale": 1,
+ "clipSkip": 0,
+ "sampler": "euler",
+ "scheduler": "simple",
+ "seed": 0,
+ "positive": "",
+ "negative": "",
+ "positiveLoras": [],
+ "negativeLoras": []
+ },
+ {
+ "id": "realismByStableYogi_v25INT8Turbo.safetensors",
+ "label": "realismByStableYogi v2.5 INT8 Turbo",
+ "steps": 8,
+ "cfgScale": 1,
+ "clipSkip": 0,
+ "sampler": "euler",
+ "scheduler": "simple",
+ "seed": 0,
+ "positive": "",
+ "negative": "",
+ "positiveLoras": [],
+ "negativeLoras": []
}
],
"presets": [
{
"id": "default",
"label": "Default",
- "model": "babesByStableYogi_v4XLLightning.safetensors",
- "style": "cinematic photo, realist detail, detailed character expressions, amazing quality,",
- "negative": "(low quality, worst quality:1.4), cgi, text, signature, watermark, extra limbs, censored, explicit, blurry, deformed, bad anatomy, logo",
+ "model": "realismByStableYogi_v25INT8Turbo.safetensors",
+ "style": "A cinematic school portrait photograph with natural window light, a neutral background, realistic skin, and sharp focus",
+ "negative": "text, signature, watermark, extra limbs, deformed anatomy, extra fingers",
"avatar": {
"width": 1024,
"height": 1024,
- "shotType": "close up, head and shoulders portrait, facing the camera, upper body visible, avatar,"
+ "shotType": "Close-up head and shoulders portrait, facing the camera, upper body visible"
},
"custom": {
"width": 896,
@@ -78,7 +106,7 @@
"fullBody": {
"width": 896,
"height": 1152,
- "shotType": "full body standing portrait, head to toe visible, neutral pose, current outfit clearly visible."
+ "shotType": "Full-body standing portrait from head to toe, neutral pose, current outfit clearly visible"
}
},
{
diff --git a/tests/HSchool.AppHost.Tests/PortraitApiTests.cs b/tests/HSchool.AppHost.Tests/PortraitApiTests.cs
index 91b7117..268ba3c 100644
--- a/tests/HSchool.AppHost.Tests/PortraitApiTests.cs
+++ b/tests/HSchool.AppHost.Tests/PortraitApiTests.cs
@@ -85,7 +85,8 @@ public class PortraitApiTests(AppHostFixture fixture)
var payload = await response.Content.ReadFromJsonAsync(TestContext.Current.CancellationToken);
Assert.NotNull(payload);
Assert.Equal("avatar", payload!.Kind);
- Assert.Contains("age", payload.Positive, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("years old", payload.Positive, StringComparison.OrdinalIgnoreCase);
+ Assert.DoesNotContain("age ", payload.Positive, StringComparison.OrdinalIgnoreCase);
Assert.False(string.IsNullOrWhiteSpace(payload.Negative));
Assert.False(string.IsNullOrWhiteSpace(payload.PresetId));
}
@@ -103,6 +104,8 @@ 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");
Assert.False(string.IsNullOrWhiteSpace(settings.ActivePresetId));
}
@@ -122,6 +125,14 @@ public class PortraitApiTests(AppHostFixture fixture)
Assert.True(list.GetArrayLength() > 0);
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);
+ var defaultPreset = presets.GetProperty("presets").EnumerateArray()
+ .First(preset => preset.GetProperty("id").GetString() == "default");
+ Assert.Equal(
+ "realismByStableYogi_v25INT8Turbo.safetensors",
+ defaultPreset.GetProperty("model").GetString());
}
[Fact]
diff --git a/tests/HSchool.Server.Tests/PortraitPromptBuilderTests.cs b/tests/HSchool.Server.Tests/PortraitPromptBuilderTests.cs
index 3b65033..56a9b36 100644
--- a/tests/HSchool.Server.Tests/PortraitPromptBuilderTests.cs
+++ b/tests/HSchool.Server.Tests/PortraitPromptBuilderTests.cs
@@ -7,18 +7,20 @@ namespace HSchool.Server.Tests;
public class PortraitPromptBuilderTests
{
[Fact]
- public void Build_IncludesAgeHairAndWornClothing()
+ public void Build_DescribesAppearanceAndClothingInProse()
{
var profile = SamplePreset().ToProfile(PortraitKind.Avatar);
var card = SampleCard();
var (positive, _) = PortraitPromptBuilder.Build(card, profile, PortraitKind.Avatar);
- Assert.Contains("age 12", positive, StringComparison.OrdinalIgnoreCase);
- Assert.Contains("black", positive, StringComparison.OrdinalIgnoreCase);
- Assert.Contains("shirt", positive, StringComparison.OrdinalIgnoreCase);
- Assert.Contains("white", positive, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("12 years old", positive, StringComparison.Ordinal);
+ Assert.Contains("black hair", positive, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("wearing a white shirt", positive, StringComparison.OrdinalIgnoreCase);
Assert.Contains("Head and shoulders.", positive, StringComparison.Ordinal);
+ Assert.DoesNotContain("age 12", positive, StringComparison.OrdinalIgnoreCase);
+ Assert.DoesNotContain("Hair colour", positive, StringComparison.Ordinal);
+ Assert.DoesNotContain("wearing shirt in white", positive, StringComparison.OrdinalIgnoreCase);
}
[Fact]
@@ -66,7 +68,7 @@ public class PortraitPromptBuilderTests
var styleAt = positive.IndexOf("School photo.", StringComparison.Ordinal);
var shotAt = positive.IndexOf("Head and shoulders.", StringComparison.Ordinal);
var subjectAt = positive.IndexOf("teenage girl", StringComparison.OrdinalIgnoreCase);
- var clothesAt = positive.IndexOf("wearing shirt", StringComparison.OrdinalIgnoreCase);
+ var clothesAt = positive.IndexOf("wearing a white shirt", StringComparison.OrdinalIgnoreCase);
Assert.True(modelAt >= 0 && styleAt > modelAt && shotAt > styleAt && subjectAt > shotAt && clothesAt > subjectAt);
Assert.DoesNotContain("pose", positive, StringComparison.OrdinalIgnoreCase);
@@ -80,6 +82,31 @@ public class PortraitPromptBuilderTests
var (positive, _) = PortraitPromptBuilder.Build(child, profile, PortraitKind.Avatar);
Assert.Contains("young girl", positive, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("8 years old", positive, StringComparison.Ordinal);
+ }
+
+ [Fact]
+ public void Build_TornClothing_PrefixesCondition()
+ {
+ var profile = SamplePreset().ToProfile(PortraitKind.Avatar);
+ var card = SampleCard() with
+ {
+ Worn =
+ [
+ new WornItemResponse(
+ "Shirt",
+ "Shirt",
+ "White",
+ "White",
+ [new DefLabelResponse(ApparelLayers.Top, "Top")],
+ 0.2f,
+ "torn",
+ [ApparelLayers.Underwear]),
+ ],
+ };
+ var (positive, _) = PortraitPromptBuilder.Build(card, profile, PortraitKind.Avatar);
+
+ Assert.Contains("wearing a torn white shirt", positive, StringComparison.OrdinalIgnoreCase);
}
private static SwarmUiPresetDefinition SamplePreset()
diff --git a/tests/HSchool.Server.Tests/SwarmUiSettingsStoreTests.cs b/tests/HSchool.Server.Tests/SwarmUiSettingsStoreTests.cs
index 9fad570..fa028ed 100644
--- a/tests/HSchool.Server.Tests/SwarmUiSettingsStoreTests.cs
+++ b/tests/HSchool.Server.Tests/SwarmUiSettingsStoreTests.cs
@@ -23,9 +23,11 @@ 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);
- Assert.Equal(1.5, 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]
@@ -196,7 +198,7 @@ public class SwarmUiSettingsStoreTests
}
[Fact]
- public void CreateDefault_SeedsEveryKnownSwarmModel()
+ public void CreateDefault_SeedsCatalogModels()
{
var config = SwarmUiConfigFile.CreateDefault();
var ids = config.Models.Select(model => model.Id).ToList();
@@ -205,5 +207,20 @@ public class SwarmUiSettingsStoreTests
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.DoesNotContain("realismByStableYogi_v20TurboGGUF.gguf", ids);
+
+ var realism = Assert.Single(config.Models, model => model.Id == SwarmUiModelDefinition.RealismV25Id);
+ Assert.Equal(8, realism.Steps);
+ Assert.Equal(1, realism.CfgScale);
+ Assert.Equal("euler", realism.Sampler);
+ Assert.Equal("simple", realism.Scheduler);
+
+ var muse = Assert.Single(config.Models, model => model.Id == SwarmUiModelDefinition.MuseId);
+ Assert.Equal(8, muse.Steps);
+ Assert.Equal(1, muse.CfgScale);
+ Assert.Equal("euler", muse.Sampler);
+ Assert.Equal("simple", muse.Scheduler);
}
}