Add Krea portrait models and write appearance as prose.
New schools default to Realism v2.5; living saves keep their catalog. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -1,10 +1,14 @@
|
||||
using HSchool.Content;
|
||||
using HSchool.Server.Api;
|
||||
|
||||
namespace HSchool.Server.Game;
|
||||
|
||||
/// <summary>Turns a person card into a Flux-style English prompt for SwarmUI.</summary>
|
||||
/// <summary>Turns a person card into an English prompt for SwarmUI.</summary>
|
||||
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<string> { 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<string> items) => items.Count switch
|
||||
{
|
||||
1 => items[0],
|
||||
2 => $"{items[0]} and {items[1]}",
|
||||
_ => $"{string.Join(", ", items.Take(items.Count - 1))}, and {items[^1]}",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -85,7 +85,8 @@ public class PortraitApiTests(AppHostFixture fixture)
|
||||
var payload = await response.Content.ReadFromJsonAsync<PortraitPromptPayload>(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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user