Refused JSON contracts built by reflection and checked all of them in the trimmed build (#1152)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-11-03 18:29:50 +01:00
1 parent f864359290
commit 0b42c0288e
23 files changed
+535 -99

No files matched your search

+26 -53
View File
@@ -1,65 +1,38 @@
using System.Text.Json;
using AIStudio.Agents;
using AIStudio.Agents.AssistantAudit;
using AIStudio.Assistants.Builder;
using AIStudio.Assistants.PromptOptimizer;
using AIStudio.Assistants.SWOTAnalysis;
using AIStudio.Assistants.VisualBriefing;
using AIStudio.Chat;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Settings.DataModel.PreviousModels;
using AIStudio.Tools;
using AIStudio.Tools.DataBackup;
using System.Text.Json.Serialization;
namespace AIStudio.TrimSmokeTest;
/// <summary>
/// The types which AI Studio reads or writes as JSON on their own, not only as part of another type.
/// The types which AI Studio reads or writes as JSON, each with the options it uses for them.
/// </summary>
/// <remarks>
/// Whatever these types hold, i.e., the types of their properties, the elements of their collections, and
/// their derived types, gets checked along with them, so only the outermost types belong here. Add a type
/// whenever AI Studio starts to store a new kind of file or to read a new kind of model answer.<br/><br/>
///
/// The answers of models are read with options of their own, which are private and only change names and
/// leniency. The default options build the same contracts for the types of .NET they hold. A type which is
/// private to its parser, such as the DTO of a slide deck, is out of reach here.
/// The options of AI Studio each name the context which knows their types, so the types registered in that
/// context are exactly those which System.Text.Json gets to read or write with these options. Whatever these
/// types hold, i.e., the types of their properties, the elements of their collections, and their derived
/// types, gets checked along with them. A new type, or new options, need nothing here: they are found
/// through their context.
/// </remarks>
public static class JsonRoots
{
public static readonly IReadOnlyList<JsonRoot> ALL =
[
// Chats, in the workspaces, among the temporary chats, and in chat archives:
new(typeof(ChatThread), WorkspaceBehaviour.JSON_OPTIONS),
/// <summary>
/// Returns every type registered in a context of the options of AI Studio, together with these options.
/// </summary>
/// <remarks>
/// Options which several members share are checked once. Like AppJsonOptions.FindAll, this needs the
/// static state of the app to be filled before.
/// </remarks>
public static IReadOnlyList<JsonRoot> FindAll() => AppJsonOptions.FindAll().Values
.Distinct(ReferenceEqualityComparer.Instance)
.Cast<JsonSerializerOptions>()
.SelectMany(options => options.TypeInfoResolverChain
.OfType<JsonSerializerContext>()
.SelectMany(RegisteredTypes)
.Select(type => new JsonRoot(type, options)))
.ToList();
// The settings, and their older versions, which the migrations still read:
new(typeof(Data), SettingsManager.JSON_OPTIONS),
new(typeof(DataV1V3), SettingsManager.JSON_OPTIONS),
new(typeof(DataV4), SettingsManager.JSON_OPTIONS),
new(typeof(DataV5), SettingsManager.JSON_OPTIONS),
// The projects of the Visual Briefing assistant:
new(typeof(VisualBriefingManifest), VisualBriefingJson.Persistence),
new(typeof(VisualBriefingBuildRecord), VisualBriefingJson.Persistence),
new(typeof(VisualBriefingEvidenceArtifact), VisualBriefingJson.Persistence),
new(typeof(VisualBriefingPlanArtifact), VisualBriefingJson.Persistence),
new(typeof(VisualBriefingContentArtifact), VisualBriefingJson.Persistence),
new(typeof(VisualBriefingPresentationArtifact), VisualBriefingJson.Persistence),
// Backups and their restore:
new(typeof(DataBackupManifest), DataBackupFormat.JSON_OPTIONS),
new(typeof(DataRestorePlan), DataBackupFormat.JSON_OPTIONS),
new(typeof(DataRestoreReport), DataBackupFormat.JSON_OPTIONS),
// Answers of models:
new(typeof(LuaResponse), JsonSerializerOptions.Default),
new(typeof(LauncherTextsResponse), JsonSerializerOptions.Default),
new(typeof(PromptOptimizationResult), JsonSerializerOptions.Default),
new(typeof(RetrievalContextValidationResult), JsonSerializerOptions.Default),
new(typeof(AssistantAuditResult), JsonSerializerOptions.Default),
new(typeof(SwotCategoryAnalysisResult), JsonSerializerOptions.Default),
new(typeof(SwotModerationResult), JsonSerializerOptions.Default),
];
private static IEnumerable<Type> RegisteredTypes(JsonSerializerContext context) => context.GetType()
.GetCustomAttributesData()
.Where(attribute => attribute.AttributeType == typeof(JsonSerializableAttribute))
.Select(attribute => (Type)attribute.ConstructorArguments[0].Value!);
}