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:
Leonid Pershin
2026-08-20 16:45:40 +03:00
co-authored by Cursor
parent 8c89f605de
commit ed015b9d6b
9 changed files with 298 additions and 42 deletions
@@ -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()