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) .GroupBy(contribution => contribution.Model, StringComparer.Ordinal)
.Select(group => .Select(group =>
{ {
var roles = group.Select(contribution => contribution.Role) var roles = group.Select(contribution => contribution.Role is VisualBriefingModelRole.DESIGN ? "presentation" : "content").Distinct(StringComparer.Ordinal);
.Distinct()
.Select(role => role is VisualBriefingModelRole.DESIGN ? "presentation" : "content");
return $"{group.Key} ({string.Join(", ", roles)})"; 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, 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.ArtifactId, presentation.ArtifactId, build.BuildId, build.OperationId, contributions, revisionId, revisionCreatedAt, embeddedAssets,
content.AssetPlan, evidence.ArtifactId, plan.ArtifactId), token); content.AssetPlan, evidence.ArtifactId, plan.ArtifactId), token);

View File

@ -51,7 +51,7 @@ internal sealed class VisualBriefingContentStage(StructuredLlmStageRunner stageR
artifact.CreatedAtUtc = DateTimeOffset.UtcNow; artifact.CreatedAtUtc = DateTimeOffset.UtcNow;
artifact.SourceCoverage = evidence.SourceCoverage; artifact.SourceCoverage = evidence.SourceCoverage;
artifact.StructuralSignature = plan.StructuralSignature; artifact.StructuralSignature = plan.StructuralSignature;
artifact.Model = VisualBriefingModelNames.ExportLabel(provider.Model); artifact.Model = VisualBriefingModelNames.ExportLabel(provider);
artifact.Data = JsonSerializer.SerializeToElement(new artifact.Data = JsonSerializer.SerializeToElement(new
{ {
slots = artifact.Slots.ToDictionary(slot => slot.SlotId, slot => slot.Value, StringComparer.Ordinal), 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, Tables = response.Tables,
SourceCoverage = response.SourceCoverage, SourceCoverage = response.SourceCoverage,
AssetPlan = response.AssetPlan, AssetPlan = response.AssetPlan,
Model = VisualBriefingModelNames.ExportLabel(provider.Model), Model = VisualBriefingModelNames.ExportLabel(provider),
}; };
await store.WriteEvidenceArtifactAsync(manifest.BriefingId, artifact, token); await store.WriteEvidenceArtifactAsync(manifest.BriefingId, artifact, token);

View File

@ -1,17 +1,46 @@
using AIStudio.Provider; using AIStudio.Provider;
using ProviderSettings = AIStudio.Settings.Provider;
namespace AIStudio.Assistants.VisualBriefing; namespace AIStudio.Assistants.VisualBriefing;
/// <summary> /// <summary>
/// Produces export-safe model labels without exposing provider model identifiers. /// Produces export-safe provider and model labels.
/// </summary> /// </summary>
internal static class VisualBriefingModelNames internal static class VisualBriefingModelNames
{ {
/// <summary> /// <summary>
/// Returns the configured display name or a neutral fallback. /// Returns the public provider family and configured model name.
/// </summary> /// </summary>
/// <param name="model">The selected provider model.</param> /// <param name="provider">The selected provider and model.</param>
/// <returns>An export-safe model label.</returns> /// <returns>An export-safe provider and model label.</returns>
internal static string ExportLabel(Model model) => internal static string ExportLabel(ProviderSettings provider) => $"{provider.UsedLLMProvider.ToName(translate: false)} — {ExportModelName(provider.Model)}";
string.IsNullOrWhiteSpace(model.DisplayName) ? "Model" : model.DisplayName.Trim();
/// <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), PayloadHash = VisualBriefingHashing.ComputeSections(payload, structuralSignature),
Sections = sections, Sections = sections,
StructuralSignature = structuralSignature, StructuralSignature = structuralSignature,
Model = VisualBriefingModelNames.ExportLabel(provider.Model), Model = VisualBriefingModelNames.ExportLabel(provider),
}; };
await store.WritePlanArtifactAsync(manifest.BriefingId, artifact, token); await store.WritePlanArtifactAsync(manifest.BriefingId, artifact, token);

View File

@ -97,7 +97,7 @@ internal sealed class VisualBriefingPresentationStage(StructuredLlmStageRunner s
Css = compiled.Css, Css = compiled.Css,
TemplateHash = compiled.TemplateHash, TemplateHash = compiled.TemplateHash,
CssHash = compiled.CssHash, CssHash = compiled.CssHash,
Model = VisualBriefingModelNames.ExportLabel(provider.Model), Model = VisualBriefingModelNames.ExportLabel(provider),
}; };
await store.WritePresentationArtifactAsync(manifest.BriefingId, artifact, token); 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)) if (build is null || string.IsNullOrWhiteSpace(build.Model))
return []; return [];
var model = VisualBriefingModelNames.ExportLabel(build.ProviderFamily, build.Model);
List<VisualBriefingModelContribution> contributions = []; List<VisualBriefingModelContribution> contributions = [];
if (build.EvidenceArtifactId is not null) 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) 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) 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) if (build.PresentationArtifactId is not null)
contributions.Add(new(VisualBriefingModelRole.DESIGN, build.Model)); contributions.Add(new(VisualBriefingModelRole.DESIGN, model));
return contributions; return contributions;
} }

View File

@ -35,9 +35,17 @@ public static class LLMProvidersExtensions
/// </remarks> /// </remarks>
/// <param name="llmProvider">The provider.</param> /// <param name="llmProvider">The provider.</param>
/// <returns>The human-readable name of the provider.</returns> /// <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.OPEN_AI => "OpenAI",
LLMProviders.ANTHROPIC => "Anthropic", LLMProviders.ANTHROPIC => "Anthropic",
@ -53,12 +61,12 @@ public static class LLMProvidersExtensions
LLMProviders.FIREWORKS => "Fireworks.ai", LLMProviders.FIREWORKS => "Fireworks.ai",
LLMProviders.HUGGINGFACE => "Hugging Face", LLMProviders.HUGGINGFACE => "Hugging Face",
LLMProviders.SELF_HOSTED => TB("Self-hosted"), LLMProviders.SELF_HOSTED => translate ? TB("Self-hosted") : "Self-hosted",
LLMProviders.HELMHOLTZ => "Helmholtz Blablador", LLMProviders.HELMHOLTZ => "Helmholtz Blablador",
LLMProviders.GWDG => "GWDG SAIA", LLMProviders.GWDG => "GWDG SAIA",
_ => TB("Unknown"), _ => translate ? TB("Unknown") : "Unknown",
}; };
/// <summary> /// <summary>