Fixed used provider and model labels in VisualBriefing artifact creation

This commit is contained in:
Thorsten Sommer 2026-07-30 21:31:57 +02:00
parent 85b9d203c4
commit a921cd9305
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
9 changed files with 58 additions and 22 deletions

View File

@ -328,9 +328,7 @@ public sealed partial class VisualBriefingArtifactService
.GroupBy(contribution => contribution.Model, StringComparer.Ordinal)
.Select(group =>
{
var roles = group.Select(contribution => contribution.Role)
.Distinct()
.Select(role => role is VisualBriefingModelRole.DESIGN ? "presentation" : "content");
var roles = group.Select(contribution => contribution.Role is VisualBriefingModelRole.DESIGN ? "presentation" : "content").Distinct(StringComparer.Ordinal);
return $"{group.Key} ({string.Join(", ", roles)})";
}));

View File

@ -382,7 +382,7 @@ internal sealed partial class VisualBriefingBuildOrchestrator
};
var revision = await this.store.AddRevisionAsync(new(manifest.BriefingId, parentRevisionId, mode, manifest.Settings.Instruction,
compiled.Data, compiled.TemplateHtml, compiled.Css, VisualBriefingModelNames.ExportLabel(provider.Model), "MindWork AI Studio",
compiled.Data, compiled.TemplateHtml, compiled.Css, VisualBriefingModelNames.ExportLabel(provider), "MindWork AI Studio",
content.ArtifactId, presentation.ArtifactId, build.BuildId, build.OperationId, contributions, revisionId, revisionCreatedAt, embeddedAssets,
content.AssetPlan, evidence.ArtifactId, plan.ArtifactId), token);

View File

