From e954881fdaca273760e0a606217fc13ba15ae1a4 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Mon, 2 Nov 2026 18:23:19 +0100 Subject: [PATCH] Generated the JSON contracts of the stored data at compile time (#1148) --- .../AssistantAudit/AssistantAuditFinding.cs | 12 +- .../SwotAnalysisContentPresentation.cs | 15 +- .../SWOTAnalysis/SwotAnalysisJsonContext.cs | 11 + app/MindWork AI Studio/Chat/ChatDraft.cs | 11 +- .../Chat/ChatJsonContext.cs | 17 + app/MindWork AI Studio/Chat/ChatThread.cs | 16 +- app/MindWork AI Studio/Chat/ContentBlock.cs | 6 +- app/MindWork AI Studio/Chat/ContentImage.cs | 2 +- .../Chat/ContentPresentation.cs | 4 +- app/MindWork AI Studio/Chat/FileAttachment.cs | 20 +- .../Chat/FileAttachmentImage.cs | 2 +- app/MindWork AI Studio/Chat/IImageSource.cs | 2 +- .../Chat/ReportedTokenUsage.cs | 6 +- .../Settings/ChatTemplate.cs | 27 +- .../Settings/DataModel/Data.cs | 80 +- .../Settings/DataModel/DataApp.cs | 2 +- .../DataAssistantPluginEnterpriseApproval.cs | 14 +- .../DataModel/DataDocumentAnalysisPolicy.cs | 10 +- .../DataModel/DataMandatoryInfoAcceptance.cs | 10 +- .../Settings/DataModel/DataSourceERI_V1.cs | 34 +- .../DataModel/DataSourceLocalDirectory.cs | 40 +- .../Settings/DataModel/DataSourceLocalFile.cs | 30 +- .../Settings/DataModel/DataSourceMailbox.cs | 46 +- .../DataModel/PreviousModels/DataV1V3.cs | 4 +- .../DataModel/PreviousModels/DataV4.cs | 30 +- .../DataModel/PreviousModels/DataV5.cs | 66 +- .../Settings/EmbeddingProvider.cs | 30 +- .../Settings/IDataSource.cs | 4 +- .../Settings/IDataSourceBase.cs | 2 +- .../Settings/IERIDataSource.cs | 14 +- .../Settings/IExternalDataSource.cs | 2 +- .../Settings/IIndexedDataSource.cs | 8 +- .../Settings/ManagedEditableDefaultState.cs | 4 +- app/MindWork AI Studio/Settings/Profile.cs | 14 + app/MindWork AI Studio/Settings/Provider.cs | 31 +- .../Settings/ProviderCapabilityOverrides.cs | 22 +- .../Settings/SettingsJsonContext.cs | 43 + .../Settings/SettingsManager.cs | 5 +- .../Settings/TranscriptionProvider.cs | 21 + .../Tools/DataBackup/DataBackupFormat.cs | 5 +- .../Tools/DataBackup/DataBackupJsonContext.cs | 13 + .../ProvenanceStore/ProvenanceJsonContext.cs | 11 + ...liteProvenanceStoreClientImplementation.cs | 8 +- .../Tools/JsonSerializerOptionsExtensions.cs | 21 + .../Assistants/PluginAssistantAudit.cs | 8 +- .../PluginSystem/ConfigurationBaseObject.cs | 10 +- .../Tools/RAG/RetrievalImageContext.cs | 2 +- .../Tools/RAG/SourcePassage.cs | 32 +- .../Tools/WorkspaceBehaviour.cs | 7 +- .../wwwroot/changelog/v26.11.1.md | 1 + .../SwotAnalysisContentPresentationTests.cs | 2 +- app/Tests/Chat/ContentPresentationTests.cs | 4 +- app/Tests/Serialization/Fixtures/chat.json | 442 +++++ .../Fixtures/data-backup-manifest.json | 21 + .../Fixtures/data-restore-plan.json | 24 + .../Fixtures/data-restore-report.json | 8 + .../provenance-surrounding-content.json | 1 + .../Serialization/Fixtures/settings-v1.json | 71 + .../Serialization/Fixtures/settings-v4.json | 305 +++ .../Serialization/Fixtures/settings-v5.json | 1583 +++++++++++++++ .../Serialization/Fixtures/settings.json | 1759 +++++++++++++++++ .../Serialization/Fixtures/swot-analysis.json | 103 + app/Tests/Serialization/JsonContainment.cs | 67 + .../Serialization/JsonContractDescription.cs | 103 + app/Tests/Serialization/JsonContractTests.cs | 23 + app/Tests/Serialization/JsonCoverageTests.cs | 25 + app/Tests/Serialization/JsonDefaultsTests.cs | 66 + app/Tests/Serialization/JsonGoldenFile.cs | 15 + .../Serialization/JsonGoldenFileTests.cs | 89 + .../Serialization/JsonReflectionBaseline.cs | 27 + app/Tests/Serialization/JsonTestRoot.cs | 14 + app/Tests/Serialization/JsonTestRoots.cs | 81 + app/Tests/Serialization/JsonTypeGraph.cs | 44 + app/Tests/TestHost.cs | 11 +- app/Tests/Tools/EmbeddingSignatureTests.cs | 12 +- 75 files changed, 5437 insertions(+), 298 deletions(-) create mode 100644 app/MindWork AI Studio/Assistants/SWOTAnalysis/SwotAnalysisJsonContext.cs create mode 100644 app/MindWork AI Studio/Chat/ChatJsonContext.cs create mode 100644 app/MindWork AI Studio/Settings/SettingsJsonContext.cs create mode 100644 app/MindWork AI Studio/Tools/DataBackup/DataBackupJsonContext.cs create mode 100644 app/MindWork AI Studio/Tools/Databases/ProvenanceStore/ProvenanceJsonContext.cs create mode 100644 app/MindWork AI Studio/Tools/JsonSerializerOptionsExtensions.cs create mode 100644 app/Tests/Serialization/Fixtures/chat.json create mode 100644 app/Tests/Serialization/Fixtures/data-backup-manifest.json create mode 100644 app/Tests/Serialization/Fixtures/data-restore-plan.json create mode 100644 app/Tests/Serialization/Fixtures/data-restore-report.json create mode 100644 app/Tests/Serialization/Fixtures/provenance-surrounding-content.json create mode 100644 app/Tests/Serialization/Fixtures/settings-v1.json create mode 100644 app/Tests/Serialization/Fixtures/settings-v4.json create mode 100644 app/Tests/Serialization/Fixtures/settings-v5.json create mode 100644 app/Tests/Serialization/Fixtures/settings.json create mode 100644 app/Tests/Serialization/Fixtures/swot-analysis.json create mode 100644 app/Tests/Serialization/JsonContainment.cs create mode 100644 app/Tests/Serialization/JsonContractDescription.cs create mode 100644 app/Tests/Serialization/JsonContractTests.cs create mode 100644 app/Tests/Serialization/JsonCoverageTests.cs create mode 100644 app/Tests/Serialization/JsonDefaultsTests.cs create mode 100644 app/Tests/Serialization/JsonGoldenFile.cs create mode 100644 app/Tests/Serialization/JsonGoldenFileTests.cs create mode 100644 app/Tests/Serialization/JsonReflectionBaseline.cs create mode 100644 app/Tests/Serialization/JsonTestRoot.cs create mode 100644 app/Tests/Serialization/JsonTestRoots.cs create mode 100644 app/Tests/Serialization/JsonTypeGraph.cs diff --git a/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditFinding.cs b/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditFinding.cs index 449052e12..0fe05d4fa 100644 --- a/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditFinding.cs +++ b/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditFinding.cs @@ -13,11 +13,11 @@ public sealed class AssistantAuditFinding /// #pragma warning restore MWAIS0005 [JsonIgnore] - public AssistantAuditLevel Severity { get; private init; } = AssistantAuditLevel.UNKNOWN; + public AssistantAuditLevel Severity { get; private set; } = AssistantAuditLevel.UNKNOWN; /// - /// Gets or initializes the JSON-facing severity label used by the audit model response. + /// Gets or sets the JSON-facing severity label used by the audit model response. /// [JsonPropertyName("severity")] public string SeverityText @@ -30,7 +30,7 @@ public sealed class AssistantAuditFinding _ => "unknown", }; - init => this.Severity = value.Trim().ToLowerInvariant() switch + set => this.Severity = value.Trim().ToLowerInvariant() switch { "critical" => AssistantAuditLevel.DANGEROUS, "medium" => AssistantAuditLevel.CAUTION, @@ -39,7 +39,7 @@ public sealed class AssistantAuditFinding }; } - public string Category { get; init; } = string.Empty; - public string Location { get; init; } = string.Empty; - public string Description { get; init; } = string.Empty; + public string Category { get; set; } = string.Empty; + public string Location { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; } diff --git a/app/MindWork AI Studio/Assistants/SWOTAnalysis/SwotAnalysisContentPresentation.cs b/app/MindWork AI Studio/Assistants/SWOTAnalysis/SwotAnalysisContentPresentation.cs index 30476cf19..926706a8f 100644 --- a/app/MindWork AI Studio/Assistants/SWOTAnalysis/SwotAnalysisContentPresentation.cs +++ b/app/MindWork AI Studio/Assistants/SWOTAnalysis/SwotAnalysisContentPresentation.cs @@ -10,13 +10,22 @@ internal static class SwotAnalysisContentPresentation /// Fails the reading of stored data which holds a null where the result expects a value, so that /// the result view never meets one. /// - private static readonly JsonSerializerOptions JSON_OPTIONS = new() + internal static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions { RespectNullableAnnotations = true, - }; + TypeInfoResolver = SwotAnalysisJsonContext.Default, + }.AsReadOnly(); + + /// + /// Writes the analysis with the defaults of System.Text.Json, which let a null pass, unlike the reading. + /// + internal static readonly JsonSerializerOptions WRITE_OPTIONS = new JsonSerializerOptions + { + TypeInfoResolver = SwotAnalysisJsonContext.Default, + }.AsReadOnly(); public static ContentPresentation Create(SwotAnalysisResult result) => - ContentPresentation.Create(ContentPresentationKind.SWOT_ANALYSIS, result); + ContentPresentation.Create(ContentPresentationKind.SWOT_ANALYSIS, result, WRITE_OPTIONS); /// /// Reads the SWOT analysis stored as the presentation of a text, so it can be shown as a matrix. diff --git a/app/MindWork AI Studio/Assistants/SWOTAnalysis/SwotAnalysisJsonContext.cs b/app/MindWork AI Studio/Assistants/SWOTAnalysis/SwotAnalysisJsonContext.cs new file mode 100644 index 000000000..09863024f --- /dev/null +++ b/app/MindWork AI Studio/Assistants/SWOTAnalysis/SwotAnalysisJsonContext.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace AIStudio.Assistants.SWOTAnalysis; + +/// +/// The contracts with which System.Text.Json reads and writes a SWOT analysis which a chat stores, built at +/// compile time. +/// +[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] +[JsonSerializable(typeof(SwotAnalysisResult))] +internal sealed partial class SwotAnalysisJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/app/MindWork AI Studio/Chat/ChatDraft.cs b/app/MindWork AI Studio/Chat/ChatDraft.cs index afbf81f06..c7e26f830 100644 --- a/app/MindWork AI Studio/Chat/ChatDraft.cs +++ b/app/MindWork AI Studio/Chat/ChatDraft.cs @@ -26,11 +26,16 @@ public sealed class ChatDraft { private readonly Lock stateLock = new(); + // + // The setters are internal only for reading the JSON, since its source generation reaches no private + // setter. Everything else changes the draft through the methods below, which hold the lock. + // + /// /// The text of the message. /// [JsonInclude] - public string Text { get; private set; } = string.Empty; + public string Text { get; internal set; } = string.Empty; /// /// The files attached to the message, the transcripts of audio and video files included. @@ -40,7 +45,7 @@ public sealed class ChatDraft /// transcript is still a transcript after the chat was loaded again. /// [JsonInclude] - public IReadOnlyList FileAttachments { get; private set; } = []; + public IReadOnlyList FileAttachments { get; internal set; } = []; /// /// The texts a preview read of the attached files, which sending takes along instead of reading the files again. @@ -53,7 +58,7 @@ public sealed class ChatDraft /// store holds the texts themselves. /// [JsonInclude] - public IReadOnlyList PreviewedTexts { get; private set; } = []; + public IReadOnlyList PreviewedTexts { get; internal set; } = []; /// /// Whether the draft holds neither text nor attachments. diff --git a/app/MindWork AI Studio/Chat/ChatJsonContext.cs b/app/MindWork AI Studio/Chat/ChatJsonContext.cs new file mode 100644 index 000000000..23d333175 --- /dev/null +++ b/app/MindWork AI Studio/Chat/ChatJsonContext.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +using ERIContentType = AIStudio.Tools.ERIClient.DataModel.ContentType; + +namespace AIStudio.Chat; + +/// +/// The contracts with which System.Text.Json reads and writes chats, built at compile time. +/// +/// +/// Two types with the same name need a name of their own for their contract, else the generator builds +/// only the first of them and warns with SYSLIB1031. +/// +[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] +[JsonSerializable(typeof(ChatThread))] +[JsonSerializable(typeof(ERIContentType), TypeInfoPropertyName = "ERIContentType")] +internal sealed partial class ChatJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/app/MindWork AI Studio/Chat/ChatThread.cs b/app/MindWork AI Studio/Chat/ChatThread.cs index c1a13db34..9d2dde6af 100644 --- a/app/MindWork AI Studio/Chat/ChatThread.cs +++ b/app/MindWork AI Studio/Chat/ChatThread.cs @@ -23,7 +23,7 @@ public sealed record ChatThread /// /// The unique identifier of the chat thread. /// - public Guid ChatId { get; init; } + public Guid ChatId { get; set; } /// /// The unique identifier of the workspace. @@ -42,16 +42,16 @@ public sealed record ChatThread /// Every copy of this thread made with a with expression shares the draft, because such a copy is /// still the same chat. A copy which becomes another chat needs a draft of its own. /// - public ChatDraft Draft { get; init; } = new(); + public ChatDraft Draft { get; set; } = new(); // Compatibility shim: until v26.9.1, a chat stored only the transcripts of its draft, in a list of their own. See documentation/compatibility-shims/2026-10-chat-pending-media-transcripts.md; remove after 2027-04-11. [JsonInclude] [JsonPropertyName("pending_media_transcripts")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - private List? LegacyPendingMediaTranscripts + internal List? LegacyPendingMediaTranscripts { get => null; - init + set { if (value is not null) this.Draft.AddAttachments(value); @@ -127,7 +127,7 @@ public sealed record ChatThread /// continues it must meet the level. /// [JsonInclude] - public ConfidenceLevel RequiredProviderConfidence { get; private set; } = ConfidenceLevel.NONE; + public ConfidenceLevel RequiredProviderConfidence { get; internal set; } = ConfidenceLevel.NONE; public void RequireProviderConfidence(ConfidenceLevel minimumProviderConfidence) { @@ -164,7 +164,7 @@ public sealed record ChatThread /// internal content demands nothing. /// [JsonInclude] - public OutboundDataRequirement RequiredOutboundDataRestriction { get; private set; } = OutboundDataRequirement.NONE; + public OutboundDataRequirement RequiredOutboundDataRestriction { get; internal set; } = OutboundDataRequirement.NONE; /// /// Tightens where this chat may still send data to what the data brought in demands, and @@ -229,7 +229,7 @@ public sealed record ChatThread /// immutable, so that a copy of the thread never shares a set which grows later. /// [JsonInclude] - public ImmutableHashSet WebAddressesWrittenByModels { get; private set; } = []; + public ImmutableHashSet WebAddressesWrittenByModels { get; internal set; } = []; /// /// The name of the chat thread. Usually generated by an AI model or manually edited by the user. @@ -244,7 +244,7 @@ public sealed record ChatThread /// /// The content blocks of the chat thread. /// - public List Blocks { get; init; } = []; + public List Blocks { get; set; } = []; /// /// The component running this thread, which decides the tools it may use and what its requests name. diff --git a/app/MindWork AI Studio/Chat/ContentBlock.cs b/app/MindWork AI Studio/Chat/ContentBlock.cs index 3961980ae..46b19ebc5 100644 --- a/app/MindWork AI Studio/Chat/ContentBlock.cs +++ b/app/MindWork AI Studio/Chat/ContentBlock.cs @@ -8,12 +8,12 @@ public class ContentBlock /// /// Time when the content block was created. /// - public DateTimeOffset Time { get; init; } + public DateTimeOffset Time { get; set; } /// /// Type of the content block, e.g., text, image, voice, etc. /// - public ContentType ContentType { get; init; } = ContentType.NONE; + public ContentType ContentType { get; set; } = ContentType.NONE; /// /// The content of the block. @@ -28,7 +28,7 @@ public class ContentBlock /// /// Should the content block be hidden from the user? /// - public bool HideFromUser { get; init; } + public bool HideFromUser { get; set; } public ContentBlock DeepClone(bool changeHideState = false, bool hideFromUser = true) => new() { diff --git a/app/MindWork AI Studio/Chat/ContentImage.cs b/app/MindWork AI Studio/Chat/ContentImage.cs index 8af5501ce..a56e3887b 100644 --- a/app/MindWork AI Studio/Chat/ContentImage.cs +++ b/app/MindWork AI Studio/Chat/ContentImage.cs @@ -58,7 +58,7 @@ public sealed class ContentImage : IContent, IImageSource /// /// Is the image source a URL, a local file path, a base64 string, etc.? /// - public required ContentImageSource SourceType { get; init; } + public required ContentImageSource SourceType { get; set; } /// /// The image source. diff --git a/app/MindWork AI Studio/Chat/ContentPresentation.cs b/app/MindWork AI Studio/Chat/ContentPresentation.cs index eca78320b..7c6703379 100644 --- a/app/MindWork AI Studio/Chat/ContentPresentation.cs +++ b/app/MindWork AI Studio/Chat/ContentPresentation.cs @@ -33,10 +33,10 @@ public sealed class ContentPresentation } } - internal static ContentPresentation Create(ContentPresentationKind kind, T data) => new() + internal static ContentPresentation Create(ContentPresentationKind kind, T data, JsonSerializerOptions options) => new() { Kind = kind, - Data = JsonSerializer.SerializeToElement(data), + Data = JsonSerializer.SerializeToElement(data, options), }; internal ContentPresentation DeepClone() => new() diff --git a/app/MindWork AI Studio/Chat/FileAttachment.cs b/app/MindWork AI Studio/Chat/FileAttachment.cs index 11cb4731d..0f518be30 100644 --- a/app/MindWork AI Studio/Chat/FileAttachment.cs +++ b/app/MindWork AI Studio/Chat/FileAttachment.cs @@ -15,8 +15,22 @@ namespace AIStudio.Chat; [JsonDerivedType(typeof(FileAttachment), typeDiscriminator: "file")] [JsonDerivedType(typeof(FileAttachmentImage), typeDiscriminator: "image")] [JsonDerivedType(typeof(ManagedTranscriptAttachment), typeDiscriminator: "managed_transcript")] -public record FileAttachment(FileAttachmentType Type, string FileName, [property: JsonConverter(typeof(DataDirectoryRelativePathConverter))] string FilePath, long FileSizeBytes) +public record FileAttachment(FileAttachmentType Type, string FileName, string FilePath, long FileSizeBytes) { + // + // The properties of the parameters, declared again to be settable. A derived attachment gets the type from + // its own constructor, and the source generation of System.Text.Json sets every init-only property while it + // creates the object, also one missing in the JSON, which would lose that type. Keep them first and in the + // order of the parameters, which is the order they get stored in: + // + public FileAttachmentType Type { get; set; } = Type; + public string FileName { get; set; } = FileName; + + [JsonConverter(typeof(DataDirectoryRelativePathConverter))] + public string FilePath { get; set; } = FilePath; + + public long FileSizeBytes { get; set; } = FileSizeBytes; + /// /// Gets a value indicating whether the file type is forbidden and should not be attached. /// @@ -69,7 +83,7 @@ public record FileAttachment(FileAttachmentType Type, string FileName, [property /// of chat templates, while null. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public Guid? ProvenanceId { get; init; } + public Guid? ProvenanceId { get; set; } /// /// The state of the file which the stored text was read from, see ProvenanceId, or null while @@ -80,7 +94,7 @@ public record FileAttachment(FileAttachmentType Type, string FileName, [property /// DataChat.ReadChangedAttachmentsAgain. Left out of the chat file while null. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public FileVersion? Version { get; init; } + public FileVersion? Version { get; set; } /// /// Whether the file changed since its text was stored. diff --git a/app/MindWork AI Studio/Chat/FileAttachmentImage.cs b/app/MindWork AI Studio/Chat/FileAttachmentImage.cs index 19d002e0d..c046229ff 100644 --- a/app/MindWork AI Studio/Chat/FileAttachmentImage.cs +++ b/app/MindWork AI Studio/Chat/FileAttachmentImage.cs @@ -10,7 +10,7 @@ public record FileAttachmentImage(string FileName, string FilePath, long FileSiz /// /// Is the image source a URL, a local file path, a base64 string, etc.? /// - public ContentImageSource SourceType { get; init; } = ContentImageSource.LOCAL_PATH; + public ContentImageSource SourceType { get; set; } = ContentImageSource.LOCAL_PATH; /// /// The image source. diff --git a/app/MindWork AI Studio/Chat/IImageSource.cs b/app/MindWork AI Studio/Chat/IImageSource.cs index 1931630f3..e073593ec 100644 --- a/app/MindWork AI Studio/Chat/IImageSource.cs +++ b/app/MindWork AI Studio/Chat/IImageSource.cs @@ -8,7 +8,7 @@ public interface IImageSource /// /// Is the image source a URL, a local file path, a base64 string, etc.? /// - public ContentImageSource SourceType { get; init; } + public ContentImageSource SourceType { get; set; } /// /// The image source. diff --git a/app/MindWork AI Studio/Chat/ReportedTokenUsage.cs b/app/MindWork AI Studio/Chat/ReportedTokenUsage.cs index b005241fd..482381090 100644 --- a/app/MindWork AI Studio/Chat/ReportedTokenUsage.cs +++ b/app/MindWork AI Studio/Chat/ReportedTokenUsage.cs @@ -20,7 +20,7 @@ public sealed record ReportedTokenUsage /// /// What everything sent to the model cost. /// - public int PromptTokens { get; init; } + public int PromptTokens { get; set; } /// /// Which model the number was charged for. @@ -31,7 +31,7 @@ public sealed record ReportedTokenUsage /// being an answer about the request which is about to be sent, and the estimate, wrong as it /// is, is at least wrong about the right model. /// - public string ModelId { get; init; } = string.Empty; + public string ModelId { get; set; } = string.Empty; /// /// How many blocks the conversation had when the request went out, this answer included. @@ -45,7 +45,7 @@ public sealed record ReportedTokenUsage /// Taken when the request is sent rather than when the report arrives: whatever is deleted /// while the answer streams in was still part of what the provider counted. /// - public int BlockCount { get; init; } + public int BlockCount { get; set; } /// /// States the stored number as a usage again. diff --git a/app/MindWork AI Studio/Settings/ChatTemplate.cs b/app/MindWork AI Studio/Settings/ChatTemplate.cs index 2e3b836e7..645c878cb 100644 --- a/app/MindWork AI Studio/Settings/ChatTemplate.cs +++ b/app/MindWork AI Studio/Settings/ChatTemplate.cs @@ -22,6 +22,23 @@ public record ChatTemplate( bool IsEnterpriseConfiguration = false, Guid EnterpriseConfigurationPluginId = default) : ConfigurationBaseObject { + // + // The properties of the parameters, declared again to be settable. The source generation of System.Text.Json + // sets every init-only property while it creates the object, also one missing in the JSON, so its value from + // the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is + // the order they get stored in: + // + public override uint Num { get; set; } = Num; + public override string Id { get; set; } = Id; + public override string Name { get; set; } = Name; + public string SystemPrompt { get; set; } = SystemPrompt; + public string PredefinedUserPrompt { get; set; } = PredefinedUserPrompt; + public List ExampleConversation { get; set; } = ExampleConversation; + public List FileAttachments { get; set; } = FileAttachments; + public bool AllowProfileUsage { get; set; } = AllowProfileUsage; + public override bool IsEnterpriseConfiguration { get; set; } = IsEnterpriseConfiguration; + public override Guid EnterpriseConfigurationPluginId { get; set; } = EnterpriseConfigurationPluginId; + private const string ATTACHMENTS_DIRECTORY = "attachments"; public ChatTemplate() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty, [], [], false) @@ -38,7 +55,7 @@ public record ChatTemplate( /// A preselection, not a limit: the user changes the selection in the chat as usual, and a /// tool still has to meet the confidence requirements of the provider in use. /// - public HashSet? ToolIds { get; init; } + public HashSet? ToolIds { get; set; } /// /// The data source options a chat started with this template begins with. @@ -49,7 +66,7 @@ public record ChatTemplate( /// list of sources: whether data sources are used at all, whether an agent picks them, and /// whether the retrieved data is validated. /// - public DataSourceOptions? DataSourceOptions { get; init; } + public DataSourceOptions? DataSourceOptions { get; set; } /// /// The profile this template preselects for a chat started with it. @@ -63,7 +80,7 @@ public record ChatTemplate( /// nothing for a template which does not allow profiles, and a profile which is gone leaves the /// chat default in place. /// - public string? ProfileId { get; init; } + public string? ProfileId { get; set; } /// /// The provider, and with it the model, this template preselects for a chat started with it. @@ -74,7 +91,7 @@ public record ChatTemplate( /// provider which is gone, or which may not be used for chats, leaves the chat to start as if /// the template said nothing. /// - public string? ProviderId { get; init; } + public string? ProviderId { get; set; } /// /// The name of the workspace a chat started with this template is kept in. @@ -89,7 +106,7 @@ public record ChatTemplate( /// workspace on purpose, for example from the workspace list, stays there, and so does every /// chat while workspaces are switched off. /// - public string? WorkspaceName { get; init; } + public string? WorkspaceName { get; set; } private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ChatTemplate).Namespace, nameof(ChatTemplate)); diff --git a/app/MindWork AI Studio/Settings/DataModel/Data.cs b/app/MindWork AI Studio/Settings/DataModel/Data.cs index 82f5c3b68..c767994da 100644 --- a/app/MindWork AI Studio/Settings/DataModel/Data.cs +++ b/app/MindWork AI Studio/Settings/DataModel/Data.cs @@ -11,32 +11,32 @@ public sealed class Data /// The version of the settings file. Allows us to upgrade the settings /// when a new version is available. /// - public Version Version { get; init; } = Version.V6; + public Version Version { get; set; } = Version.V6; /// /// List of configured providers. /// - public List Providers { get; init; } = []; + public List Providers { get; set; } = []; /// /// Settings concerning confidence levels. /// - public DataConfidence Confidence { get; init; } = new(x => x.Confidence); + public DataConfidence Confidence { get; set; } = new(x => x.Confidence); /// /// Settings concerning data source security checks. /// - public DataSourceSecuritySettings DataSourceSecurity { get; init; } = new(x => x.DataSourceSecurity); + public DataSourceSecuritySettings DataSourceSecurity { get; set; } = new(x => x.DataSourceSecurity); /// /// A collection of embedding providers configured. /// - public List EmbeddingProviders { get; init; } = []; + public List EmbeddingProviders { get; set; } = []; /// /// A collection of speech providers configured. /// - public List TranscriptionProviders { get; init; } = []; + public List TranscriptionProviders { get; set; } = []; /// /// A collection of data sources configured. @@ -59,17 +59,17 @@ public sealed class Data /// /// Settings which apply to all mailboxes. /// - public DataMailboxes MailboxSettings { get; init; } = new(x => x.MailboxSettings); + public DataMailboxes MailboxSettings { get; set; } = new(x => x.MailboxSettings); /// /// List of configured profiles. /// - public List Profiles { get; init; } = []; + public List Profiles { get; set; } = []; /// /// List of configured chat templates. /// - public List ChatTemplates { get; init; } = []; + public List ChatTemplates { get; set; } = []; /// /// List of enabled plugins. @@ -161,74 +161,74 @@ public sealed class Data /// public uint NextDocumentAnalysisPolicyNum { get; set; } = 1; - public DataApp App { get; init; } = new(x => x.App); + public DataApp App { get; set; } = new(x => x.App); - public DataChat Chat { get; init; } = new(x => x.Chat); + public DataChat Chat { get; set; } = new(x => x.Chat); - public DataWorkspace Workspace { get; init; } = new(); + public DataWorkspace Workspace { get; set; } = new(); - public DataIconFinder IconFinder { get; init; } = new(); + public DataIconFinder IconFinder { get; set; } = new(); - public DataTranslation Translation { get; init; } = new(); + public DataTranslation Translation { get; set; } = new(); - public DataCoding Coding { get; init; } = new(); + public DataCoding Coding { get; set; } = new(); - public DataERI ERI { get; init; } = new(); + public DataERI ERI { get; set; } = new(); - public DataDocumentAnalysis DocumentAnalysis { get; init; } = new(); + public DataDocumentAnalysis DocumentAnalysis { get; set; } = new(); /// /// Gets the managed Batch Processing Assistant defaults. /// - public DataBatchProcessing BatchProcessing { get; init; } = new(x => x.BatchProcessing); + public DataBatchProcessing BatchProcessing { get; set; } = new(x => x.BatchProcessing); - public DataMandatoryInformation MandatoryInformation { get; init; } = new(); + public DataMandatoryInformation MandatoryInformation { get; set; } = new(); - public DataTextSummarizer TextSummarizer { get; init; } = new(); + public DataTextSummarizer TextSummarizer { get; set; } = new(); - public DataSWOTAnalysis SWOTAnalysis { get; init; } = new(); + public DataSWOTAnalysis SWOTAnalysis { get; set; } = new(); - public DataTextContentCleaner TextContentCleaner { get; init; } = new(); + public DataTextContentCleaner TextContentCleaner { get; set; } = new(); - public DataAgentDataSourceSelection AgentDataSourceSelection { get; init; } = new(x => x.AgentDataSourceSelection); + public DataAgentDataSourceSelection AgentDataSourceSelection { get; set; } = new(x => x.AgentDataSourceSelection); - public DataAgentRetrievalContextValidation AgentRetrievalContextValidation { get; init; } = new(x => x.AgentRetrievalContextValidation); + public DataAgentRetrievalContextValidation AgentRetrievalContextValidation { get; set; } = new(x => x.AgentRetrievalContextValidation); - public DataAssistantPluginAudit AssistantPluginAudit { get; init; } = new(x => x.AssistantPluginAudit); + public DataAssistantPluginAudit AssistantPluginAudit { get; set; } = new(x => x.AssistantPluginAudit); - public DataAgenda Agenda { get; init; } = new(); + public DataAgenda Agenda { get; set; } = new(); - public DataGrammarSpelling GrammarSpelling { get; init; } = new(); + public DataGrammarSpelling GrammarSpelling { get; set; } = new(); - public DataRewriteImprove RewriteImprove { get; init; } = new(); + public DataRewriteImprove RewriteImprove { get; set; } = new(); - public DataPromptOptimizer PromptOptimizer { get; init; } = new(); + public DataPromptOptimizer PromptOptimizer { get; set; } = new(); - public DataEMail EMail { get; init; } = new(); + public DataEMail EMail { get; set; } = new(); - public DataSlideBuilder SlideBuilder { get; init; } = new(x => x.SlideBuilder); + public DataSlideBuilder SlideBuilder { get; set; } = new(x => x.SlideBuilder); /// /// Gets the managed Assistant Builder defaults. /// - public DataAssistantBuilder AssistantBuilder { get; init; } = new(x => x.AssistantBuilder); + public DataAssistantBuilder AssistantBuilder { get; set; } = new(x => x.AssistantBuilder); /// /// Gets the managed Visual Briefing Assistant defaults. /// - public DataVisualBriefing VisualBriefing { get; init; } = new(x => x.VisualBriefing); + public DataVisualBriefing VisualBriefing { get; set; } = new(x => x.VisualBriefing); - public DataLegalCheck LegalCheck { get; init; } = new(); + public DataLegalCheck LegalCheck { get; set; } = new(); - public DataSynonyms Synonyms { get; init; } = new(); + public DataSynonyms Synonyms { get; set; } = new(); - public DataMyTasks MyTasks { get; init; } = new(); + public DataMyTasks MyTasks { get; set; } = new(); - public DataJobPostings JobPostings { get; init; } = new(); + public DataJobPostings JobPostings { get; set; } = new(); - public DataBiasOfTheDay BiasOfTheDay { get; init; } = new(); + public DataBiasOfTheDay BiasOfTheDay { get; set; } = new(); - public DataI18N I18N { get; init; } = new(); + public DataI18N I18N { get; set; } = new(); - public DataTools Tools { get; init; } = new(x => x.Tools); + public DataTools Tools { get; set; } = new(x => x.Tools); } diff --git a/app/MindWork AI Studio/Settings/DataModel/DataApp.cs b/app/MindWork AI Studio/Settings/DataModel/DataApp.cs index a9fc8e3bf..5616f45ee 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataApp.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataApp.cs @@ -398,7 +398,7 @@ public sealed class DataApp(Expression>? configSelection = n /// /// Settings for indexing local data sources. /// - public DataDataSourceIndexing DataSourceIndexing { get; init; } = new(x => x.App.DataSourceIndexing); + public DataDataSourceIndexing DataSourceIndexing { get; set; } = new(x => x.App.DataSourceIndexing); /// /// List of assistants that should be hidden from the UI. diff --git a/app/MindWork AI Studio/Settings/DataModel/DataAssistantPluginEnterpriseApproval.cs b/app/MindWork AI Studio/Settings/DataModel/DataAssistantPluginEnterpriseApproval.cs index d9026c6ba..44a7dd1a9 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataAssistantPluginEnterpriseApproval.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataAssistantPluginEnterpriseApproval.cs @@ -5,11 +5,11 @@ namespace AIStudio.Settings.DataModel; /// public sealed class DataAssistantPluginEnterpriseApproval { - public string PluginHash { get; init; } = string.Empty; - public string DisplayName { get; init; } = string.Empty; - public string Comment { get; init; } = string.Empty; - public string ApprovedBy { get; init; } = string.Empty; - public DateTimeOffset? ApprovedAtUtc { get; init; } + public string PluginHash { get; set; } = string.Empty; + public string DisplayName { get; set; } = string.Empty; + public string Comment { get; set; } = string.Empty; + public string ApprovedBy { get; set; } = string.Empty; + public DateTimeOffset? ApprovedAtUtc { get; set; } /// /// Whether the organization wants this assistant plugin to be enabled, instead of leaving that @@ -21,7 +21,7 @@ public sealed class DataAssistantPluginEnterpriseApproval /// colleague still has to switch it on. This field is how an organization makes that decision /// instead. /// - public bool Activate { get; init; } + public bool Activate { get; set; } /// /// Whether the user may switch an assistant plugin the organization activated off again. @@ -32,5 +32,5 @@ public sealed class DataAssistantPluginEnterpriseApproval /// change. It has no meaning of its own while Activate is false, because there is nothing to /// override then. /// - public bool AllowUserOverride { get; init; } + public bool AllowUserOverride { get; set; } } diff --git a/app/MindWork AI Studio/Settings/DataModel/DataDocumentAnalysisPolicy.cs b/app/MindWork AI Studio/Settings/DataModel/DataDocumentAnalysisPolicy.cs index 91e5aea64..40b07eee5 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataDocumentAnalysisPolicy.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataDocumentAnalysisPolicy.cs @@ -12,20 +12,20 @@ public sealed record DataDocumentAnalysisPolicy : ConfigurationBaseObject private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger(); /// - public override string Id { get; init; } = string.Empty; + public override string Id { get; set; } = string.Empty; /// - public override uint Num { get; init; } + public override uint Num { get; set; } /// public override string Name { get => this.PolicyName; - init => this.PolicyName = value; + set => this.PolicyName = value; } /// - public override bool IsEnterpriseConfiguration { get; init; } + public override bool IsEnterpriseConfiguration { get; set; } /// /// The name of the document analysis policy. @@ -43,7 +43,7 @@ public sealed record DataDocumentAnalysisPolicy : ConfigurationBaseObject public bool IsProtected { get; set; } /// - public override Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty; + public override Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty; /// /// The rules for the document analysis policy. diff --git a/app/MindWork AI Studio/Settings/DataModel/DataMandatoryInfoAcceptance.cs b/app/MindWork AI Studio/Settings/DataModel/DataMandatoryInfoAcceptance.cs index e24969d0a..0951f3e3a 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataMandatoryInfoAcceptance.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataMandatoryInfoAcceptance.cs @@ -5,25 +5,25 @@ public sealed record DataMandatoryInfoAcceptance /// /// The ID of the mandatory info that was accepted. /// - public string InfoId { get; init; } = string.Empty; + public string InfoId { get; set; } = string.Empty; /// /// The accepted version string. /// - public string AcceptedVersion { get; init; } = string.Empty; + public string AcceptedVersion { get; set; } = string.Empty; /// /// The accepted hash of the mandatory info content. /// - public string AcceptedHash { get; init; } = string.Empty; + public string AcceptedHash { get; set; } = string.Empty; /// /// The UTC time of the acceptance. /// - public DateTimeOffset AcceptedAtUtc { get; init; } + public DateTimeOffset AcceptedAtUtc { get; set; } /// /// The plugin that provided the accepted info at the time of acceptance. /// - public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty; + public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty; } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/DataModel/DataSourceERI_V1.cs b/app/MindWork AI Studio/Settings/DataModel/DataSourceERI_V1.cs index 031942aed..426bad57e 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataSourceERI_V1.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataSourceERI_V1.cs @@ -20,7 +20,7 @@ namespace AIStudio.Settings.DataModel; /// /// An external data source, accessed via an ERI server, cf. https://github.com/MindWorkAI/ERI. /// -public readonly record struct DataSourceERI_V1 : IERIDataSource +public record struct DataSourceERI_V1: IERIDataSource { private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); @@ -34,52 +34,52 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource } /// - public uint Num { get; init; } + public uint Num { get; set; } /// - public string Id { get; init; } = Guid.Empty.ToString(); + public string Id { get; set; } = Guid.Empty.ToString(); /// - public string Name { get; init; } = string.Empty; + public string Name { get; set; } = string.Empty; /// - public DataSourceType Type { get; init; } = DataSourceType.NONE; + public DataSourceType Type { get; set; } = DataSourceType.NONE; /// - public string Hostname { get; init; } = string.Empty; + public string Hostname { get; set; } = string.Empty; /// - public int Port { get; init; } + public int Port { get; set; } /// - public AuthMethod AuthMethod { get; init; } = AuthMethod.NONE; + public AuthMethod AuthMethod { get; set; } = AuthMethod.NONE; /// - public string Username { get; init; } = string.Empty; + public string Username { get; set; } = string.Empty; /// - public DataSourceERIUsernamePasswordMode UsernamePasswordMode { get; init; } = DataSourceERIUsernamePasswordMode.USER_MANAGED; + public DataSourceERIUsernamePasswordMode UsernamePasswordMode { get; set; } = DataSourceERIUsernamePasswordMode.USER_MANAGED; /// - public DataSourceSecurity SecurityPolicy { get; init; } = DataSourceSecurity.NOT_SPECIFIED; + public DataSourceSecurity SecurityPolicy { get; set; } = DataSourceSecurity.NOT_SPECIFIED; /// - public OutboundDataRestriction OutboundDataRestriction { get; init; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION; + public OutboundDataRestriction OutboundDataRestriction { get; set; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION; /// - public bool IsEnterpriseConfiguration { get; init; } + public bool IsEnterpriseConfiguration { get; set; } /// - public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty; + public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty; /// - public ERIVersion Version { get; init; } = ERIVersion.V1; + public ERIVersion Version { get; set; } = ERIVersion.V1; /// - public string SelectedRetrievalId { get; init; } = string.Empty; + public string SelectedRetrievalId { get; set; } = string.Empty; /// - public ushort MaxMatches { get; init; } = 10; + public ushort MaxMatches { get; set; } = 10; /// public async Task> RetrieveDataAsync(IContent lastUserPrompt, ChatThread thread, CancellationToken token = default) diff --git a/app/MindWork AI Studio/Settings/DataModel/DataSourceLocalDirectory.cs b/app/MindWork AI Studio/Settings/DataModel/DataSourceLocalDirectory.cs index e21320e2b..13cdbbe2f 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataSourceLocalDirectory.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataSourceLocalDirectory.cs @@ -10,7 +10,7 @@ namespace AIStudio.Settings.DataModel; /// /// Represents a local directory as a data source. /// -public readonly record struct DataSourceLocalDirectory : IInternalDataSource +public record struct DataSourceLocalDirectory: IInternalDataSource { // Selects what folder data sources indexed before they had file patterns, plus every other // file whose content is readable text, e.g., a Terraform file or a script without an extension. @@ -27,46 +27,46 @@ public readonly record struct DataSourceLocalDirectory : IInternalDataSource } /// - public uint Num { get; init; } + public uint Num { get; set; } /// - public string Id { get; init; } = Guid.Empty.ToString(); + public string Id { get; set; } = Guid.Empty.ToString(); /// - public string Name { get; init; } = string.Empty; + public string Name { get; set; } = string.Empty; /// /// The description of the data source. What kind of data does it contain? /// What is the data source used for? /// - public string Description { get; init; } = string.Empty; + public string Description { get; set; } = string.Empty; /// - public DataSourceType Type { get; init; } = DataSourceType.NONE; + public DataSourceType Type { get; set; } = DataSourceType.NONE; /// - public string EmbeddingId { get; init; } = Guid.Empty.ToString(); + public string EmbeddingId { get; set; } = Guid.Empty.ToString(); /// - public int MaxChunkTokenLength { get; init; } + public int MaxChunkTokenLength { get; set; } /// - public int ChunkOverlapTokenLength { get; init; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH; + public int ChunkOverlapTokenLength { get; set; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH; /// - public ConfidenceLevel ConfidenceLevel { get; init; } = ConfidenceLevel.UNKNOWN; + public ConfidenceLevel ConfidenceLevel { get; set; } = ConfidenceLevel.UNKNOWN; /// - public bool IsEnterpriseConfiguration { get; init; } + public bool IsEnterpriseConfiguration { get; set; } /// - public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty; + public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty; /// - public ushort MaxMatches { get; init; } = 10; + public ushort MaxMatches { get; set; } = 10; /// - public OutboundDataRestriction OutboundDataRestriction { get; init; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION; + public OutboundDataRestriction OutboundDataRestriction { get; set; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION; /// public Task> RetrieveDataAsync(IContent lastUserPrompt, ChatThread thread, CancellationToken token = default) => @@ -79,31 +79,31 @@ public readonly record struct DataSourceLocalDirectory : IInternalDataSource /// /// The path to the directory. /// - public string Path { get; init; } = string.Empty; + public string Path { get; set; } = string.Empty; /// /// Semicolon-separated file-name patterns which decide which files below the path /// are considered for indexing. /// - public string FilePatterns { get; init; } = DEFAULT_FILE_PATTERNS; + public string FilePatterns { get; set; } = DEFAULT_FILE_PATTERNS; /// /// The files and folders below the path which are left out, even when a file pattern selects /// them, cf. FileExclusions. Data sources from before this list existed get the default list, /// so their build outputs and dependencies leave the index during the next run. /// - public string ExcludePatterns { get; init; } = FileExclusions.DEFAULT_PATTERNS; + public string ExcludePatterns { get; set; } = FileExclusions.DEFAULT_PATTERNS; /// /// The largest file below the path which is indexed, in megabytes. A larger file which the /// patterns select is not indexed, and the embedding page tells the user about it. /// - public int MaxFileSizeMegabytes { get; init; } = FolderFileSelection.DEFAULT_MAX_FILE_SIZE_MEGABYTES; + public int MaxFileSizeMegabytes { get; set; } = FolderFileSelection.DEFAULT_MAX_FILE_SIZE_MEGABYTES; /// /// Whether files in folders below the path are considered for indexing. /// - public bool IncludeSubdirectories { get; init; } = DEFAULT_INCLUDE_SUBDIRECTORIES; + public bool IncludeSubdirectories { get; set; } = DEFAULT_INCLUDE_SUBDIRECTORIES; /// /// Whether hidden files and the files in hidden folders below the path are considered for @@ -114,7 +114,7 @@ public readonly record struct DataSourceLocalDirectory : IInternalDataSource /// the hidden files it names, cf. FileNamePatterns.NamesHiddenFile. Hidden folders are entered /// only while this is on. /// - public bool IncludeHiddenFilesAndFolders { get; init; } = DEFAULT_INCLUDE_HIDDEN_FILES_AND_FOLDERS; + public bool IncludeHiddenFilesAndFolders { get; set; } = DEFAULT_INCLUDE_HIDDEN_FILES_AND_FOLDERS; /// /// The patterns used at runtime. An invalid value can only come from malformed or hand-edited diff --git a/app/MindWork AI Studio/Settings/DataModel/DataSourceLocalFile.cs b/app/MindWork AI Studio/Settings/DataModel/DataSourceLocalFile.cs index f2bcf3160..638c2b7d0 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataSourceLocalFile.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataSourceLocalFile.cs @@ -8,53 +8,53 @@ namespace AIStudio.Settings.DataModel; /// /// Represents one local file as a data source. /// -public readonly record struct DataSourceLocalFile : IInternalDataSource +public record struct DataSourceLocalFile: IInternalDataSource { public DataSourceLocalFile() { } /// - public uint Num { get; init; } + public uint Num { get; set; } /// - public string Id { get; init; } = Guid.Empty.ToString(); + public string Id { get; set; } = Guid.Empty.ToString(); /// - public string Name { get; init; } = string.Empty; + public string Name { get; set; } = string.Empty; /// /// The description of the data source. What kind of data does it contain? /// What is the data source used for? /// - public string Description { get; init; } = string.Empty; + public string Description { get; set; } = string.Empty; /// - public DataSourceType Type { get; init; } = DataSourceType.NONE; + public DataSourceType Type { get; set; } = DataSourceType.NONE; /// - public string EmbeddingId { get; init; } = Guid.Empty.ToString(); + public string EmbeddingId { get; set; } = Guid.Empty.ToString(); /// - public int MaxChunkTokenLength { get; init; } + public int MaxChunkTokenLength { get; set; } /// - public int ChunkOverlapTokenLength { get; init; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH; + public int ChunkOverlapTokenLength { get; set; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH; /// - public ConfidenceLevel ConfidenceLevel { get; init; } = ConfidenceLevel.UNKNOWN; + public ConfidenceLevel ConfidenceLevel { get; set; } = ConfidenceLevel.UNKNOWN; /// - public bool IsEnterpriseConfiguration { get; init; } + public bool IsEnterpriseConfiguration { get; set; } /// - public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty; + public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty; /// - public ushort MaxMatches { get; init; } = 10; + public ushort MaxMatches { get; set; } = 10; /// - public OutboundDataRestriction OutboundDataRestriction { get; init; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION; + public OutboundDataRestriction OutboundDataRestriction { get; set; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION; /// public Task> RetrieveDataAsync(IContent lastUserPrompt, ChatThread thread, CancellationToken token = default) => @@ -67,5 +67,5 @@ public readonly record struct DataSourceLocalFile : IInternalDataSource /// /// The path to the file. /// - public string FilePath { get; init; } = string.Empty; + public string FilePath { get; set; } = string.Empty; } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/DataModel/DataSourceMailbox.cs b/app/MindWork AI Studio/Settings/DataModel/DataSourceMailbox.cs index b07a876fa..9e14338ba 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataSourceMailbox.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataSourceMailbox.cs @@ -13,70 +13,70 @@ namespace AIStudio.Settings.DataModel; /// IDataSource: classic RAG, Semantic Search and the agents never see a mailbox. Only the mail /// tools read from one. /// -public readonly record struct DataSourceMailbox : IIndexedDataSource, ISecretId +public record struct DataSourceMailbox: IIndexedDataSource, ISecretId { public DataSourceMailbox() { } /// - public uint Num { get; init; } + public uint Num { get; set; } /// - public string Id { get; init; } = Guid.Empty.ToString(); + public string Id { get; set; } = Guid.Empty.ToString(); /// - public string Name { get; init; } = string.Empty; + public string Name { get; set; } = string.Empty; /// - public DataSourceType Type { get; init; } = DataSourceType.MAILBOX; + public DataSourceType Type { get; set; } = DataSourceType.MAILBOX; /// - public bool IsEnterpriseConfiguration { get; init; } + public bool IsEnterpriseConfiguration { get; set; } /// - public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty; + public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty; /// - public string EmbeddingId { get; init; } = Guid.Empty.ToString(); + public string EmbeddingId { get; set; } = Guid.Empty.ToString(); /// - public int MaxChunkTokenLength { get; init; } + public int MaxChunkTokenLength { get; set; } /// - public int ChunkOverlapTokenLength { get; init; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH; + public int ChunkOverlapTokenLength { get; set; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH; /// /// /// There is no default, the user has to choose one of the levels which IsAllowedMailboxConfidence /// accepts. Until then, the level is NONE, and no provider may read the mailbox. /// - public ConfidenceLevel ConfidenceLevel { get; init; } = ConfidenceLevel.NONE; + public ConfidenceLevel ConfidenceLevel { get; set; } = ConfidenceLevel.NONE; /// /// The host name of the IMAP server, e.g., imap.example.org. /// - public string Host { get; init; } = string.Empty; + public string Host { get; set; } = string.Empty; /// /// The port of the IMAP server. The default is the one for IMAP with TLS from the first byte on. /// - public int Port { get; init; } = MailboxTransportSecurityExtensions.SSL_ON_CONNECT_PORT; + public int Port { get; set; } = MailboxTransportSecurityExtensions.SSL_ON_CONNECT_PORT; /// /// How the connection to the IMAP server is encrypted. /// - public MailboxTransportSecurity TransportSecurity { get; init; } = MailboxTransportSecurity.SSL_ON_CONNECT; + public MailboxTransportSecurity TransportSecurity { get; set; } = MailboxTransportSecurity.SSL_ON_CONNECT; /// /// How AI Studio signs in to the IMAP server. /// - public MailboxAuthMethod AuthMethod { get; init; } = MailboxAuthMethod.PASSWORD; + public MailboxAuthMethod AuthMethod { get; set; } = MailboxAuthMethod.PASSWORD; /// /// The username to sign in with, often the e-mail address. /// - public string Username { get; init; } = string.Empty; + public string Username { get; set; } = string.Empty; /// /// The folder to which the synchronization and all mail tools are restricted, together with its subfolders. @@ -86,7 +86,7 @@ public readonly record struct DataSourceMailbox : IIndexedDataSource, ISecretId /// means the whole mailbox, apart from the trash and the junk folder. The sent mails and the /// drafts may come along from outside of it, see IncludeSentAndDrafts. /// - public string RootFolder { get; init; } = string.Empty; + public string RootFolder { get; set; } = string.Empty; /// /// Whether the folders for sent mails and for drafts are synchronized as well, when they lie outside the root folder. @@ -96,22 +96,22 @@ public readonly record struct DataSourceMailbox : IIndexedDataSource, ISecretId /// marks as such count, without their subfolders. A mailbox stored before this setting existed /// loads with it switched on, so that its next sync fetches them. /// - public bool IncludeSentAndDrafts { get; init; } = true; + public bool IncludeSentAndDrafts { get; set; } = true; /// /// How far back the index reaches. Flagged mails and drafts are indexed regardless of their age. /// - public MailboxMaxAge MaxAge { get; init; } = MailboxMaxAge.LAST_12_MONTHS; + public MailboxMaxAge MaxAge { get; set; } = MailboxMaxAge.LAST_12_MONTHS; /// /// Whether the text of attached documents is indexed as well. /// - public bool IndexAttachments { get; init; } = true; + public bool IndexAttachments { get; set; } = true; /// /// The size in megabytes up to which the text of an attachment is indexed. Of a larger one, only the name is. /// - public int MaxAttachmentSizeMegabytes { get; init; } = 10; + public int MaxAttachmentSizeMegabytes { get; set; } = 10; /// /// Where a chat may still send data, once it has read from this mailbox. @@ -119,12 +119,12 @@ public readonly record struct DataSourceMailbox : IIndexedDataSource, ISecretId /// /// An organization may demand a stricter one, see DataMailboxes.MinimumOutboundDataRestriction. /// - public OutboundDataRestriction OutboundDataRestriction { get; init; } = OutboundDataRestriction.ONLY_CONFIGURED_SERVICES; + public OutboundDataRestriction OutboundDataRestriction { get; set; } = OutboundDataRestriction.ONLY_CONFIGURED_SERVICES; /// /// The maximum number of mails one search returns. Searched page by page, it is the size of a page. /// - public ushort MaxMatches { get; init; } = 10; + public ushort MaxMatches { get; set; } = 10; #region Implementation of ISecretId diff --git a/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV1V3.cs b/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV1V3.cs index 88a8a3d29..a459f818a 100644 --- a/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV1V3.cs +++ b/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV1V3.cs @@ -13,12 +13,12 @@ public sealed class DataV1V3 /// The version of the settings file. Allows us to upgrade the settings /// when a new version is available. /// - public Version Version { get; init; } = Version.V3; + public Version Version { get; set; } = Version.V3; /// /// List of configured providers. /// - public List Providers { get; init; } = []; + public List Providers { get; set; } = []; /// /// The next provider number to use. diff --git a/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV4.cs b/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV4.cs index 694064671..c91f1fcff 100644 --- a/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV4.cs +++ b/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV4.cs @@ -6,22 +6,22 @@ public sealed class DataV4 /// The version of the settings file. Allows us to upgrade the settings /// when a new version is available. /// - public Version Version { get; init; } + public Version Version { get; set; } /// /// List of configured providers. /// - public List Providers { get; init; } = []; + public List Providers { get; set; } = []; /// /// Settings concerning the LLM providers. /// - public DataLLMProvidersV5 LLMProviders { get; init; } = new(); + public DataLLMProvidersV5 LLMProviders { get; set; } = new(); /// /// List of configured profiles. /// - public List Profiles { get; init; } = []; + public List Profiles { get; set; } = []; /// /// The next provider number to use. @@ -33,27 +33,27 @@ public sealed class DataV4 /// public uint NextProfileNum { get; set; } = 1; - public DataApp App { get; init; } = new(x => x.App); + public DataApp App { get; set; } = new(x => x.App); - public DataChat Chat { get; init; } = new(); + public DataChat Chat { get; set; } = new(); - public DataWorkspace Workspace { get; init; } = new(); + public DataWorkspace Workspace { get; set; } = new(); - public DataIconFinder IconFinder { get; init; } = new(); + public DataIconFinder IconFinder { get; set; } = new(); - public DataTranslation Translation { get; init; } = new(); + public DataTranslation Translation { get; set; } = new(); - public DataCoding Coding { get; init; } = new(); + public DataCoding Coding { get; set; } = new(); - public DataTextSummarizer TextSummarizer { get; init; } = new(); + public DataTextSummarizer TextSummarizer { get; set; } = new(); - public DataTextContentCleaner TextContentCleaner { get; init; } = new(); + public DataTextContentCleaner TextContentCleaner { get; set; } = new(); - public DataAgenda Agenda { get; init; } = new(); + public DataAgenda Agenda { get; set; } = new(); - public DataGrammarSpelling GrammarSpelling { get; init; } = new(); + public DataGrammarSpelling GrammarSpelling { get; set; } = new(); - public DataRewriteImprove RewriteImprove { get; init; } = new(); + public DataRewriteImprove RewriteImprove { get; set; } = new(); public DataEMail EMail { get; set; } = new(); diff --git a/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV5.cs b/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV5.cs index 3f568ce6b..4a7e3afdc 100644 --- a/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV5.cs +++ b/app/MindWork AI Studio/Settings/DataModel/PreviousModels/DataV5.cs @@ -8,27 +8,27 @@ public sealed class DataV5 /// The version of the settings file. Allows us to upgrade the settings /// when a new version is available. /// - public Version Version { get; init; } = Version.V5; + public Version Version { get; set; } = Version.V5; /// /// List of configured providers. /// - public List Providers { get; init; } = []; + public List Providers { get; set; } = []; /// /// Settings concerning the LLM providers. /// - public DataLLMProvidersV5 LLMProviders { get; init; } = new(); + public DataLLMProvidersV5 LLMProviders { get; set; } = new(); /// /// A collection of embedding providers configured. /// - public List EmbeddingProviders { get; init; } = []; + public List EmbeddingProviders { get; set; } = []; /// /// A collection of speech providers configured. /// - public List TranscriptionProviders { get; init; } = []; + public List TranscriptionProviders { get; set; } = []; /// /// A collection of data sources configured. @@ -38,12 +38,12 @@ public sealed class DataV5 /// /// List of configured profiles. /// - public List Profiles { get; init; } = []; + public List Profiles { get; set; } = []; /// /// List of configured chat templates. /// - public List ChatTemplates { get; init; } = []; + public List ChatTemplates { get; set; } = []; /// /// List of enabled plugins. @@ -95,55 +95,55 @@ public sealed class DataV5 /// public uint NextDocumentAnalysisPolicyNum { get; set; } = 1; - public DataApp App { get; init; } = new(x => x.App); + public DataApp App { get; set; } = new(x => x.App); - public DataChat Chat { get; init; } = new(); + public DataChat Chat { get; set; } = new(); - public DataWorkspace Workspace { get; init; } = new(); + public DataWorkspace Workspace { get; set; } = new(); - public DataIconFinder IconFinder { get; init; } = new(); + public DataIconFinder IconFinder { get; set; } = new(); - public DataTranslation Translation { get; init; } = new(); + public DataTranslation Translation { get; set; } = new(); - public DataCoding Coding { get; init; } = new(); + public DataCoding Coding { get; set; } = new(); - public DataERI ERI { get; init; } = new(); + public DataERI ERI { get; set; } = new(); - public DataDocumentAnalysis DocumentAnalysis { get; init; } = new(); + public DataDocumentAnalysis DocumentAnalysis { get; set; } = new(); - public DataMandatoryInformation MandatoryInformation { get; init; } = new(); + public DataMandatoryInformation MandatoryInformation { get; set; } = new(); - public DataTextSummarizer TextSummarizer { get; init; } = new(); + public DataTextSummarizer TextSummarizer { get; set; } = new(); - public DataTextContentCleaner TextContentCleaner { get; init; } = new(); + public DataTextContentCleaner TextContentCleaner { get; set; } = new(); - public DataAgentDataSourceSelection AgentDataSourceSelection { get; init; } = new(); + public DataAgentDataSourceSelection AgentDataSourceSelection { get; set; } = new(); - public DataAgentRetrievalContextValidation AgentRetrievalContextValidation { get; init; } = new(); + public DataAgentRetrievalContextValidation AgentRetrievalContextValidation { get; set; } = new(); - public DataAssistantPluginAudit AssistantPluginAudit { get; init; } = new(x => x.AssistantPluginAudit); + public DataAssistantPluginAudit AssistantPluginAudit { get; set; } = new(x => x.AssistantPluginAudit); - public DataAgenda Agenda { get; init; } = new(); + public DataAgenda Agenda { get; set; } = new(); - public DataGrammarSpelling GrammarSpelling { get; init; } = new(); + public DataGrammarSpelling GrammarSpelling { get; set; } = new(); - public DataRewriteImprove RewriteImprove { get; init; } = new(); + public DataRewriteImprove RewriteImprove { get; set; } = new(); - public DataPromptOptimizer PromptOptimizer { get; init; } = new(); + public DataPromptOptimizer PromptOptimizer { get; set; } = new(); - public DataEMail EMail { get; init; } = new(); + public DataEMail EMail { get; set; } = new(); - public DataSlideBuilder SlideBuilder { get; init; } = new(); + public DataSlideBuilder SlideBuilder { get; set; } = new(); - public DataLegalCheck LegalCheck { get; init; } = new(); + public DataLegalCheck LegalCheck { get; set; } = new(); - public DataSynonyms Synonyms { get; init; } = new(); + public DataSynonyms Synonyms { get; set; } = new(); - public DataMyTasks MyTasks { get; init; } = new(); + public DataMyTasks MyTasks { get; set; } = new(); - public DataJobPostings JobPostings { get; init; } = new(); + public DataJobPostings JobPostings { get; set; } = new(); - public DataBiasOfTheDay BiasOfTheDay { get; init; } = new(); + public DataBiasOfTheDay BiasOfTheDay { get; set; } = new(); - public DataI18N I18N { get; init; } = new(); + public DataI18N I18N { get; set; } = new(); } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/EmbeddingProvider.cs b/app/MindWork AI Studio/Settings/EmbeddingProvider.cs index b59b5f4a4..430134b4d 100644 --- a/app/MindWork AI Studio/Settings/EmbeddingProvider.cs +++ b/app/MindWork AI Studio/Settings/EmbeddingProvider.cs @@ -23,7 +23,7 @@ public sealed record EmbeddingProvider( Guid EnterpriseConfigurationPluginId = default, string Hostname = "http://localhost:1234", Host Host = Host.NONE, - [property: JsonConverter(typeof(DataDirectoryRelativePathConverter))] string TokenizerPath = "", + string TokenizerPath = "", string TokenizerFingerprint = "", int EmbeddingBatchSize = 0, int TokenLimit = 0, @@ -32,6 +32,34 @@ public sealed record EmbeddingProvider( HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE, bool PromptForUserAPIKey = false) : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey { + // + // The properties of the parameters, declared again to be settable. The source generation of System.Text.Json + // sets every init-only property while it creates the object, also one missing in the JSON, so its value from + // the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is + // the order they get stored in: + // + public override uint Num { get; set; } = Num; + public override string Id { get; set; } = Id; + public override string Name { get; set; } = Name; + public LLMProviders UsedLLMProvider { get; set; } = UsedLLMProvider; + public Model Model { get; set; } = Model; + public bool IsSelfHosted { get; set; } = IsSelfHosted; + public override bool IsEnterpriseConfiguration { get; set; } = IsEnterpriseConfiguration; + public override Guid EnterpriseConfigurationPluginId { get; set; } = EnterpriseConfigurationPluginId; + public string Hostname { get; set; } = Hostname; + public Host Host { get; set; } = Host; + + [JsonConverter(typeof(DataDirectoryRelativePathConverter))] + public string TokenizerPath { get; set; } = TokenizerPath; + + public string TokenizerFingerprint { get; set; } = TokenizerFingerprint; + public int EmbeddingBatchSize { get; set; } = EmbeddingBatchSize; + public int TokenLimit { get; set; } = TokenLimit; + public bool AllowUserProvidedAPIKey { get; set; } = AllowUserProvidedAPIKey; + public string CustomIconDataUrl { get; set; } = CustomIconDataUrl; + public HFInferenceProvider HFInferenceProvider { get; set; } = HFInferenceProvider; + public bool PromptForUserAPIKey { get; set; } = PromptForUserAPIKey; + public const int DEFAULT_TOKEN_LIMIT = 8192; public const int DEFAULT_EMBEDDING_BATCH_SIZE = 1; diff --git a/app/MindWork AI Studio/Settings/IDataSource.cs b/app/MindWork AI Studio/Settings/IDataSource.cs index ef6598cbd..d16d824fc 100644 --- a/app/MindWork AI Studio/Settings/IDataSource.cs +++ b/app/MindWork AI Studio/Settings/IDataSource.cs @@ -18,7 +18,7 @@ public interface IDataSource : IDataSourceBase /// /// The maximum number of matches one retrieval returns. Searched page by page, it is the size of a page. /// - public ushort MaxMatches { get; init; } + public ushort MaxMatches { get; set; } /// /// Where a chat may still send data, once content of this data source reached it. @@ -29,7 +29,7 @@ public interface IDataSource : IDataSourceBase /// DataSourceSecuritySettings.MinimumOutboundDataRestriction, and so does a data source which only /// self-hosted providers may see. /// - public OutboundDataRestriction OutboundDataRestriction { get; init; } + public OutboundDataRestriction OutboundDataRestriction { get; set; } /// /// The last page which can be retrieved from this data source. diff --git a/app/MindWork AI Studio/Settings/IDataSourceBase.cs b/app/MindWork AI Studio/Settings/IDataSourceBase.cs index 9cd88dfbe..12fd42f4f 100644 --- a/app/MindWork AI Studio/Settings/IDataSourceBase.cs +++ b/app/MindWork AI Studio/Settings/IDataSourceBase.cs @@ -17,5 +17,5 @@ public interface IDataSourceBase : IConfigurationObject /// /// Which type of data source is this? /// - public DataSourceType Type { get; init; } + public DataSourceType Type { get; set; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/IERIDataSource.cs b/app/MindWork AI Studio/Settings/IERIDataSource.cs index 40dd625d4..f58ed7e76 100644 --- a/app/MindWork AI Studio/Settings/IERIDataSource.cs +++ b/app/MindWork AI Studio/Settings/IERIDataSource.cs @@ -9,35 +9,35 @@ public interface IERIDataSource : IExternalDataSource /// /// The hostname of the ERI server. /// - public string Hostname { get; init; } + public string Hostname { get; set; } /// /// The port of the ERI server. /// - public int Port { get; init; } + public int Port { get; set; } /// /// The authentication method to use. /// - public AuthMethod AuthMethod { get; init; } + public AuthMethod AuthMethod { get; set; } /// /// The username to use for authentication, when the auth. method is USERNAME_PASSWORD. /// - public string Username { get; init; } + public string Username { get; set; } /// /// How username/password authentication should obtain the username. /// - public DataSourceERIUsernamePasswordMode UsernamePasswordMode { get; init; } + public DataSourceERIUsernamePasswordMode UsernamePasswordMode { get; set; } /// /// The ERI specification to use. /// - public ERIVersion Version { get; init; } + public ERIVersion Version { get; set; } /// /// The ID of the selected retrieval process. /// - public string SelectedRetrievalId { get; init; } + public string SelectedRetrievalId { get; set; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/IExternalDataSource.cs b/app/MindWork AI Studio/Settings/IExternalDataSource.cs index 74d41f2a7..9b3fc7501 100644 --- a/app/MindWork AI Studio/Settings/IExternalDataSource.cs +++ b/app/MindWork AI Studio/Settings/IExternalDataSource.cs @@ -9,7 +9,7 @@ public interface IExternalDataSource : IDataSource, ISecretId /// /// Which data security policy is applied to this external data source? /// - public DataSourceSecurity SecurityPolicy { get; init; } + public DataSourceSecurity SecurityPolicy { get; set; } #region Implementation of ISecretId diff --git a/app/MindWork AI Studio/Settings/IIndexedDataSource.cs b/app/MindWork AI Studio/Settings/IIndexedDataSource.cs index 0bd26d441..da094f972 100644 --- a/app/MindWork AI Studio/Settings/IIndexedDataSource.cs +++ b/app/MindWork AI Studio/Settings/IIndexedDataSource.cs @@ -15,21 +15,21 @@ public interface IIndexedDataSource : IDataSourceBase /// /// Which provider confidence level is required by this data source? /// - public ConfidenceLevel ConfidenceLevel { get; init; } + public ConfidenceLevel ConfidenceLevel { get; set; } /// /// The unique identifier of the embedding provider used by this data source. /// - public string EmbeddingId { get; init; } + public string EmbeddingId { get; set; } /// /// Optional maximum number of tokens per embedding chunk for this data source. /// A value of 0 means the embedding provider's setting is used. /// - public int MaxChunkTokenLength { get; init; } + public int MaxChunkTokenLength { get; set; } /// /// Optional number of tokens to overlap between consecutive chunks. /// - public int ChunkOverlapTokenLength { get; init; } + public int ChunkOverlapTokenLength { get; set; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/ManagedEditableDefaultState.cs b/app/MindWork AI Studio/Settings/ManagedEditableDefaultState.cs index 8904c4979..7507420a5 100644 --- a/app/MindWork AI Studio/Settings/ManagedEditableDefaultState.cs +++ b/app/MindWork AI Studio/Settings/ManagedEditableDefaultState.cs @@ -2,7 +2,7 @@ namespace AIStudio.Settings; public sealed class ManagedEditableDefaultState { - public Guid ConfigPluginId { get; init; } = Guid.Empty; + public Guid ConfigPluginId { get; set; } = Guid.Empty; - public string LastAppliedValue { get; init; } = string.Empty; + public string LastAppliedValue { get; set; } = string.Empty; } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/Profile.cs b/app/MindWork AI Studio/Settings/Profile.cs index 925a5936c..fc675ddfb 100644 --- a/app/MindWork AI Studio/Settings/Profile.cs +++ b/app/MindWork AI Studio/Settings/Profile.cs @@ -15,6 +15,20 @@ public record Profile( bool IsEnterpriseConfiguration = false, Guid EnterpriseConfigurationPluginId = default): ConfigurationBaseObject { + // + // The properties of the parameters, declared again to be settable. The source generation of System.Text.Json + // sets every init-only property while it creates the object, also one missing in the JSON, so its value from + // the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is + // the order they get stored in: + // + public override uint Num { get; set; } = Num; + public override string Id { get; set; } = Id; + public override string Name { get; set; } = Name; + public string NeedToKnow { get; set; } = NeedToKnow; + public string Actions { get; set; } = Actions; + public override bool IsEnterpriseConfiguration { get; set; } = IsEnterpriseConfiguration; + public override Guid EnterpriseConfigurationPluginId { get; set; } = EnterpriseConfigurationPluginId; + public Profile() : this(0, Guid.Empty.ToString(), string.Empty, string.Empty, string.Empty) { } diff --git a/app/MindWork AI Studio/Settings/Provider.cs b/app/MindWork AI Studio/Settings/Provider.cs index 3498de8f2..601f8a71d 100644 --- a/app/MindWork AI Studio/Settings/Provider.cs +++ b/app/MindWork AI Studio/Settings/Provider.cs @@ -38,12 +38,39 @@ public sealed record Provider( Host Host = Host.NONE, HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE, string AdditionalJsonApiParameters = "", - [property: JsonConverter(typeof(DataDirectoryRelativePathConverter))] string TokenizerPath = "", + string TokenizerPath = "", ProviderCapabilityOverrides? CapabilityOverrides = null, bool AllowUserProvidedAPIKey = false, string CustomIconDataUrl = "", bool PromptForUserAPIKey = false) : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey { + // + // The properties of the parameters, declared again to be settable. The source generation of System.Text.Json + // sets every init-only property while it creates the object, also one missing in the JSON, so its value from + // the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is + // the order they get stored in: + // + public override uint Num { get; set; } = Num; + public override string Id { get; set; } = Id; + public string InstanceName { get; set; } = InstanceName; + public LLMProviders UsedLLMProvider { get; set; } = UsedLLMProvider; + public Model Model { get; set; } = Model; + public bool IsSelfHosted { get; set; } = IsSelfHosted; + public override bool IsEnterpriseConfiguration { get; set; } = IsEnterpriseConfiguration; + public override Guid EnterpriseConfigurationPluginId { get; set; } = EnterpriseConfigurationPluginId; + public string Hostname { get; set; } = Hostname; + public Host Host { get; set; } = Host; + public HFInferenceProvider HFInferenceProvider { get; set; } = HFInferenceProvider; + public string AdditionalJsonApiParameters { get; set; } = AdditionalJsonApiParameters; + + [JsonConverter(typeof(DataDirectoryRelativePathConverter))] + public string TokenizerPath { get; set; } = TokenizerPath; + + public ProviderCapabilityOverrides? CapabilityOverrides { get; set; } = CapabilityOverrides; + public bool AllowUserProvidedAPIKey { get; set; } = AllowUserProvidedAPIKey; + public string CustomIconDataUrl { get; set; } = CustomIconDataUrl; + public bool PromptForUserAPIKey { get; set; } = PromptForUserAPIKey; + private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); public static readonly Provider NONE = new(); @@ -86,7 +113,7 @@ public sealed record Provider( public override string Name { get => this.InstanceName; - init => this.InstanceName = value; + set => this.InstanceName = value; } #endregion diff --git a/app/MindWork AI Studio/Settings/ProviderCapabilityOverrides.cs b/app/MindWork AI Studio/Settings/ProviderCapabilityOverrides.cs index 598e1cd37..2d1293f60 100644 --- a/app/MindWork AI Studio/Settings/ProviderCapabilityOverrides.cs +++ b/app/MindWork AI Studio/Settings/ProviderCapabilityOverrides.cs @@ -89,35 +89,35 @@ public sealed record ProviderCapabilityOverrides [JsonPropertyName("AUDIO_INPUT")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? AudioInput { get; init; } + public bool? AudioInput { get; set; } [JsonPropertyName("FUNCTION_CALLING")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? FunctionCalling { get; init; } + public bool? FunctionCalling { get; set; } [JsonPropertyName("MULTIPLE_IMAGE_INPUT")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? MultipleImageInput { get; init; } + public bool? MultipleImageInput { get; set; } [JsonPropertyName("SPEECH_INPUT")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? SpeechInput { get; init; } + public bool? SpeechInput { get; set; } [JsonPropertyName("VIDEO_INPUT")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? VideoInput { get; init; } + public bool? VideoInput { get; set; } [JsonPropertyName("OPTIONAL_REASONING")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? OptionalReasoning { get; init; } + public bool? OptionalReasoning { get; set; } [JsonPropertyName("ALWAYS_REASONING")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? AlwaysReasoning { get; init; } + public bool? AlwaysReasoning { get; set; } [JsonPropertyName("REASONING_BY_DEFAULT")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public bool? ReasoningByDefault { get; init; } + public bool? ReasoningByDefault { get; set; } /// /// How many tokens this installation reads and writes, or null to keep the automatic answer. @@ -131,21 +131,21 @@ public sealed record ProviderCapabilityOverrides /// [JsonPropertyName(CONTEXT_WINDOW_KEY)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public int? ContextWindowTokens { get; init; } + public int? ContextWindowTokens { get; set; } /// /// How many images one message may carry, or null to keep the automatic answer. /// [JsonPropertyName(MAX_IMAGES_PER_MESSAGE_KEY)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public int? MaxImagesPerMessage { get; init; } + public int? MaxImagesPerMessage { get; set; } /// /// How many images one request may carry, or null to keep the automatic answer. /// [JsonPropertyName(MAX_IMAGES_PER_REQUEST_KEY)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public int? MaxImagesPerRequest { get; init; } + public int? MaxImagesPerRequest { get; set; } [JsonIgnore] public bool HasOverrides => diff --git a/app/MindWork AI Studio/Settings/SettingsJsonContext.cs b/app/MindWork AI Studio/Settings/SettingsJsonContext.cs new file mode 100644 index 000000000..089328312 --- /dev/null +++ b/app/MindWork AI Studio/Settings/SettingsJsonContext.cs @@ -0,0 +1,43 @@ +using System.Text.Json.Serialization; + +using AIStudio.Provider; +using AIStudio.Settings.DataModel; +using AIStudio.Settings.DataModel.PreviousModels; + +using EMailWritingStyles = AIStudio.Assistants.EMail.WritingStyles; +using ERIContentType = AIStudio.Tools.ERIClient.DataModel.ContentType; +using ProviderV4 = AIStudio.Settings.DataModel.PreviousModels.Provider; +using RewriteImproveWritingStyles = AIStudio.Assistants.RewriteImprove.WritingStyles; + +namespace AIStudio.Settings; + +/// +/// The contracts with which System.Text.Json reads and writes the settings, built at compile time. +/// +/// +/// Besides the settings and their older versions, which the migrations still read, the values of managed +/// settings get written and read on their own, as the type of their setting. That type is often an interface, +/// such as ISet<string>, which the settings themselves never use. A test fails for a managed setting +/// whose type is missing here.

+/// +/// Two types with the same name need a name of their own for their contract, else the generator builds +/// only the first of them and warns with SYSLIB1031. +///
+[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] +[JsonSerializable(typeof(Data))] +[JsonSerializable(typeof(DataV1V3))] +[JsonSerializable(typeof(DataV4))] +[JsonSerializable(typeof(DataV5))] +[JsonSerializable(typeof(ISet))] +[JsonSerializable(typeof(ISet))] +[JsonSerializable(typeof(ISet))] +[JsonSerializable(typeof(ISet))] +[JsonSerializable(typeof(IList))] +[JsonSerializable(typeof(IList))] +[JsonSerializable(typeof(IDictionary))] +[JsonSerializable(typeof(ProviderV4), TypeInfoPropertyName = "ProviderV4")] +[JsonSerializable(typeof(List), TypeInfoPropertyName = "ListProviderV4")] +[JsonSerializable(typeof(EMailWritingStyles), TypeInfoPropertyName = "EMailWritingStyles")] +[JsonSerializable(typeof(RewriteImproveWritingStyles), TypeInfoPropertyName = "RewriteImproveWritingStyles")] +[JsonSerializable(typeof(ERIContentType), TypeInfoPropertyName = "ERIContentType")] +internal sealed partial class SettingsJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/SettingsManager.cs b/app/MindWork AI Studio/Settings/SettingsManager.cs index fc4ac224c..3c4d7c418 100644 --- a/app/MindWork AI Studio/Settings/SettingsManager.cs +++ b/app/MindWork AI Studio/Settings/SettingsManager.cs @@ -26,11 +26,12 @@ public sealed class SettingsManager private readonly record struct CurrentSettingsReadResult(Data? SettingsData, SettingsWriteBlockReason FailureReason); - internal static readonly JsonSerializerOptions JSON_OPTIONS = new() + internal static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions { WriteIndented = true, Converters = { new TolerantEnumConverter() }, - }; + TypeInfoResolver = SettingsJsonContext.Default, + }.AsReadOnly(); /// /// From this duration on, storing the settings is reported as slow. diff --git a/app/MindWork AI Studio/Settings/TranscriptionProvider.cs b/app/MindWork AI Studio/Settings/TranscriptionProvider.cs index f5dec6b84..7473720f7 100644 --- a/app/MindWork AI Studio/Settings/TranscriptionProvider.cs +++ b/app/MindWork AI Studio/Settings/TranscriptionProvider.cs @@ -27,6 +27,27 @@ public sealed record TranscriptionProvider( HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE, bool PromptForUserAPIKey = false) : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey { + // + // The properties of the parameters, declared again to be settable. The source generation of System.Text.Json + // sets every init-only property while it creates the object, also one missing in the JSON, so its value from + // the parameterless constructor would get lost. Keep them first and in the order of the parameters, which is + // the order they get stored in: + // + public override uint Num { get; set; } = Num; + public override string Id { get; set; } = Id; + public override string Name { get; set; } = Name; + public LLMProviders UsedLLMProvider { get; set; } = UsedLLMProvider; + public Model Model { get; set; } = Model; + public bool IsSelfHosted { get; set; } = IsSelfHosted; + public override bool IsEnterpriseConfiguration { get; set; } = IsEnterpriseConfiguration; + public override Guid EnterpriseConfigurationPluginId { get; set; } = EnterpriseConfigurationPluginId; + public string Hostname { get; set; } = Hostname; + public Host Host { get; set; } = Host; + public bool AllowUserProvidedAPIKey { get; set; } = AllowUserProvidedAPIKey; + public string CustomIconDataUrl { get; set; } = CustomIconDataUrl; + public HFInferenceProvider HFInferenceProvider { get; set; } = HFInferenceProvider; + public bool PromptForUserAPIKey { get; set; } = PromptForUserAPIKey; + private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); public static readonly TranscriptionProvider NONE = new(); diff --git a/app/MindWork AI Studio/Tools/DataBackup/DataBackupFormat.cs b/app/MindWork AI Studio/Tools/DataBackup/DataBackupFormat.cs index 60db85142..f487a68b4 100644 --- a/app/MindWork AI Studio/Tools/DataBackup/DataBackupFormat.cs +++ b/app/MindWork AI Studio/Tools/DataBackup/DataBackupFormat.cs @@ -51,12 +51,13 @@ public static class DataBackupFormat /// How the manifest and the plan of a restore are written: readable for anybody who opens them, /// and for the runtime, which applies the plan. /// - public static readonly JsonSerializerOptions JSON_OPTIONS = new() + public static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, Converters = { new JsonStringEnumConverter() }, - }; + TypeInfoResolver = DataBackupJsonContext.Default, + }.AsReadOnly(); /// /// File types which are compressed already. Compressing them once more costs time and makes the diff --git a/app/MindWork AI Studio/Tools/DataBackup/DataBackupJsonContext.cs b/app/MindWork AI Studio/Tools/DataBackup/DataBackupJsonContext.cs new file mode 100644 index 000000000..1289f9818 --- /dev/null +++ b/app/MindWork AI Studio/Tools/DataBackup/DataBackupJsonContext.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace AIStudio.Tools.DataBackup; + +/// +/// The contracts with which System.Text.Json reads and writes the manifest of a backup and the plan and report of +/// a restore, built at compile time. +/// +[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] +[JsonSerializable(typeof(DataBackupManifest))] +[JsonSerializable(typeof(DataRestorePlan))] +[JsonSerializable(typeof(DataRestoreReport))] +internal sealed partial class DataBackupJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Databases/ProvenanceStore/ProvenanceJsonContext.cs b/app/MindWork AI Studio/Tools/Databases/ProvenanceStore/ProvenanceJsonContext.cs new file mode 100644 index 000000000..4831a6169 --- /dev/null +++ b/app/MindWork AI Studio/Tools/Databases/ProvenanceStore/ProvenanceJsonContext.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace AIStudio.Tools.Databases.ProvenanceStore; + +/// +/// The contracts with which System.Text.Json reads and writes the content around a passage, built at compile time. +/// +[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] +[JsonSerializable(typeof(IReadOnlyList))] +[JsonSerializable(typeof(List))] +internal sealed partial class ProvenanceJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Databases/ProvenanceStore/SqliteProvenanceStoreClientImplementation.cs b/app/MindWork AI Studio/Tools/Databases/ProvenanceStore/SqliteProvenanceStoreClientImplementation.cs index 7b677eb67..2414d0f25 100644 --- a/app/MindWork AI Studio/Tools/Databases/ProvenanceStore/SqliteProvenanceStoreClientImplementation.cs +++ b/app/MindWork AI Studio/Tools/Databases/ProvenanceStore/SqliteProvenanceStoreClientImplementation.cs @@ -39,7 +39,11 @@ public sealed class SqliteProvenanceStoreClientImplementation(string name, strin /// Writes the content around a passage the way it reads: the default encoder would turn every /// character outside ASCII into an escape, cf. ToolExecutionResult.MODEL_CONTENT_OPTIONS. /// - private static readonly JsonSerializerOptions JSON_OPTIONS = new() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; + internal static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + TypeInfoResolver = ProvenanceJsonContext.Default, + }.AsReadOnly(); private readonly string databasePath = databasePath; private readonly string lastCleanupPath = Path.Join(basePath, LAST_CLEANUP_FILENAME); @@ -351,7 +355,7 @@ public sealed class SqliteProvenanceStoreClientImplementation(string name, strin try { - return JsonSerializer.Deserialize>(row.SurroundingContent) ?? []; + return JsonSerializer.Deserialize>(row.SurroundingContent, JSON_OPTIONS) ?? []; } catch (JsonException exception) { diff --git a/app/MindWork AI Studio/Tools/JsonSerializerOptionsExtensions.cs b/app/MindWork AI Studio/Tools/JsonSerializerOptionsExtensions.cs new file mode 100644 index 000000000..d6b198651 --- /dev/null +++ b/app/MindWork AI Studio/Tools/JsonSerializerOptionsExtensions.cs @@ -0,0 +1,21 @@ +using System.Text.Json; + +namespace AIStudio.Tools; + +public static class JsonSerializerOptionsExtensions +{ + /// + /// Makes the options read-only and returns them, so that a static field gets its options in one expression. + /// + /// + /// Options with contracts of their own, i.e., with a TypeInfoResolver, should never change after they were + /// declared: whoever used them before such a change would have read or written with other contracts. + /// + /// The options, with their TypeInfoResolver set. + /// The same options, now read-only. + public static JsonSerializerOptions AsReadOnly(this JsonSerializerOptions options) + { + options.MakeReadOnly(); + return options; + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistantAudit.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistantAudit.cs index 6f46cc1ab..6048b2cd3 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistantAudit.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistantAudit.cs @@ -4,13 +4,13 @@ namespace AIStudio.Tools.PluginSystem.Assistants; public sealed class PluginAssistantAudit { - public Guid PluginId { get; init; } - public string PluginHash { get; init; } = string.Empty; + public Guid PluginId { get; set; } + public string PluginHash { get; set; } = string.Empty; public DateTimeOffset AuditedAtUtc { get; set; } public string AuditProviderId { get; set; } = string.Empty; public string AuditProviderName { get; set; } = string.Empty; - public AssistantAuditLevel Level { get; init; } = AssistantAuditLevel.UNKNOWN; - public string Summary { get; init; } = string.Empty; + public AssistantAuditLevel Level { get; set; } = AssistantAuditLevel.UNKNOWN; + public string Summary { get; set; } = string.Empty; public float Confidence { get; set; } public string PromptPreview { get; set; } = string.Empty; public List Findings { get; set; } = []; diff --git a/app/MindWork AI Studio/Tools/PluginSystem/ConfigurationBaseObject.cs b/app/MindWork AI Studio/Tools/PluginSystem/ConfigurationBaseObject.cs index 97d48d616..d23edf42b 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/ConfigurationBaseObject.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/ConfigurationBaseObject.cs @@ -5,19 +5,19 @@ public abstract record ConfigurationBaseObject : IConfigurationObject #region Implementation of IConfigurationObject /// - public abstract string Id { get; init; } + public abstract string Id { get; set; } /// - public abstract uint Num { get; init; } + public abstract uint Num { get; set; } /// - public abstract string Name { get; init; } + public abstract string Name { get; set; } /// - public abstract bool IsEnterpriseConfiguration { get; init; } + public abstract bool IsEnterpriseConfiguration { get; set; } /// - public abstract Guid EnterpriseConfigurationPluginId { get; init; } + public abstract Guid EnterpriseConfigurationPluginId { get; set; } #endregion } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/RAG/RetrievalImageContext.cs b/app/MindWork AI Studio/Tools/RAG/RetrievalImageContext.cs index 4d50e6975..c69cd2854 100644 --- a/app/MindWork AI Studio/Tools/RAG/RetrievalImageContext.cs +++ b/app/MindWork AI Studio/Tools/RAG/RetrievalImageContext.cs @@ -32,7 +32,7 @@ public sealed class RetrievalImageContext : IRetrievalContext, IImageSource /// /// Is the image source a URL, a local file path, a base64 string, etc.? /// - public required ContentImageSource SourceType { get; init; } + public required ContentImageSource SourceType { get; set; } /// /// The image source. diff --git a/app/MindWork AI Studio/Tools/RAG/SourcePassage.cs b/app/MindWork AI Studio/Tools/RAG/SourcePassage.cs index 30ed436af..76d8eae1a 100644 --- a/app/MindWork AI Studio/Tools/RAG/SourcePassage.cs +++ b/app/MindWork AI Studio/Tools/RAG/SourcePassage.cs @@ -25,27 +25,27 @@ public sealed record SourcePassage /// the passage. The list of sources tells sources apart by their address without the fragment, /// and so does the search for the passages of a source, see GetPassagesOf. /// - public string SourceUrl { get; init; } = string.Empty; + public string SourceUrl { get; set; } = string.Empty; /// /// The name of where the passage was found, e.g., a document or a database. /// - public string DataSourceName { get; init; } = string.Empty; + public string DataSourceName { get; set; } = string.Empty; /// /// The path to the content, e.g., a file path or a URL. /// - public string Path { get; init; } = string.Empty; + public string Path { get; set; } = string.Empty; /// /// The category of the content, like text or image. /// - public RetrievalContentCategory Category { get; init; } + public RetrievalContentCategory Category { get; set; } /// /// What type of content the passage is, like a document or a transcript. /// - public RetrievalContentType Type { get; init; } + public RetrievalContentType Type { get; set; } /// /// The entry of the provenance store which holds the matched text and the content around it, @@ -57,7 +57,7 @@ public sealed record SourcePassage /// stored, Text and SurroundingContent are empty. Left out of the chat file while null. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public Guid? ProvenanceId { get; init; } + public Guid? ProvenanceId { get; set; } /// /// The matched text, filtered for prompt injections, as long as it is not stored, see ProvenanceId. @@ -65,22 +65,22 @@ public sealed record SourcePassage /// /// Empty for an image which matched: the AI is only told that it did, never shown the image. /// - public string Text { get; init; } = string.Empty; + public string Text { get; set; } = string.Empty; /// /// The content around the matched text, filtered for prompt injections, as long as it is not stored, see ProvenanceId. /// - public IReadOnlyList SurroundingContent { get; init; } = []; + public IReadOnlyList SurroundingContent { get; set; } = []; /// /// The page the passage starts on, or null when it has none. /// - public int? PageNumber { get; init; } + public int? PageNumber { get; set; } /// /// The page the passage ends on, or null when it is not known. /// - public int? EndPageNumber { get; init; } + public int? EndPageNumber { get; set; } // // Where a passage without pages is. Left out of the chat file while unknown, which they are for @@ -91,31 +91,31 @@ public sealed record SourcePassage /// The sheet of a spreadsheet the passage is from, or null. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? SheetName { get; init; } + public string? SheetName { get; set; } /// /// The row the passage starts at, counted from one: a row of its sheet, or of a table such as a CSV file. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public int? StartRow { get; init; } + public int? StartRow { get; set; } /// /// The row the passage ends at, or null when it is not known. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public int? EndRow { get; init; } + public int? EndRow { get; set; } /// /// The line the passage starts at, counted from one, for text and source code, or null. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public int? StartLine { get; init; } + public int? StartLine { get; set; } /// /// The line the passage ends at, or null when it is not known. /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public int? EndLine { get; init; } + public int? EndLine { get; set; } /// /// Which part of its document the passage is, counted from one, or null when the data source @@ -126,5 +126,5 @@ public sealed record SourcePassage /// indexed before these were kept. Wherever they are known, they say it better, see /// SourcePassageExtensions.GetLocation. /// - public int? ChunkNumber { get; init; } + public int? ChunkNumber { get; set; } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/WorkspaceBehaviour.cs b/app/MindWork AI Studio/Tools/WorkspaceBehaviour.cs index f15c3cf6e..eedf5c70d 100644 --- a/app/MindWork AI Studio/Tools/WorkspaceBehaviour.cs +++ b/app/MindWork AI Studio/Tools/WorkspaceBehaviour.cs @@ -63,7 +63,7 @@ public static partial class WorkspaceBehaviour private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(WorkspaceBehaviour).Namespace, nameof(WorkspaceBehaviour)); - public static readonly JsonSerializerOptions JSON_OPTIONS = new() + public static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions { WriteIndented = true, AllowTrailingCommas = true, @@ -73,8 +73,9 @@ public static partial class WorkspaceBehaviour Converters = { new JsonStringEnumConverter(JsonNamingPolicy.SnakeCaseUpper), - } - }; + }, + TypeInfoResolver = ChatJsonContext.Default, + }.AsReadOnly(); private static readonly TimeSpan PREFETCH_DELAY_DURATION = TimeSpan.FromMilliseconds(45); diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.11.1.md b/app/MindWork AI Studio/wwwroot/changelog/v26.11.1.md index 63a2dd1d7..fc3e17dda 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v26.11.1.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v26.11.1.md @@ -3,6 +3,7 @@ - Improved the documentation for IT departments, which now lives in our [wiki](https://github.com/MindWorkAI/AI-Studio/wiki/Enterprise-IT). It is sorted by task, explains the setup on Windows, Linux, and macOS, and offers complete configurations for typical needs, such as using only local models. The link in the app settings leads there, and the wiki also holds the installation guide in English and German. - Improved the German translation throughout: we proofread all texts and corrected wrong labels as well as spelling and grammar mistakes. The texts now always address you formally and use the same terms everywhere. - Improved the security of the connection between the app window and AI Studio. +- Improved how reliably AI Studio reads and stores your settings, chats, and backups. - Changed the size of AI Studio on laptop screens: it now appears in the same size on every laptop, whatever the scaling of your operating system. If you prefer the previous size, choose "System scaling" for the interface size in the app settings. Contributed by Paul Koudelka. - Fixed AI Studio becoming unusable when an update failed after its download. The app now restarts with the version you have installed, so you can simply try the update again. - Fixed configuration plugins you installed yourself being able to change the tool settings of your organization, such as the minimum provider confidence of a tool. These settings now follow the same rules as all other settings, which also gives you back your own values once your organization removes its configuration. diff --git a/app/Tests/Assistants/SWOTAnalysis/SwotAnalysisContentPresentationTests.cs b/app/Tests/Assistants/SWOTAnalysis/SwotAnalysisContentPresentationTests.cs index a23143ecd..6b181006d 100644 --- a/app/Tests/Assistants/SWOTAnalysis/SwotAnalysisContentPresentationTests.cs +++ b/app/Tests/Assistants/SWOTAnalysis/SwotAnalysisContentPresentationTests.cs @@ -54,7 +54,7 @@ public sealed class SwotAnalysisContentPresentationTests var content = new ContentText { Text = "Fallback Markdown", - Presentation = ContentPresentation.Create(ContentPresentationKind.SWOT_ANALYSIS, "not a SWOT result"), + Presentation = ContentPresentation.Create(ContentPresentationKind.SWOT_ANALYSIS, "not a SWOT result", JsonSerializerOptions.Default), }; var presentationWasRead = SwotAnalysisContentPresentation.TryRead(content.Presentation, out _); diff --git a/app/Tests/Chat/ContentPresentationTests.cs b/app/Tests/Chat/ContentPresentationTests.cs index b67f450a4..dbff5558f 100644 --- a/app/Tests/Chat/ContentPresentationTests.cs +++ b/app/Tests/Chat/ContentPresentationTests.cs @@ -10,7 +10,7 @@ public sealed class ContentPresentationTests [Test] public void TheDataHashCodeFollowsNewData() { - var presentation = ContentPresentation.Create(ContentPresentationKind.SWOT_ANALYSIS, new { heading = "First" }); + var presentation = ContentPresentation.Create(ContentPresentationKind.SWOT_ANALYSIS, new { heading = "First" }, JsonSerializerOptions.Default); var firstHashCode = presentation.GetDataHashCode(); presentation.Data = JsonSerializer.SerializeToElement(new { heading = "Second" }); @@ -33,7 +33,7 @@ public sealed class ContentPresentationTests [Test] public void ACloneHasTheSameDataHashCode() { - var presentation = ContentPresentation.Create(ContentPresentationKind.SWOT_ANALYSIS, new { heading = "First" }); + var presentation = ContentPresentation.Create(ContentPresentationKind.SWOT_ANALYSIS, new { heading = "First" }, JsonSerializerOptions.Default); Assert.That(presentation.DeepClone().GetDataHashCode(), Is.EqualTo(presentation.GetDataHashCode())); } diff --git a/app/Tests/Serialization/Fixtures/chat.json b/app/Tests/Serialization/Fixtures/chat.json new file mode 100644 index 000000000..c2fdd2b39 --- /dev/null +++ b/app/Tests/Serialization/Fixtures/chat.json @@ -0,0 +1,442 @@ +{ + "chat_id": "00000000-0000-4000-8000-000000000001", + "workspace_id": "00000000-0000-4000-8000-000000000002", + "last_media_transcript_number": 9000000000, + "draft": { + "text": "text sample 3", + "file_attachments": [ + { + "$type": "file", + "type": "IMAGE", + "file_name": "FileName sample 4", + "file_path": "/samples/FilePath-5.txt", + "file_size_bytes": 4000000006, + "is_forbidden": false, + "is_valid": true, + "is_image": true, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-5.txt", + "provenance_id": "00000000-0000-4000-8000-000000000007", + "version": { + "last_write_utc": "2026-10-29T11:08:00Z", + "length": 4000000009 + } + }, + { + "$type": "image", + "source_type": "LOCAL_PATH", + "type": "IMAGE", + "file_name": "FileName sample 10", + "file_path": "/samples/FilePath-11.txt", + "file_size_bytes": 4000000012, + "is_forbidden": false, + "is_valid": true, + "is_image": true, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-11.txt", + "provenance_id": "00000000-0000-4000-8000-000000000013", + "version": { + "last_write_utc": "2026-10-29T11:14:00Z", + "length": 4000000015 + } + }, + { + "$type": "managed_transcript", + "original_file_name": "OriginalFileName sample 19", + "is_staged": true, + "type": "IMAGE", + "file_name": "FileName sample 16", + "file_path": "/samples/FilePath-17.txt", + "file_size_bytes": 4000000018, + "is_forbidden": false, + "is_valid": true, + "is_image": false, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-17.txt", + "provenance_id": "00000000-0000-4000-8000-000000000020", + "version": { + "last_write_utc": "2026-10-29T11:21:00Z", + "length": 4000000022 + } + }, + { + "$type": "managed_transcript", + "original_file_name": "OriginalFileName sample 34", + "is_staged": true, + "type": "IMAGE", + "file_name": "FileName sample 31", + "file_path": "/samples/FilePath-32.txt", + "file_size_bytes": 4000000033, + "is_forbidden": false, + "is_valid": true, + "is_image": false, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-32.txt", + "provenance_id": "00000000-0000-4000-8000-000000000035", + "version": { + "last_write_utc": "2026-10-29T11:36:00Z", + "length": 4000000037 + } + }, + { + "$type": "managed_transcript", + "original_file_name": "OriginalFileName sample 41", + "is_staged": true, + "type": "IMAGE", + "file_name": "FileName sample 38", + "file_path": "/samples/FilePath-39.txt", + "file_size_bytes": 4000000040, + "is_forbidden": false, + "is_valid": true, + "is_image": false, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-39.txt", + "provenance_id": "00000000-0000-4000-8000-000000000042", + "version": { + "last_write_utc": "2026-10-29T11:43:00Z", + "length": 4000000044 + } + } + ], + "previewed_texts": [ + { + "file_path": "/samples/FilePath-23.txt", + "provenance_id": "00000000-0000-4000-8000-000000000024", + "version": { + "last_write_utc": "2026-10-29T11:25:00Z", + "length": 4000000026 + } + }, + { + "file_path": "/samples/FilePath-27.txt", + "provenance_id": "00000000-0000-4000-8000-000000000028", + "version": { + "last_write_utc": "2026-10-29T11:29:00Z", + "length": 4000000030 + } + } + ] + }, + "selected_provider": "selected_provider sample 45", + "selected_profile": "selected_profile sample 46", + "selected_chat_template": "selected_chat_template sample 47", + "selected_tool_ids": [ + "selected_tool_ids sample 48", + "selected_tool_ids sample 49" + ], + "include_date_time": true, + "data_source_options": { + "disable_data_sources": true, + "automatic_data_source_selection": true, + "automatic_validation": true, + "retrieval_mode": "EVERY_MESSAGE", + "preselected_data_source_ids": [ + "preselected_data_source_ids sample 50", + "preselected_data_source_ids sample 51" + ] + }, + "ai_selected_data_sources": [ + { + "data_source": { + "$type_discriminator": "LOCAL_DIRECTORY", + "num": 7, + "id": "id sample 52", + "name": "name sample 53", + "description": "description sample 54", + "type": "LOCAL_FILE", + "embedding_id": "embedding_id sample 55", + "max_chunk_token_length": 46, + "chunk_overlap_token_length": 47, + "confidence_level": "UNTRUSTED", + "is_enterprise_configuration": true, + "enterprise_configuration_plugin_id": "00000000-0000-4000-8000-000000000058", + "max_matches": 8, + "outbound_data_restriction": "ONLY_LINKS_FROM_CHAT", + "path": "/samples/path-59.txt", + "file_patterns": "file_patterns sample 60", + "exclude_patterns": "exclude_patterns sample 61", + "max_file_size_megabytes": 52, + "include_subdirectories": true, + "include_hidden_files_and_folders": true + }, + "ai_decision": { + "id": "id sample 63", + "reason": "reason sample 64", + "confidence": 2.25 + }, + "selected": true + }, + { + "data_source": { + "$type_discriminator": "LOCAL_FILE", + "num": 7, + "id": "id sample 65", + "name": "name sample 66", + "description": "description sample 67", + "type": "LOCAL_FILE", + "embedding_id": "embedding_id sample 68", + "max_chunk_token_length": 59, + "chunk_overlap_token_length": 60, + "confidence_level": "UNTRUSTED", + "is_enterprise_configuration": true, + "enterprise_configuration_plugin_id": "00000000-0000-4000-8000-000000000071", + "max_matches": 8, + "outbound_data_restriction": "ONLY_LINKS_FROM_CHAT", + "file_path": "/samples/file_path-72.txt" + }, + "ai_decision": { + "id": "id sample 73", + "reason": "reason sample 74", + "confidence": 2.25 + }, + "selected": true + } + ], + "data_security": "ALLOW_ANY", + "required_provider_confidence": "UNTRUSTED", + "required_outbound_data_restriction": { + "restriction": "ONLY_LINKS_FROM_CHAT", + "origin": "MAILBOX", + "reference": "Reference sample 75" + }, + "web_addresses_written_by_models": [ + "web_addresses_written_by_models sample 77", + "web_addresses_written_by_models sample 76" + ], + "name": "name sample 78", + "system_prompt": "system_prompt sample 79", + "blocks": [ + { + "time": "2026-10-29T13:50:15+01:00", + "content_type": "UNKNOWN", + "content": { + "$type": "text", + "sources": [ + { + "title": "Title sample 81", + "url": "URL sample 82", + "origin": "RAG" + }, + { + "title": "Title sample 83", + "url": "URL sample 84", + "origin": "RAG" + } + ], + "file_attachments": [ + { + "$type": "file", + "type": "IMAGE", + "file_name": "FileName sample 85", + "file_path": "/samples/FilePath-86.txt", + "file_size_bytes": 4000000087, + "is_forbidden": false, + "is_valid": true, + "is_image": true, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-86.txt", + "provenance_id": "00000000-0000-4000-8000-000000000088", + "version": { + "last_write_utc": "2026-10-29T12:29:00Z", + "length": 4000000090 + } + }, + { + "$type": "image", + "source_type": "LOCAL_PATH", + "type": "IMAGE", + "file_name": "FileName sample 91", + "file_path": "/samples/FilePath-92.txt", + "file_size_bytes": 4000000093, + "is_forbidden": false, + "is_valid": true, + "is_image": true, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-92.txt", + "provenance_id": "00000000-0000-4000-8000-000000000094", + "version": { + "last_write_utc": "2026-10-29T12:35:00Z", + "length": 4000000096 + } + }, + { + "$type": "managed_transcript", + "original_file_name": "OriginalFileName sample 100", + "is_staged": true, + "type": "IMAGE", + "file_name": "FileName sample 97", + "file_path": "/samples/FilePath-98.txt", + "file_size_bytes": 4000000099, + "is_forbidden": false, + "is_valid": true, + "is_image": false, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-98.txt", + "provenance_id": "00000000-0000-4000-8000-000000000101", + "version": { + "last_write_utc": "2026-10-29T12:42:00Z", + "length": 4000000103 + } + } + ], + "tool_invocations": [ + { + "order": 44, + "tool_id": "tool_id sample 105", + "tool_name": "tool_name sample 106", + "tool_icon": "tool_icon sample 107", + "tool_call_id": "tool_call_id sample 108", + "status": "SUCCESS", + "was_executed": true, + "status_message": "status_message sample 109", + "arguments": { + "argumentsKey sample 110": "arguments sample 111", + "argumentsKey sample 112": "arguments sample 113" + } + }, + { + "order": 54, + "tool_id": "tool_id sample 115", + "tool_name": "tool_name sample 116", + "tool_icon": "tool_icon sample 117", + "tool_call_id": "tool_call_id sample 118", + "status": "SUCCESS", + "was_executed": true, + "status_message": "status_message sample 119", + "arguments": { + "argumentsKey sample 120": "arguments sample 121", + "argumentsKey sample 122": "arguments sample 123" + } + } + ], + "presentation": { + "kind": "SWOT_ANALYSIS", + "data": { + "sample": 124, + "text": "Gr\u00F6\u00DFe" + } + }, + "source_passages": [ + { + "source_url": "source_url sample 125", + "data_source_name": "data_source_name sample 126", + "path": "/samples/path-127.txt", + "category": "UNKNOWN", + "type": "UNKNOWN", + "provenance_id": "00000000-0000-4000-8000-000000000128", + "text": "text sample 129", + "surrounding_content": [ + "surrounding_content sample 130", + "surrounding_content sample 131" + ], + "page_number": 72, + "end_page_number": 73, + "sheet_name": "sheet_name sample 134", + "start_row": 75, + "end_row": 76, + "start_line": 77, + "end_line": 78, + "chunk_number": 79 + }, + { + "source_url": "source_url sample 140", + "data_source_name": "data_source_name sample 141", + "path": "/samples/path-142.txt", + "category": "UNKNOWN", + "type": "UNKNOWN", + "provenance_id": "00000000-0000-4000-8000-000000000143", + "text": "text sample 144", + "surrounding_content": [ + "surrounding_content sample 145", + "surrounding_content sample 146" + ], + "page_number": 87, + "end_page_number": 88, + "sheet_name": "sheet_name sample 149", + "start_row": 40, + "end_row": 41, + "start_line": 42, + "end_line": 43, + "chunk_number": 44 + } + ], + "reported_usage": { + "prompt_tokens": 45, + "model_id": "model_id sample 156", + "block_count": 47 + }, + "text": "text sample 158" + }, + "role": "UNKNOWN", + "hide_from_user": true + }, + { + "time": "2026-10-29T15:09:15+01:00", + "content_type": "UNKNOWN", + "content": { + "$type": "image", + "sources": [ + { + "title": "Title sample 160", + "url": "URL sample 161", + "origin": "RAG" + }, + { + "title": "Title sample 162", + "url": "URL sample 163", + "origin": "RAG" + } + ], + "file_attachments": [ + { + "$type": "file", + "type": "IMAGE", + "file_name": "FileName sample 164", + "file_path": "/samples/FilePath-165.txt", + "file_size_bytes": 4000000166, + "is_forbidden": false, + "is_valid": true, + "is_image": true, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-165.txt", + "provenance_id": "00000000-0000-4000-8000-000000000167", + "version": { + "last_write_utc": "2026-10-29T13:48:00Z", + "length": 4000000169 + } + }, + { + "$type": "image", + "source_type": "LOCAL_PATH", + "type": "IMAGE", + "file_name": "FileName sample 170", + "file_path": "/samples/FilePath-171.txt", + "file_size_bytes": 4000000172, + "is_forbidden": false, + "is_valid": true, + "is_image": true, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-171.txt", + "provenance_id": "00000000-0000-4000-8000-000000000173", + "version": { + "last_write_utc": "2026-10-29T13:54:00Z", + "length": 4000000175 + } + }, + { + "$type": "managed_transcript", + "original_file_name": "OriginalFileName sample 179", + "is_staged": true, + "type": "IMAGE", + "file_name": "FileName sample 176", + "file_path": "/samples/FilePath-177.txt", + "file_size_bytes": 4000000178, + "is_forbidden": false, + "is_valid": true, + "is_image": false, + "file_path_as_url": "/local/file?path=%2Fsamples%2FFilePath-177.txt", + "provenance_id": "00000000-0000-4000-8000-000000000180", + "version": { + "last_write_utc": "2026-10-29T14:01:00Z", + "length": 4000000182 + } + } + ], + "source_type": "LOCAL_PATH", + "source": "source sample 183" + }, + "role": "UNKNOWN", + "hide_from_user": true + } + ] +} \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/data-backup-manifest.json b/app/Tests/Serialization/Fixtures/data-backup-manifest.json new file mode 100644 index 000000000..0bb6ba6d6 --- /dev/null +++ b/app/Tests/Serialization/Fixtures/data-backup-manifest.json @@ -0,0 +1,21 @@ +{ + "format_version": 41, + "app_version": "AppVersion sample 2", + "settings_version": "SettingsVersion sample 3", + "created_at_utc": "2026-10-29T12:34:15+01:00", + "source_data_directory": "SourceDataDirectory sample 5", + "source_config_directory": "SourceConfigDirectory sample 6", + "includes_data_source_indexes": true, + "entries": [ + { + "name": "Name sample 7", + "size": 4000000008, + "sha256": "Sha256 sample 9" + }, + { + "name": "Name sample 10", + "size": 4000000011, + "sha256": "Sha256 sample 12" + } + ] +} \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/data-restore-plan.json b/app/Tests/Serialization/Fixtures/data-restore-plan.json new file mode 100644 index 000000000..5bd50c119 --- /dev/null +++ b/app/Tests/Serialization/Fixtures/data-restore-plan.json @@ -0,0 +1,24 @@ +{ + "format_version": 41, + "prepared_at_utc": "2026-10-29T12:32:15+01:00", + "backup_created_at_utc": "2026-10-29T12:33:15+01:00", + "backup_app_version": "BackupAppVersion sample 4", + "elements": [ + { + "location": "CONFIG_DIRECTORY", + "relative_path": "/samples/RelativePath-5.txt", + "keep": [ + "Keep sample 6", + "Keep sample 7" + ] + }, + { + "location": "CONFIG_DIRECTORY", + "relative_path": "/samples/RelativePath-8.txt", + "keep": [ + "Keep sample 9", + "Keep sample 10" + ] + } + ] +} \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/data-restore-report.json b/app/Tests/Serialization/Fixtures/data-restore-report.json new file mode 100644 index 000000000..b2b8530aa --- /dev/null +++ b/app/Tests/Serialization/Fixtures/data-restore-report.json @@ -0,0 +1,8 @@ +{ + "outcome": "REFUSED", + "detail": "Detail sample 1", + "prepared_at_utc": "PreparedAtUtc sample 2", + "backup_created_at_utc": "BackupCreatedAtUtc sample 3", + "backup_app_version": "BackupAppVersion sample 4", + "finished_at_utc": "FinishedAtUtc sample 5" +} \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/provenance-surrounding-content.json b/app/Tests/Serialization/Fixtures/provenance-surrounding-content.json new file mode 100644 index 000000000..89358c4dd --- /dev/null +++ b/app/Tests/Serialization/Fixtures/provenance-surrounding-content.json @@ -0,0 +1 @@ +["Vor dem Treffer: Größe & \"Zitat\" für C# 14","Nach dem Treffer: 50 % – fertig, 'oder?'"] \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/settings-v1.json b/app/Tests/Serialization/Fixtures/settings-v1.json new file mode 100644 index 000000000..563d99c2c --- /dev/null +++ b/app/Tests/Serialization/Fixtures/settings-v1.json @@ -0,0 +1,71 @@ +{ + "Version": "V1", + "Providers": [ + { + "Num": 7, + "Id": "Id sample 1", + "InstanceName": "InstanceName sample 2", + "UsedProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 3", + "DisplayName": "DisplayName sample 4", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "Hostname": "Hostname sample 5", + "Host": "LM_STUDIO" + }, + { + "Num": 7, + "Id": "Id sample 6", + "InstanceName": "InstanceName sample 7", + "UsedProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 8", + "DisplayName": "DisplayName sample 9", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "Hostname": "Hostname sample 10", + "Host": "LM_STUDIO" + } + ], + "NextProviderNum": 7, + "IsSavingEnergy": true, + "EnableSpellchecking": true, + "UpdateInterval": "DISABLE_UPDATES", + "NavigationBehavior": "NEVER_EXPAND_USE_TOOLTIPS", + "ShortcutSendBehavior": "MODIFER_ENTER_IS_SENDING", + "PreselectChatOptions": true, + "PreselectedChatProvider": "PreselectedChatProvider sample 11", + "WorkspaceStorageBehavior": "STORE_CHATS_AUTOMATICALLY", + "WorkspaceStorageTemporaryMaintenancePolicy": "DELETE_OLDER_THAN_7_DAYS", + "PreselectIconOptions": true, + "PreselectedIconSource": "ICONS8", + "PreselectedIconProvider": "PreselectedIconProvider sample 12", + "LiveTranslationDebounceIntervalMilliseconds": 53, + "PreselectTranslationOptions": true, + "PreselectLiveTranslation": true, + "HideWebContentReaderForTranslation": true, + "PreselectWebContentReaderForTranslation": true, + "PreselectContentCleanerAgentForTranslation": true, + "PreselectedTranslationTargetLanguage": "EN_US", + "PreselectTranslationOtherLanguage": "PreselectTranslationOtherLanguage sample 14", + "PreselectedTranslationProvider": "PreselectedTranslationProvider sample 15", + "PreselectCodingOptions": true, + "PreselectCodingCompilerMessages": true, + "PreselectedCodingLanguage": "BASH", + "PreselectedCodingOtherLanguage": "PreselectedCodingOtherLanguage sample 16", + "PreselectedCodingProvider": "PreselectedCodingProvider sample 17", + "PreselectTextSummarizerOptions": true, + "HideWebContentReaderForTextSummarizer": true, + "PreselectWebContentReaderForTextSummarizer": true, + "PreselectContentCleanerAgentForTextSummarizer": true, + "PreselectedTextSummarizerTargetLanguage": "EN_US", + "PreselectedTextSummarizerOtherLanguage": "PreselectedTextSummarizerOtherLanguage sample 18", + "PreselectedTextSummarizerComplexity": "SIMPLE_LANGUAGE", + "PreselectedTextSummarizerExpertInField": "PreselectedTextSummarizerExpertInField sample 19", + "PreselectedTextSummarizerProvider": "PreselectedTextSummarizerProvider sample 20", + "PreselectAgentTextContentCleanerOptions": true, + "PreselectedAgentTextContentCleanerProvider": "PreselectedAgentTextContentCleanerProvider sample 21" +} \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/settings-v4.json b/app/Tests/Serialization/Fixtures/settings-v4.json new file mode 100644 index 000000000..282da8c5c --- /dev/null +++ b/app/Tests/Serialization/Fixtures/settings-v4.json @@ -0,0 +1,305 @@ +{ + "Version": "V4", + "Providers": [ + { + "Num": 7, + "Id": "Id sample 1", + "InstanceName": "InstanceName sample 2", + "UsedProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 3", + "DisplayName": "DisplayName sample 4", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "Hostname": "Hostname sample 5", + "Host": "LM_STUDIO" + }, + { + "Num": 7, + "Id": "Id sample 6", + "InstanceName": "InstanceName sample 7", + "UsedProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 8", + "DisplayName": "DisplayName sample 9", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "Hostname": "Hostname sample 10", + "Host": "LM_STUDIO" + } + ], + "LLMProviders": { + "EnforceGlobalMinimumConfidence": true, + "GlobalMinimumConfidence": "UNTRUSTED", + "ShowProviderConfidence": true, + "ConfidenceScheme": "TRUST_USA_EUROPE", + "CustomConfidenceScheme": { + "OPEN_AI": "UNTRUSTED", + "ANTHROPIC": "UNKNOWN" + } + }, + "Profiles": [ + { + "Num": 7, + "Id": "Id sample 11", + "Name": "Name sample 12", + "NeedToKnow": "NeedToKnow sample 13", + "Actions": "Actions sample 14", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000015" + }, + { + "Num": 7, + "Id": "Id sample 16", + "Name": "Name sample 17", + "NeedToKnow": "NeedToKnow sample 18", + "Actions": "Actions sample 19", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000020" + } + ], + "NextProviderNum": 7, + "NextProfileNum": 7, + "App": { + "LanguageBehavior": "MANUAL", + "LanguagePluginId": "00000000-0000-4000-8000-000000000021", + "PreferredTheme": "LIGHT", + "IsSavingEnergy": true, + "EnableSpellchecking": true, + "CloseExportMessagesAutomatically": true, + "UpdateInterval": "DISABLE_UPDATES", + "UpdateInstallation": "AUTOMATIC", + "NavigationBehavior": "NEVER_EXPAND_USE_TOOLTIPS", + "StartPage": "CHAT", + "InterfaceScreenSizes": [ + { + "ScreenKey": "ScreenKey sample 22", + "Size": "SYSTEM_SCALING", + "CustomPercent": 63 + }, + { + "ScreenKey": "ScreenKey sample 24", + "Size": "SYSTEM_SCALING", + "CustomPercent": 65 + } + ], + "ShowPromptInjectionAlert": true, + "ShowIntroduction": true, + "ShowQuickStartGuide": true, + "ShowLastChangelog": true, + "ShowVision": true, + "PreviewVisibility": "RELEASE_CANDIDATE", + "EnabledPreviewFeatures": [ + "PRE_WRITER_MODE_2024", + "PRE_RAG_2024" + ], + "PreselectedProvider": "PreselectedProvider sample 26", + "PreselectedProfile": "PreselectedProfile sample 27", + "PreselectedChatTemplate": "PreselectedChatTemplate sample 28", + "UseTranscriptionProvider": "UseTranscriptionProvider sample 29", + "OpusBitrate": "KBPS_32", + "ShortcutVoiceRecording": "ShortcutVoiceRecording sample 30", + "ShortcutVoiceRecordingDisplayName": "ShortcutVoiceRecordingDisplayName sample 31", + "ShortcutVoiceRecordingDisplaySource": "ShortcutVoiceRecordingDisplaySource sample 32", + "HttpClientTimeoutSeconds": 73, + "ShareFeatureUsageWithSelfHostedServerOperators": true, + "ExternalHttpCustomRootCertificatesEnabled": true, + "ExternalHttpCustomRootCertificateBundlePath": "/samples/ExternalHttpCustomRootCertificateBundlePath-34.txt", + "ExternalHttpCustomRootCertificateAllowedHosts": [ + "ExternalHttpCustomRootCertificateAllowedHosts sample 35", + "ExternalHttpCustomRootCertificateAllowedHosts sample 36" + ], + "AllowUserToAddProvider": true, + "AllowUserToAddLLMProvider": true, + "AllowUserToAddEmbeddingProvider": true, + "AllowUserToAddTranscriptionProvider": true, + "ProhibitedProviders": [ + "OPEN_AI", + "ANTHROPIC" + ], + "AllowUserToAddMailbox": true, + "AllowUserToImportConfigurationSnippets": true, + "AllowUserToImportProfile": true, + "AllowUserToImportLLMProvider": true, + "AllowUserToImportEmbeddingProvider": true, + "AllowUserToImportTranscriptionProvider": true, + "AllowUserToImportChatTemplate": true, + "AllowUserToImportERIDataSource": true, + "AllowUserToImportDocumentAnalysisPolicy": true, + "AllowUserToImportPlugins": true, + "AllowUserToImportConfigurationPlugins": true, + "AllowUserToSharePlugins": true, + "AllowUserToBackUpData": true, + "AllowUserToRestoreData": true, + "ShowAdminSettings": true, + "EnterpriseEncryptionExportFormat": "V2", + "AddAIGeneratedContentDisclosure": true, + "DataSourceIndexing": { + "AutomaticRefresh": true, + "MinimumPauseBetweenStepsSeconds": 77, + "MaximumPauseBetweenStepsSeconds": 78 + }, + "HiddenAssistants": [ + "UNKNOWN", + "GRAMMAR_SPELLING_ASSISTANT" + ] + }, + "Chat": { + "ShortcutSendBehavior": "MODIFER_ENTER_IS_SENDING", + "LoadingProviderBehavior": "ALWAYS_USE_DEFAULT_CHAT_PROVIDER", + "AddChatProviderBehavior": "ADDED_CHATS_USE_LATEST_PROVIDER", + "SendToChatDataSourceBehavior": "APPLY_STANDARD_CHAT_DATA_SOURCE_OPTIONS", + "PreselectOptions": true, + "PreselectedProvider": "PreselectedProvider sample 39", + "PreselectedProfile": "PreselectedProfile sample 40", + "PreselectedChatTemplate": "PreselectedChatTemplate sample 41", + "PreselectedDataSourcesDisabled": true, + "PreselectedDataSourcesAutomaticSelection": true, + "PreselectedDataSourcesAutomaticValidation": true, + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 44", + "PreselectedDataSourceIds sample 45" + ], + "PreselectedDataSourcesRetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceOptions": { + "DisableDataSources": true, + "AutomaticDataSourceSelection": true, + "AutomaticValidation": true, + "RetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 44", + "PreselectedDataSourceIds sample 45" + ] + }, + "ShowLatestMessageAfterLoading": true, + "ReadChangedAttachmentsAgain": true + }, + "Workspace": { + "StorageBehavior": "STORE_CHATS_AUTOMATICALLY", + "StorageTemporaryMaintenancePolicy": "DELETE_OLDER_THAN_7_DAYS", + "DisplayBehavior": "TOGGLE_SIDEBAR", + "IsSidebarVisible": true, + "SplitterPosition": 1.5 + }, + "IconFinder": { + "PreselectOptions": true, + "PreselectedSource": "ICONS8", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 46" + }, + "Translation": { + "DebounceIntervalMilliseconds": 87, + "PreselectOptions": true, + "PreselectLiveTranslation": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 48", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 49" + }, + "Coding": { + "PreselectOptions": true, + "PreselectCompilerMessages": true, + "PreselectedProgrammingLanguage": "BASH", + "PreselectedOtherProgrammingLanguage": "PreselectedOtherProgrammingLanguage sample 50", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 51", + "PreselectedProfile": "PreselectedProfile sample 52" + }, + "TextSummarizer": { + "PreselectOptions": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 53", + "PreselectedComplexity": "SIMPLE_LANGUAGE", + "PreselectedExpertInField": "PreselectedExpertInField sample 54", + "PreselectedImportantAspects": "PreselectedImportantAspects sample 55", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 56" + }, + "TextContentCleaner": { + "PreselectAgentOptions": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 57" + }, + "Agenda": { + "PreselectOptions": true, + "PreselectName": "PreselectName sample 58", + "PreselectTopic": "PreselectTopic sample 59", + "PreselectObjective": "PreselectObjective sample 60", + "PreselectModerator": "PreselectModerator sample 61", + "PreselectDuration": "PreselectDuration sample 62", + "PreselectStartTime": "PreselectStartTime sample 63", + "PreselectIntroduceParticipants": true, + "PreselectNumberParticipants": "PEER_TO_PEER", + "PreselectActiveParticipation": true, + "PreselectIsMeetingVirtual": true, + "PreselectLocation": "PreselectLocation sample 64", + "PreselectJointDinner": true, + "PreselectSocialActivity": true, + "PreselectArriveAndDepart": true, + "PreselectLunchTime": 55, + "PreselectBreakTime": 56, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 67", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 68", + "PreselectedProfile": "PreselectedProfile sample 69" + }, + "GrammarSpelling": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 70", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 71" + }, + "RewriteImprove": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 72", + "PreselectedWritingStyle": "EVERYDAY", + "PreselectedSentenceStructure": "ACTIVE", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 73" + }, + "EMail": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 74", + "PreselectedWritingStyle": "BUSINESS_FORMAL", + "Greeting": "Greeting sample 75", + "SenderName": "SenderName sample 76", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 77", + "PreselectedProfile": "PreselectedProfile sample 78" + }, + "LegalCheck": { + "PreselectOptions": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 79", + "PreselectedProfile": "PreselectedProfile sample 80" + }, + "Synonyms": { + "PreselectOptions": true, + "PreselectedLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 81", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 82" + }, + "MyTasks": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 83", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 84", + "PreselectedProfile": "PreselectedProfile sample 85" + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/settings-v5.json b/app/Tests/Serialization/Fixtures/settings-v5.json new file mode 100644 index 000000000..328838924 --- /dev/null +++ b/app/Tests/Serialization/Fixtures/settings-v5.json @@ -0,0 +1,1583 @@ +{ + "Version": "V5", + "Providers": [ + { + "Num": 7, + "Id": "Id sample 1", + "InstanceName": "Name sample 13", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 3", + "DisplayName": "DisplayName sample 4", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000005", + "Hostname": "Hostname sample 6", + "Host": "LM_STUDIO", + "HFInferenceProvider": "AUTOMATIC", + "AdditionalJsonApiParameters": "AdditionalJsonApiParameters sample 7", + "TokenizerPath": "/samples/TokenizerPath-8.txt", + "CapabilityOverrides": { + "AUDIO_INPUT": true, + "FUNCTION_CALLING": true, + "MULTIPLE_IMAGE_INPUT": true, + "SPEECH_INPUT": true, + "VIDEO_INPUT": true, + "OPTIONAL_REASONING": true, + "ALWAYS_REASONING": true, + "REASONING_BY_DEFAULT": true, + "CONTEXT_WINDOW": 49, + "MAX_IMAGES_PER_MESSAGE": 50, + "MAX_IMAGES_PER_REQUEST": 51 + }, + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 12", + "PromptForUserAPIKey": true, + "Name": "Name sample 13" + }, + { + "Num": 7, + "Id": "Id sample 14", + "InstanceName": "Name sample 26", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 16", + "DisplayName": "DisplayName sample 17", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000018", + "Hostname": "Hostname sample 19", + "Host": "LM_STUDIO", + "HFInferenceProvider": "AUTOMATIC", + "AdditionalJsonApiParameters": "AdditionalJsonApiParameters sample 20", + "TokenizerPath": "/samples/TokenizerPath-21.txt", + "CapabilityOverrides": { + "AUDIO_INPUT": true, + "FUNCTION_CALLING": true, + "MULTIPLE_IMAGE_INPUT": true, + "SPEECH_INPUT": true, + "VIDEO_INPUT": true, + "OPTIONAL_REASONING": true, + "ALWAYS_REASONING": true, + "REASONING_BY_DEFAULT": true, + "CONTEXT_WINDOW": 62, + "MAX_IMAGES_PER_MESSAGE": 63, + "MAX_IMAGES_PER_REQUEST": 64 + }, + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 25", + "PromptForUserAPIKey": true, + "Name": "Name sample 26" + } + ], + "LLMProviders": { + "EnforceGlobalMinimumConfidence": true, + "GlobalMinimumConfidence": "UNTRUSTED", + "ShowProviderConfidence": true, + "ConfidenceScheme": "TRUST_USA_EUROPE", + "CustomConfidenceScheme": { + "OPEN_AI": "UNTRUSTED", + "ANTHROPIC": "UNKNOWN" + } + }, + "EmbeddingProviders": [ + { + "Num": 7, + "Id": "Id sample 27", + "Name": "Name sample 28", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 29", + "DisplayName": "DisplayName sample 30", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000031", + "Hostname": "Hostname sample 32", + "Host": "LM_STUDIO", + "TokenizerPath": "/samples/TokenizerPath-33.txt", + "TokenizerFingerprint": "TokenizerFingerprint sample 34", + "EmbeddingBatchSize": 75, + "TokenLimit": 76, + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 37", + "HFInferenceProvider": "AUTOMATIC", + "PromptForUserAPIKey": true + }, + { + "Num": 7, + "Id": "Id sample 38", + "Name": "Name sample 39", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 40", + "DisplayName": "DisplayName sample 41", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000042", + "Hostname": "Hostname sample 43", + "Host": "LM_STUDIO", + "TokenizerPath": "/samples/TokenizerPath-44.txt", + "TokenizerFingerprint": "TokenizerFingerprint sample 45", + "EmbeddingBatchSize": 86, + "TokenLimit": 87, + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 48", + "HFInferenceProvider": "AUTOMATIC", + "PromptForUserAPIKey": true + } + ], + "TranscriptionProviders": [ + { + "Num": 7, + "Id": "Id sample 49", + "Name": "Name sample 50", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 51", + "DisplayName": "DisplayName sample 52", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000053", + "Hostname": "Hostname sample 54", + "Host": "LM_STUDIO", + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 55", + "HFInferenceProvider": "AUTOMATIC", + "PromptForUserAPIKey": true + }, + { + "Num": 7, + "Id": "Id sample 56", + "Name": "Name sample 57", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 58", + "DisplayName": "DisplayName sample 59", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000060", + "Hostname": "Hostname sample 61", + "Host": "LM_STUDIO", + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 62", + "HFInferenceProvider": "AUTOMATIC", + "PromptForUserAPIKey": true + } + ], + "DataSources": [ + { + "$type_discriminator": "LOCAL_DIRECTORY", + "Num": 7, + "Id": "Id sample 63", + "Name": "Name sample 64", + "Description": "Description sample 65", + "Type": "LOCAL_FILE", + "EmbeddingId": "EmbeddingId sample 66", + "MaxChunkTokenLength": 57, + "ChunkOverlapTokenLength": 58, + "ConfidenceLevel": "UNTRUSTED", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000069", + "MaxMatches": 8, + "OutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "Path": "/samples/Path-70.txt", + "FilePatterns": "FilePatterns sample 71", + "ExcludePatterns": "ExcludePatterns sample 72", + "MaxFileSizeMegabytes": 63, + "IncludeSubdirectories": true, + "IncludeHiddenFilesAndFolders": true + }, + { + "$type_discriminator": "LOCAL_FILE", + "Num": 7, + "Id": "Id sample 74", + "Name": "Name sample 75", + "Description": "Description sample 76", + "Type": "LOCAL_FILE", + "EmbeddingId": "EmbeddingId sample 77", + "MaxChunkTokenLength": 68, + "ChunkOverlapTokenLength": 69, + "ConfidenceLevel": "UNTRUSTED", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000080", + "MaxMatches": 8, + "OutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "FilePath": "/samples/FilePath-81.txt" + }, + { + "$type_discriminator": "ERI_V1", + "Num": 7, + "Id": "Id sample 82", + "Name": "Name sample 83", + "Type": "LOCAL_FILE", + "Hostname": "Hostname sample 84", + "Port": 75, + "AuthMethod": "KERBEROS", + "Username": "Username sample 86", + "UsernamePasswordMode": "SHARED_USERNAME_AND_PASSWORD", + "SecurityPolicy": "ALLOW_ANY", + "OutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000087", + "Version": "V1", + "SelectedRetrievalId": "SelectedRetrievalId sample 88", + "MaxMatches": 8 + } + ], + "Profiles": [ + { + "Num": 7, + "Id": "Id sample 89", + "Name": "Name sample 90", + "NeedToKnow": "NeedToKnow sample 91", + "Actions": "Actions sample 92", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000093" + }, + { + "Num": 7, + "Id": "Id sample 94", + "Name": "Name sample 95", + "NeedToKnow": "NeedToKnow sample 96", + "Actions": "Actions sample 97", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000098" + } + ], + "ChatTemplates": [ + { + "Num": 7, + "Id": "Id sample 99", + "Name": "Name sample 100", + "SystemPrompt": "SystemPrompt sample 101", + "PredefinedUserPrompt": "PredefinedUserPrompt sample 102", + "ExampleConversation": [ + { + "Time": "2026-10-29T14:13:15+01:00", + "ContentType": "UNKNOWN", + "Content": { + "$type": "text", + "Sources": [ + { + "Title": "Title sample 104", + "URL": "URL sample 105", + "Origin": "RAG" + }, + { + "Title": "Title sample 106", + "URL": "URL sample 107", + "Origin": "RAG" + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 108", + "FilePath": "/samples/FilePath-109.txt", + "FileSizeBytes": 4000000110, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-109.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000111", + "Version": { + "LastWriteUtc": "2026-10-29T12:52:00Z", + "Length": 4000000113 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 114", + "FilePath": "/samples/FilePath-115.txt", + "FileSizeBytes": 4000000116, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-115.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000117", + "Version": { + "LastWriteUtc": "2026-10-29T12:58:00Z", + "Length": 4000000119 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 123", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 120", + "FilePath": "/samples/FilePath-121.txt", + "FileSizeBytes": 4000000122, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-121.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000124", + "Version": { + "LastWriteUtc": "2026-10-29T13:05:00Z", + "Length": 4000000126 + } + } + ], + "ToolInvocations": [ + { + "Order": 67, + "ToolId": "ToolId sample 128", + "ToolName": "ToolName sample 129", + "ToolIcon": "ToolIcon sample 130", + "ToolCallId": "ToolCallId sample 131", + "Status": "SUCCESS", + "WasExecuted": true, + "StatusMessage": "StatusMessage sample 132", + "Arguments": { + "ArgumentsKey sample 133": "Arguments sample 134", + "ArgumentsKey sample 135": "Arguments sample 136" + } + }, + { + "Order": 77, + "ToolId": "ToolId sample 138", + "ToolName": "ToolName sample 139", + "ToolIcon": "ToolIcon sample 140", + "ToolCallId": "ToolCallId sample 141", + "Status": "SUCCESS", + "WasExecuted": true, + "StatusMessage": "StatusMessage sample 142", + "Arguments": { + "ArgumentsKey sample 143": "Arguments sample 144", + "ArgumentsKey sample 145": "Arguments sample 146" + } + } + ], + "Presentation": { + "Kind": "SWOT_ANALYSIS", + "Data": { + "sample": 147, + "text": "Gr\u00F6\u00DFe" + } + }, + "SourcePassages": [ + { + "SourceUrl": "SourceUrl sample 148", + "DataSourceName": "DataSourceName sample 149", + "Path": "/samples/Path-150.txt", + "Category": "UNKNOWN", + "Type": "UNKNOWN", + "ProvenanceId": "00000000-0000-4000-8000-000000000151", + "Text": "Text sample 152", + "SurroundingContent": [ + "SurroundingContent sample 153", + "SurroundingContent sample 154" + ], + "PageNumber": 45, + "EndPageNumber": 46, + "SheetName": "SheetName sample 157", + "StartRow": 48, + "EndRow": 49, + "StartLine": 50, + "EndLine": 51, + "ChunkNumber": 52 + }, + { + "SourceUrl": "SourceUrl sample 163", + "DataSourceName": "DataSourceName sample 164", + "Path": "/samples/Path-165.txt", + "Category": "UNKNOWN", + "Type": "UNKNOWN", + "ProvenanceId": "00000000-0000-4000-8000-000000000166", + "Text": "Text sample 167", + "SurroundingContent": [ + "SurroundingContent sample 168", + "SurroundingContent sample 169" + ], + "PageNumber": 60, + "EndPageNumber": 61, + "SheetName": "SheetName sample 172", + "StartRow": 63, + "EndRow": 64, + "StartLine": 65, + "EndLine": 66, + "ChunkNumber": 67 + } + ], + "ReportedUsage": { + "PromptTokens": 68, + "ModelId": "ModelId sample 179", + "BlockCount": 70 + }, + "Text": "Text sample 181" + }, + "Role": "UNKNOWN", + "HideFromUser": true + }, + { + "Time": "2026-10-29T15:32:15+01:00", + "ContentType": "UNKNOWN", + "Content": { + "$type": "image", + "Sources": [ + { + "Title": "Title sample 183", + "URL": "URL sample 184", + "Origin": "RAG" + }, + { + "Title": "Title sample 185", + "URL": "URL sample 186", + "Origin": "RAG" + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 187", + "FilePath": "/samples/FilePath-188.txt", + "FileSizeBytes": 4000000189, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-188.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000190", + "Version": { + "LastWriteUtc": "2026-10-29T14:11:00Z", + "Length": 4000000192 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 193", + "FilePath": "/samples/FilePath-194.txt", + "FileSizeBytes": 4000000195, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-194.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000196", + "Version": { + "LastWriteUtc": "2026-10-29T14:17:00Z", + "Length": 4000000198 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 202", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 199", + "FilePath": "/samples/FilePath-200.txt", + "FileSizeBytes": 4000000201, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-200.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000203", + "Version": { + "LastWriteUtc": "2026-10-29T14:24:00Z", + "Length": 4000000205 + } + } + ], + "SourceType": "LOCAL_PATH", + "Source": "Source sample 206" + }, + "Role": "UNKNOWN", + "HideFromUser": true + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 207", + "FilePath": "/samples/FilePath-208.txt", + "FileSizeBytes": 4000000209, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-208.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000210", + "Version": { + "LastWriteUtc": "2026-10-29T14:31:00Z", + "Length": 4000000212 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 213", + "FilePath": "/samples/FilePath-214.txt", + "FileSizeBytes": 4000000215, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-214.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000216", + "Version": { + "LastWriteUtc": "2026-10-29T14:37:00Z", + "Length": 4000000218 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 222", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 219", + "FilePath": "/samples/FilePath-220.txt", + "FileSizeBytes": 4000000221, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-220.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000223", + "Version": { + "LastWriteUtc": "2026-10-29T14:44:00Z", + "Length": 4000000225 + } + } + ], + "AllowProfileUsage": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000226", + "ToolIds": [ + "ToolIds sample 227", + "ToolIds sample 228" + ], + "DataSourceOptions": { + "DisableDataSources": true, + "AutomaticDataSourceSelection": true, + "AutomaticValidation": true, + "RetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 229", + "PreselectedDataSourceIds sample 230" + ] + }, + "ProfileId": "ProfileId sample 231", + "ProviderId": "ProviderId sample 232", + "WorkspaceName": "WorkspaceName sample 233" + }, + { + "Num": 7, + "Id": "Id sample 234", + "Name": "Name sample 235", + "SystemPrompt": "SystemPrompt sample 236", + "PredefinedUserPrompt": "PredefinedUserPrompt sample 237", + "ExampleConversation": [ + { + "Time": "2026-10-29T16:28:15+01:00", + "ContentType": "UNKNOWN", + "Content": { + "$type": "text", + "Sources": [ + { + "Title": "Title sample 239", + "URL": "URL sample 240", + "Origin": "RAG" + }, + { + "Title": "Title sample 241", + "URL": "URL sample 242", + "Origin": "RAG" + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 243", + "FilePath": "/samples/FilePath-244.txt", + "FileSizeBytes": 4000000245, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-244.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000246", + "Version": { + "LastWriteUtc": "2026-10-29T15:07:00Z", + "Length": 4000000248 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 249", + "FilePath": "/samples/FilePath-250.txt", + "FileSizeBytes": 4000000251, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-250.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000252", + "Version": { + "LastWriteUtc": "2026-10-29T15:13:00Z", + "Length": 4000000254 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 258", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 255", + "FilePath": "/samples/FilePath-256.txt", + "FileSizeBytes": 4000000257, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-256.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000259", + "Version": { + "LastWriteUtc": "2026-10-29T15:20:00Z", + "Length": 4000000261 + } + } + ], + "ToolInvocations": [ + { + "Order": 52, + "ToolId": "ToolId sample 263", + "ToolName": "ToolName sample 264", + "ToolIcon": "ToolIcon sample 265", + "ToolCallId": "ToolCallId sample 266", + "Status": "SUCCESS", + "WasExecuted": true, + "StatusMessage": "StatusMessage sample 267", + "Arguments": { + "ArgumentsKey sample 268": "Arguments sample 269", + "ArgumentsKey sample 270": "Arguments sample 271" + } + }, + { + "Order": 62, + "ToolId": "ToolId sample 273", + "ToolName": "ToolName sample 274", + "ToolIcon": "ToolIcon sample 275", + "ToolCallId": "ToolCallId sample 276", + "Status": "SUCCESS", + "WasExecuted": true, + "StatusMessage": "StatusMessage sample 277", + "Arguments": { + "ArgumentsKey sample 278": "Arguments sample 279", + "ArgumentsKey sample 280": "Arguments sample 281" + } + } + ], + "Presentation": { + "Kind": "SWOT_ANALYSIS", + "Data": { + "sample": 282, + "text": "Gr\u00F6\u00DFe" + } + }, + "SourcePassages": [ + { + "SourceUrl": "SourceUrl sample 283", + "DataSourceName": "DataSourceName sample 284", + "Path": "/samples/Path-285.txt", + "Category": "UNKNOWN", + "Type": "UNKNOWN", + "ProvenanceId": "00000000-0000-4000-8000-000000000286", + "Text": "Text sample 287", + "SurroundingContent": [ + "SurroundingContent sample 288", + "SurroundingContent sample 289" + ], + "PageNumber": 80, + "EndPageNumber": 81, + "SheetName": "SheetName sample 292", + "StartRow": 83, + "EndRow": 84, + "StartLine": 85, + "EndLine": 86, + "ChunkNumber": 87 + }, + { + "SourceUrl": "SourceUrl sample 298", + "DataSourceName": "DataSourceName sample 299", + "Path": "/samples/Path-300.txt", + "Category": "UNKNOWN", + "Type": "UNKNOWN", + "ProvenanceId": "00000000-0000-4000-8000-000000000301", + "Text": "Text sample 302", + "SurroundingContent": [ + "SurroundingContent sample 303", + "SurroundingContent sample 304" + ], + "PageNumber": 45, + "EndPageNumber": 46, + "SheetName": "SheetName sample 307", + "StartRow": 48, + "EndRow": 49, + "StartLine": 50, + "EndLine": 51, + "ChunkNumber": 52 + } + ], + "ReportedUsage": { + "PromptTokens": 53, + "ModelId": "ModelId sample 314", + "BlockCount": 55 + }, + "Text": "Text sample 316" + }, + "Role": "UNKNOWN", + "HideFromUser": true + }, + { + "Time": "2026-10-29T17:47:15+01:00", + "ContentType": "UNKNOWN", + "Content": { + "$type": "image", + "Sources": [ + { + "Title": "Title sample 318", + "URL": "URL sample 319", + "Origin": "RAG" + }, + { + "Title": "Title sample 320", + "URL": "URL sample 321", + "Origin": "RAG" + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 322", + "FilePath": "/samples/FilePath-323.txt", + "FileSizeBytes": 4000000324, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-323.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000325", + "Version": { + "LastWriteUtc": "2026-10-29T16:26:00Z", + "Length": 4000000327 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 328", + "FilePath": "/samples/FilePath-329.txt", + "FileSizeBytes": 4000000330, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-329.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000331", + "Version": { + "LastWriteUtc": "2026-10-29T16:32:00Z", + "Length": 4000000333 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 337", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 334", + "FilePath": "/samples/FilePath-335.txt", + "FileSizeBytes": 4000000336, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-335.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000338", + "Version": { + "LastWriteUtc": "2026-10-29T16:39:00Z", + "Length": 4000000340 + } + } + ], + "SourceType": "LOCAL_PATH", + "Source": "Source sample 341" + }, + "Role": "UNKNOWN", + "HideFromUser": true + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 342", + "FilePath": "/samples/FilePath-343.txt", + "FileSizeBytes": 4000000344, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-343.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000345", + "Version": { + "LastWriteUtc": "2026-10-29T16:46:00Z", + "Length": 4000000347 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 348", + "FilePath": "/samples/FilePath-349.txt", + "FileSizeBytes": 4000000350, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-349.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000351", + "Version": { + "LastWriteUtc": "2026-10-29T16:52:00Z", + "Length": 4000000353 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 357", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 354", + "FilePath": "/samples/FilePath-355.txt", + "FileSizeBytes": 4000000356, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-355.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000358", + "Version": { + "LastWriteUtc": "2026-10-29T16:59:00Z", + "Length": 4000000360 + } + } + ], + "AllowProfileUsage": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000361", + "ToolIds": [ + "ToolIds sample 362", + "ToolIds sample 363" + ], + "DataSourceOptions": { + "DisableDataSources": true, + "AutomaticDataSourceSelection": true, + "AutomaticValidation": true, + "RetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 364", + "PreselectedDataSourceIds sample 365" + ] + }, + "ProfileId": "ProfileId sample 366", + "ProviderId": "ProviderId sample 367", + "WorkspaceName": "WorkspaceName sample 368" + } + ], + "EnabledPlugins": [ + "00000000-0000-4000-8000-000000000369", + "00000000-0000-4000-8000-000000000370" + ], + "ManagedEditableDefaults": { + "ManagedEditableDefaultsKey sample 371": { + "ConfigPluginId": "00000000-0000-4000-8000-000000000372", + "LastAppliedValue": "LastAppliedValue sample 373" + }, + "ManagedEditableDefaultsKey sample 374": { + "ConfigPluginId": "00000000-0000-4000-8000-000000000375", + "LastAppliedValue": "LastAppliedValue sample 376" + } + }, + "AssistantPluginAudits": [ + { + "PluginId": "00000000-0000-4000-8000-000000000377", + "PluginHash": "PluginHash sample 378", + "AuditedAtUtc": "2026-10-29T18:49:15+01:00", + "AuditProviderId": "AuditProviderId sample 380", + "AuditProviderName": "AuditProviderName sample 381", + "Level": "DANGEROUS", + "Summary": "Summary sample 382", + "Confidence": 2.25, + "PromptPreview": "PromptPreview sample 383", + "Findings": [ + { + "severity": "unknown", + "Category": "Category sample 385", + "Location": "Location sample 386", + "Description": "Description sample 387" + }, + { + "severity": "unknown", + "Category": "Category sample 389", + "Location": "Location sample 390", + "Description": "Description sample 391" + } + ] + }, + { + "PluginId": "00000000-0000-4000-8000-000000000392", + "PluginHash": "PluginHash sample 393", + "AuditedAtUtc": "2026-10-29T19:04:15+01:00", + "AuditProviderId": "AuditProviderId sample 395", + "AuditProviderName": "AuditProviderName sample 396", + "Level": "DANGEROUS", + "Summary": "Summary sample 397", + "Confidence": 2.25, + "PromptPreview": "PromptPreview sample 398", + "Findings": [ + { + "severity": "unknown", + "Category": "Category sample 400", + "Location": "Location sample 401", + "Description": "Description sample 402" + }, + { + "severity": "unknown", + "Category": "Category sample 404", + "Location": "Location sample 405", + "Description": "Description sample 406" + } + ] + } + ], + "NextProviderNum": 7, + "NextEmbeddingNum": 7, + "NextTranscriptionNum": 7, + "NextDataSourceNum": 7, + "NextProfileNum": 7, + "NextChatTemplateNum": 7, + "NextDocumentAnalysisPolicyNum": 7, + "App": { + "LanguageBehavior": "MANUAL", + "LanguagePluginId": "00000000-0000-4000-8000-000000000407", + "PreferredTheme": "LIGHT", + "IsSavingEnergy": true, + "EnableSpellchecking": true, + "CloseExportMessagesAutomatically": true, + "UpdateInterval": "DISABLE_UPDATES", + "UpdateInstallation": "AUTOMATIC", + "NavigationBehavior": "NEVER_EXPAND_USE_TOOLTIPS", + "StartPage": "CHAT", + "InterfaceScreenSizes": [ + { + "ScreenKey": "ScreenKey sample 408", + "Size": "SYSTEM_SCALING", + "CustomPercent": 49 + }, + { + "ScreenKey": "ScreenKey sample 410", + "Size": "SYSTEM_SCALING", + "CustomPercent": 51 + } + ], + "ShowPromptInjectionAlert": true, + "ShowIntroduction": true, + "ShowQuickStartGuide": true, + "ShowLastChangelog": true, + "ShowVision": true, + "PreviewVisibility": "RELEASE_CANDIDATE", + "EnabledPreviewFeatures": [ + "PRE_WRITER_MODE_2024", + "PRE_RAG_2024" + ], + "PreselectedProvider": "PreselectedProvider sample 412", + "PreselectedProfile": "PreselectedProfile sample 413", + "PreselectedChatTemplate": "PreselectedChatTemplate sample 414", + "UseTranscriptionProvider": "UseTranscriptionProvider sample 415", + "OpusBitrate": "KBPS_32", + "ShortcutVoiceRecording": "ShortcutVoiceRecording sample 416", + "ShortcutVoiceRecordingDisplayName": "ShortcutVoiceRecordingDisplayName sample 417", + "ShortcutVoiceRecordingDisplaySource": "ShortcutVoiceRecordingDisplaySource sample 418", + "HttpClientTimeoutSeconds": 59, + "ShareFeatureUsageWithSelfHostedServerOperators": true, + "ExternalHttpCustomRootCertificatesEnabled": true, + "ExternalHttpCustomRootCertificateBundlePath": "/samples/ExternalHttpCustomRootCertificateBundlePath-420.txt", + "ExternalHttpCustomRootCertificateAllowedHosts": [ + "ExternalHttpCustomRootCertificateAllowedHosts sample 421", + "ExternalHttpCustomRootCertificateAllowedHosts sample 422" + ], + "AllowUserToAddProvider": true, + "AllowUserToAddLLMProvider": true, + "AllowUserToAddEmbeddingProvider": true, + "AllowUserToAddTranscriptionProvider": true, + "ProhibitedProviders": [ + "OPEN_AI", + "ANTHROPIC" + ], + "AllowUserToAddMailbox": true, + "AllowUserToImportConfigurationSnippets": true, + "AllowUserToImportProfile": true, + "AllowUserToImportLLMProvider": true, + "AllowUserToImportEmbeddingProvider": true, + "AllowUserToImportTranscriptionProvider": true, + "AllowUserToImportChatTemplate": true, + "AllowUserToImportERIDataSource": true, + "AllowUserToImportDocumentAnalysisPolicy": true, + "AllowUserToImportPlugins": true, + "AllowUserToImportConfigurationPlugins": true, + "AllowUserToSharePlugins": true, + "AllowUserToBackUpData": true, + "AllowUserToRestoreData": true, + "ShowAdminSettings": true, + "EnterpriseEncryptionExportFormat": "V2", + "AddAIGeneratedContentDisclosure": true, + "DataSourceIndexing": { + "AutomaticRefresh": true, + "MinimumPauseBetweenStepsSeconds": 63, + "MaximumPauseBetweenStepsSeconds": 64 + }, + "HiddenAssistants": [ + "UNKNOWN", + "GRAMMAR_SPELLING_ASSISTANT" + ] + }, + "Chat": { + "ShortcutSendBehavior": "MODIFER_ENTER_IS_SENDING", + "LoadingProviderBehavior": "ALWAYS_USE_DEFAULT_CHAT_PROVIDER", + "AddChatProviderBehavior": "ADDED_CHATS_USE_LATEST_PROVIDER", + "SendToChatDataSourceBehavior": "APPLY_STANDARD_CHAT_DATA_SOURCE_OPTIONS", + "PreselectOptions": true, + "PreselectedProvider": "PreselectedProvider sample 425", + "PreselectedProfile": "PreselectedProfile sample 426", + "PreselectedChatTemplate": "PreselectedChatTemplate sample 427", + "PreselectedDataSourcesDisabled": true, + "PreselectedDataSourcesAutomaticSelection": true, + "PreselectedDataSourcesAutomaticValidation": true, + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 430", + "PreselectedDataSourceIds sample 431" + ], + "PreselectedDataSourcesRetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceOptions": { + "DisableDataSources": true, + "AutomaticDataSourceSelection": true, + "AutomaticValidation": true, + "RetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 430", + "PreselectedDataSourceIds sample 431" + ] + }, + "ShowLatestMessageAfterLoading": true, + "ReadChangedAttachmentsAgain": true + }, + "Workspace": { + "StorageBehavior": "STORE_CHATS_AUTOMATICALLY", + "StorageTemporaryMaintenancePolicy": "DELETE_OLDER_THAN_7_DAYS", + "DisplayBehavior": "TOGGLE_SIDEBAR", + "IsSidebarVisible": true, + "SplitterPosition": 1.5 + }, + "IconFinder": { + "PreselectOptions": true, + "PreselectedSource": "ICONS8", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 432" + }, + "Translation": { + "DebounceIntervalMilliseconds": 73, + "PreselectOptions": true, + "PreselectLiveTranslation": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 434", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 435" + }, + "Coding": { + "PreselectOptions": true, + "PreselectCompilerMessages": true, + "PreselectedProgrammingLanguage": "BASH", + "PreselectedOtherProgrammingLanguage": "PreselectedOtherProgrammingLanguage sample 436", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 437", + "PreselectedProfile": "PreselectedProfile sample 438" + }, + "ERI": { + "AutoSaveChanges": true, + "PreselectOptions": true, + "ERIServers": [ + { + "ServerName": "ServerName sample 439", + "ServerDescription": "ServerDescription sample 440", + "ERIVersion": "V1", + "ProgrammingLanguage": "C", + "OtherProgrammingLanguage": "OtherProgrammingLanguage sample 441", + "DataSource": "CUSTOM", + "DataSourceProductName": "DataSourceProductName sample 442", + "OtherDataSource": "OtherDataSource sample 443", + "DataSourceHostname": "DataSourceHostname sample 444", + "DataSourcePort": 85, + "UserTypedPort": true, + "AuthMethods": [ + "KERBEROS", + "USERNAME_PASSWORD" + ], + "AuthDescription": "AuthDescription sample 446", + "OperatingSystem": "WINDOWS", + "AllowedLLMProviders": "ANY", + "EmbeddingInfos": [ + { + "EmbeddingType": "EmbeddingType sample 447", + "EmbeddingName": "EmbeddingName sample 448", + "Description": "Description sample 449", + "UsedWhen": "UsedWhen sample 450", + "Link": "Link sample 451" + }, + { + "EmbeddingType": "EmbeddingType sample 452", + "EmbeddingName": "EmbeddingName sample 453", + "Description": "Description sample 454", + "UsedWhen": "UsedWhen sample 455", + "Link": "Link sample 456" + } + ], + "RetrievalInfos": [ + { + "Name": "Name sample 457", + "Description": "Description sample 458", + "Link": "Link sample 459", + "ParametersDescription": { + "ParametersDescriptionKey sample 460": "ParametersDescription sample 461", + "ParametersDescriptionKey sample 462": "ParametersDescription sample 463" + }, + "Embeddings": [ + { + "EmbeddingType": "EmbeddingType sample 464", + "EmbeddingName": "EmbeddingName sample 465", + "Description": "Description sample 466", + "UsedWhen": "UsedWhen sample 467", + "Link": "Link sample 468" + }, + { + "EmbeddingType": "EmbeddingType sample 469", + "EmbeddingName": "EmbeddingName sample 470", + "Description": "Description sample 471", + "UsedWhen": "UsedWhen sample 472", + "Link": "Link sample 473" + } + ] + }, + { + "Name": "Name sample 474", + "Description": "Description sample 475", + "Link": "Link sample 476", + "ParametersDescription": { + "ParametersDescriptionKey sample 477": "ParametersDescription sample 478", + "ParametersDescriptionKey sample 479": "ParametersDescription sample 480" + }, + "Embeddings": [ + { + "EmbeddingType": "EmbeddingType sample 481", + "EmbeddingName": "EmbeddingName sample 482", + "Description": "Description sample 483", + "UsedWhen": "UsedWhen sample 484", + "Link": "Link sample 485" + }, + { + "EmbeddingType": "EmbeddingType sample 486", + "EmbeddingName": "EmbeddingName sample 487", + "Description": "Description sample 488", + "UsedWhen": "UsedWhen sample 489", + "Link": "Link sample 490" + } + ] + } + ], + "AdditionalLibraries": "AdditionalLibraries sample 491", + "WriteToFilesystem": true, + "BaseDirectory": "BaseDirectory sample 492", + "PreviouslyGeneratedFiles": [ + "PreviouslyGeneratedFiles sample 493", + "PreviouslyGeneratedFiles sample 494" + ] + }, + { + "ServerName": "ServerName sample 495", + "ServerDescription": "ServerDescription sample 496", + "ERIVersion": "V1", + "ProgrammingLanguage": "C", + "OtherProgrammingLanguage": "OtherProgrammingLanguage sample 497", + "DataSource": "CUSTOM", + "DataSourceProductName": "DataSourceProductName sample 498", + "OtherDataSource": "OtherDataSource sample 499", + "DataSourceHostname": "DataSourceHostname sample 500", + "DataSourcePort": 41, + "UserTypedPort": true, + "AuthMethods": [ + "KERBEROS", + "USERNAME_PASSWORD" + ], + "AuthDescription": "AuthDescription sample 502", + "OperatingSystem": "WINDOWS", + "AllowedLLMProviders": "ANY", + "EmbeddingInfos": [ + { + "EmbeddingType": "EmbeddingType sample 503", + "EmbeddingName": "EmbeddingName sample 504", + "Description": "Description sample 505", + "UsedWhen": "UsedWhen sample 506", + "Link": "Link sample 507" + }, + { + "EmbeddingType": "EmbeddingType sample 508", + "EmbeddingName": "EmbeddingName sample 509", + "Description": "Description sample 510", + "UsedWhen": "UsedWhen sample 511", + "Link": "Link sample 512" + } + ], + "RetrievalInfos": [ + { + "Name": "Name sample 513", + "Description": "Description sample 514", + "Link": "Link sample 515", + "ParametersDescription": { + "ParametersDescriptionKey sample 516": "ParametersDescription sample 517", + "ParametersDescriptionKey sample 518": "ParametersDescription sample 519" + }, + "Embeddings": [ + { + "EmbeddingType": "EmbeddingType sample 520", + "EmbeddingName": "EmbeddingName sample 521", + "Description": "Description sample 522", + "UsedWhen": "UsedWhen sample 523", + "Link": "Link sample 524" + }, + { + "EmbeddingType": "EmbeddingType sample 525", + "EmbeddingName": "EmbeddingName sample 526", + "Description": "Description sample 527", + "UsedWhen": "UsedWhen sample 528", + "Link": "Link sample 529" + } + ] + }, + { + "Name": "Name sample 530", + "Description": "Description sample 531", + "Link": "Link sample 532", + "ParametersDescription": { + "ParametersDescriptionKey sample 533": "ParametersDescription sample 534", + "ParametersDescriptionKey sample 535": "ParametersDescription sample 536" + }, + "Embeddings": [ + { + "EmbeddingType": "EmbeddingType sample 537", + "EmbeddingName": "EmbeddingName sample 538", + "Description": "Description sample 539", + "UsedWhen": "UsedWhen sample 540", + "Link": "Link sample 541" + }, + { + "EmbeddingType": "EmbeddingType sample 542", + "EmbeddingName": "EmbeddingName sample 543", + "Description": "Description sample 544", + "UsedWhen": "UsedWhen sample 545", + "Link": "Link sample 546" + } + ] + } + ], + "AdditionalLibraries": "AdditionalLibraries sample 547", + "WriteToFilesystem": true, + "BaseDirectory": "BaseDirectory sample 548", + "PreviouslyGeneratedFiles": [ + "PreviouslyGeneratedFiles sample 549", + "PreviouslyGeneratedFiles sample 550" + ] + } + ], + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 551", + "PreselectedProfile": "PreselectedProfile sample 552" + }, + "DocumentAnalysis": { + "Policies": [ + { + "Id": "Id sample 553", + "Num": 7, + "Name": "PolicyName sample 555", + "IsEnterpriseConfiguration": true, + "PolicyName": "PolicyName sample 555", + "PolicyDescription": "PolicyDescription sample 556", + "IsProtected": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000557", + "AnalysisRules": "AnalysisRules sample 558", + "OutputRules": "OutputRules sample 559", + "MinimumProviderConfidence": "UNTRUSTED", + "AllowedToolIds": [ + "AllowedToolIds sample 560", + "AllowedToolIds sample 561" + ], + "PreselectedProvider": "PreselectedProvider sample 562", + "PreselectedProfile": "PreselectedProfile sample 563", + "HidePolicyDefinition": true + }, + { + "Id": "Id sample 564", + "Num": 7, + "Name": "PolicyName sample 566", + "IsEnterpriseConfiguration": true, + "PolicyName": "PolicyName sample 566", + "PolicyDescription": "PolicyDescription sample 567", + "IsProtected": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000568", + "AnalysisRules": "AnalysisRules sample 569", + "OutputRules": "OutputRules sample 570", + "MinimumProviderConfidence": "UNTRUSTED", + "AllowedToolIds": [ + "AllowedToolIds sample 571", + "AllowedToolIds sample 572" + ], + "PreselectedProvider": "PreselectedProvider sample 573", + "PreselectedProfile": "PreselectedProfile sample 574", + "HidePolicyDefinition": true + } + ] + }, + "MandatoryInformation": { + "Acceptances": [ + { + "InfoId": "InfoId sample 575", + "AcceptedVersion": "AcceptedVersion sample 576", + "AcceptedHash": "AcceptedHash sample 577", + "AcceptedAtUtc": "2026-10-29T22:08:15+01:00", + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000579" + }, + { + "InfoId": "InfoId sample 580", + "AcceptedVersion": "AcceptedVersion sample 581", + "AcceptedHash": "AcceptedHash sample 582", + "AcceptedAtUtc": "2026-10-29T22:13:15+01:00", + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000584" + } + ] + }, + "TextSummarizer": { + "PreselectOptions": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 585", + "PreselectedComplexity": "SIMPLE_LANGUAGE", + "PreselectedExpertInField": "PreselectedExpertInField sample 586", + "PreselectedImportantAspects": "PreselectedImportantAspects sample 587", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 588" + }, + "TextContentCleaner": { + "PreselectAgentOptions": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 589" + }, + "AgentDataSourceSelection": { + "PreselectAgentOptions": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 590" + }, + "AgentRetrievalContextValidation": { + "EnableRetrievalContextValidation": true, + "PreselectAgentOptions": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 591", + "NumParallelValidations": 82 + }, + "AssistantPluginAudit": { + "RequireAuditBeforeActivation": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 593", + "MinimumLevel": "DANGEROUS", + "BlockActivationBelowMinimum": true, + "AutomaticallyAuditAssistants": true, + "EnterpriseApprovedPlugins": [ + { + "PluginHash": "PluginHash sample 594", + "DisplayName": "DisplayName sample 595", + "Comment": "Comment sample 596", + "ApprovedBy": "ApprovedBy sample 597", + "ApprovedAtUtc": "2026-10-29T22:28:15+01:00", + "Activate": true, + "AllowUserOverride": true + }, + { + "PluginHash": "PluginHash sample 599", + "DisplayName": "DisplayName sample 600", + "Comment": "Comment sample 601", + "ApprovedBy": "ApprovedBy sample 602", + "ApprovedAtUtc": "2026-10-29T22:33:15+01:00", + "Activate": true, + "AllowUserOverride": true + } + ] + }, + "Agenda": { + "PreselectOptions": true, + "PreselectName": "PreselectName sample 604", + "PreselectTopic": "PreselectTopic sample 605", + "PreselectObjective": "PreselectObjective sample 606", + "PreselectModerator": "PreselectModerator sample 607", + "PreselectDuration": "PreselectDuration sample 608", + "PreselectStartTime": "PreselectStartTime sample 609", + "PreselectIntroduceParticipants": true, + "PreselectNumberParticipants": "PEER_TO_PEER", + "PreselectActiveParticipation": true, + "PreselectIsMeetingVirtual": true, + "PreselectLocation": "PreselectLocation sample 610", + "PreselectJointDinner": true, + "PreselectSocialActivity": true, + "PreselectArriveAndDepart": true, + "PreselectLunchTime": 51, + "PreselectBreakTime": 52, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 613", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 614", + "PreselectedProfile": "PreselectedProfile sample 615" + }, + "GrammarSpelling": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 616", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 617" + }, + "RewriteImprove": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 618", + "PreselectedWritingStyle": "EVERYDAY", + "PreselectedSentenceStructure": "ACTIVE", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 619" + }, + "PromptOptimizer": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 620", + "PreselectedImportantAspects": "PreselectedImportantAspects sample 621", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 622" + }, + "EMail": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 623", + "PreselectedWritingStyle": "BUSINESS_FORMAL", + "Greeting": "Greeting sample 624", + "SenderName": "SenderName sample 625", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 626", + "PreselectedProfile": "PreselectedProfile sample 627" + }, + "SlideBuilder": { + "TemplatePath": "/samples/TemplatePath-628.txt", + "PreselectedFooterText": "PreselectedFooterText sample 629", + "PreselectedMaxBulletPointsPerSlide": 70, + "PreselectedKeepEmptyPicturePlaceholders": true, + "PreselectedPictureLibraryPath": "/samples/PreselectedPictureLibraryPath-631.txt", + "PreselectedLayouts": { + "TABLE_OF_CONTENTS": "PreselectedLayouts sample 632", + "AGENDA": "PreselectedLayouts sample 633" + }, + "PreselectedTemplateSlideUses": { + "74": "KEEP", + "75": "EDIT" + }, + "PreselectOptions": true, + "PreselectedProfile": "PreselectedProfile sample 636", + "PreselectedProvider": "PreselectedProvider sample 637", + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 638", + "PreselectedAudienceProfile": "STUDENTS", + "PreselectedAudienceAgeGroup": "CHILDREN", + "PreselectedAudienceOrganizationalLevel": "TRAINEES", + "PreselectedAudienceExpertise": "NON_EXPERTS", + "PreselectedOccasion": "TALK", + "PreselectedOtherOccasion": "PreselectedOtherOccasion sample 639", + "PreselectedImportantAspects": "PreselectedImportantAspects sample 640", + "MinimumProviderConfidence": "UNTRUSTED" + }, + "LegalCheck": { + "PreselectOptions": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 641", + "PreselectedProfile": "PreselectedProfile sample 642" + }, + "Synonyms": { + "PreselectOptions": true, + "PreselectedLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 643", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 644" + }, + "MyTasks": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 645", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 646", + "PreselectedProfile": "PreselectedProfile sample 647" + }, + "JobPostings": { + "PreselectOptions": true, + "PreselectedMandatoryInformation": "PreselectedMandatoryInformation sample 648", + "PreselectedJobDescription": "PreselectedJobDescription sample 649", + "PreselectedQualifications": "PreselectedQualifications sample 650", + "PreselectedResponsibilities": "PreselectedResponsibilities sample 651", + "PreselectedCompanyName": "PreselectedCompanyName sample 652", + "PreselectedWorkLocation": "PreselectedWorkLocation sample 653", + "PreselectedCountryLegalFramework": "PreselectedCountryLegalFramework sample 654", + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 655", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 656" + }, + "BiasOfTheDay": { + "UsedBias": [ + 47, + 48 + ], + "DateLastBiasDrawn": "2026-10-29", + "BiasOfTheDayChatId": "00000000-0000-4000-8000-000000000659", + "BiasOfTheDayId": "00000000-0000-4000-8000-000000000660", + "RestrictOneBiasPerDay": true, + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 661", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProfile": "PreselectedProfile sample 662", + "PreselectedProvider": "PreselectedProvider sample 663" + }, + "I18N": { + "PreselectOptions": true, + "PreselectedLanguagePluginId": "00000000-0000-4000-8000-000000000664", + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 665", + "PreselectedProvider": "PreselectedProvider sample 666" + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/settings.json b/app/Tests/Serialization/Fixtures/settings.json new file mode 100644 index 000000000..9ced33f11 --- /dev/null +++ b/app/Tests/Serialization/Fixtures/settings.json @@ -0,0 +1,1759 @@ +{ + "Version": "V6", + "Providers": [ + { + "Num": 7, + "Id": "Id sample 1", + "InstanceName": "Name sample 13", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 3", + "DisplayName": "DisplayName sample 4", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000005", + "Hostname": "Hostname sample 6", + "Host": "LM_STUDIO", + "HFInferenceProvider": "AUTOMATIC", + "AdditionalJsonApiParameters": "AdditionalJsonApiParameters sample 7", + "TokenizerPath": "/samples/TokenizerPath-8.txt", + "CapabilityOverrides": { + "AUDIO_INPUT": true, + "FUNCTION_CALLING": true, + "MULTIPLE_IMAGE_INPUT": true, + "SPEECH_INPUT": true, + "VIDEO_INPUT": true, + "OPTIONAL_REASONING": true, + "ALWAYS_REASONING": true, + "REASONING_BY_DEFAULT": true, + "CONTEXT_WINDOW": 49, + "MAX_IMAGES_PER_MESSAGE": 50, + "MAX_IMAGES_PER_REQUEST": 51 + }, + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 12", + "PromptForUserAPIKey": true, + "Name": "Name sample 13" + }, + { + "Num": 7, + "Id": "Id sample 14", + "InstanceName": "Name sample 26", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 16", + "DisplayName": "DisplayName sample 17", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000018", + "Hostname": "Hostname sample 19", + "Host": "LM_STUDIO", + "HFInferenceProvider": "AUTOMATIC", + "AdditionalJsonApiParameters": "AdditionalJsonApiParameters sample 20", + "TokenizerPath": "/samples/TokenizerPath-21.txt", + "CapabilityOverrides": { + "AUDIO_INPUT": true, + "FUNCTION_CALLING": true, + "MULTIPLE_IMAGE_INPUT": true, + "SPEECH_INPUT": true, + "VIDEO_INPUT": true, + "OPTIONAL_REASONING": true, + "ALWAYS_REASONING": true, + "REASONING_BY_DEFAULT": true, + "CONTEXT_WINDOW": 62, + "MAX_IMAGES_PER_MESSAGE": 63, + "MAX_IMAGES_PER_REQUEST": 64 + }, + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 25", + "PromptForUserAPIKey": true, + "Name": "Name sample 26" + } + ], + "Confidence": { + "EnforceGlobalMinimumConfidence": true, + "GlobalMinimumConfidence": "UNTRUSTED", + "ShowProviderConfidence": true, + "ConfidenceScheme": "TRUST_USA_EUROPE", + "CustomConfidenceScheme": { + "OPEN_AI": "UNTRUSTED", + "ANTHROPIC": "UNKNOWN" + } + }, + "DataSourceSecurity": { + "TrustedProviderIds": [ + "TrustedProviderIds sample 27", + "TrustedProviderIds sample 28" + ], + "MinimumOutboundDataRestriction": "ONLY_LINKS_FROM_CHAT" + }, + "EmbeddingProviders": [ + { + "Num": 7, + "Id": "Id sample 29", + "Name": "Name sample 30", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 31", + "DisplayName": "DisplayName sample 32", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000033", + "Hostname": "Hostname sample 34", + "Host": "LM_STUDIO", + "TokenizerPath": "/samples/TokenizerPath-35.txt", + "TokenizerFingerprint": "TokenizerFingerprint sample 36", + "EmbeddingBatchSize": 77, + "TokenLimit": 78, + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 39", + "HFInferenceProvider": "AUTOMATIC", + "PromptForUserAPIKey": true + }, + { + "Num": 7, + "Id": "Id sample 40", + "Name": "Name sample 41", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 42", + "DisplayName": "DisplayName sample 43", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000044", + "Hostname": "Hostname sample 45", + "Host": "LM_STUDIO", + "TokenizerPath": "/samples/TokenizerPath-46.txt", + "TokenizerFingerprint": "TokenizerFingerprint sample 47", + "EmbeddingBatchSize": 88, + "TokenLimit": 89, + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 50", + "HFInferenceProvider": "AUTOMATIC", + "PromptForUserAPIKey": true + } + ], + "TranscriptionProviders": [ + { + "Num": 7, + "Id": "Id sample 51", + "Name": "Name sample 52", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 53", + "DisplayName": "DisplayName sample 54", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000055", + "Hostname": "Hostname sample 56", + "Host": "LM_STUDIO", + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 57", + "HFInferenceProvider": "AUTOMATIC", + "PromptForUserAPIKey": true + }, + { + "Num": 7, + "Id": "Id sample 58", + "Name": "Name sample 59", + "UsedLLMProvider": "OPEN_AI", + "Model": { + "Id": "Id sample 60", + "DisplayName": "DisplayName sample 61", + "IsSystemModel": false + }, + "IsSelfHosted": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000062", + "Hostname": "Hostname sample 63", + "Host": "LM_STUDIO", + "AllowUserProvidedAPIKey": true, + "CustomIconDataUrl": "CustomIconDataUrl sample 64", + "HFInferenceProvider": "AUTOMATIC", + "PromptForUserAPIKey": true + } + ], + "DataSources": [ + { + "$type_discriminator": "LOCAL_DIRECTORY", + "Num": 7, + "Id": "Id sample 65", + "Name": "Name sample 66", + "Description": "Description sample 67", + "Type": "LOCAL_FILE", + "EmbeddingId": "EmbeddingId sample 68", + "MaxChunkTokenLength": 59, + "ChunkOverlapTokenLength": 60, + "ConfidenceLevel": "UNTRUSTED", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000071", + "MaxMatches": 8, + "OutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "Path": "/samples/Path-72.txt", + "FilePatterns": "FilePatterns sample 73", + "ExcludePatterns": "ExcludePatterns sample 74", + "MaxFileSizeMegabytes": 65, + "IncludeSubdirectories": true, + "IncludeHiddenFilesAndFolders": true + }, + { + "$type_discriminator": "LOCAL_FILE", + "Num": 7, + "Id": "Id sample 76", + "Name": "Name sample 77", + "Description": "Description sample 78", + "Type": "LOCAL_FILE", + "EmbeddingId": "EmbeddingId sample 79", + "MaxChunkTokenLength": 70, + "ChunkOverlapTokenLength": 71, + "ConfidenceLevel": "UNTRUSTED", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000082", + "MaxMatches": 8, + "OutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "FilePath": "/samples/FilePath-83.txt" + }, + { + "$type_discriminator": "ERI_V1", + "Num": 7, + "Id": "Id sample 84", + "Name": "Name sample 85", + "Type": "LOCAL_FILE", + "Hostname": "Hostname sample 86", + "Port": 77, + "AuthMethod": "KERBEROS", + "Username": "Username sample 88", + "UsernamePasswordMode": "SHARED_USERNAME_AND_PASSWORD", + "SecurityPolicy": "ALLOW_ANY", + "OutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000089", + "Version": "V1", + "SelectedRetrievalId": "SelectedRetrievalId sample 90", + "MaxMatches": 8 + } + ], + "Mailboxes": [ + { + "Num": 7, + "Id": "Id sample 91", + "Name": "Name sample 92", + "Type": "LOCAL_FILE", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000093", + "EmbeddingId": "EmbeddingId sample 94", + "MaxChunkTokenLength": 85, + "ChunkOverlapTokenLength": 86, + "ConfidenceLevel": "UNTRUSTED", + "Host": "Host sample 97", + "Port": 88, + "TransportSecurity": "SSL_ON_CONNECT", + "AuthMethod": "PASSWORD", + "Username": "Username sample 99", + "RootFolder": "RootFolder sample 100", + "IncludeSentAndDrafts": true, + "MaxAge": "LAST_6_MONTHS", + "IndexAttachments": true, + "MaxAttachmentSizeMegabytes": 41, + "OutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "MaxMatches": 8 + }, + { + "Num": 7, + "Id": "Id sample 102", + "Name": "Name sample 103", + "Type": "LOCAL_FILE", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000104", + "EmbeddingId": "EmbeddingId sample 105", + "MaxChunkTokenLength": 46, + "ChunkOverlapTokenLength": 47, + "ConfidenceLevel": "UNTRUSTED", + "Host": "Host sample 108", + "Port": 49, + "TransportSecurity": "SSL_ON_CONNECT", + "AuthMethod": "PASSWORD", + "Username": "Username sample 110", + "RootFolder": "RootFolder sample 111", + "IncludeSentAndDrafts": true, + "MaxAge": "LAST_6_MONTHS", + "IndexAttachments": true, + "MaxAttachmentSizeMegabytes": 52, + "OutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "MaxMatches": 8 + } + ], + "MailboxSettings": { + "MinimumOutboundDataRestriction": "ONLY_LINKS_FROM_CHAT", + "AllowOnlyOrganizationMailServers": true + }, + "Profiles": [ + { + "Num": 7, + "Id": "Id sample 113", + "Name": "Name sample 114", + "NeedToKnow": "NeedToKnow sample 115", + "Actions": "Actions sample 116", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000117" + }, + { + "Num": 7, + "Id": "Id sample 118", + "Name": "Name sample 119", + "NeedToKnow": "NeedToKnow sample 120", + "Actions": "Actions sample 121", + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000122" + } + ], + "ChatTemplates": [ + { + "Num": 7, + "Id": "Id sample 123", + "Name": "Name sample 124", + "SystemPrompt": "SystemPrompt sample 125", + "PredefinedUserPrompt": "PredefinedUserPrompt sample 126", + "ExampleConversation": [ + { + "Time": "2026-10-29T14:37:15+01:00", + "ContentType": "UNKNOWN", + "Content": { + "$type": "text", + "Sources": [ + { + "Title": "Title sample 128", + "URL": "URL sample 129", + "Origin": "RAG" + }, + { + "Title": "Title sample 130", + "URL": "URL sample 131", + "Origin": "RAG" + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 132", + "FilePath": "/samples/FilePath-133.txt", + "FileSizeBytes": 4000000134, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-133.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000135", + "Version": { + "LastWriteUtc": "2026-10-29T13:16:00Z", + "Length": 4000000137 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 138", + "FilePath": "/samples/FilePath-139.txt", + "FileSizeBytes": 4000000140, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-139.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000141", + "Version": { + "LastWriteUtc": "2026-10-29T13:22:00Z", + "Length": 4000000143 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 147", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 144", + "FilePath": "/samples/FilePath-145.txt", + "FileSizeBytes": 4000000146, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-145.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000148", + "Version": { + "LastWriteUtc": "2026-10-29T13:29:00Z", + "Length": 4000000150 + } + } + ], + "ToolInvocations": [ + { + "Order": 41, + "ToolId": "ToolId sample 152", + "ToolName": "ToolName sample 153", + "ToolIcon": "ToolIcon sample 154", + "ToolCallId": "ToolCallId sample 155", + "Status": "SUCCESS", + "WasExecuted": true, + "StatusMessage": "StatusMessage sample 156", + "Arguments": { + "ArgumentsKey sample 157": "Arguments sample 158", + "ArgumentsKey sample 159": "Arguments sample 160" + } + }, + { + "Order": 51, + "ToolId": "ToolId sample 162", + "ToolName": "ToolName sample 163", + "ToolIcon": "ToolIcon sample 164", + "ToolCallId": "ToolCallId sample 165", + "Status": "SUCCESS", + "WasExecuted": true, + "StatusMessage": "StatusMessage sample 166", + "Arguments": { + "ArgumentsKey sample 167": "Arguments sample 168", + "ArgumentsKey sample 169": "Arguments sample 170" + } + } + ], + "Presentation": { + "Kind": "SWOT_ANALYSIS", + "Data": { + "sample": 171, + "text": "Gr\u00F6\u00DFe" + } + }, + "SourcePassages": [ + { + "SourceUrl": "SourceUrl sample 172", + "DataSourceName": "DataSourceName sample 173", + "Path": "/samples/Path-174.txt", + "Category": "UNKNOWN", + "Type": "UNKNOWN", + "ProvenanceId": "00000000-0000-4000-8000-000000000175", + "Text": "Text sample 176", + "SurroundingContent": [ + "SurroundingContent sample 177", + "SurroundingContent sample 178" + ], + "PageNumber": 69, + "EndPageNumber": 70, + "SheetName": "SheetName sample 181", + "StartRow": 72, + "EndRow": 73, + "StartLine": 74, + "EndLine": 75, + "ChunkNumber": 76 + }, + { + "SourceUrl": "SourceUrl sample 187", + "DataSourceName": "DataSourceName sample 188", + "Path": "/samples/Path-189.txt", + "Category": "UNKNOWN", + "Type": "UNKNOWN", + "ProvenanceId": "00000000-0000-4000-8000-000000000190", + "Text": "Text sample 191", + "SurroundingContent": [ + "SurroundingContent sample 192", + "SurroundingContent sample 193" + ], + "PageNumber": 84, + "EndPageNumber": 85, + "SheetName": "SheetName sample 196", + "StartRow": 87, + "EndRow": 88, + "StartLine": 89, + "EndLine": 40, + "ChunkNumber": 41 + } + ], + "ReportedUsage": { + "PromptTokens": 42, + "ModelId": "ModelId sample 203", + "BlockCount": 44 + }, + "Text": "Text sample 205" + }, + "Role": "UNKNOWN", + "HideFromUser": true + }, + { + "Time": "2026-10-29T15:56:15+01:00", + "ContentType": "UNKNOWN", + "Content": { + "$type": "image", + "Sources": [ + { + "Title": "Title sample 207", + "URL": "URL sample 208", + "Origin": "RAG" + }, + { + "Title": "Title sample 209", + "URL": "URL sample 210", + "Origin": "RAG" + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 211", + "FilePath": "/samples/FilePath-212.txt", + "FileSizeBytes": 4000000213, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-212.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000214", + "Version": { + "LastWriteUtc": "2026-10-29T14:35:00Z", + "Length": 4000000216 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 217", + "FilePath": "/samples/FilePath-218.txt", + "FileSizeBytes": 4000000219, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-218.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000220", + "Version": { + "LastWriteUtc": "2026-10-29T14:41:00Z", + "Length": 4000000222 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 226", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 223", + "FilePath": "/samples/FilePath-224.txt", + "FileSizeBytes": 4000000225, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-224.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000227", + "Version": { + "LastWriteUtc": "2026-10-29T14:48:00Z", + "Length": 4000000229 + } + } + ], + "SourceType": "LOCAL_PATH", + "Source": "Source sample 230" + }, + "Role": "UNKNOWN", + "HideFromUser": true + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 231", + "FilePath": "/samples/FilePath-232.txt", + "FileSizeBytes": 4000000233, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-232.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000234", + "Version": { + "LastWriteUtc": "2026-10-29T14:55:00Z", + "Length": 4000000236 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 237", + "FilePath": "/samples/FilePath-238.txt", + "FileSizeBytes": 4000000239, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-238.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000240", + "Version": { + "LastWriteUtc": "2026-10-29T15:01:00Z", + "Length": 4000000242 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 246", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 243", + "FilePath": "/samples/FilePath-244.txt", + "FileSizeBytes": 4000000245, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-244.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000247", + "Version": { + "LastWriteUtc": "2026-10-29T15:08:00Z", + "Length": 4000000249 + } + } + ], + "AllowProfileUsage": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000250", + "ToolIds": [ + "ToolIds sample 251", + "ToolIds sample 252" + ], + "DataSourceOptions": { + "DisableDataSources": true, + "AutomaticDataSourceSelection": true, + "AutomaticValidation": true, + "RetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 253", + "PreselectedDataSourceIds sample 254" + ] + }, + "ProfileId": "ProfileId sample 255", + "ProviderId": "ProviderId sample 256", + "WorkspaceName": "WorkspaceName sample 257" + }, + { + "Num": 7, + "Id": "Id sample 258", + "Name": "Name sample 259", + "SystemPrompt": "SystemPrompt sample 260", + "PredefinedUserPrompt": "PredefinedUserPrompt sample 261", + "ExampleConversation": [ + { + "Time": "2026-10-29T16:52:15+01:00", + "ContentType": "UNKNOWN", + "Content": { + "$type": "text", + "Sources": [ + { + "Title": "Title sample 263", + "URL": "URL sample 264", + "Origin": "RAG" + }, + { + "Title": "Title sample 265", + "URL": "URL sample 266", + "Origin": "RAG" + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 267", + "FilePath": "/samples/FilePath-268.txt", + "FileSizeBytes": 4000000269, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-268.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000270", + "Version": { + "LastWriteUtc": "2026-10-29T15:31:00Z", + "Length": 4000000272 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 273", + "FilePath": "/samples/FilePath-274.txt", + "FileSizeBytes": 4000000275, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-274.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000276", + "Version": { + "LastWriteUtc": "2026-10-29T15:37:00Z", + "Length": 4000000278 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 282", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 279", + "FilePath": "/samples/FilePath-280.txt", + "FileSizeBytes": 4000000281, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-280.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000283", + "Version": { + "LastWriteUtc": "2026-10-29T15:44:00Z", + "Length": 4000000285 + } + } + ], + "ToolInvocations": [ + { + "Order": 76, + "ToolId": "ToolId sample 287", + "ToolName": "ToolName sample 288", + "ToolIcon": "ToolIcon sample 289", + "ToolCallId": "ToolCallId sample 290", + "Status": "SUCCESS", + "WasExecuted": true, + "StatusMessage": "StatusMessage sample 291", + "Arguments": { + "ArgumentsKey sample 292": "Arguments sample 293", + "ArgumentsKey sample 294": "Arguments sample 295" + } + }, + { + "Order": 86, + "ToolId": "ToolId sample 297", + "ToolName": "ToolName sample 298", + "ToolIcon": "ToolIcon sample 299", + "ToolCallId": "ToolCallId sample 300", + "Status": "SUCCESS", + "WasExecuted": true, + "StatusMessage": "StatusMessage sample 301", + "Arguments": { + "ArgumentsKey sample 302": "Arguments sample 303", + "ArgumentsKey sample 304": "Arguments sample 305" + } + } + ], + "Presentation": { + "Kind": "SWOT_ANALYSIS", + "Data": { + "sample": 306, + "text": "Gr\u00F6\u00DFe" + } + }, + "SourcePassages": [ + { + "SourceUrl": "SourceUrl sample 307", + "DataSourceName": "DataSourceName sample 308", + "Path": "/samples/Path-309.txt", + "Category": "UNKNOWN", + "Type": "UNKNOWN", + "ProvenanceId": "00000000-0000-4000-8000-000000000310", + "Text": "Text sample 311", + "SurroundingContent": [ + "SurroundingContent sample 312", + "SurroundingContent sample 313" + ], + "PageNumber": 54, + "EndPageNumber": 55, + "SheetName": "SheetName sample 316", + "StartRow": 57, + "EndRow": 58, + "StartLine": 59, + "EndLine": 60, + "ChunkNumber": 61 + }, + { + "SourceUrl": "SourceUrl sample 322", + "DataSourceName": "DataSourceName sample 323", + "Path": "/samples/Path-324.txt", + "Category": "UNKNOWN", + "Type": "UNKNOWN", + "ProvenanceId": "00000000-0000-4000-8000-000000000325", + "Text": "Text sample 326", + "SurroundingContent": [ + "SurroundingContent sample 327", + "SurroundingContent sample 328" + ], + "PageNumber": 69, + "EndPageNumber": 70, + "SheetName": "SheetName sample 331", + "StartRow": 72, + "EndRow": 73, + "StartLine": 74, + "EndLine": 75, + "ChunkNumber": 76 + } + ], + "ReportedUsage": { + "PromptTokens": 77, + "ModelId": "ModelId sample 338", + "BlockCount": 79 + }, + "Text": "Text sample 340" + }, + "Role": "UNKNOWN", + "HideFromUser": true + }, + { + "Time": "2026-10-29T18:11:15+01:00", + "ContentType": "UNKNOWN", + "Content": { + "$type": "image", + "Sources": [ + { + "Title": "Title sample 342", + "URL": "URL sample 343", + "Origin": "RAG" + }, + { + "Title": "Title sample 344", + "URL": "URL sample 345", + "Origin": "RAG" + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 346", + "FilePath": "/samples/FilePath-347.txt", + "FileSizeBytes": 4000000348, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-347.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000349", + "Version": { + "LastWriteUtc": "2026-10-29T16:50:00Z", + "Length": 4000000351 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 352", + "FilePath": "/samples/FilePath-353.txt", + "FileSizeBytes": 4000000354, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-353.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000355", + "Version": { + "LastWriteUtc": "2026-10-29T16:56:00Z", + "Length": 4000000357 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 361", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 358", + "FilePath": "/samples/FilePath-359.txt", + "FileSizeBytes": 4000000360, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-359.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000362", + "Version": { + "LastWriteUtc": "2026-10-29T17:03:00Z", + "Length": 4000000364 + } + } + ], + "SourceType": "LOCAL_PATH", + "Source": "Source sample 365" + }, + "Role": "UNKNOWN", + "HideFromUser": true + } + ], + "FileAttachments": [ + { + "$type": "file", + "Type": "IMAGE", + "FileName": "FileName sample 366", + "FilePath": "/samples/FilePath-367.txt", + "FileSizeBytes": 4000000368, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-367.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000369", + "Version": { + "LastWriteUtc": "2026-10-29T17:10:00Z", + "Length": 4000000371 + } + }, + { + "$type": "image", + "SourceType": "LOCAL_PATH", + "Type": "IMAGE", + "FileName": "FileName sample 372", + "FilePath": "/samples/FilePath-373.txt", + "FileSizeBytes": 4000000374, + "IsForbidden": false, + "IsValid": true, + "IsImage": true, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-373.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000375", + "Version": { + "LastWriteUtc": "2026-10-29T17:16:00Z", + "Length": 4000000377 + } + }, + { + "$type": "managed_transcript", + "OriginalFileName": "OriginalFileName sample 381", + "IsStaged": true, + "Type": "IMAGE", + "FileName": "FileName sample 378", + "FilePath": "/samples/FilePath-379.txt", + "FileSizeBytes": 4000000380, + "IsForbidden": false, + "IsValid": true, + "IsImage": false, + "FilePathAsUrl": "/local/file?path=%2Fsamples%2FFilePath-379.txt", + "ProvenanceId": "00000000-0000-4000-8000-000000000382", + "Version": { + "LastWriteUtc": "2026-10-29T17:23:00Z", + "Length": 4000000384 + } + } + ], + "AllowProfileUsage": true, + "IsEnterpriseConfiguration": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000385", + "ToolIds": [ + "ToolIds sample 386", + "ToolIds sample 387" + ], + "DataSourceOptions": { + "DisableDataSources": true, + "AutomaticDataSourceSelection": true, + "AutomaticValidation": true, + "RetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 388", + "PreselectedDataSourceIds sample 389" + ] + }, + "ProfileId": "ProfileId sample 390", + "ProviderId": "ProviderId sample 391", + "WorkspaceName": "WorkspaceName sample 392" + } + ], + "EnabledPlugins": [ + "00000000-0000-4000-8000-000000000393", + "00000000-0000-4000-8000-000000000394" + ], + "ManagedEditableDefaults": { + "ManagedEditableDefaultsKey sample 395": { + "ConfigPluginId": "00000000-0000-4000-8000-000000000396", + "LastAppliedValue": "LastAppliedValue sample 397" + }, + "ManagedEditableDefaultsKey sample 398": { + "ConfigPluginId": "00000000-0000-4000-8000-000000000399", + "LastAppliedValue": "LastAppliedValue sample 400" + } + }, + "ManagedLockedConfigurations": { + "ManagedLockedConfigurationsKey sample 401": "00000000-0000-4000-8000-000000000402", + "ManagedLockedConfigurationsKey sample 403": "00000000-0000-4000-8000-000000000404" + }, + "ManagedUserValueSnapshots": { + "ManagedUserValueSnapshotsKey sample 405": "ManagedUserValueSnapshots sample 406", + "ManagedUserValueSnapshotsKey sample 407": "ManagedUserValueSnapshots sample 408" + }, + "AssistantPluginAudits": [ + { + "PluginId": "00000000-0000-4000-8000-000000000409", + "PluginHash": "PluginHash sample 410", + "AuditedAtUtc": "2026-10-29T19:21:15+01:00", + "AuditProviderId": "AuditProviderId sample 412", + "AuditProviderName": "AuditProviderName sample 413", + "Level": "DANGEROUS", + "Summary": "Summary sample 414", + "Confidence": 2.25, + "PromptPreview": "PromptPreview sample 415", + "Findings": [ + { + "severity": "unknown", + "Category": "Category sample 417", + "Location": "Location sample 418", + "Description": "Description sample 419" + }, + { + "severity": "unknown", + "Category": "Category sample 421", + "Location": "Location sample 422", + "Description": "Description sample 423" + } + ] + }, + { + "PluginId": "00000000-0000-4000-8000-000000000424", + "PluginHash": "PluginHash sample 425", + "AuditedAtUtc": "2026-10-29T19:36:15+01:00", + "AuditProviderId": "AuditProviderId sample 427", + "AuditProviderName": "AuditProviderName sample 428", + "Level": "DANGEROUS", + "Summary": "Summary sample 429", + "Confidence": 2.25, + "PromptPreview": "PromptPreview sample 430", + "Findings": [ + { + "severity": "unknown", + "Category": "Category sample 432", + "Location": "Location sample 433", + "Description": "Description sample 434" + }, + { + "severity": "unknown", + "Category": "Category sample 436", + "Location": "Location sample 437", + "Description": "Description sample 438" + } + ] + } + ], + "AppliedEnterpriseAssistantActivations": [ + "AppliedEnterpriseAssistantActivations sample 439", + "AppliedEnterpriseAssistantActivations sample 440" + ], + "StoredDataPathsAreRelative": true, + "NextProviderNum": 7, + "NextEmbeddingNum": 7, + "NextTranscriptionNum": 7, + "NextDataSourceNum": 7, + "NextProfileNum": 7, + "NextChatTemplateNum": 7, + "NextDocumentAnalysisPolicyNum": 7, + "App": { + "LanguageBehavior": "MANUAL", + "LanguagePluginId": "00000000-0000-4000-8000-000000000441", + "PreferredTheme": "LIGHT", + "IsSavingEnergy": true, + "EnableSpellchecking": true, + "CloseExportMessagesAutomatically": true, + "UpdateInterval": "DISABLE_UPDATES", + "UpdateInstallation": "AUTOMATIC", + "NavigationBehavior": "NEVER_EXPAND_USE_TOOLTIPS", + "StartPage": "CHAT", + "InterfaceScreenSizes": [ + { + "ScreenKey": "ScreenKey sample 442", + "Size": "SYSTEM_SCALING", + "CustomPercent": 83 + }, + { + "ScreenKey": "ScreenKey sample 444", + "Size": "SYSTEM_SCALING", + "CustomPercent": 85 + } + ], + "ShowPromptInjectionAlert": true, + "ShowIntroduction": true, + "ShowQuickStartGuide": true, + "ShowLastChangelog": true, + "ShowVision": true, + "PreviewVisibility": "RELEASE_CANDIDATE", + "EnabledPreviewFeatures": [ + "PRE_WRITER_MODE_2024", + "PRE_RAG_2024" + ], + "PreselectedProvider": "PreselectedProvider sample 446", + "PreselectedProfile": "PreselectedProfile sample 447", + "PreselectedChatTemplate": "PreselectedChatTemplate sample 448", + "UseTranscriptionProvider": "UseTranscriptionProvider sample 449", + "OpusBitrate": "KBPS_32", + "ShortcutVoiceRecording": "ShortcutVoiceRecording sample 450", + "ShortcutVoiceRecordingDisplayName": "ShortcutVoiceRecordingDisplayName sample 451", + "ShortcutVoiceRecordingDisplaySource": "ShortcutVoiceRecordingDisplaySource sample 452", + "HttpClientTimeoutSeconds": 43, + "ShareFeatureUsageWithSelfHostedServerOperators": true, + "ExternalHttpCustomRootCertificatesEnabled": true, + "ExternalHttpCustomRootCertificateBundlePath": "/samples/ExternalHttpCustomRootCertificateBundlePath-454.txt", + "ExternalHttpCustomRootCertificateAllowedHosts": [ + "ExternalHttpCustomRootCertificateAllowedHosts sample 455", + "ExternalHttpCustomRootCertificateAllowedHosts sample 456" + ], + "AllowUserToAddProvider": true, + "AllowUserToAddLLMProvider": true, + "AllowUserToAddEmbeddingProvider": true, + "AllowUserToAddTranscriptionProvider": true, + "ProhibitedProviders": [ + "OPEN_AI", + "ANTHROPIC" + ], + "AllowUserToAddMailbox": true, + "AllowUserToImportConfigurationSnippets": true, + "AllowUserToImportProfile": true, + "AllowUserToImportLLMProvider": true, + "AllowUserToImportEmbeddingProvider": true, + "AllowUserToImportTranscriptionProvider": true, + "AllowUserToImportChatTemplate": true, + "AllowUserToImportERIDataSource": true, + "AllowUserToImportDocumentAnalysisPolicy": true, + "AllowUserToImportPlugins": true, + "AllowUserToImportConfigurationPlugins": true, + "AllowUserToSharePlugins": true, + "AllowUserToBackUpData": true, + "AllowUserToRestoreData": true, + "ShowAdminSettings": true, + "EnterpriseEncryptionExportFormat": "V2", + "AddAIGeneratedContentDisclosure": true, + "DataSourceIndexing": { + "AutomaticRefresh": true, + "MinimumPauseBetweenStepsSeconds": 47, + "MaximumPauseBetweenStepsSeconds": 48 + }, + "HiddenAssistants": [ + "UNKNOWN", + "GRAMMAR_SPELLING_ASSISTANT" + ] + }, + "Chat": { + "ShortcutSendBehavior": "MODIFER_ENTER_IS_SENDING", + "LoadingProviderBehavior": "ALWAYS_USE_DEFAULT_CHAT_PROVIDER", + "AddChatProviderBehavior": "ADDED_CHATS_USE_LATEST_PROVIDER", + "SendToChatDataSourceBehavior": "APPLY_STANDARD_CHAT_DATA_SOURCE_OPTIONS", + "PreselectOptions": true, + "PreselectedProvider": "PreselectedProvider sample 459", + "PreselectedProfile": "PreselectedProfile sample 460", + "PreselectedChatTemplate": "PreselectedChatTemplate sample 461", + "PreselectedDataSourcesDisabled": true, + "PreselectedDataSourcesAutomaticSelection": true, + "PreselectedDataSourcesAutomaticValidation": true, + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 464", + "PreselectedDataSourceIds sample 465" + ], + "PreselectedDataSourcesRetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceOptions": { + "DisableDataSources": true, + "AutomaticDataSourceSelection": true, + "AutomaticValidation": true, + "RetrievalMode": "EVERY_MESSAGE", + "PreselectedDataSourceIds": [ + "PreselectedDataSourceIds sample 464", + "PreselectedDataSourceIds sample 465" + ] + }, + "ShowLatestMessageAfterLoading": true, + "ReadChangedAttachmentsAgain": true + }, + "Workspace": { + "StorageBehavior": "STORE_CHATS_AUTOMATICALLY", + "StorageTemporaryMaintenancePolicy": "DELETE_OLDER_THAN_7_DAYS", + "DisplayBehavior": "TOGGLE_SIDEBAR", + "IsSidebarVisible": true, + "SplitterPosition": 1.5 + }, + "IconFinder": { + "PreselectOptions": true, + "PreselectedSource": "ICONS8", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 466" + }, + "Translation": { + "DebounceIntervalMilliseconds": 57, + "PreselectOptions": true, + "PreselectLiveTranslation": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 468", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 469" + }, + "Coding": { + "PreselectOptions": true, + "PreselectCompilerMessages": true, + "PreselectedProgrammingLanguage": "BASH", + "PreselectedOtherProgrammingLanguage": "PreselectedOtherProgrammingLanguage sample 470", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 471", + "PreselectedProfile": "PreselectedProfile sample 472" + }, + "ERI": { + "AutoSaveChanges": true, + "PreselectOptions": true, + "ERIServers": [ + { + "ServerName": "ServerName sample 473", + "ServerDescription": "ServerDescription sample 474", + "ERIVersion": "V1", + "ProgrammingLanguage": "C", + "OtherProgrammingLanguage": "OtherProgrammingLanguage sample 475", + "DataSource": "CUSTOM", + "DataSourceProductName": "DataSourceProductName sample 476", + "OtherDataSource": "OtherDataSource sample 477", + "DataSourceHostname": "DataSourceHostname sample 478", + "DataSourcePort": 69, + "UserTypedPort": true, + "AuthMethods": [ + "KERBEROS", + "USERNAME_PASSWORD" + ], + "AuthDescription": "AuthDescription sample 480", + "OperatingSystem": "WINDOWS", + "AllowedLLMProviders": "ANY", + "EmbeddingInfos": [ + { + "EmbeddingType": "EmbeddingType sample 481", + "EmbeddingName": "EmbeddingName sample 482", + "Description": "Description sample 483", + "UsedWhen": "UsedWhen sample 484", + "Link": "Link sample 485" + }, + { + "EmbeddingType": "EmbeddingType sample 486", + "EmbeddingName": "EmbeddingName sample 487", + "Description": "Description sample 488", + "UsedWhen": "UsedWhen sample 489", + "Link": "Link sample 490" + } + ], + "RetrievalInfos": [ + { + "Name": "Name sample 491", + "Description": "Description sample 492", + "Link": "Link sample 493", + "ParametersDescription": { + "ParametersDescriptionKey sample 494": "ParametersDescription sample 495", + "ParametersDescriptionKey sample 496": "ParametersDescription sample 497" + }, + "Embeddings": [ + { + "EmbeddingType": "EmbeddingType sample 498", + "EmbeddingName": "EmbeddingName sample 499", + "Description": "Description sample 500", + "UsedWhen": "UsedWhen sample 501", + "Link": "Link sample 502" + }, + { + "EmbeddingType": "EmbeddingType sample 503", + "EmbeddingName": "EmbeddingName sample 504", + "Description": "Description sample 505", + "UsedWhen": "UsedWhen sample 506", + "Link": "Link sample 507" + } + ] + }, + { + "Name": "Name sample 508", + "Description": "Description sample 509", + "Link": "Link sample 510", + "ParametersDescription": { + "ParametersDescriptionKey sample 511": "ParametersDescription sample 512", + "ParametersDescriptionKey sample 513": "ParametersDescription sample 514" + }, + "Embeddings": [ + { + "EmbeddingType": "EmbeddingType sample 515", + "EmbeddingName": "EmbeddingName sample 516", + "Description": "Description sample 517", + "UsedWhen": "UsedWhen sample 518", + "Link": "Link sample 519" + }, + { + "EmbeddingType": "EmbeddingType sample 520", + "EmbeddingName": "EmbeddingName sample 521", + "Description": "Description sample 522", + "UsedWhen": "UsedWhen sample 523", + "Link": "Link sample 524" + } + ] + } + ], + "AdditionalLibraries": "AdditionalLibraries sample 525", + "WriteToFilesystem": true, + "BaseDirectory": "BaseDirectory sample 526", + "PreviouslyGeneratedFiles": [ + "PreviouslyGeneratedFiles sample 527", + "PreviouslyGeneratedFiles sample 528" + ] + }, + { + "ServerName": "ServerName sample 529", + "ServerDescription": "ServerDescription sample 530", + "ERIVersion": "V1", + "ProgrammingLanguage": "C", + "OtherProgrammingLanguage": "OtherProgrammingLanguage sample 531", + "DataSource": "CUSTOM", + "DataSourceProductName": "DataSourceProductName sample 532", + "OtherDataSource": "OtherDataSource sample 533", + "DataSourceHostname": "DataSourceHostname sample 534", + "DataSourcePort": 75, + "UserTypedPort": true, + "AuthMethods": [ + "KERBEROS", + "USERNAME_PASSWORD" + ], + "AuthDescription": "AuthDescription sample 536", + "OperatingSystem": "WINDOWS", + "AllowedLLMProviders": "ANY", + "EmbeddingInfos": [ + { + "EmbeddingType": "EmbeddingType sample 537", + "EmbeddingName": "EmbeddingName sample 538", + "Description": "Description sample 539", + "UsedWhen": "UsedWhen sample 540", + "Link": "Link sample 541" + }, + { + "EmbeddingType": "EmbeddingType sample 542", + "EmbeddingName": "EmbeddingName sample 543", + "Description": "Description sample 544", + "UsedWhen": "UsedWhen sample 545", + "Link": "Link sample 546" + } + ], + "RetrievalInfos": [ + { + "Name": "Name sample 547", + "Description": "Description sample 548", + "Link": "Link sample 549", + "ParametersDescription": { + "ParametersDescriptionKey sample 550": "ParametersDescription sample 551", + "ParametersDescriptionKey sample 552": "ParametersDescription sample 553" + }, + "Embeddings": [ + { + "EmbeddingType": "EmbeddingType sample 554", + "EmbeddingName": "EmbeddingName sample 555", + "Description": "Description sample 556", + "UsedWhen": "UsedWhen sample 557", + "Link": "Link sample 558" + }, + { + "EmbeddingType": "EmbeddingType sample 559", + "EmbeddingName": "EmbeddingName sample 560", + "Description": "Description sample 561", + "UsedWhen": "UsedWhen sample 562", + "Link": "Link sample 563" + } + ] + }, + { + "Name": "Name sample 564", + "Description": "Description sample 565", + "Link": "Link sample 566", + "ParametersDescription": { + "ParametersDescriptionKey sample 567": "ParametersDescription sample 568", + "ParametersDescriptionKey sample 569": "ParametersDescription sample 570" + }, + "Embeddings": [ + { + "EmbeddingType": "EmbeddingType sample 571", + "EmbeddingName": "EmbeddingName sample 572", + "Description": "Description sample 573", + "UsedWhen": "UsedWhen sample 574", + "Link": "Link sample 575" + }, + { + "EmbeddingType": "EmbeddingType sample 576", + "EmbeddingName": "EmbeddingName sample 577", + "Description": "Description sample 578", + "UsedWhen": "UsedWhen sample 579", + "Link": "Link sample 580" + } + ] + } + ], + "AdditionalLibraries": "AdditionalLibraries sample 581", + "WriteToFilesystem": true, + "BaseDirectory": "BaseDirectory sample 582", + "PreviouslyGeneratedFiles": [ + "PreviouslyGeneratedFiles sample 583", + "PreviouslyGeneratedFiles sample 584" + ] + } + ], + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 585", + "PreselectedProfile": "PreselectedProfile sample 586" + }, + "DocumentAnalysis": { + "Policies": [ + { + "Id": "Id sample 587", + "Num": 7, + "Name": "PolicyName sample 589", + "IsEnterpriseConfiguration": true, + "PolicyName": "PolicyName sample 589", + "PolicyDescription": "PolicyDescription sample 590", + "IsProtected": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000591", + "AnalysisRules": "AnalysisRules sample 592", + "OutputRules": "OutputRules sample 593", + "MinimumProviderConfidence": "UNTRUSTED", + "AllowedToolIds": [ + "AllowedToolIds sample 594", + "AllowedToolIds sample 595" + ], + "PreselectedProvider": "PreselectedProvider sample 596", + "PreselectedProfile": "PreselectedProfile sample 597", + "HidePolicyDefinition": true + }, + { + "Id": "Id sample 598", + "Num": 7, + "Name": "PolicyName sample 600", + "IsEnterpriseConfiguration": true, + "PolicyName": "PolicyName sample 600", + "PolicyDescription": "PolicyDescription sample 601", + "IsProtected": true, + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000602", + "AnalysisRules": "AnalysisRules sample 603", + "OutputRules": "OutputRules sample 604", + "MinimumProviderConfidence": "UNTRUSTED", + "AllowedToolIds": [ + "AllowedToolIds sample 605", + "AllowedToolIds sample 606" + ], + "PreselectedProvider": "PreselectedProvider sample 607", + "PreselectedProfile": "PreselectedProfile sample 608", + "HidePolicyDefinition": true + } + ] + }, + "BatchProcessing": { + "PreselectOptions": true, + "InputDirectory": "InputDirectory sample 609", + "OutputDirectory": "OutputDirectory sample 610", + "FilePatterns": "FilePatterns sample 611", + "IncludeSubdirectories": true, + "IncludeHiddenFilesAndFolders": true, + "ExcludePatterns": "ExcludePatterns sample 612", + "MaxFileSizeMegabytes": 53, + "PromptSource": "POLICY", + "FreePrompt": "FreePrompt sample 614", + "PromptFilePath": "/samples/PromptFilePath-615.txt", + "PreselectedPolicyId": "PreselectedPolicyId sample 616", + "OutputMode": "TABLE_ONLY", + "ResultFileFormat": "UNKNOWN", + "CsvFileName": "CsvFileName sample 617", + "ResultColumnHeader": "ResultColumnHeader sample 618", + "CsvSeparator": "SEMICOLON", + "CustomCsvSeparator": "CustomCsvSeparator sample 619", + "MinimumDelaySeconds": 60, + "MaximumDelaySeconds": 61, + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 622" + }, + "MandatoryInformation": { + "Acceptances": [ + { + "InfoId": "InfoId sample 623", + "AcceptedVersion": "AcceptedVersion sample 624", + "AcceptedHash": "AcceptedHash sample 625", + "AcceptedAtUtc": "2026-10-29T22:56:15+01:00", + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000627" + }, + { + "InfoId": "InfoId sample 628", + "AcceptedVersion": "AcceptedVersion sample 629", + "AcceptedHash": "AcceptedHash sample 630", + "AcceptedAtUtc": "2026-10-29T23:01:15+01:00", + "EnterpriseConfigurationPluginId": "00000000-0000-4000-8000-000000000632" + } + ] + }, + "TextSummarizer": { + "PreselectOptions": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 633", + "PreselectedComplexity": "SIMPLE_LANGUAGE", + "PreselectedExpertInField": "PreselectedExpertInField sample 634", + "PreselectedImportantAspects": "PreselectedImportantAspects sample 635", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 636" + }, + "SWOTAnalysis": { + "PreselectOptions": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 637", + "PreselectedImportantAspects": "PreselectedImportantAspects sample 638", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 639" + }, + "TextContentCleaner": { + "PreselectAgentOptions": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 640" + }, + "AgentDataSourceSelection": { + "PreselectAgentOptions": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 641" + }, + "AgentRetrievalContextValidation": { + "EnableRetrievalContextValidation": true, + "PreselectAgentOptions": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 642", + "NumParallelValidations": 83 + }, + "AssistantPluginAudit": { + "RequireAuditBeforeActivation": true, + "PreselectedAgentProvider": "PreselectedAgentProvider sample 644", + "MinimumLevel": "DANGEROUS", + "BlockActivationBelowMinimum": true, + "AutomaticallyAuditAssistants": true, + "EnterpriseApprovedPlugins": [ + { + "PluginHash": "PluginHash sample 645", + "DisplayName": "DisplayName sample 646", + "Comment": "Comment sample 647", + "ApprovedBy": "ApprovedBy sample 648", + "ApprovedAtUtc": "2026-10-29T23:19:15+01:00", + "Activate": true, + "AllowUserOverride": true + }, + { + "PluginHash": "PluginHash sample 650", + "DisplayName": "DisplayName sample 651", + "Comment": "Comment sample 652", + "ApprovedBy": "ApprovedBy sample 653", + "ApprovedAtUtc": "2026-10-29T23:24:15+01:00", + "Activate": true, + "AllowUserOverride": true + } + ] + }, + "Agenda": { + "PreselectOptions": true, + "PreselectName": "PreselectName sample 655", + "PreselectTopic": "PreselectTopic sample 656", + "PreselectObjective": "PreselectObjective sample 657", + "PreselectModerator": "PreselectModerator sample 658", + "PreselectDuration": "PreselectDuration sample 659", + "PreselectStartTime": "PreselectStartTime sample 660", + "PreselectIntroduceParticipants": true, + "PreselectNumberParticipants": "PEER_TO_PEER", + "PreselectActiveParticipation": true, + "PreselectIsMeetingVirtual": true, + "PreselectLocation": "PreselectLocation sample 661", + "PreselectJointDinner": true, + "PreselectSocialActivity": true, + "PreselectArriveAndDepart": true, + "PreselectLunchTime": 52, + "PreselectBreakTime": 53, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 664", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 665", + "PreselectedProfile": "PreselectedProfile sample 666" + }, + "GrammarSpelling": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 667", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 668" + }, + "RewriteImprove": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 669", + "PreselectedWritingStyle": "EVERYDAY", + "PreselectedSentenceStructure": "ACTIVE", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 670" + }, + "PromptOptimizer": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 671", + "PreselectedImportantAspects": "PreselectedImportantAspects sample 672", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 673" + }, + "EMail": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 674", + "PreselectedWritingStyle": "BUSINESS_FORMAL", + "Greeting": "Greeting sample 675", + "SenderName": "SenderName sample 676", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 677", + "PreselectedProfile": "PreselectedProfile sample 678" + }, + "SlideBuilder": { + "TemplatePath": "/samples/TemplatePath-679.txt", + "PreselectedFooterText": "PreselectedFooterText sample 680", + "PreselectedMaxBulletPointsPerSlide": 71, + "PreselectedKeepEmptyPicturePlaceholders": true, + "PreselectedPictureLibraryPath": "/samples/PreselectedPictureLibraryPath-682.txt", + "PreselectedLayouts": { + "TABLE_OF_CONTENTS": "PreselectedLayouts sample 683", + "AGENDA": "PreselectedLayouts sample 684" + }, + "PreselectedTemplateSlideUses": { + "75": "KEEP", + "76": "EDIT" + }, + "PreselectOptions": true, + "PreselectedProfile": "PreselectedProfile sample 687", + "PreselectedProvider": "PreselectedProvider sample 688", + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 689", + "PreselectedAudienceProfile": "STUDENTS", + "PreselectedAudienceAgeGroup": "CHILDREN", + "PreselectedAudienceOrganizationalLevel": "TRAINEES", + "PreselectedAudienceExpertise": "NON_EXPERTS", + "PreselectedOccasion": "TALK", + "PreselectedOtherOccasion": "PreselectedOtherOccasion sample 690", + "PreselectedImportantAspects": "PreselectedImportantAspects sample 691", + "MinimumProviderConfidence": "UNTRUSTED" + }, + "AssistantBuilder": { + "PreselectOptions": true, + "PreselectedProvider": "PreselectedProvider sample 692", + "PreselectedOutputLanguage": "EN_US", + "PreselectedOtherOutputLanguage": "PreselectedOtherOutputLanguage sample 693", + "MinimumProviderConfidence": "UNTRUSTED" + }, + "VisualBriefing": { + "PreselectedProfile": "PreselectedProfile sample 694", + "PreselectedProvider": "PreselectedProvider sample 695", + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 696", + "PreselectedAudienceProfile": "STUDENTS", + "PreselectedAudienceAgeGroup": "CHILDREN", + "PreselectedAudienceOrganizationalLevel": "TRAINEES", + "PreselectedAudienceExpertise": "NON_EXPERTS", + "ShowSourceReferences": true, + "OptimizeImages": true, + "MinimumProviderConfidence": "UNTRUSTED" + }, + "LegalCheck": { + "PreselectOptions": true, + "HideWebContentReader": true, + "PreselectWebContentReader": true, + "PreselectContentCleanerAgent": true, + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 697", + "PreselectedProfile": "PreselectedProfile sample 698" + }, + "Synonyms": { + "PreselectOptions": true, + "PreselectedLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 699", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 700" + }, + "MyTasks": { + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 701", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 702", + "PreselectedProfile": "PreselectedProfile sample 703" + }, + "JobPostings": { + "PreselectOptions": true, + "PreselectedMandatoryInformation": "PreselectedMandatoryInformation sample 704", + "PreselectedJobDescription": "PreselectedJobDescription sample 705", + "PreselectedQualifications": "PreselectedQualifications sample 706", + "PreselectedResponsibilities": "PreselectedResponsibilities sample 707", + "PreselectedCompanyName": "PreselectedCompanyName sample 708", + "PreselectedWorkLocation": "PreselectedWorkLocation sample 709", + "PreselectedCountryLegalFramework": "PreselectedCountryLegalFramework sample 710", + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 711", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProvider": "PreselectedProvider sample 712" + }, + "BiasOfTheDay": { + "UsedBias": [ + 53, + 54 + ], + "DateLastBiasDrawn": "2026-10-29", + "BiasOfTheDayChatId": "00000000-0000-4000-8000-000000000715", + "BiasOfTheDayId": "00000000-0000-4000-8000-000000000716", + "RestrictOneBiasPerDay": true, + "PreselectOptions": true, + "PreselectedTargetLanguage": "EN_US", + "PreselectedOtherLanguage": "PreselectedOtherLanguage sample 717", + "MinimumProviderConfidence": "UNTRUSTED", + "PreselectedProfile": "PreselectedProfile sample 718", + "PreselectedProvider": "PreselectedProvider sample 719" + }, + "I18N": { + "PreselectOptions": true, + "PreselectedLanguagePluginId": "00000000-0000-4000-8000-000000000720", + "PreselectedTargetLanguage": "EN_US", + "PreselectOtherLanguage": "PreselectOtherLanguage sample 721", + "PreselectedProvider": "PreselectedProvider sample 722" + }, + "Tools": { + "Settings": { + "SettingsKey sample 723": { + "SettingsKey sample 724": "Settings sample 725", + "SettingsKey sample 726": "Settings sample 727" + }, + "SettingsKey sample 728": { + "SettingsKey sample 729": "Settings sample 730", + "SettingsKey sample 731": "Settings sample 732" + } + }, + "DefaultToolIdsByComponent": { + "DefaultToolIdsByComponentKey sample 733": [ + "DefaultToolIdsByComponent sample 734", + "DefaultToolIdsByComponent sample 735" + ], + "DefaultToolIdsByComponentKey sample 736": [ + "DefaultToolIdsByComponent sample 737", + "DefaultToolIdsByComponent sample 738" + ] + }, + "VisibleToolSelectionComponents": [ + "VisibleToolSelectionComponents sample 739", + "VisibleToolSelectionComponents sample 740" + ], + "EnableTools": true, + "DisabledToolIds": [ + "DisabledToolIds sample 741", + "DisabledToolIds sample 742" + ], + "UserDisabledToolIds": [ + "UserDisabledToolIds sample 743", + "UserDisabledToolIds sample 744" + ], + "MinimumProviderConfidenceByToolId": { + "MinimumProviderConfidenceByToolIdKey sample 745": "MinimumProviderConfidenceByToolId sample 746", + "MinimumProviderConfidenceByToolIdKey sample 747": "MinimumProviderConfidenceByToolId sample 748" + }, + "LockedToolSettings": { + "LockedToolSettingsKey sample 749": "LockedToolSettings sample 750", + "LockedToolSettingsKey sample 751": "LockedToolSettings sample 752" + }, + "DefaultToolSettings": { + "DefaultToolSettingsKey sample 753": "DefaultToolSettings sample 754", + "DefaultToolSettingsKey sample 755": "DefaultToolSettings sample 756" + } + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/Fixtures/swot-analysis.json b/app/Tests/Serialization/Fixtures/swot-analysis.json new file mode 100644 index 000000000..c90416f47 --- /dev/null +++ b/app/Tests/Serialization/Fixtures/swot-analysis.json @@ -0,0 +1,103 @@ +{ + "matrix_heading": "matrix_heading sample 1", + "internal_label": "internal_label sample 2", + "external_label": "external_label sample 3", + "positive_label": "positive_label sample 4", + "negative_label": "negative_label sample 5", + "strengths": { + "label": "label sample 6", + "empty_message": "empty_message sample 7", + "findings": [ + { + "summary": "summary sample 8", + "explanation": "explanation sample 9" + }, + { + "summary": "summary sample 10", + "explanation": "explanation sample 11" + } + ] + }, + "weaknesses": { + "label": "label sample 12", + "empty_message": "empty_message sample 13", + "findings": [ + { + "summary": "summary sample 14", + "explanation": "explanation sample 15" + }, + { + "summary": "summary sample 16", + "explanation": "explanation sample 17" + } + ] + }, + "opportunities": { + "label": "label sample 18", + "empty_message": "empty_message sample 19", + "findings": [ + { + "summary": "summary sample 20", + "explanation": "explanation sample 21" + }, + { + "summary": "summary sample 22", + "explanation": "explanation sample 23" + } + ] + }, + "threats": { + "label": "label sample 24", + "empty_message": "empty_message sample 25", + "findings": [ + { + "summary": "summary sample 26", + "explanation": "explanation sample 27" + }, + { + "summary": "summary sample 28", + "explanation": "explanation sample 29" + } + ] + }, + "overall_assessment": { + "label": "label sample 30", + "text": "text sample 31" + }, + "prioritized_actions": { + "label": "label sample 32", + "empty_message": "empty_message sample 33", + "items": [ + { + "action": "action sample 34", + "rationale": "rationale sample 35", + "addressed_factors": [ + "addressed_factors sample 36", + "addressed_factors sample 37" + ] + }, + { + "action": "action sample 38", + "rationale": "rationale sample 39", + "addressed_factors": [ + "addressed_factors sample 40", + "addressed_factors sample 41" + ] + } + ] + }, + "moderation_notes": { + "label": "label sample 42", + "empty_message": "empty_message sample 43", + "items": [ + { + "change": "change sample 44", + "reason": "reason sample 45" + }, + { + "change": "change sample 46", + "reason": "reason sample 47" + } + ] + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonContainment.cs b/app/Tests/Serialization/JsonContainment.cs new file mode 100644 index 000000000..4bf04bcd9 --- /dev/null +++ b/app/Tests/Serialization/JsonContainment.cs @@ -0,0 +1,67 @@ +using System.Text.Json.Nodes; + +namespace AIStudio.Tests.Serialization; + +/// +/// Finds the values of one JSON document which another one lacks. +/// +public static class JsonContainment +{ + /// + /// Returns the path of every value in the expected document which the actual one lacks or holds otherwise. + /// + /// + /// Properties only the actual document has don't count, e.g., one added to a type after the expected document + /// was written. A property which is null in the expected document may be missing in the actual one, since + /// some properties are only written with a value. Arrays have to hold the same elements, in any order: an + /// ImmutableHashSet of strings orders them by their hash codes, which differ from process to process. + /// + /// The document whose values have to be there. + /// The document which has to hold them. + /// One line per value, with its path and both values. + public static IReadOnlyList FindMissing(JsonNode? expected, JsonNode? actual) + { + var missing = new List(); + Compare(expected, actual, "$", missing); + return missing; + } + + private static void Compare(JsonNode? expected, JsonNode? actual, string path, List missing) + { + switch (expected) + { + case JsonObject expectedObject when actual is JsonObject actualObject: + foreach (var (name, expectedValue) in expectedObject) + { + if (actualObject.TryGetPropertyValue(name, out var actualValue)) + Compare(expectedValue, actualValue, $"{path}.{name}", missing); + else if (expectedValue is not null) + missing.Add($"{path}.{name}: missing, expected {expectedValue.ToJsonString()}"); + } + + break; + + case JsonArray expectedArray when actual is JsonArray actualArray && expectedArray.Count == actualArray.Count: + var unmatched = actualArray.ToList(); + for (var index = 0; index < expectedArray.Count; index++) + { + var match = unmatched.FindIndex(candidate => FindMissing(expectedArray[index], candidate).Count is 0); + if (match < 0) + { + missing.Add($"{path}[{index}]: no element holds {expectedArray[index]?.ToJsonString() ?? "null"}"); + continue; + } + + unmatched.RemoveAt(match); + } + + break; + + default: + if (!JsonNode.DeepEquals(expected, actual)) + missing.Add($"{path}: {actual?.ToJsonString() ?? "null"}, expected {expected?.ToJsonString() ?? "null"}"); + + break; + } + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonContractDescription.cs b/app/Tests/Serialization/JsonContractDescription.cs new file mode 100644 index 000000000..5980b58cc --- /dev/null +++ b/app/Tests/Serialization/JsonContractDescription.cs @@ -0,0 +1,103 @@ +using System.Text; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace AIStudio.Tests.Serialization; + +/// +/// Describes the contract System.Text.Json uses for a type as text, so that two contracts can be compared and +/// a difference shows up readable in the message of a test. +/// +public static class JsonContractDescription +{ + /// + /// Describes everything in a contract which decides what gets written and what a read accepts. + /// + /// The contract. + /// One line for the type, and one line per property, polymorphism, and callback. + public static string Describe(JsonTypeInfo typeInfo) + { + // + // For a struct, reflection takes the default of the struct and sets every property, while source generation + // passes the values to the constructor of a positional record struct. Both give the same struct, as long as + // no parameter has a default value of its own, which JsonDefaultsTests checks. A class with required + // properties is alike: source generation sets them while it creates the object, reflection right after. + // Hence, the description leaves out how such a type gets created: + // + var isCreatedWithItsValues = typeInfo.Kind is JsonTypeInfoKind.Object && (typeInfo.Type.IsValueType || typeInfo.Properties.Any(property => property.IsRequired)); + var creation = isCreatedWithItsValues + ? "created with its values" + : $"converter {ConverterName(typeInfo.Converter)}, created {(typeInfo.CreateObject is null ? "otherwise" : "without arguments")}"; + + var text = new StringBuilder(); + text.AppendLine($"{TypeName(typeInfo.Type)}: {typeInfo.Kind}, {creation}"); + text.AppendLine($" handling: numbers {typeInfo.NumberHandling}, unmapped members {typeInfo.UnmappedMemberHandling}, object creation {typeInfo.PreferredPropertyObjectCreationHandling}"); + text.AppendLine($" callbacks: serializing {typeInfo.OnSerializing is not null}, serialized {typeInfo.OnSerialized is not null}, deserializing {typeInfo.OnDeserializing is not null}, deserialized {typeInfo.OnDeserialized is not null}"); + if (typeInfo.KeyType is { } keyType) + text.AppendLine($" key: {TypeName(keyType)}"); + + if (typeInfo.ElementType is { } elementType) + text.AppendLine($" element: {TypeName(elementType)}"); + + if (typeInfo.PolymorphismOptions is { } polymorphism) + { + text.AppendLine($" polymorphism: discriminator {polymorphism.TypeDiscriminatorPropertyName}, unrecognized ignored {polymorphism.IgnoreUnrecognizedTypeDiscriminators}, unknown types {polymorphism.UnknownDerivedTypeHandling}"); + foreach (var derivedType in polymorphism.DerivedTypes) + text.AppendLine($" derived: {TypeName(derivedType.DerivedType)} as {derivedType.TypeDiscriminator}"); + } + + foreach (var property in typeInfo.Properties) + { + // + // An ignored property keeps its name in both contracts, but only reflection keeps its type: + // + if (IsIgnored(property)) + { + text.AppendLine($" property {property.Name}: ignored"); + continue; + } + + var setting = isCreatedWithItsValues + ?$"set {property.Set is not null || property.AssociatedParameter is not null}" + : $"set {property.Set is not null}, constructor parameter {property.AssociatedParameter?.Name ?? "none"}"; + + text.AppendLine($" property {property.Name}: {TypeName(property.PropertyType)}, order {property.Order}, get {property.Get is not null}, {setting}, required {property.IsRequired}, extension data {property.IsExtensionData}, conditional {property.ShouldSerialize is not null}, converter {ConverterName(property.CustomConverter)}, nullable get {property.IsGetNullable}, nullable set {property.IsSetNullable}, numbers {property.NumberHandling}, object creation {property.ObjectCreationHandling}"); + } + + return text.ToString(); + } + + /// + /// Whether System.Text.Json neither writes nor reads a property, e.g., one marked with JsonIgnore. + /// + /// The property of a contract. + public static bool IsIgnored(JsonPropertyInfo property) => property is { Get: null, Set: null, AssociatedParameter: null }; + + /// + /// Returns the name of a type the way C# writes it, e.g., Dictionary<String, Int32>. + /// + /// The type. + public static string TypeName(Type type) + { + var arity = type.Name.IndexOf('`'); + var name = arity < 0 ? type.Name : $"{type.Name[..arity]}<{string.Join(", ", type.GetGenericArguments().Select(TypeName))}>"; + return type is { IsNested: true, IsGenericParameter: false } ?$"{TypeName(type.DeclaringType!)}.{name}" : name; + } + + // + // Reflection reads the constructor parameters of a record through converters of its own: one for up to four + // parameters, and others whose names end with "WithReflection". Source generation reads them all with the + // converter for many parameters, which reads the same JSON. The same goes for immutable collections: + // + private static string ConverterName(JsonConverter? converter) + { + if (converter is null) + return "none"; + + var name = converter.GetType().Name; + if (name.Contains("ObjectWithParameterizedConstructorConverter", StringComparison.Ordinal)) + return $"ObjectWithParameterizedConstructorConverter<{TypeName(converter.Type!)}>"; + + return TypeName(converter.GetType()).Replace("WithReflection", string.Empty, StringComparison.Ordinal); + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonContractTests.cs b/app/Tests/Serialization/JsonContractTests.cs new file mode 100644 index 000000000..d88a05909 --- /dev/null +++ b/app/Tests/Serialization/JsonContractTests.cs @@ -0,0 +1,23 @@ +namespace AIStudio.Tests.Serialization; + +/// +/// Checks that the contracts AI Studio uses for its JSON match those reflection builds. +/// +/// +/// AI Studio wrote all its files with contracts which reflection built. Any other contract, e.g., one which +/// source generation builds, has to match them in every detail, or AI Studio no longer reads what it wrote +/// before: a property with another name, without a setter, or without its converter silently loses its value. +/// +[TestFixture] +public sealed class JsonContractTests +{ + [TestCaseSource(typeof(JsonTestRoots), nameof(JsonTestRoots.Stored))] + public void TheContractsMatchThoseOfReflection(JsonTestRoot root) + { + var baseline = JsonReflectionBaseline.Of(root.Options); + var expected = JsonTypeGraph.Collect(root.Type, baseline).Select(type => JsonContractDescription.Describe(baseline.GetTypeInfo(type))); + var actual = JsonTypeGraph.Collect(root.Type, root.Options).Select(type => JsonContractDescription.Describe(root.Options.GetTypeInfo(type))); + + Assert.That(actual, Is.EqualTo(expected)); + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonCoverageTests.cs b/app/Tests/Serialization/JsonCoverageTests.cs new file mode 100644 index 000000000..b900e49f3 --- /dev/null +++ b/app/Tests/Serialization/JsonCoverageTests.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace AIStudio.Tests.Serialization; + +/// +/// Checks that every contract AI Studio uses for its JSON comes from source generation, never from reflection. +/// +/// +/// A type which no context of the options knows makes System.Text.Json throw, unless the options fall back to +/// reflection. Such a fallback hides the missing type until a trimmed build removes what reflection needs, and +/// only a published build shows it then. +/// +[TestFixture] +public sealed class JsonCoverageTests +{ + [TestCaseSource(typeof(JsonTestRoots), nameof(JsonTestRoots.Stored))] + public void EveryContractComesFromSourceGeneration(JsonTestRoot root) + { + var typesWithoutGeneratedContract = JsonTypeGraph.Collect(root.Type, root.Options) + .Where(type => root.Options.GetTypeInfo(type).OriginatingResolver is not JsonSerializerContext) + .Select(JsonContractDescription.TypeName); + + Assert.That(typesWithoutGeneratedContract, Is.Empty); + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonDefaultsTests.cs b/app/Tests/Serialization/JsonDefaultsTests.cs new file mode 100644 index 000000000..c5986ad24 --- /dev/null +++ b/app/Tests/Serialization/JsonDefaultsTests.cs @@ -0,0 +1,66 @@ +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; + +namespace AIStudio.Tests.Serialization; + +/// +/// Checks that a property missing in the JSON keeps the value a new object starts with. +/// +/// +/// Files written by an older version of AI Studio lack every property added since. Reading them has to give +/// those properties their defaults, as a new object has them, e.g., a sub-object of the settings instead of +/// null. Source generation in .NET 10 breaks this for init-only properties: it sets them all while creating +/// the object, and a property missing in the JSON gets the default of its type instead (dotnet/runtime#84484). +/// Hence, the types AI Studio stores use set instead of init.

