mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 05:27:21 +00:00
Replaced the anonymous JSON shapes of the Visual Briefing assistant with records
This commit is contained in:
27 files changed
+409
-219
No files matched your search
+10
-12
@@ -381,26 +381,24 @@ public sealed partial class VisualBriefingArtifactService
|
|||||||
dictionary.Remove("footerTemplates");
|
dictionary.Remove("footerTemplates");
|
||||||
dictionary.Remove("protectionLabel");
|
dictionary.Remove("protectionLabel");
|
||||||
dictionary.Remove("_mwai");
|
dictionary.Remove("_mwai");
|
||||||
dictionary["_mwai"] = JsonSerializer.SerializeToElement(new
|
dictionary["_mwai"] = JsonSerializer.SerializeToElement(new VisualBriefingProtectedData(
|
||||||
{
|
VisualBriefingVersions.SCHEMA,
|
||||||
schemaVersion = VisualBriefingVersions.SCHEMA,
|
VisualBriefingVersions.RUNTIME,
|
||||||
runtimeVersion = VisualBriefingVersions.RUNTIME,
|
Assembly.GetExecutingAssembly().GetCustomAttribute<MetaDataAttribute>()?.Version ?? "unknown",
|
||||||
aiStudioVersion = Assembly.GetExecutingAssembly().GetCustomAttribute<MetaDataAttribute>()?.Version ?? "unknown",
|
request.EmbeddedAssets ?? new Dictionary<string, string>(StringComparer.Ordinal),
|
||||||
assets = request.EmbeddedAssets ?? new Dictionary<string, string>(StringComparer.Ordinal),
|
(request.AssetPlan ?? []).ToDictionary(
|
||||||
assetMetadata = (request.AssetPlan ?? []).ToDictionary(
|
|
||||||
asset => asset.AssetId,
|
asset => asset.AssetId,
|
||||||
asset => new { asset.Description, asset.AltText },
|
asset => new VisualBriefingAssetMetadata(asset.Description, asset.AltText),
|
||||||
StringComparer.Ordinal),
|
StringComparer.Ordinal),
|
||||||
footer = BuildFooter(manifest, request),
|
BuildFooter(manifest, request)), JSON_OPTIONS);
|
||||||
}, JSON_OPTIONS);
|
|
||||||
|
|
||||||
return JsonSerializer.SerializeToElement(dictionary, JSON_OPTIONS);
|
return JsonSerializer.SerializeToElement(dictionary, JSON_OPTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines <c>BuildFooter</c> for the visual briefing feature.
|
/// Defines <c>BuildFooter</c> for the visual briefing feature.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static object BuildFooter(VisualBriefingManifest manifest, VisualBriefingRevisionRequest request)
|
private static Dictionary<string, string> BuildFooter(VisualBriefingManifest manifest, VisualBriefingRevisionRequest request)
|
||||||
{
|
{
|
||||||
var source = request.ExportMetadataSource;
|
var source = request.ExportMetadataSource;
|
||||||
var protectionLevel = source?.ProtectionLevel ?? manifest.Settings.ProtectionLevel;
|
var protectionLevel = source?.ProtectionLevel ?? manifest.Settings.ProtectionLevel;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What the protected data of an exported briefing tells about one of its pictures.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Description">What the picture shows.</param>
|
||||||
|
/// <param name="AltText">The text for people who cannot see the picture.</param>
|
||||||
|
internal sealed record VisualBriefingAssetMetadata(string Description, string AltText);
|
||||||
+5
-6
@@ -382,16 +382,15 @@ public partial class VisualBriefingAssistant
|
|||||||
/// The fingerprint is serialized from exactly the values that SaveCurrentAsync
|
/// The fingerprint is serialized from exactly the values that SaveCurrentAsync
|
||||||
/// hands to the store. That is deliberate: a handwritten field list would silently stop
|
/// hands to the store. That is deliberate: a handwritten field list would silently stop
|
||||||
/// auto-saving whenever a new setting is added and someone forgets to list it here. Sources are
|
/// auto-saving whenever a new setting is added and someone forgets to list it here. Sources are
|
||||||
/// projected into a named shape because <c>System.Text.Json</c> ignores tuple fields and would
|
/// projected into a named record because <c>System.Text.Json</c> ignores tuple fields and would
|
||||||
/// otherwise serialize every source list into the same empty object.
|
/// otherwise serialize every source list into the same empty object.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>The fingerprint of the current editor state.</returns>
|
/// <returns>The fingerprint of the current editor state.</returns>
|
||||||
private string BuildPersistenceFingerprint() => JsonSerializer.Serialize(
|
private string BuildPersistenceFingerprint() => JsonSerializer.Serialize(
|
||||||
new
|
new VisualBriefingPersistenceFingerprint(
|
||||||
{
|
|
||||||
this.editor.Name,
|
this.editor.Name,
|
||||||
this.editor.Author,
|
this.editor.Author,
|
||||||
Settings = this.editor.ToSettings(),
|
this.editor.ToSettings(),
|
||||||
Sources = this.editor.ToSources().Select(source => new { source.Path, source.Kind }).ToArray(),
|
[.. this.editor.ToSources().Select(source => new VisualBriefingSourceFingerprint(source.Path, source.Kind))]),
|
||||||
}, VisualBriefingJson.Canonical);
|
VisualBriefingJson.Canonical);
|
||||||
}
|
}
|
||||||
+1
-15
@@ -121,21 +121,7 @@ internal sealed partial class VisualBriefingBuildOrchestrator
|
|||||||
var validationDataProperties = compiled.Data.EnumerateObject()
|
var validationDataProperties = compiled.Data.EnumerateObject()
|
||||||
.ToDictionary(property => property.Name, property => property.Value.Clone(), StringComparer.Ordinal);
|
.ToDictionary(property => property.Name, property => property.Value.Clone(), StringComparer.Ordinal);
|
||||||
|
|
||||||
validationDataProperties["_mwai"] = JsonSerializer.SerializeToElement(new
|
validationDataProperties["_mwai"] = JsonSerializer.SerializeToElement(VisualBriefingProtectedData.ForValidation(content.AssetPlan), VisualBriefingJson.Canonical);
|
||||||
{
|
|
||||||
schemaVersion = VisualBriefingVersions.SCHEMA,
|
|
||||||
runtimeVersion = VisualBriefingVersions.RUNTIME,
|
|
||||||
aiStudioVersion = "validation",
|
|
||||||
assets = content.AssetPlan.ToDictionary(asset => asset.AssetId, _ => "data:image/png;base64,AA==", StringComparer.Ordinal),
|
|
||||||
footer = new
|
|
||||||
{
|
|
||||||
createdWith = "validation",
|
|
||||||
models = "validation",
|
|
||||||
createdAt = "validation",
|
|
||||||
authors = "validation",
|
|
||||||
protection = "validation",
|
|
||||||
},
|
|
||||||
}, VisualBriefingJson.Canonical);
|
|
||||||
|
|
||||||
VisualBriefingCompilerInvariant.Guard(
|
VisualBriefingCompilerInvariant.Guard(
|
||||||
VisualBriefingBuildStage.COMPILATION,
|
VisualBriefingBuildStage.COMPILATION,
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
namespace AIStudio.Assistants.VisualBriefing;
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Turns a validated chart specification into a branded chart-library option object.
|
/// Turns a validated chart specification into a branded chart-library option object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The option object is built as JSON nodes rather than as types of its own: it follows the options of Apache
|
||||||
|
/// ECharts, which differ for every kind of chart, and nothing in AI Studio reads it again. The order of its
|
||||||
|
/// properties is part of the compiled data, which gets hashed.
|
||||||
|
/// </remarks>
|
||||||
internal static class VisualBriefingChartCompiler
|
internal static class VisualBriefingChartCompiler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -14,114 +20,116 @@ internal static class VisualBriefingChartCompiler
|
|||||||
/// <returns>The branded chart option.</returns>
|
/// <returns>The branded chart option.</returns>
|
||||||
internal static JsonElement Compile(VisualBriefingChartSpec chart)
|
internal static JsonElement Compile(VisualBriefingChartSpec chart)
|
||||||
{
|
{
|
||||||
object series = chart.Kind switch
|
var series = chart.Kind switch
|
||||||
{
|
{
|
||||||
VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT =>
|
VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT => new JsonArray(
|
||||||
chart.Categories.Select((category, index) => new
|
[
|
||||||
|
.. chart.Categories.Select((category, index) => new JsonObject
|
||||||
{
|
{
|
||||||
name = category,
|
["name"] = category,
|
||||||
value = chart.Series[0].Values[index],
|
["value"] = chart.Series[0].Values[index],
|
||||||
}).ToArray(),
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
VisualBriefingChartKind.RADAR => chart.Series.Select(item => new
|
VisualBriefingChartKind.RADAR => new JsonArray(
|
||||||
{
|
[
|
||||||
name = item.Name,
|
.. chart.Series.Select(item => new JsonObject
|
||||||
type = "radar",
|
|
||||||
data = new[]
|
|
||||||
{
|
{
|
||||||
new
|
["name"] = item.Name,
|
||||||
|
["type"] = "radar",
|
||||||
|
["data"] = new JsonArray(new JsonObject
|
||||||
{
|
{
|
||||||
value = item.Values,
|
["value"] = Numbers(item.Values),
|
||||||
name = item.Name,
|
["name"] = item.Name,
|
||||||
},
|
}),
|
||||||
},
|
}),
|
||||||
}).ToArray(),
|
]),
|
||||||
|
|
||||||
_ => chart.Series.Select(item => new
|
_ => new JsonArray(
|
||||||
{
|
[
|
||||||
name = item.Name,
|
.. chart.Series.Select(item => new JsonObject
|
||||||
type = SeriesType(chart.Kind),
|
{
|
||||||
stack = chart.Kind is VisualBriefingChartKind.STACKED_BAR ? "total" : null,
|
["name"] = item.Name,
|
||||||
areaStyle = chart.Kind is VisualBriefingChartKind.AREA ? new { opacity = 0.18 } : null,
|
["type"] = SeriesType(chart.Kind),
|
||||||
smooth = chart.Kind is VisualBriefingChartKind.LINE or VisualBriefingChartKind.AREA,
|
["stack"] = chart.Kind is VisualBriefingChartKind.STACKED_BAR ? "total" : null,
|
||||||
showSymbol = chart.Kind is VisualBriefingChartKind.SCATTER,
|
["areaStyle"] = chart.Kind is VisualBriefingChartKind.AREA ? new JsonObject { ["opacity"] = 0.18 } : null,
|
||||||
symbolSize = chart.Kind is VisualBriefingChartKind.SCATTER ? 10 : 6,
|
["smooth"] = chart.Kind is VisualBriefingChartKind.LINE or VisualBriefingChartKind.AREA,
|
||||||
itemStyle = chart.Kind is VisualBriefingChartKind.BAR or VisualBriefingChartKind.STACKED_BAR
|
["showSymbol"] = chart.Kind is VisualBriefingChartKind.SCATTER,
|
||||||
? new { borderRadius = new[] { 6, 6, 0, 0 } } : null,
|
["symbolSize"] = chart.Kind is VisualBriefingChartKind.SCATTER ? 10 : 6,
|
||||||
data = item.Values,
|
["itemStyle"] = chart.Kind is VisualBriefingChartKind.BAR or VisualBriefingChartKind.STACKED_BAR
|
||||||
}).ToArray(),
|
? new JsonObject { ["borderRadius"] = new JsonArray(6, 6, 0, 0) } : null,
|
||||||
|
["data"] = Numbers(item.Values),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
var option = new
|
var isCircular = chart.Kind is VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT;
|
||||||
|
var hasAxes = chart.Kind is not (VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT or VisualBriefingChartKind.RADAR);
|
||||||
|
var option = new JsonObject
|
||||||
{
|
{
|
||||||
color = new[] { "#236A50", "#F2D264", "#79AE90", "#C97857", "#4E7894", "#9B6B8F" },
|
["color"] = Texts("#236A50", "#F2D264", "#79AE90", "#C97857", "#4E7894", "#9B6B8F"),
|
||||||
backgroundColor = "transparent",
|
["backgroundColor"] = "transparent",
|
||||||
textStyle = new
|
["textStyle"] = new JsonObject
|
||||||
{
|
{
|
||||||
color = "#172A24",
|
["color"] = "#172A24",
|
||||||
fontFamily = "system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif",
|
["fontFamily"] = "system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif",
|
||||||
},
|
},
|
||||||
|
|
||||||
tooltip = new
|
["tooltip"] = new JsonObject
|
||||||
{
|
{
|
||||||
trigger = chart.Kind is VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT ? "item" : "axis",
|
["trigger"] = isCircular ? "item" : "axis",
|
||||||
borderColor = "#D6E2DC",
|
["borderColor"] = "#D6E2DC",
|
||||||
backgroundColor = "#FFFEFA",
|
["backgroundColor"] = "#FFFEFA",
|
||||||
textStyle = new { color = "#172A24" },
|
["textStyle"] = new JsonObject { ["color"] = "#172A24" },
|
||||||
},
|
},
|
||||||
|
|
||||||
legend = new { show = true, top = 0, textStyle = new { color = "#4F635B" } },
|
["legend"] = new JsonObject { ["show"] = true, ["top"] = 0, ["textStyle"] = new JsonObject { ["color"] = "#4F635B" } },
|
||||||
grid = chart.Kind is VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT or VisualBriefingChartKind.RADAR
|
["grid"] = hasAxes
|
||||||
? null
|
? new JsonObject { ["left"] = 8, ["right"] = 16, ["top"] = 48, ["bottom"] = 8, ["containLabel"] = true }
|
||||||
: new { left = 8, right = 16, top = 48, bottom = 8, containLabel = true },
|
: null,
|
||||||
|
|
||||||
xAxis = chart.Kind is VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT or VisualBriefingChartKind.RADAR
|
["xAxis"] = hasAxes
|
||||||
? null
|
? new JsonObject
|
||||||
: new
|
|
||||||
{
|
{
|
||||||
type = "category",
|
["type"] = "category",
|
||||||
data = chart.Categories,
|
["data"] = Texts(chart.Categories),
|
||||||
axisLine = new { lineStyle = new { color = "#B8C9C0" } },
|
["axisLine"] = new JsonObject { ["lineStyle"] = new JsonObject { ["color"] = "#B8C9C0" } },
|
||||||
axisTick = new { show = false },
|
["axisTick"] = new JsonObject { ["show"] = false },
|
||||||
axisLabel = new { color = "#5E7169" },
|
["axisLabel"] = new JsonObject { ["color"] = "#5E7169" },
|
||||||
},
|
|
||||||
|
|
||||||
yAxis = chart.Kind is VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT or VisualBriefingChartKind.RADAR
|
|
||||||
? null
|
|
||||||
: new
|
|
||||||
{
|
|
||||||
type = "value",
|
|
||||||
axisLine = new { show = false },
|
|
||||||
axisTick = new { show = false },
|
|
||||||
axisLabel = new { color = "#5E7169" },
|
|
||||||
splitLine = new { lineStyle = new { color = "#E1EAE5" } },
|
|
||||||
},
|
|
||||||
|
|
||||||
radar = chart.Kind is VisualBriefingChartKind.RADAR
|
|
||||||
? new
|
|
||||||
{
|
|
||||||
indicator = chart.Categories.Select(name => new { name }).ToArray(),
|
|
||||||
splitArea = new { areaStyle = new { color = new[] { "#FFFEFA", "#EAF1EC" } } },
|
|
||||||
axisName = new { color = "#5E7169" },
|
|
||||||
splitLine = new { lineStyle = new { color = "#B8C9C0" } },
|
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
|
||||||
series = chart.Kind is VisualBriefingChartKind.PIE or VisualBriefingChartKind.DONUT
|
["yAxis"] = hasAxes
|
||||||
? new[]
|
? new JsonObject
|
||||||
{
|
{
|
||||||
new
|
["type"] = "value",
|
||||||
{
|
["axisLine"] = new JsonObject { ["show"] = false },
|
||||||
type = "pie",
|
["axisTick"] = new JsonObject { ["show"] = false },
|
||||||
radius = chart.Kind is VisualBriefingChartKind.DONUT
|
["axisLabel"] = new JsonObject { ["color"] = "#5E7169" },
|
||||||
? new[] { "45%", "70%" }
|
["splitLine"] = new JsonObject { ["lineStyle"] = new JsonObject { ["color"] = "#E1EAE5" } },
|
||||||
: new[] { "0%", "70%" },
|
|
||||||
padAngle = 2,
|
|
||||||
itemStyle = new { borderColor = "#FFFEFA", borderWidth = 2, borderRadius = 5 },
|
|
||||||
label = new { color = "#4F635B" },
|
|
||||||
data = series,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
: null,
|
||||||
|
|
||||||
|
["radar"] = chart.Kind is VisualBriefingChartKind.RADAR
|
||||||
|
? new JsonObject
|
||||||
|
{
|
||||||
|
["indicator"] = new JsonArray([.. chart.Categories.Select(name => new JsonObject { ["name"] = name })]),
|
||||||
|
["splitArea"] = new JsonObject { ["areaStyle"] = new JsonObject { ["color"] = Texts("#FFFEFA", "#EAF1EC") } },
|
||||||
|
["axisName"] = new JsonObject { ["color"] = "#5E7169" },
|
||||||
|
["splitLine"] = new JsonObject { ["lineStyle"] = new JsonObject { ["color"] = "#B8C9C0" } },
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
|
||||||
|
["series"] = isCircular
|
||||||
|
? new JsonArray(new JsonObject
|
||||||
|
{
|
||||||
|
["type"] = "pie",
|
||||||
|
["radius"] = chart.Kind is VisualBriefingChartKind.DONUT ? Texts("45%", "70%") : Texts("0%", "70%"),
|
||||||
|
["padAngle"] = 2,
|
||||||
|
["itemStyle"] = new JsonObject { ["borderColor"] = "#FFFEFA", ["borderWidth"] = 2, ["borderRadius"] = 5 },
|
||||||
|
["label"] = new JsonObject { ["color"] = "#4F635B" },
|
||||||
|
["data"] = series,
|
||||||
|
})
|
||||||
: series,
|
: series,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -141,4 +149,8 @@ internal static class VisualBriefingChartCompiler
|
|||||||
VisualBriefingChartKind.RADAR => "radar",
|
VisualBriefingChartKind.RADAR => "radar",
|
||||||
_ => "line",
|
_ => "line",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static JsonArray Texts(params IEnumerable<string> texts) => new([.. texts.Select(text => JsonValue.Create(text))]);
|
||||||
|
|
||||||
|
private static JsonArray Numbers(IEnumerable<decimal> numbers) => new([.. numbers.Select(number => JsonValue.Create(number))]);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The data of a compiled briefing, which its template binds to.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Slots">The content of each slot, by its ID.</param>
|
||||||
|
/// <param name="Charts">The chart-library option object of each chart, by the ID of its component.</param>
|
||||||
|
/// <param name="Interactions">The controls, their state, and the formulas of the briefing.</param>
|
||||||
|
/// <param name="Accessibility">The text for people who cannot see a component, by the ID of the component.</param>
|
||||||
|
/// <param name="SourceReferences">The sources of each component, by the ID of the component.</param>
|
||||||
|
/// <param name="Labels">The fixed labels which the runtime of the briefing shows.</param>
|
||||||
|
internal sealed record VisualBriefingCompiledData(
|
||||||
|
Dictionary<string, JsonElement> Slots,
|
||||||
|
Dictionary<string, JsonElement> Charts,
|
||||||
|
JsonElement Interactions,
|
||||||
|
Dictionary<string, string> Accessibility,
|
||||||
|
Dictionary<string, List<string>> SourceReferences,
|
||||||
|
VisualBriefingLabels Labels);
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The data of a content artifact, as the content stage keeps it along with the artifact.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Slots">The content of each slot, by its ID.</param>
|
||||||
|
/// <param name="Charts">The validated chart specifications.</param>
|
||||||
|
/// <param name="Controls">The validated interaction controls.</param>
|
||||||
|
/// <param name="Formulas">The validated formula specifications.</param>
|
||||||
|
/// <param name="Accessibility">The text for people who cannot see a component, by the ID of the component.</param>
|
||||||
|
/// <param name="SourceReferences">The sources of each component, by the ID of the component.</param>
|
||||||
|
/// <param name="Labels">The fixed labels which the runtime of the briefing shows.</param>
|
||||||
|
internal sealed record VisualBriefingContentData(
|
||||||
|
Dictionary<string, JsonElement> Slots,
|
||||||
|
List<VisualBriefingChartSpec> Charts,
|
||||||
|
List<VisualBriefingControlSpec> Controls,
|
||||||
|
List<VisualBriefingFormulaSpec> Formulas,
|
||||||
|
Dictionary<string, string> Accessibility,
|
||||||
|
Dictionary<string, List<string>> SourceReferences,
|
||||||
|
VisualBriefingLabels Labels)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gathers the data of a content artifact.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="artifact">The content artifact.</param>
|
||||||
|
/// <returns>The data of the artifact.</returns>
|
||||||
|
internal static VisualBriefingContentData Of(VisualBriefingContentArtifact artifact) => new(
|
||||||
|
artifact.Slots.ToDictionary(slot => slot.SlotId, slot => slot.Value, StringComparer.Ordinal),
|
||||||
|
artifact.Charts,
|
||||||
|
artifact.Controls,
|
||||||
|
artifact.Formulas,
|
||||||
|
artifact.AccessibilityTexts,
|
||||||
|
artifact.SourceReferences,
|
||||||
|
VisualBriefingLabels.Of(artifact.ResetLabel));
|
||||||
|
}
|
||||||
@@ -52,20 +52,7 @@ internal sealed class VisualBriefingContentStage(StructuredLlmStageRunner stageR
|
|||||||
artifact.SourceCoverage = evidence.SourceCoverage;
|
artifact.SourceCoverage = evidence.SourceCoverage;
|
||||||
artifact.StructuralSignature = plan.StructuralSignature;
|
artifact.StructuralSignature = plan.StructuralSignature;
|
||||||
artifact.Model = VisualBriefingModelNames.ExportLabel(provider);
|
artifact.Model = VisualBriefingModelNames.ExportLabel(provider);
|
||||||
artifact.Data = JsonSerializer.SerializeToElement(new
|
artifact.Data = JsonSerializer.SerializeToElement(VisualBriefingContentData.Of(artifact), VisualBriefingJson.Canonical);
|
||||||
{
|
|
||||||
slots = artifact.Slots.ToDictionary(slot => slot.SlotId, slot => slot.Value, StringComparer.Ordinal),
|
|
||||||
charts = artifact.Charts,
|
|
||||||
controls = artifact.Controls,
|
|
||||||
formulas = artifact.Formulas,
|
|
||||||
accessibility = artifact.AccessibilityTexts,
|
|
||||||
sourceReferences = artifact.SourceReferences,
|
|
||||||
labels = new
|
|
||||||
{
|
|
||||||
reset = artifact.ResetLabel,
|
|
||||||
brand = "MindWork AI Studio",
|
|
||||||
},
|
|
||||||
}, VisualBriefingJson.Canonical);
|
|
||||||
|
|
||||||
artifact.PayloadHash = VisualBriefingPayloadHash.ForContent(artifact.Slots, artifact.Charts, artifact.Controls, artifact.Formulas, artifact.AccessibilityTexts,
|
artifact.PayloadHash = VisualBriefingPayloadHash.ForContent(artifact.Slots, artifact.Charts, artifact.Controls, artifact.Formulas, artifact.AccessibilityTexts,
|
||||||
artifact.SourceReferences, artifact.ResetLabel, artifact.SourceCoverage, artifact.AssetPlan, artifact.StructuralSignature);
|
artifact.SourceReferences, artifact.ResetLabel, artifact.SourceCoverage, artifact.AssetPlan, artifact.StructuralSignature);
|
||||||
@@ -117,21 +104,10 @@ internal sealed class VisualBriefingContentStage(StructuredLlmStageRunner stageR
|
|||||||
var requiredSlots = plan.Sections
|
var requiredSlots = plan.Sections
|
||||||
.SelectMany(section => new[]
|
.SelectMany(section => new[]
|
||||||
{
|
{
|
||||||
new
|
new VisualBriefingRequiredSlot(section.TitleSlotId, VisualBriefingSlotRole.TITLE, VisualBriefingSlotType.TEXT),
|
||||||
{
|
new VisualBriefingRequiredSlot(section.SummarySlotId, VisualBriefingSlotRole.SUMMARY, VisualBriefingSlotType.TEXT),
|
||||||
SlotId = section.TitleSlotId,
|
}.Concat(section.Components.SelectMany(component => component.Slots.Select(slot => new VisualBriefingRequiredSlot(slot.SlotId, slot.Role, VisualBriefingSlotTypes.Expected(slot))))))
|
||||||
Role = VisualBriefingSlotRole.TITLE,
|
.ToArray();
|
||||||
Type = VisualBriefingSlotType.TEXT,
|
|
||||||
},
|
|
||||||
|
|
||||||
new
|
|
||||||
{
|
|
||||||
SlotId = section.SummarySlotId,
|
|
||||||
Role = VisualBriefingSlotRole.SUMMARY,
|
|
||||||
Type = VisualBriefingSlotType.TEXT,
|
|
||||||
},
|
|
||||||
}.Concat(section.Components.SelectMany(component => component.Slots.Select(slot => new { slot.SlotId, slot.Role, Type = VisualBriefingSlotTypes.Expected(slot), }
|
|
||||||
)))).ToArray();
|
|
||||||
|
|
||||||
var chartComponentIds = components
|
var chartComponentIds = components
|
||||||
.Where(component => component.Kind is VisualBriefingComponentKind.CHART)
|
.Where(component => component.Kind is VisualBriefingComponentKind.CHART)
|
||||||
@@ -141,21 +117,20 @@ internal sealed class VisualBriefingContentStage(StructuredLlmStageRunner stageR
|
|||||||
// Filterable tables are absent here: AI Studio derives their controls from the table data:
|
// Filterable tables are absent here: AI Studio derives their controls from the table data:
|
||||||
var controlRequirements = components
|
var controlRequirements = components
|
||||||
.Where(component => component.Kind is VisualBriefingComponentKind.TABS or VisualBriefingComponentKind.SIMULATION)
|
.Where(component => component.Kind is VisualBriefingComponentKind.TABS or VisualBriefingComponentKind.SIMULATION)
|
||||||
.Select(component => new
|
.Select(component => new VisualBriefingControlRequirement(
|
||||||
{
|
|
||||||
component.ComponentId,
|
component.ComponentId,
|
||||||
component.Kind,
|
component.Kind,
|
||||||
|
|
||||||
PanelSlotIds = component.Slots
|
PanelSlotIds: component.Slots
|
||||||
.Where(slot => slot.Role is VisualBriefingSlotRole.PANEL)
|
.Where(slot => slot.Role is VisualBriefingSlotRole.PANEL)
|
||||||
.Select(slot => slot.SlotId)
|
.Select(slot => slot.SlotId)
|
||||||
.ToArray(),
|
.ToArray(),
|
||||||
|
|
||||||
ResultSlotIds = component.Slots
|
ResultSlotIds: component.Slots
|
||||||
.Where(slot => slot.Role is VisualBriefingSlotRole.RESULT)
|
.Where(slot => slot.Role is VisualBriefingSlotRole.RESULT)
|
||||||
.Select(slot => slot.SlotId)
|
.Select(slot => slot.SlotId)
|
||||||
.ToArray(),
|
.ToArray()))
|
||||||
}).ToArray();
|
.ToArray();
|
||||||
|
|
||||||
return $"""
|
return $"""
|
||||||
Target language: {manifest.Settings.TargetLanguage.PromptGeneralPurpose(manifest.Settings.CustomTargetLanguage)}
|
Target language: {manifest.Settings.TargetLanguage.PromptGeneralPurpose(manifest.Settings.CustomTargetLanguage)}
|
||||||
@@ -167,7 +142,7 @@ internal sealed class VisualBriefingContentStage(StructuredLlmStageRunner stageR
|
|||||||
Exact chart component IDs, each to receive exactly one chart: {JsonSerializer.Serialize(chartComponentIds, VisualBriefingJson.Canonical)}
|
Exact chart component IDs, each to receive exactly one chart: {JsonSerializer.Serialize(chartComponentIds, VisualBriefingJson.Canonical)}
|
||||||
Exact control and formula requirements, no controls for any other component: {JsonSerializer.Serialize(controlRequirements, VisualBriefingJson.Canonical)}
|
Exact control and formula requirements, no controls for any other component: {JsonSerializer.Serialize(controlRequirements, VisualBriefingJson.Canonical)}
|
||||||
Plan: {JsonSerializer.Serialize(plan.Sections, VisualBriefingJson.Canonical)}
|
Plan: {JsonSerializer.Serialize(plan.Sections, VisualBriefingJson.Canonical)}
|
||||||
Evidence: {JsonSerializer.Serialize(new { evidence.Facts, evidence.Metrics, evidence.Tables, evidence.AssetPlan }, VisualBriefingJson.Canonical)}
|
Evidence: {JsonSerializer.Serialize(VisualBriefingEvidencePrompt.Of(evidence), VisualBriefingJson.Canonical)}
|
||||||
""";
|
""";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,21 +209,7 @@ internal sealed class VisualBriefingContentStage(StructuredLlmStageRunner stageR
|
|||||||
var compiled = VisualBriefingCompilerInvariant.Guard(VisualBriefingBuildStage.CONTENT, () => VisualBriefingLayoutCompiler.Compile(plan, projection, layout, VisualBriefingDesignProfile.EDITORIAL));
|
var compiled = VisualBriefingCompilerInvariant.Guard(VisualBriefingBuildStage.CONTENT, () => VisualBriefingLayoutCompiler.Compile(plan, projection, layout, VisualBriefingDesignProfile.EDITORIAL));
|
||||||
var data = compiled.Data.EnumerateObject().ToDictionary(property => property.Name, property => property.Value.Clone(), StringComparer.Ordinal);
|
var data = compiled.Data.EnumerateObject().ToDictionary(property => property.Name, property => property.Value.Clone(), StringComparer.Ordinal);
|
||||||
|
|
||||||
data["_mwai"] = JsonSerializer.SerializeToElement(new
|
data["_mwai"] = JsonSerializer.SerializeToElement(VisualBriefingProtectedData.ForValidation(evidence.AssetPlan), VisualBriefingJson.Canonical);
|
||||||
{
|
|
||||||
schemaVersion = VisualBriefingVersions.SCHEMA,
|
|
||||||
runtimeVersion = VisualBriefingVersions.RUNTIME,
|
|
||||||
aiStudioVersion = "validation",
|
|
||||||
assets = evidence.AssetPlan.ToDictionary(asset => asset.AssetId, _ => "data:image/png;base64,AA==", StringComparer.Ordinal),
|
|
||||||
footer = new
|
|
||||||
{
|
|
||||||
createdWith = "validation",
|
|
||||||
models = "validation",
|
|
||||||
createdAt = "validation",
|
|
||||||
authors = "validation",
|
|
||||||
protection = "validation",
|
|
||||||
},
|
|
||||||
}, VisualBriefingJson.Canonical);
|
|
||||||
|
|
||||||
var validationData = JsonSerializer.SerializeToElement(data, VisualBriefingJson.Canonical);
|
var validationData = JsonSerializer.SerializeToElement(data, VisualBriefingJson.Canonical);
|
||||||
VisualBriefingCompilerInvariant.Guard(VisualBriefingBuildStage.CONTENT,
|
VisualBriefingCompilerInvariant.Guard(VisualBriefingBuildStage.CONTENT,
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component for which the content stage asks its model to write controls and formulas.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ComponentId">The ID of the component.</param>
|
||||||
|
/// <param name="Kind">The kind of the component.</param>
|
||||||
|
/// <param name="PanelSlotIds">The IDs of the panel slots of the component, in their order.</param>
|
||||||
|
/// <param name="ResultSlotIds">The IDs of the result slots of the component, in their order.</param>
|
||||||
|
internal sealed record VisualBriefingControlRequirement(string ComponentId, VisualBriefingComponentKind Kind, string[] PanelSlotIds, string[] ResultSlotIds);
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The evidence which the plan and content stages show their models.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Facts">The facts of the evidence.</param>
|
||||||
|
/// <param name="Metrics">The metrics of the evidence.</param>
|
||||||
|
/// <param name="Tables">The tables of the evidence.</param>
|
||||||
|
/// <param name="AssetPlan">The planned pictures of the briefing.</param>
|
||||||
|
internal sealed record VisualBriefingEvidencePrompt(
|
||||||
|
List<VisualBriefingEvidenceFact> Facts,
|
||||||
|
List<VisualBriefingEvidenceMetric> Metrics,
|
||||||
|
List<VisualBriefingEvidenceTable> Tables,
|
||||||
|
List<VisualBriefingAssetPlanItem> AssetPlan)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gathers what a model gets to see of an evidence artifact.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="evidence">The evidence artifact.</param>
|
||||||
|
/// <returns>The evidence for the prompt.</returns>
|
||||||
|
internal static VisualBriefingEvidencePrompt Of(VisualBriefingEvidenceArtifact evidence) => new(evidence.Facts, evidence.Metrics, evidence.Tables, evidence.AssetPlan);
|
||||||
|
}
|
||||||
@@ -102,13 +102,11 @@ internal sealed class VisualBriefingEvidenceStage(StructuredLlmStageRunner stage
|
|||||||
// The model never sees internal source GUIDs, only short handles. The file name is what lets
|
// The model never sees internal source GUIDs, only short handles. The file name is what lets
|
||||||
// it tell the attached documents apart, which are supplied in the same canonical order:
|
// it tell the attached documents apart, which are supplied in the same canonical order:
|
||||||
var handles = VisualBriefingSourceHandles.Map(manifest);
|
var handles = VisualBriefingSourceHandles.Map(manifest);
|
||||||
var sources = handles.Select(item => new
|
var sources = handles.Select(item => new VisualBriefingSourcePrompt(
|
||||||
{
|
item.Handle,
|
||||||
sourceId = item.Handle,
|
|
||||||
item.Source.Kind,
|
item.Source.Kind,
|
||||||
assetId = string.IsNullOrWhiteSpace(item.Source.AssetId) ? null : item.Source.AssetId,
|
string.IsNullOrWhiteSpace(item.Source.AssetId) ? null : item.Source.AssetId,
|
||||||
name = Path.GetFileName(item.Source.Path),
|
Path.GetFileName(item.Source.Path)));
|
||||||
});
|
|
||||||
|
|
||||||
var transcripts = handles
|
var transcripts = handles
|
||||||
.Where(item => preparedSources.Transcripts.ContainsKey(item.Source.SourceId))
|
.Where(item => preparedSources.Transcripts.ContainsKey(item.Source.SourceId))
|
||||||
|
|||||||
+1
-6
@@ -27,12 +27,7 @@ internal static class VisualBriefingInteractionCompiler
|
|||||||
formula => formula.Formula,
|
formula => formula.Formula,
|
||||||
StringComparer.Ordinal);
|
StringComparer.Ordinal);
|
||||||
|
|
||||||
return JsonSerializer.SerializeToElement(new
|
return JsonSerializer.SerializeToElement(new VisualBriefingInteractionData(controls, state, formulaMap), VisualBriefingJson.Canonical);
|
||||||
{
|
|
||||||
controls,
|
|
||||||
state,
|
|
||||||
formulas = formulaMap,
|
|
||||||
}, VisualBriefingJson.Canonical);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The interactions in the data of a briefing: its controls, their state, and the formulas which depend on them.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Controls">The validated interaction controls.</param>
|
||||||
|
/// <param name="State">The initial value of each control, by its ID.</param>
|
||||||
|
/// <param name="Formulas">The formula of each result slot, by the ID of the slot.</param>
|
||||||
|
internal sealed record VisualBriefingInteractionData(
|
||||||
|
IReadOnlyList<VisualBriefingControlSpec> Controls,
|
||||||
|
Dictionary<string, JsonElement> State,
|
||||||
|
Dictionary<string, VisualBriefingFormulaNode> Formulas);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The fixed labels in the data of a briefing, which its runtime shows.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Reset">The label of the buttons which reset a simulation.</param>
|
||||||
|
/// <param name="Brand">The name of the app which created the briefing.</param>
|
||||||
|
internal sealed record VisualBriefingLabels(string Reset, string Brand)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the labels of a briefing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resetLabel">The label of the buttons which reset a simulation.</param>
|
||||||
|
/// <returns>The labels.</returns>
|
||||||
|
internal static VisualBriefingLabels Of(string resetLabel) => new(resetLabel, "MindWork AI Studio");
|
||||||
|
}
|
||||||
@@ -49,19 +49,9 @@ internal sealed class VisualBriefingLayoutCompiler
|
|||||||
|
|
||||||
var interactions = VisualBriefingInteractionCompiler.Compile(content.Controls, content.Formulas);
|
var interactions = VisualBriefingInteractionCompiler.Compile(content.Controls, content.Formulas);
|
||||||
|
|
||||||
var data = JsonSerializer.SerializeToElement(new
|
var data = JsonSerializer.SerializeToElement(
|
||||||
{
|
new VisualBriefingCompiledData(slots, chartOptions, interactions, content.AccessibilityTexts, content.SourceReferences, VisualBriefingLabels.Of(content.ResetLabel)),
|
||||||
slots,
|
VisualBriefingJson.Canonical);
|
||||||
charts = chartOptions,
|
|
||||||
interactions,
|
|
||||||
accessibility = content.AccessibilityTexts,
|
|
||||||
sourceReferences = content.SourceReferences,
|
|
||||||
labels = new
|
|
||||||
{
|
|
||||||
reset = content.ResetLabel,
|
|
||||||
brand = "MindWork AI Studio",
|
|
||||||
},
|
|
||||||
}, VisualBriefingJson.Canonical);
|
|
||||||
|
|
||||||
var html = CompileNode(layout, sections, components, content, true);
|
var html = CompileNode(layout, sections, components, content, true);
|
||||||
var css = CompileCss(profile, layout);
|
var css = CompileCss(profile, layout);
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The design of the previous revision, which the presentation stage shows its model when the design changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Layout">The layout tree of the previous revision.</param>
|
||||||
|
/// <param name="Profile">The design profile of the previous revision.</param>
|
||||||
|
internal sealed record VisualBriefingParentDesign(VisualBriefingLayoutNode Layout, VisualBriefingDesignProfile Profile);
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What the editor of the Visual Briefing assistant hands to the store, so that it notices any change worth saving.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Name">The name of the briefing.</param>
|
||||||
|
/// <param name="Author">The author of the briefing.</param>
|
||||||
|
/// <param name="Settings">The settings of the briefing.</param>
|
||||||
|
/// <param name="Sources">The sources of the briefing.</param>
|
||||||
|
internal sealed record VisualBriefingPersistenceFingerprint(string Name, string Author, VisualBriefingLocalSettings Settings, VisualBriefingSourceFingerprint[] Sources);
|
||||||
@@ -112,6 +112,6 @@ internal sealed class VisualBriefingPlanStage(StructuredLlmStageRunner stageRunn
|
|||||||
$"""
|
$"""
|
||||||
Audience: {manifest.Settings.AudienceProfile}; {manifest.Settings.AudienceAgeGroup}; {manifest.Settings.AudienceOrganizationalLevel}; {manifest.Settings.AudienceExpertise}
|
Audience: {manifest.Settings.AudienceProfile}; {manifest.Settings.AudienceAgeGroup}; {manifest.Settings.AudienceOrganizationalLevel}; {manifest.Settings.AudienceExpertise}
|
||||||
Scope instruction: {manifest.Settings.Instruction}
|
Scope instruction: {manifest.Settings.Instruction}
|
||||||
Evidence: {JsonSerializer.Serialize(new { evidence.Facts, evidence.Metrics, evidence.Tables, evidence.AssetPlan }, VisualBriefingJson.Canonical)}
|
Evidence: {JsonSerializer.Serialize(VisualBriefingEvidencePrompt.Of(evidence), VisualBriefingJson.Canonical)}
|
||||||
""";
|
""";
|
||||||
}
|
}
|
||||||
+2
-21
@@ -127,26 +127,7 @@ internal sealed class VisualBriefingPresentationStage(StructuredLlmStageRunner s
|
|||||||
() => VisualBriefingLayoutCompiler.Compile(plan, content, response.Layout, response.Profile));
|
() => VisualBriefingLayoutCompiler.Compile(plan, content, response.Layout, response.Profile));
|
||||||
|
|
||||||
var data = compiled.Data.EnumerateObject().ToDictionary(property => property.Name, property => property.Value.Clone(), StringComparer.Ordinal);
|
var data = compiled.Data.EnumerateObject().ToDictionary(property => property.Name, property => property.Value.Clone(), StringComparer.Ordinal);
|
||||||
data["_mwai"] = JsonSerializer.SerializeToElement(new
|
data["_mwai"] = JsonSerializer.SerializeToElement(VisualBriefingProtectedData.ForValidation(content.AssetPlan), VisualBriefingJson.Canonical);
|
||||||
{
|
|
||||||
schemaVersion = VisualBriefingVersions.SCHEMA,
|
|
||||||
runtimeVersion = VisualBriefingVersions.RUNTIME,
|
|
||||||
aiStudioVersion = "validation",
|
|
||||||
|
|
||||||
assets = content.AssetPlan.ToDictionary(
|
|
||||||
asset => asset.AssetId,
|
|
||||||
_ => "data:image/png;base64,AA==",
|
|
||||||
StringComparer.Ordinal),
|
|
||||||
|
|
||||||
footer = new
|
|
||||||
{
|
|
||||||
createdWith = "validation",
|
|
||||||
models = "validation",
|
|
||||||
createdAt = "validation",
|
|
||||||
authors = "validation",
|
|
||||||
protection = "validation",
|
|
||||||
},
|
|
||||||
}, VisualBriefingJson.Canonical);
|
|
||||||
|
|
||||||
var validationData = JsonSerializer.SerializeToElement(data, VisualBriefingJson.Canonical);
|
var validationData = JsonSerializer.SerializeToElement(data, VisualBriefingJson.Canonical);
|
||||||
VisualBriefingCompilerInvariant.Guard(VisualBriefingBuildStage.DESIGN,
|
VisualBriefingCompilerInvariant.Guard(VisualBriefingBuildStage.DESIGN,
|
||||||
@@ -184,7 +165,7 @@ internal sealed class VisualBriefingPresentationStage(StructuredLlmStageRunner s
|
|||||||
// Internal, so that a test can check the JSON this prompt carries:
|
// Internal, so that a test can check the JSON this prompt carries:
|
||||||
internal static string BuildPrompt(VisualBriefingManifest manifest, VisualBriefingPlanArtifact plan, VisualBriefingPresentationArtifact? parent)
|
internal static string BuildPrompt(VisualBriefingManifest manifest, VisualBriefingPlanArtifact plan, VisualBriefingPresentationArtifact? parent)
|
||||||
{
|
{
|
||||||
var parentJson = parent is null ? "none" : JsonSerializer.Serialize(new { parent.Layout, parent.Profile }, VisualBriefingJson.Canonical);
|
var parentJson = parent is null ? "none" : JsonSerializer.Serialize(new VisualBriefingParentDesign(parent.Layout, parent.Profile), VisualBriefingJson.Canonical);
|
||||||
return $"""
|
return $"""
|
||||||
Operation: {(parent is null ? "CREATE_DESIGN" : "CHANGE_DESIGN")}
|
Operation: {(parent is null ? "CREATE_DESIGN" : "CHANGE_DESIGN")}
|
||||||
Design instruction: {manifest.Settings.Instruction}
|
Design instruction: {manifest.Settings.Instruction}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The data of a briefing which AI Studio writes itself and no model may change, under the key <c>_mwai</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="SchemaVersion">The version of the schema of the briefing data.</param>
|
||||||
|
/// <param name="RuntimeVersion">The version of the runtime script of the briefing.</param>
|
||||||
|
/// <param name="AIStudioVersion">The version of AI Studio which created the briefing.</param>
|
||||||
|
/// <param name="Assets">The embedded pictures, by their asset IDs.</param>
|
||||||
|
/// <param name="AssetMetadata">What the briefing tells about each picture, or null where the data only gets validated.</param>
|
||||||
|
/// <param name="Footer">The texts of the footer, by their keys.</param>
|
||||||
|
internal sealed record VisualBriefingProtectedData(
|
||||||
|
int SchemaVersion,
|
||||||
|
int RuntimeVersion,
|
||||||
|
string AIStudioVersion,
|
||||||
|
IReadOnlyDictionary<string, string> Assets,
|
||||||
|
[property: JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] Dictionary<string, VisualBriefingAssetMetadata>? AssetMetadata,
|
||||||
|
Dictionary<string, string> Footer)
|
||||||
|
{
|
||||||
|
private const string VALIDATION_VALUE = "validation";
|
||||||
|
|
||||||
|
private const string VALIDATION_ASSET = "data:image/png;base64,AA==";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates stand-in protected data for validating a compiled briefing before it gets assembled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assetPlan">The planned pictures of the briefing.</param>
|
||||||
|
/// <returns>The stand-in protected data.</returns>
|
||||||
|
internal static VisualBriefingProtectedData ForValidation(IEnumerable<VisualBriefingAssetPlanItem> assetPlan) => new(
|
||||||
|
VisualBriefingVersions.SCHEMA,
|
||||||
|
VisualBriefingVersions.RUNTIME,
|
||||||
|
VALIDATION_VALUE,
|
||||||
|
assetPlan.ToDictionary(asset => asset.AssetId, _ => VALIDATION_ASSET, StringComparer.Ordinal),
|
||||||
|
null,
|
||||||
|
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
["createdWith"] = VALIDATION_VALUE,
|
||||||
|
["models"] = VALIDATION_VALUE,
|
||||||
|
["createdAt"] = VALIDATION_VALUE,
|
||||||
|
["authors"] = VALIDATION_VALUE,
|
||||||
|
["protection"] = VALIDATION_VALUE,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A slot which the content stage asks its model to fill, with what the slot is for.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="SlotId">The ID of the slot.</param>
|
||||||
|
/// <param name="Role">The semantic role of the slot.</param>
|
||||||
|
/// <param name="Type">The type of content the slot takes.</param>
|
||||||
|
internal sealed record VisualBriefingRequiredSlot(string SlotId, VisualBriefingSlotRole Role, VisualBriefingSlotType Type);
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A source as the fingerprint of the editor names it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Path">The path of the source.</param>
|
||||||
|
/// <param name="Kind">The kind of the source.</param>
|
||||||
|
internal sealed record VisualBriefingSourceFingerprint(string Path, VisualBriefingSourceKind Kind);
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A source as the evidence stage shows it its model.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The model never sees the internal ID of a source, only its short handle.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="SourceId">The short handle of the source.</param>
|
||||||
|
/// <param name="Kind">The kind of the source.</param>
|
||||||
|
/// <param name="AssetId">The asset ID of the source, or null when it is no picture of the briefing.</param>
|
||||||
|
/// <param name="Name">The file name of the source.</param>
|
||||||
|
internal sealed record VisualBriefingSourcePrompt(string SourceId, VisualBriefingSourceKind Kind, string? AssetId, string Name);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"slots":{"overview-title":"Text of overview-title, with Gr\u00F6\u00DFe and \u20AC","overview-summary":"Text of overview-summary, with Gr\u00F6\u00DFe and \u20AC","chart-line-title":"Text of chart-line-title, with Gr\u00F6\u00DFe and \u20AC","chart-line-caption":"Text of chart-line-caption, with Gr\u00F6\u00DFe and \u20AC","chart-area-title":"Text of chart-area-title, with Gr\u00F6\u00DFe and \u20AC","chart-area-caption":"Text of chart-area-caption, with Gr\u00F6\u00DFe and \u20AC","chart-bar-title":"Text of chart-bar-title, with Gr\u00F6\u00DFe and \u20AC","chart-bar-caption":"Text of chart-bar-caption, with Gr\u00F6\u00DFe and \u20AC","chart-stacked_bar-title":"Text of chart-stacked_bar-title, with Gr\u00F6\u00DFe and \u20AC","chart-stacked_bar-caption":"Text of chart-stacked_bar-caption, with Gr\u00F6\u00DFe and \u20AC","chart-scatter-title":"Text of chart-scatter-title, with Gr\u00F6\u00DFe and \u20AC","chart-scatter-caption":"Text of chart-scatter-caption, with Gr\u00F6\u00DFe and \u20AC","chart-pie-title":"Text of chart-pie-title, with Gr\u00F6\u00DFe and \u20AC","chart-pie-caption":"Text of chart-pie-caption, with Gr\u00F6\u00DFe and \u20AC","chart-donut-title":"Text of chart-donut-title, with Gr\u00F6\u00DFe and \u20AC","chart-donut-caption":"Text of chart-donut-caption, with Gr\u00F6\u00DFe and \u20AC","chart-radar-title":"Text of chart-radar-title, with Gr\u00F6\u00DFe and \u20AC","chart-radar-caption":"Text of chart-radar-caption, with Gr\u00F6\u00DFe and \u20AC","simulation-title":"Text of simulation-title, with Gr\u00F6\u00DFe and \u20AC","simulation-summary":"Text of simulation-summary, with Gr\u00F6\u00DFe and \u20AC","simulation-result":"Text of simulation-result, with Gr\u00F6\u00DFe and \u20AC"},"charts":[{"componentId":"chart-line","kind":"LINE","categories":["Q1","Q2","Q3"],"series":[{"name":"Revenue","values":[12.5,14,9.75]},{"name":"Costs","values":[8,7.25,10]}]},{"componentId":"chart-area","kind":"AREA","categories":["Q1","Q2","Q3"],"series":[{"name":"Revenue","values":[12.5,14,9.75]},{"name":"Costs","values":[8,7.25,10]}]},{"componentId":"chart-bar","kind":"BAR","categories":["Q1","Q2","Q3"],"series":[{"name":"Revenue","values":[12.5,14,9.75]},{"name":"Costs","values":[8,7.25,10]}]},{"componentId":"chart-stacked_bar","kind":"STACKED_BAR","categories":["Q1","Q2","Q3"],"series":[{"name":"Revenue","values":[12.5,14,9.75]},{"name":"Costs","values":[8,7.25,10]}]},{"componentId":"chart-scatter","kind":"SCATTER","categories":["Q1","Q2","Q3"],"series":[{"name":"Revenue","values":[12.5,14,9.75]},{"name":"Costs","values":[8,7.25,10]}]},{"componentId":"chart-pie","kind":"PIE","categories":["Q1","Q2","Q3"],"series":[{"name":"Revenue","values":[12.5,14,9.75]},{"name":"Costs","values":[8,7.25,10]}]},{"componentId":"chart-donut","kind":"DONUT","categories":["Q1","Q2","Q3"],"series":[{"name":"Revenue","values":[12.5,14,9.75]},{"name":"Costs","values":[8,7.25,10]}]},{"componentId":"chart-radar","kind":"RADAR","categories":["Q1","Q2","Q3"],"series":[{"name":"Revenue","values":[12.5,14,9.75]},{"name":"Costs","values":[8,7.25,10]}]}],"controls":[{"controlId":"growth","componentId":"simulation","kind":"RANGE","initialValue":3,"options":[]},{"controlId":"scenario","componentId":"simulation","kind":"SELECT","initialValue":"base","options":[{"value":"base","label":"Base case"},{"value":"high","label":"Gr\u00F6\u00DFeres Wachstum"}]}],"formulas":[{"componentId":"simulation","outputSlotId":"simulation-result","formula":{"formulaVersion":1,"op":"multiply","args":[{"path":"interactions.state.growth"},{"value":2}]}}],"accessibility":{"chart-line":"Revenue rises in Q2 and falls in Q3.","simulation":"The growth changes the result."},"sourceReferences":{"chart-line":["S1","S2"]},"labels":{"reset":"Zur\u00FCcksetzen","brand":"MindWork AI Studio"}}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"name":"name sample 3","author":"author sample 4","settings":{"providerId":"providerId sample 7","modelId":"modelId sample 8","profileId":"profileId sample 9","targetLanguage":1,"customTargetLanguage":"customTargetLanguage sample 10","audienceProfile":1,"audienceAgeGroup":1,"audienceOrganizationalLevel":1,"audienceExpertise":1,"showSourceReferences":true,"optimizeImages":true,"instruction":"instruction sample 11","protectionLevel":"INTERNAL","customProtectionLevel":"customProtectionLevel sample 12"},"sources":[{"path":"/samples/path-17.txt","kind":"VISUAL_ASSET"},{"path":"/samples/path-22.txt","kind":"VISUAL_ASSET"}]}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"schemaVersion":2,"runtimeVersion":1,"aiStudioVersion":"validation","assets":{"assetId sample 48":"data:image/png;base64,AA==","assetId sample 51":"data:image/png;base64,AA=="},"footer":{"createdWith":"validation","models":"validation","createdAt":"validation","authors":"validation","protection":"validation"}}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using AIStudio.Assistants.VisualBriefing;
|
||||||
|
|
||||||
|
namespace AIStudio.Tests.Serialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks that the records of the Visual Briefing assistant write the same JSON as the anonymous shapes they replaced.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The files in the folder Fixtures were written by the anonymous shapes themselves, before the records existed. The
|
||||||
|
/// content data and the protected data for the validation reach the HTML of a briefing, and the fingerprint of the
|
||||||
|
/// editor decides whether a briefing gets saved: a field under another name or in another place changes all three.
|
||||||
|
/// </remarks>
|
||||||
|
[TestFixture]
|
||||||
|
public sealed class VisualBriefingShapeTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TheContentDataGetsWrittenAsBefore()
|
||||||
|
{
|
||||||
|
var data = JsonSerializer.SerializeToElement(VisualBriefingContentData.Of(VisualBriefingSamples.Content()), VisualBriefingJson.Canonical);
|
||||||
|
Assert.That(data.GetRawText(), Is.EqualTo(JsonGoldenFileTests.ReadFixture("visual-briefing-content-data.json")));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TheProtectedDataForTheValidationGetsWrittenAsBefore()
|
||||||
|
{
|
||||||
|
var evidence = Read<VisualBriefingEvidenceArtifact>("visual-briefing-evidence-artifact.json");
|
||||||
|
var data = JsonSerializer.SerializeToElement(VisualBriefingProtectedData.ForValidation(evidence.AssetPlan), VisualBriefingJson.Canonical);
|
||||||
|
Assert.That(data.GetRawText(), Is.EqualTo(JsonGoldenFileTests.ReadFixture("visual-briefing-validation-protected-data.json")));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TheFingerprintOfTheEditorGetsWrittenAsBefore()
|
||||||
|
{
|
||||||
|
var manifest = Read<VisualBriefingManifest>("visual-briefing-manifest.json");
|
||||||
|
var fingerprint = new VisualBriefingPersistenceFingerprint(
|
||||||
|
manifest.Name,
|
||||||
|
manifest.Author,
|
||||||
|
manifest.Settings,
|
||||||
|
[.. manifest.Sources.Select(source => new VisualBriefingSourceFingerprint(source.Path, source.Kind))]);
|
||||||
|
|
||||||
|
Assert.That(JsonSerializer.Serialize(fingerprint, VisualBriefingJson.Canonical), Is.EqualTo(JsonGoldenFileTests.ReadFixture("visual-briefing-persistence-fingerprint.json")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T Read<T>(string fileName) => JsonSerializer.Deserialize<T>(JsonGoldenFileTests.ReadFixture(fileName), VisualBriefingJson.Persistence)!;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user