@ -51,7 +51,7 @@ internal sealed class VisualBriefingContentStage(StructuredLlmStageRunner stageR
artifact.CreatedAtUtc = DateTimeOffset.UtcNow;
artifact.SourceCoverage = evidence.SourceCoverage;
artifact.StructuralSignature = plan.StructuralSignature;
artifact.Model = VisualBriefingModelNames.ExportLabel(provider.Model);
artifact.Model = VisualBriefingModelNames.ExportLabel(provider);
artifact.Data = JsonSerializer.SerializeToElement(new
{
slots = artifact.Slots.ToDictionary(slot => slot.SlotId, slot => slot.Value, StringComparer.Ordinal),

View File

@ -63,7 +63,7 @@ internal sealed class VisualBriefingEvidenceStage(StructuredLlmStageRunner stage
Tables = response.Tables,
SourceCoverage = response.SourceCoverage,
AssetPlan = response.AssetPlan,
Model = VisualBriefingModelNames.ExportLabel(provider.Model),
Model = VisualBriefingModelNames.ExportLabel(provider),
};
await store.WriteEvidenceArtifactAsync(manifest.BriefingId, artifact, token);

View File

@ -1,17 +1,46 @@
using AIStudio.Provider;
using ProviderSettings = AIStudio.Settings.Provider;
namespace AIStudio.Assistants.VisualBriefing;
/// <summary>
/// Produces export-safe model labels without exposing provider model identifiers.
/// Produces export-safe provider and model labels.
/// </summary>
internal static class VisualBriefingModelNames
{
/// <summary>
/// Returns the configured display name or a neutral fallback.
/// Returns the public provider family and configured model name.
/// </summary>
/// <param name="model">The selected provider model.</param>
/// <returns>An export-safe model label.</returns>
internal static string ExportLabel(Model model) =>
string.IsNullOrWhiteSpace(model.DisplayName) ? "Model" : model.DisplayName.Trim();
/// <param name="provider">The selected provider and model.</param>
/// <returns>An export-safe provider and model label.</returns>
internal static string ExportLabel(ProviderSettings provider) => $"{provider.UsedLLMProvider.ToName(translate: false)} — {ExportModelName(provider.Model)}";
/// <summary>
/// Reconstructs an export label from persisted build provenance.
/// </summary>
/// <param name="providerFamily">The persisted provider family.</param>
/// <param name="model">The persisted model name.</param>
/// <returns>An export-safe provider and model label.</returns>
internal static string ExportLabel(string providerFamily, string model)
{
var providerName = Enum.TryParse<LLMProviders>(providerFamily, out var parsedProvider) ? parsedProvider.ToName(translate: false) : string.IsNullOrWhiteSpace(providerFamily) ? "Unknown provider" : providerFamily.Trim();
var modelName = string.IsNullOrWhiteSpace(model) ? "model not reported" : model.Trim();
return $"{providerName} — {modelName}";
}
/// <summary>
/// Returns the configured display name, model ID, or provider-managed fallback.
/// </summary>
private static string ExportModelName(Model model)
{
if (!string.IsNullOrWhiteSpace(model.DisplayName))
return model.DisplayName.Trim();
if (model.IsSystemModel)
return "provider-configured model";
return string.IsNullOrWhiteSpace(model.Id) ? "model not reported" : model.Id.Trim();
}
}

View File

@ -64,7 +64,7 @@ internal sealed class VisualBriefingPlanStage(StructuredLlmStageRunner stageRunn
PayloadHash = VisualBriefingHashing.ComputeSections(payload, structuralSignature),
Sections = sections,
StructuralSignature = structuralSignature,
Model = VisualBriefingModelNames.ExportLabel(provider.Model),
Model = VisualBriefingModelNames.ExportLabel(provider),
};
await store.WritePlanArtifactAsync(manifest.BriefingId, artifact, token);

View File

@ -97,7 +97,7 @@ internal sealed class VisualBriefingPresentationStage(StructuredLlmStageRunner s
Css = compiled.Css,
TemplateHash = compiled.TemplateHash,
CssHash = compiled.CssHash,
Model = VisualBriefingModelNames.ExportLabel(provider.Model),
Model = VisualBriefingModelNames.ExportLabel(provider),
};
await store.WritePresentationArtifactAsync(manifest.BriefingId, artifact, token);

View File

@ -194,18 +194,19 @@ public sealed partial class VisualBriefingStore
if (build is null || string.IsNullOrWhiteSpace(build.Model))
return [];
var model = VisualBriefingModelNames.ExportLabel(build.ProviderFamily, build.Model);
List<VisualBriefingModelContribution> contributions = [];
if (build.EvidenceArtifactId is not null)
contributions.Add(new(VisualBriefingModelRole.EVIDENCE, build.Model));
contributions.Add(new(VisualBriefingModelRole.EVIDENCE, model));
if (build.PlanArtifactId is not null)
contributions.Add(new(VisualBriefingModelRole.PLAN, build.Model));
contributions.Add(new(VisualBriefingModelRole.PLAN, model));
if (build.ContentArtifactId is not null)
contributions.Add(new(VisualBriefingModelRole.CONTENT, build.Model));
contributions.Add(new(VisualBriefingModelRole.CONTENT, model));
if (build.PresentationArtifactId is not null)
contributions.Add(new(VisualBriefingModelRole.DESIGN, build.Model));
contributions.Add(new(VisualBriefingModelRole.DESIGN, model));
return contributions;
}

View File

@ -35,9 +35,17 @@ public static class LLMProvidersExtensions
/// </remarks>
/// <param name="llmProvider">The provider.</param>
/// <returns>The human-readable name of the provider.</returns>
public static string ToName(this LLMProviders llmProvider) => llmProvider switch
public static string ToName(this LLMProviders llmProvider) => llmProvider.ToName(translate: true);
/// <summary>
/// Returns the human-readable name of the provider.
/// </summary>
/// <param name="llmProvider">The provider.</param>
/// <param name="translate">Whether generic provider names should be translated.</param>
/// <returns>The human-readable name of the provider.</returns>
public static string ToName(this LLMProviders llmProvider, bool translate) => llmProvider switch
{
LLMProviders.NONE => TB("No provider selected"),
LLMProviders.NONE => translate ? TB("No provider selected") : "No provider selected",
LLMProviders.OPEN_AI => "OpenAI",
LLMProviders.ANTHROPIC => "Anthropic",
@ -53,12 +61,12 @@ public static class LLMProvidersExtensions
LLMProviders.FIREWORKS => "Fireworks.ai",
LLMProviders.HUGGINGFACE => "Hugging Face",
LLMProviders.SELF_HOSTED => TB("Self-hosted"),
LLMProviders.SELF_HOSTED => translate ? TB("Self-hosted") : "Self-hosted",
LLMProviders.HELMHOLTZ => "Helmholtz Blablador",
LLMProviders.GWDG => "GWDG SAIA",
_ => TB("Unknown"),
_ => translate ? TB("Unknown") : "Unknown",
};
/// <summary>