+/// +/// A new object is created the way reflection creates it: with its parameterless constructor, or, for a +/// struct, as the default of the struct. Source generation passes the values of a positional record struct +/// to its constructor instead, where a parameter with a default value of its own would differ. +///
+[TestFixture] +public sealed class JsonDefaultsTests +{ + [TestCaseSource(typeof(JsonTestRoots), nameof(JsonTestRoots.Stored))] + public void AnEmptyObjectGetsTheDefaultsOfANewOne(JsonTestRoot root) + { + var baseline = JsonReflectionBaseline.Of(root.Options); + var failures = new List(); + foreach (var type in JsonTypeGraph.Collect(root.Type, root.Options)) + { + // + // A class which gets created through its constructor receives every value as an argument, so nothing + // gets lost there. A required property refuses an empty object anyway: + // + var typeInfo = root.Options.GetTypeInfo(type); + if (typeInfo.Kind is not JsonTypeInfoKind.Object || baseline.GetTypeInfo(type).CreateObject is not { } createObject || typeInfo.Properties.Any(property => property.IsRequired)) + continue; + + var newObject = createObject(); + var otherNewObject = createObject(); + var readObject = JsonSerializer.Deserialize("{}", typeInfo)!; + foreach (var property in typeInfo.Properties.Where(property => property.Get is not null)) + { + var expected = ValueAsJson(property, newObject, root.Options); + var actual = ValueAsJson(property, readObject, root.Options); + + // + // Some properties start with a value of their own each time, e.g., a new ID. There, the read object + // just needs any value but the default of the type: + // + if (expected == ValueAsJson(property, otherNewObject, root.Options)) + { + if (actual != expected) + failures.Add($"{JsonContractDescription.TypeName(type)}.{property.Name}: read as {actual}, while a new object has {expected}."); + } + else if (actual == DefaultAsJson(property.PropertyType, root.Options)) + failures.Add($"{JsonContractDescription.TypeName(type)}.{property.Name}: read as {actual}, while a new object gets a value of its own."); + } + } + + Assert.That(failures, Is.Empty); + } + + private static string ValueAsJson(JsonPropertyInfo property, object instance, JsonSerializerOptions options) => JsonSerializer.Serialize(property.Get!(instance), property.PropertyType, options); + + private static string DefaultAsJson(Type type, JsonSerializerOptions options) => type.IsValueType ? JsonSerializer.Serialize(Activator.CreateInstance(type), type, options) : "null"; +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonGoldenFile.cs b/app/Tests/Serialization/JsonGoldenFile.cs new file mode 100644 index 000000000..929889eac --- /dev/null +++ b/app/Tests/Serialization/JsonGoldenFile.cs @@ -0,0 +1,15 @@ +using System.Text.Json; + +namespace AIStudio.Tests.Serialization; + +/// +/// A stored file in the fixtures, together with the type and the options AI Studio reads it with. +/// +/// The name of the file in the folder Fixtures next to the tests. +/// The type AI Studio reads the file as. +/// The options AI Studio reads and writes the file with. +public sealed record JsonGoldenFile(string FileName, Type Type, JsonSerializerOptions Options) +{ + /// + public override string ToString() => this.FileName; +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonGoldenFileTests.cs b/app/Tests/Serialization/JsonGoldenFileTests.cs new file mode 100644 index 000000000..1c43590aa --- /dev/null +++ b/app/Tests/Serialization/JsonGoldenFileTests.cs @@ -0,0 +1,89 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; + +using AIStudio.Assistants.SWOTAnalysis; +using AIStudio.Chat; +using AIStudio.Settings; +using AIStudio.Settings.DataModel; +using AIStudio.Settings.DataModel.PreviousModels; +using AIStudio.Tools; +using AIStudio.Tools.Databases.ProvenanceStore; +using AIStudio.Tools.DataBackup; + +using Microsoft.Extensions.Logging.Abstractions; + +using Version = AIStudio.Settings.Version; + +namespace AIStudio.Tests.Serialization; + +/// +/// Reads and writes stored files of AI Studio, and checks that nothing gets lost and nothing gets written +/// otherwise than with the contracts of reflection. +/// +/// +/// The files in the folder Fixtures were written with the contracts of reflection, before AI Studio got +/// contracts of its own, and every property holds a value. They are made up: no real chat or setting of +/// anybody is among them. A property added later is missing in them, which the tests allow. +/// +[TestFixture] +public sealed class JsonGoldenFileTests +{ + private static IEnumerable GoldenFiles() + { + TestHost.PrepareStaticState(); + yield return new("settings.json", typeof(Data), SettingsManager.JSON_OPTIONS); + yield return new("settings-v1.json", typeof(DataV1V3), SettingsManager.JSON_OPTIONS); + yield return new("settings-v4.json", typeof(DataV4), SettingsManager.JSON_OPTIONS); + yield return new("settings-v5.json", typeof(DataV5), SettingsManager.JSON_OPTIONS); + yield return new("chat.json", typeof(ChatThread), WorkspaceBehaviour.JSON_OPTIONS); + yield return new("swot-analysis.json", typeof(SwotAnalysisResult), SwotAnalysisContentPresentation.JSON_OPTIONS); + yield return new("data-backup-manifest.json", typeof(DataBackupManifest), DataBackupFormat.JSON_OPTIONS); + yield return new("data-restore-plan.json", typeof(DataRestorePlan), DataBackupFormat.JSON_OPTIONS); + yield return new("data-restore-report.json", typeof(DataRestoreReport), DataBackupFormat.JSON_OPTIONS); + yield return new("provenance-surrounding-content.json", typeof(List), SqliteProvenanceStoreClientImplementation.JSON_OPTIONS); + } + + [TestCaseSource(nameof(GoldenFiles))] + public void AStoredFileIsWrittenAgainAsWithReflection(JsonGoldenFile goldenFile) + { + var json = ReadFixture(goldenFile.FileName); + var baseline = JsonReflectionBaseline.Of(goldenFile.Options); + + var writtenAgain = JsonSerializer.Serialize(JsonSerializer.Deserialize(json, goldenFile.Type, goldenFile.Options), goldenFile.Type, goldenFile.Options); + var writtenAgainByReflection = JsonSerializer.Serialize(JsonSerializer.Deserialize(json, goldenFile.Type, baseline), goldenFile.Type, baseline); + + Assert.That(writtenAgain, Is.EqualTo(writtenAgainByReflection)); + } + + [TestCaseSource(nameof(GoldenFiles))] + public void AStoredFileKeepsAllItsValues(JsonGoldenFile goldenFile) + { + var json = ReadFixture(goldenFile.FileName); + var writtenAgain = JsonSerializer.Serialize(JsonSerializer.Deserialize(json, goldenFile.Type, goldenFile.Options), goldenFile.Type, goldenFile.Options); + + Assert.That(JsonContainment.FindMissing(JsonNode.Parse(json), JsonNode.Parse(writtenAgain)), Is.Empty); + } + + [TestCase("settings-v1.json", Version.V1)] + [TestCase("settings-v4.json", Version.V4)] + [TestCase("settings-v5.json", Version.V5)] + public void OldSettingsMigrateAsWithReflection(string fileName, Version version) + { + var json = ReadFixture(fileName); + var baseline = JsonReflectionBaseline.Of(SettingsManager.JSON_OPTIONS); + + var migrated = SettingsMigrations.Migrate(NullLogger.Instance, version, json, SettingsManager.JSON_OPTIONS); + var migratedByReflection = SettingsMigrations.Migrate(NullLogger.Instance, version, json, baseline); + + Assert.That(JsonSerializer.Serialize(migrated, SettingsManager.JSON_OPTIONS), Is.EqualTo(JsonSerializer.Serialize(migratedByReflection, baseline))); + } + + /// + /// Reads a file from the fixtures next to this test. + /// + /// + /// Read from the source tree, the way the other fixtures are, so the file needs no entry in the project file. + /// + private static string ReadFixture(string fileName, [CallerFilePath] string sourceFilePath = "") => File.ReadAllText(Path.Combine(Path.GetDirectoryName(sourceFilePath)!, "Fixtures", fileName)); +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonReflectionBaseline.cs b/app/Tests/Serialization/JsonReflectionBaseline.cs new file mode 100644 index 000000000..aa85b4037 --- /dev/null +++ b/app/Tests/Serialization/JsonReflectionBaseline.cs @@ -0,0 +1,27 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; + +namespace AIStudio.Tests.Serialization; + +/// +/// The options of AI Studio as System.Text.Json uses them without source generation: the same settings, but +/// with contracts which reflection builds. +/// +/// +/// AI Studio stored all its files with such contracts, so they are the baseline every other contract has to +/// match. Each baseline is built once per options, since reflection builds its contracts once per options, too. +/// +public static class JsonReflectionBaseline +{ + private static readonly ConditionalWeakTable BASELINES = new(); + + /// + /// Returns a copy of the given options, whose contracts reflection builds. + /// + /// The options AI Studio uses. + public static JsonSerializerOptions Of(JsonSerializerOptions options) => BASELINES.GetValue(options, static original => new JsonSerializerOptions(original) + { + TypeInfoResolver = new DefaultJsonTypeInfoResolver(), + }); +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonTestRoot.cs b/app/Tests/Serialization/JsonTestRoot.cs new file mode 100644 index 000000000..3a43ec8de --- /dev/null +++ b/app/Tests/Serialization/JsonTestRoot.cs @@ -0,0 +1,14 @@ +using System.Text.Json; + +namespace AIStudio.Tests.Serialization; + +/// +/// A type which AI Studio reads or writes as JSON on its own, together with the options it uses for that. +/// +/// The type which gets serialized. Whatever it holds gets checked along with it. +/// The options AI Studio passes to System.Text.Json for this type. +public sealed record JsonTestRoot(Type Type, JsonSerializerOptions Options) +{ + /// + public override string ToString() => JsonContractDescription.TypeName(this.Type); +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonTestRoots.cs b/app/Tests/Serialization/JsonTestRoots.cs new file mode 100644 index 000000000..d2324b970 --- /dev/null +++ b/app/Tests/Serialization/JsonTestRoots.cs @@ -0,0 +1,81 @@ +using AIStudio.Assistants.SWOTAnalysis; +using AIStudio.Chat; +using AIStudio.Settings; +using AIStudio.Settings.DataModel; +using AIStudio.Settings.DataModel.PreviousModels; +using AIStudio.Tools; +using AIStudio.Tools.Databases.ProvenanceStore; +using AIStudio.Tools.DataBackup; + +namespace AIStudio.Tests.Serialization; + +/// +/// The types which AI Studio reads or writes as JSON on their own, for the tests which compare the contracts of +/// System.Text.Json. +/// +/// +/// Only the outermost types belong here: whatever they hold gets checked along with them. Add a type whenever +/// AI Studio starts to store a new kind of file, with the options it stores the file with. +/// +public static class JsonTestRoots +{ + /// + /// The data AI Studio keeps on the disk of the user. + /// + public static IEnumerable Stored() + { + TestHost.PrepareStaticState(); + + // Chats, in the workspaces, among the temporary chats, and in chat archives: + yield return Case(new(typeof(ChatThread), WorkspaceBehaviour.JSON_OPTIONS)); + + // The settings, and their older versions, which the migrations still read: + yield return Case(new(typeof(Data), SettingsManager.JSON_OPTIONS)); + yield return Case(new(typeof(DataV1V3), SettingsManager.JSON_OPTIONS)); + yield return Case(new(typeof(DataV4), SettingsManager.JSON_OPTIONS)); + yield return Case(new(typeof(DataV5), SettingsManager.JSON_OPTIONS)); + + // + // The values of managed settings, which get written and read on their own, as the type of their + // setting. That type is often an interface, such as ISet, which the settings themselves + // never use: + // + foreach (var valueType in ManagedSettingValueTypes()) + yield return Case(new(valueType, SettingsManager.JSON_OPTIONS)); + + // The SWOT analysis, which a chat stores as the presentation of a message: + yield return Case(new(typeof(SwotAnalysisResult), SwotAnalysisContentPresentation.JSON_OPTIONS), "read"); + yield return Case(new(typeof(SwotAnalysisResult), SwotAnalysisContentPresentation.WRITE_OPTIONS), "written"); + + // Backups and their restore: + yield return Case(new(typeof(DataBackupManifest), DataBackupFormat.JSON_OPTIONS)); + yield return Case(new(typeof(DataRestorePlan), DataBackupFormat.JSON_OPTIONS)); + yield return Case(new(typeof(DataRestoreReport), DataBackupFormat.JSON_OPTIONS)); + + // The text around a passage which the provenance store keeps: + yield return Case(new(typeof(IReadOnlyList), SqliteProvenanceStoreClientImplementation.JSON_OPTIONS)); + yield return Case(new(typeof(List), SqliteProvenanceStoreClientImplementation.JSON_OPTIONS)); + } + + private static TestCaseData Case(JsonTestRoot root, string? purpose = null) + { + // + // The same System.Text.Json does with the first call of Serialize or Deserialize. Only then, the + // options hand out contracts: + // + root.Options.MakeReadOnly(populateMissingResolver: true); + return new TestCaseData(root).SetArgDisplayNames(purpose is null ? root.ToString() : $"{root}, {purpose}"); + } + + private static IEnumerable ManagedSettingValueTypes() + { + // + // Creating the settings registers every managed setting, just like the start of the app does: + // + _ = new Data(); + return ManagedConfiguration.RegisteredSettings + .Select(setting => setting.GetType().GetGenericArguments()[1]) + .Distinct() + .OrderBy(JsonContractDescription.TypeName, StringComparer.Ordinal); + } +} \ No newline at end of file diff --git a/app/Tests/Serialization/JsonTypeGraph.cs b/app/Tests/Serialization/JsonTypeGraph.cs new file mode 100644 index 000000000..a4cde8498 --- /dev/null +++ b/app/Tests/Serialization/JsonTypeGraph.cs @@ -0,0 +1,44 @@ +using System.Text.Json; + +namespace AIStudio.Tests.Serialization; + +/// +/// Collects every type System.Text.Json meets while it reads or writes a root. +/// +public static class JsonTypeGraph +{ + /// + /// Returns the root and every type it holds: the types of its properties, the keys and elements of its + /// collections, and its derived types, each of them once and in the order they were met. The type of an + /// ignored property doesn't count, since System.Text.Json never meets it. + /// + /// The type to start with. + /// The options whose contracts tell what a type holds. + public static IReadOnlyList Collect(Type root, JsonSerializerOptions options) + { + var types = new List(); + Visit(root, options, [], types); + return types; + } + + private static void Visit(Type type, JsonSerializerOptions options, HashSet visited, List types) + { + if (!visited.Add(type)) + return; + + types.Add(type); + var typeInfo = options.GetTypeInfo(type); + foreach (var property in typeInfo.Properties.Where(property => !JsonContractDescription.IsIgnored(property))) + Visit(property.PropertyType, options, visited, types); + + if (typeInfo.KeyType is { } keyType) + Visit(keyType, options, visited, types); + + if (typeInfo.ElementType is { } elementType) + Visit(elementType, options, visited, types); + + if (typeInfo.PolymorphismOptions is { } polymorphism) + foreach (var derivedType in polymorphism.DerivedTypes) + Visit(derivedType.DerivedType, options, visited, types); + } +} \ No newline at end of file diff --git a/app/Tests/TestHost.cs b/app/Tests/TestHost.cs index 553ce5485..9668ad21b 100644 --- a/app/Tests/TestHost.cs +++ b/app/Tests/TestHost.cs @@ -12,7 +12,16 @@ namespace AIStudio.Tests; public sealed class TestHost { [OneTimeSetUp] - public void PrepareStaticApplicationState() + public void PrepareStaticApplicationState() => PrepareStaticState(); + + /// + /// Fills the static state of the app which the tests need. + /// + /// + /// NUnit builds the cases of a TestCaseSource while it discovers the tests, before this fixture runs. + /// A source which touches a type of the app with a static logger calls this first. + /// + public static void PrepareStaticState() { // // A number of types in the app hold a static logger field that is initialized from diff --git a/app/Tests/Tools/EmbeddingSignatureTests.cs b/app/Tests/Tools/EmbeddingSignatureTests.cs index cf8ed3f76..ca017cd1f 100644 --- a/app/Tests/Tools/EmbeddingSignatureTests.cs +++ b/app/Tests/Tools/EmbeddingSignatureTests.cs @@ -234,7 +234,7 @@ public sealed class EmbeddingSignatureTests /// /// A data source which is embedded but not kept in DataSources, the way mailboxes are. /// - private readonly record struct IndexedElsewhere() : IIndexedDataSource + private record struct IndexedElsewhere() : IIndexedDataSource { public string Id => "0c3f9b52-7d4e-4a1b-9e6f-2b8c5d7a1e40"; @@ -246,14 +246,14 @@ public sealed class EmbeddingSignatureTests public Guid EnterpriseConfigurationPluginId => Guid.Empty; - public DataSourceType Type { get; init; } = DataSourceType.NONE; + public DataSourceType Type { get; set; } = DataSourceType.NONE; - public ConfidenceLevel ConfidenceLevel { get; init; } = ConfidenceLevel.LOW; + public ConfidenceLevel ConfidenceLevel { get; set; } = ConfidenceLevel.LOW; - public string EmbeddingId { get; init; } = Guid.Empty.ToString(); + public string EmbeddingId { get; set; } = Guid.Empty.ToString(); - public int MaxChunkTokenLength { get; init; } + public int MaxChunkTokenLength { get; set; } - public int ChunkOverlapTokenLength { get; init; } + public int ChunkOverlapTokenLength { get; set; } } }