Add SwarmUI settings management and portrait generation enhancements

- Introduced new API endpoints for managing SwarmUI settings, including fetching and saving presets and age rules.
- Updated the portrait generation logic to utilize the new settings structure, allowing for dynamic preset selection based on age.
- Enhanced UI components to support SwarmUI settings, including localization for new strings and improved styling for settings sections.
- Added tests to verify the functionality of new settings endpoints and portrait generation behavior.

This commit lays the groundwork for more flexible and user-friendly portrait generation options.
This commit is contained in:
Leonid Pershin
2026-08-20 06:06:45 +03:00
parent e0b7122f3b
commit 5a400be792
32 changed files with 2178 additions and 184 deletions
+49 -16
View File
@@ -55,11 +55,39 @@ internal sealed class SwarmUiClient
}
}
public async Task<SwarmUiDiscovery> FetchDiscoveryAsync(CancellationToken cancellationToken)
{
if (!IsConfigured)
{
return SwarmUiDiscovery.Offline;
}
try
{
return await RunWithSessionAsync(async () =>
{
using var content = new StringContent(
JsonSerializer.Serialize(new { session_id = _sessionId }),
Encoding.UTF8,
"application/json");
using var response = await _http.PostAsync("/API/ListT2IParams", content, cancellationToken);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync(cancellationToken);
using var document = JsonDocument.Parse(json);
return SwarmUiDiscoveryParser.Parse(document.RootElement);
}, cancellationToken);
}
catch (Exception ex)
{
_logger.LogDebug(ex, "SwarmUI discovery failed.");
return SwarmUiDiscovery.Offline;
}
}
public async Task<byte[]> GenerateAsync(
string prompt,
string negativePrompt,
SwarmUiSettings settings,
PortraitKind kind,
SwarmUiResolvedProfile profile,
CancellationToken cancellationToken)
{
if (!IsConfigured)
@@ -69,7 +97,7 @@ internal sealed class SwarmUiClient
return await RunWithSessionAsync(async () =>
{
var preset = settings.PresetFor(kind);
var kind = profile.KindPreset;
var body = new Dictionary<string, object?>
{
["session_id"] = _sessionId,
@@ -77,28 +105,33 @@ internal sealed class SwarmUiClient
["donotsave"] = true,
["prompt"] = prompt,
["negativeprompt"] = negativePrompt,
["model"] = settings.Model,
["steps"] = settings.Steps,
["cfgscale"] = settings.CfgScale,
["width"] = preset.Width,
["height"] = preset.Height,
["seed"] = settings.Seed,
["model"] = profile.Model,
["steps"] = profile.Steps,
["cfgscale"] = profile.CfgScale,
["width"] = kind.Width,
["height"] = kind.Height,
["seed"] = profile.Seed,
};
if (!string.IsNullOrWhiteSpace(settings.Sampler))
if (!string.IsNullOrWhiteSpace(profile.Sampler))
{
body["sampler"] = settings.Sampler;
body["sampler"] = profile.Sampler;
}
if (!string.IsNullOrWhiteSpace(settings.Scheduler))
if (!string.IsNullOrWhiteSpace(profile.Scheduler))
{
body["scheduler"] = settings.Scheduler;
body["scheduler"] = profile.Scheduler;
}
if (settings.ClipSkip > 0)
if (profile.ClipSkip > 0)
{
// SwarmUI "CLIP Stop At Layer" — clip skip N is layer -N from the end.
body["clipstopatlayer"] = -settings.ClipSkip;
body["clipstopatlayer"] = -profile.ClipSkip;
}
var loras = SwarmUiLoraFormatter.FormatForApi(profile.PositiveLoras);
if (loras is not null)
{
body["loras"] = loras;
}
using var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");