mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-02 05:47:21 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a755082c0f
|
||
|
|
22b5d16c72
|
||
|
|
620afd1611
|
||
|
|
7b65d96cd8
|
||
|
|
eafc134ab8
|
No files matched your search
@@ -13,11 +13,11 @@ public sealed class AssistantAuditFinding
|
||||
/// </summary>
|
||||
#pragma warning restore MWAIS0005
|
||||
[JsonIgnore]
|
||||
public AssistantAuditLevel Severity { get; private init; } = AssistantAuditLevel.UNKNOWN;
|
||||
public AssistantAuditLevel Severity { get; private set; } = AssistantAuditLevel.UNKNOWN;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
private static readonly JsonSerializerOptions JSON_OPTIONS = new()
|
||||
internal static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions
|
||||
{
|
||||
RespectNullableAnnotations = true,
|
||||
};
|
||||
TypeInfoResolver = SwotAnalysisJsonContext.Default,
|
||||
}.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Writes the analysis with the defaults of System.Text.Json, which let a null pass, unlike the reading.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the SWOT analysis stored as the presentation of a text, so it can be shown as a matrix.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Assistants.SWOTAnalysis;
|
||||
|
||||
/// <summary>
|
||||
/// The contracts with which System.Text.Json reads and writes a SWOT analysis which a chat stores, built at
|
||||
/// compile time.
|
||||
/// </summary>
|
||||
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||
[JsonSerializable(typeof(SwotAnalysisResult))]
|
||||
internal sealed partial class SwotAnalysisJsonContext : JsonSerializerContext;
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// The text of the message.
|
||||
/// </summary>
|
||||
[JsonInclude]
|
||||
public string Text { get; private set; } = string.Empty;
|
||||
public string Text { get; internal set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[JsonInclude]
|
||||
public IReadOnlyList<FileAttachment> FileAttachments { get; private set; } = [];
|
||||
public IReadOnlyList<FileAttachment> FileAttachments { get; internal set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[JsonInclude]
|
||||
public IReadOnlyList<PreviewedText> PreviewedTexts { get; private set; } = [];
|
||||
public IReadOnlyList<PreviewedText> PreviewedTexts { get; internal set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Whether the draft holds neither text nor attachments.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using ERIContentType = AIStudio.Tools.ERIClient.DataModel.ContentType;
|
||||
|
||||
namespace AIStudio.Chat;
|
||||
|
||||
/// <summary>
|
||||
/// The contracts with which System.Text.Json reads and writes chats, built at compile time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||
[JsonSerializable(typeof(ChatThread))]
|
||||
[JsonSerializable(typeof(ERIContentType), TypeInfoPropertyName = "ERIContentType")]
|
||||
internal sealed partial class ChatJsonContext : JsonSerializerContext;
|
||||
@@ -23,7 +23,7 @@ public sealed record ChatThread
|
||||
/// <summary>
|
||||
/// The unique identifier of the chat thread.
|
||||
/// </summary>
|
||||
public Guid ChatId { get; init; }
|
||||
public Guid ChatId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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<ManagedTranscriptAttachment>? LegacyPendingMediaTranscripts
|
||||
internal List<ManagedTranscriptAttachment>? 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.
|
||||
/// </summary>
|
||||
[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.
|
||||
/// </remarks>
|
||||
[JsonInclude]
|
||||
public OutboundDataRequirement RequiredOutboundDataRestriction { get; private set; } = OutboundDataRequirement.NONE;
|
||||
public OutboundDataRequirement RequiredOutboundDataRestriction { get; internal set; } = OutboundDataRequirement.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[JsonInclude]
|
||||
public ImmutableHashSet<string> WebAddressesWrittenByModels { get; private set; } = [];
|
||||
public ImmutableHashSet<string> WebAddressesWrittenByModels { get; internal set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <summary>
|
||||
/// The content blocks of the chat thread.
|
||||
/// </summary>
|
||||
public List<ContentBlock> Blocks { get; init; } = [];
|
||||
public List<ContentBlock> Blocks { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The component running this thread, which decides the tools it may use and what its requests name.
|
||||
|
||||
@@ -8,12 +8,12 @@ public class ContentBlock
|
||||
/// <summary>
|
||||
/// Time when the content block was created.
|
||||
/// </summary>
|
||||
public DateTimeOffset Time { get; init; }
|
||||
public DateTimeOffset Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of the content block, e.g., text, image, voice, etc.
|
||||
/// </summary>
|
||||
public ContentType ContentType { get; init; } = ContentType.NONE;
|
||||
public ContentType ContentType { get; set; } = ContentType.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The content of the block.
|
||||
@@ -28,7 +28,7 @@ public class ContentBlock
|
||||
/// <summary>
|
||||
/// Should the content block be hidden from the user?
|
||||
/// </summary>
|
||||
public bool HideFromUser { get; init; }
|
||||
public bool HideFromUser { get; set; }
|
||||
|
||||
public ContentBlock DeepClone(bool changeHideState = false, bool hideFromUser = true) => new()
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ public sealed class ContentImage : IContent, IImageSource
|
||||
/// <remarks>
|
||||
/// Is the image source a URL, a local file path, a base64 string, etc.?
|
||||
/// </remarks>
|
||||
public required ContentImageSource SourceType { get; init; }
|
||||
public required ContentImageSource SourceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The image source.
|
||||
|
||||
@@ -33,10 +33,10 @@ public sealed class ContentPresentation
|
||||
}
|
||||
}
|
||||
|
||||
internal static ContentPresentation Create<T>(ContentPresentationKind kind, T data) => new()
|
||||
internal static ContentPresentation Create<T>(ContentPresentationKind kind, T data, JsonSerializerOptions options) => new()
|
||||
{
|
||||
Kind = kind,
|
||||
Data = JsonSerializer.SerializeToElement(data),
|
||||
Data = JsonSerializer.SerializeToElement(data, options),
|
||||
};
|
||||
|
||||
internal ContentPresentation DeepClone() => new()
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the file type is forbidden and should not be attached.
|
||||
/// </summary>
|
||||
@@ -69,7 +83,7 @@ public record FileAttachment(FileAttachmentType Type, string FileName, [property
|
||||
/// of chat templates, while null.
|
||||
/// </remarks>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public Guid? ProvenanceId { get; init; }
|
||||
public Guid? ProvenanceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public FileVersion? Version { get; init; }
|
||||
public FileVersion? Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the file changed since its text was stored.
|
||||
|
||||
@@ -10,7 +10,7 @@ public record FileAttachmentImage(string FileName, string FilePath, long FileSiz
|
||||
/// <remarks>
|
||||
/// Is the image source a URL, a local file path, a base64 string, etc.?
|
||||
/// </remarks>
|
||||
public ContentImageSource SourceType { get; init; } = ContentImageSource.LOCAL_PATH;
|
||||
public ContentImageSource SourceType { get; set; } = ContentImageSource.LOCAL_PATH;
|
||||
|
||||
/// <summary>
|
||||
/// The image source.
|
||||
|
||||
@@ -8,7 +8,7 @@ public interface IImageSource
|
||||
/// <remarks>
|
||||
/// Is the image source a URL, a local file path, a base64 string, etc.?
|
||||
/// </remarks>
|
||||
public ContentImageSource SourceType { get; init; }
|
||||
public ContentImageSource SourceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The image source.
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed record ReportedTokenUsage
|
||||
/// <summary>
|
||||
/// What everything sent to the model cost.
|
||||
/// </summary>
|
||||
public int PromptTokens { get; init; }
|
||||
public int PromptTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public string ModelId { get; init; } = string.Empty;
|
||||
public string ModelId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public int BlockCount { get; init; }
|
||||
public int BlockCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// States the stored number as a usage again.
|
||||
|
||||
@@ -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<ContentBlock> ExampleConversation { get; set; } = ExampleConversation;
|
||||
public List<FileAttachment> 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.
|
||||
/// </remarks>
|
||||
public HashSet<string>? ToolIds { get; init; }
|
||||
public HashSet<string>? ToolIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public DataSourceOptions? DataSourceOptions { get; init; }
|
||||
public DataSourceOptions? DataSourceOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public string? ProfileId { get; init; }
|
||||
public string? ProfileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public string? ProviderId { get; init; }
|
||||
public string? ProviderId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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));
|
||||
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
public Version Version { get; init; } = Version.V6;
|
||||
public Version Version { get; set; } = Version.V6;
|
||||
|
||||
/// <summary>
|
||||
/// List of configured providers.
|
||||
/// </summary>
|
||||
public List<Provider> Providers { get; init; } = [];
|
||||
public List<Provider> Providers { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Settings concerning confidence levels.
|
||||
/// </summary>
|
||||
public DataConfidence Confidence { get; init; } = new(x => x.Confidence);
|
||||
public DataConfidence Confidence { get; set; } = new(x => x.Confidence);
|
||||
|
||||
/// <summary>
|
||||
/// Settings concerning data source security checks.
|
||||
/// </summary>
|
||||
public DataSourceSecuritySettings DataSourceSecurity { get; init; } = new(x => x.DataSourceSecurity);
|
||||
public DataSourceSecuritySettings DataSourceSecurity { get; set; } = new(x => x.DataSourceSecurity);
|
||||
|
||||
/// <summary>
|
||||
/// A collection of embedding providers configured.
|
||||
/// </summary>
|
||||
public List<EmbeddingProvider> EmbeddingProviders { get; init; } = [];
|
||||
public List<EmbeddingProvider> EmbeddingProviders { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A collection of speech providers configured.
|
||||
/// </summary>
|
||||
public List<TranscriptionProvider> TranscriptionProviders { get; init; } = [];
|
||||
public List<TranscriptionProvider> TranscriptionProviders { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A collection of data sources configured.
|
||||
@@ -59,17 +59,17 @@ public sealed class Data
|
||||
/// <summary>
|
||||
/// Settings which apply to all mailboxes.
|
||||
/// </summary>
|
||||
public DataMailboxes MailboxSettings { get; init; } = new(x => x.MailboxSettings);
|
||||
public DataMailboxes MailboxSettings { get; set; } = new(x => x.MailboxSettings);
|
||||
|
||||
/// <summary>
|
||||
/// List of configured profiles.
|
||||
/// </summary>
|
||||
public List<Profile> Profiles { get; init; } = [];
|
||||
public List<Profile> Profiles { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// List of configured chat templates.
|
||||
/// </summary>
|
||||
public List<ChatTemplate> ChatTemplates { get; init; } = [];
|
||||
public List<ChatTemplate> ChatTemplates { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// List of enabled plugins.
|
||||
@@ -161,74 +161,74 @@ public sealed class Data
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the managed Batch Processing Assistant defaults.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the managed Assistant Builder defaults.
|
||||
/// </summary>
|
||||
public DataAssistantBuilder AssistantBuilder { get; init; } = new(x => x.AssistantBuilder);
|
||||
public DataAssistantBuilder AssistantBuilder { get; set; } = new(x => x.AssistantBuilder);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the managed Visual Briefing Assistant defaults.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
@@ -398,7 +398,7 @@ public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = n
|
||||
/// <summary>
|
||||
/// Settings for indexing local data sources.
|
||||
/// </summary>
|
||||
public DataDataSourceIndexing DataSourceIndexing { get; init; } = new(x => x.App.DataSourceIndexing);
|
||||
public DataDataSourceIndexing DataSourceIndexing { get; set; } = new(x => x.App.DataSourceIndexing);
|
||||
|
||||
/// <summary>
|
||||
/// List of assistants that should be hidden from the UI.
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace AIStudio.Settings.DataModel;
|
||||
/// </summary>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public bool Activate { get; init; }
|
||||
public bool Activate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public bool AllowUserOverride { get; init; }
|
||||
public bool AllowUserOverride { get; set; }
|
||||
}
|
||||
@@ -12,20 +12,20 @@ public sealed record DataDocumentAnalysisPolicy : ConfigurationBaseObject
|
||||
private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger<DataDocumentAnalysisPolicy>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Id { get; init; } = string.Empty;
|
||||
public override string Id { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override uint Num { get; init; }
|
||||
public override uint Num { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name
|
||||
{
|
||||
get => this.PolicyName;
|
||||
init => this.PolicyName = value;
|
||||
set => this.PolicyName = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsEnterpriseConfiguration { get; init; }
|
||||
public override bool IsEnterpriseConfiguration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the document analysis policy.
|
||||
@@ -43,7 +43,7 @@ public sealed record DataDocumentAnalysisPolicy : ConfigurationBaseObject
|
||||
public bool IsProtected { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty;
|
||||
public override Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The rules for the document analysis policy.
|
||||
|
||||
@@ -5,25 +5,25 @@ public sealed record DataMandatoryInfoAcceptance
|
||||
/// <summary>
|
||||
/// The ID of the mandatory info that was accepted.
|
||||
/// </summary>
|
||||
public string InfoId { get; init; } = string.Empty;
|
||||
public string InfoId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The accepted version string.
|
||||
/// </summary>
|
||||
public string AcceptedVersion { get; init; } = string.Empty;
|
||||
public string AcceptedVersion { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The accepted hash of the mandatory info content.
|
||||
/// </summary>
|
||||
public string AcceptedHash { get; init; } = string.Empty;
|
||||
public string AcceptedHash { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The UTC time of the acceptance.
|
||||
/// </summary>
|
||||
public DateTimeOffset AcceptedAtUtc { get; init; }
|
||||
public DateTimeOffset AcceptedAtUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The plugin that provided the accepted info at the time of acceptance.
|
||||
/// </summary>
|
||||
public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty;
|
||||
public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace AIStudio.Settings.DataModel;
|
||||
/// <summary>
|
||||
/// An external data source, accessed via an ERI server, cf. https://github.com/MindWorkAI/ERI.
|
||||
/// </summary>
|
||||
public readonly record struct DataSourceERI_V1 : IERIDataSource
|
||||
public record struct DataSourceERI_V1: IERIDataSource
|
||||
{
|
||||
private static readonly ILogger<DataSourceERI_V1> LOGGER = Program.LOGGER_FACTORY.CreateLogger<DataSourceERI_V1>();
|
||||
|
||||
@@ -34,52 +34,52 @@ public readonly record struct DataSourceERI_V1 : IERIDataSource
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint Num { get; init; }
|
||||
public uint Num { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Id { get; init; } = Guid.Empty.ToString();
|
||||
public string Id { get; set; } = Guid.Empty.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataSourceType Type { get; init; } = DataSourceType.NONE;
|
||||
public DataSourceType Type { get; set; } = DataSourceType.NONE;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Hostname { get; init; } = string.Empty;
|
||||
public string Hostname { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Port { get; init; }
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public AuthMethod AuthMethod { get; init; } = AuthMethod.NONE;
|
||||
public AuthMethod AuthMethod { get; set; } = AuthMethod.NONE;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Username { get; init; } = string.Empty;
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataSourceERIUsernamePasswordMode UsernamePasswordMode { get; init; } = DataSourceERIUsernamePasswordMode.USER_MANAGED;
|
||||
public DataSourceERIUsernamePasswordMode UsernamePasswordMode { get; set; } = DataSourceERIUsernamePasswordMode.USER_MANAGED;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataSourceSecurity SecurityPolicy { get; init; } = DataSourceSecurity.NOT_SPECIFIED;
|
||||
public DataSourceSecurity SecurityPolicy { get; set; } = DataSourceSecurity.NOT_SPECIFIED;
|
||||
|
||||
/// <inheritdoc />
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; init; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION;
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; set; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnterpriseConfiguration { get; init; }
|
||||
public bool IsEnterpriseConfiguration { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty;
|
||||
public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ERIVersion Version { get; init; } = ERIVersion.V1;
|
||||
public ERIVersion Version { get; set; } = ERIVersion.V1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string SelectedRetrievalId { get; init; } = string.Empty;
|
||||
public string SelectedRetrievalId { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ushort MaxMatches { get; init; } = 10;
|
||||
public ushort MaxMatches { get; set; } = 10;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<IRetrievalContext>> RetrieveDataAsync(IContent lastUserPrompt, ChatThread thread, CancellationToken token = default)
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace AIStudio.Settings.DataModel;
|
||||
/// <summary>
|
||||
/// Represents a local directory as a data source.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint Num { get; init; }
|
||||
public uint Num { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Id { get; init; } = Guid.Empty.ToString();
|
||||
public string Id { get; set; } = Guid.Empty.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The description of the data source. What kind of data does it contain?
|
||||
/// What is the data source used for?
|
||||
/// </summary>
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataSourceType Type { get; init; } = DataSourceType.NONE;
|
||||
public DataSourceType Type { get; set; } = DataSourceType.NONE;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string EmbeddingId { get; init; } = Guid.Empty.ToString();
|
||||
public string EmbeddingId { get; set; } = Guid.Empty.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public int MaxChunkTokenLength { get; init; }
|
||||
public int MaxChunkTokenLength { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ChunkOverlapTokenLength { get; init; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH;
|
||||
public int ChunkOverlapTokenLength { get; set; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConfidenceLevel ConfidenceLevel { get; init; } = ConfidenceLevel.UNKNOWN;
|
||||
public ConfidenceLevel ConfidenceLevel { get; set; } = ConfidenceLevel.UNKNOWN;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnterpriseConfiguration { get; init; }
|
||||
public bool IsEnterpriseConfiguration { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty;
|
||||
public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ushort MaxMatches { get; init; } = 10;
|
||||
public ushort MaxMatches { get; set; } = 10;
|
||||
|
||||
/// <inheritdoc />
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; init; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION;
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; set; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<IRetrievalContext>> RetrieveDataAsync(IContent lastUserPrompt, ChatThread thread, CancellationToken token = default) =>
|
||||
@@ -79,31 +79,31 @@ public readonly record struct DataSourceLocalDirectory : IInternalDataSource
|
||||
/// <summary>
|
||||
/// The path to the directory.
|
||||
/// </summary>
|
||||
public string Path { get; init; } = string.Empty;
|
||||
public string Path { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Semicolon-separated file-name patterns which decide which files below the path
|
||||
/// are considered for indexing.
|
||||
/// </summary>
|
||||
public string FilePatterns { get; init; } = DEFAULT_FILE_PATTERNS;
|
||||
public string FilePatterns { get; set; } = DEFAULT_FILE_PATTERNS;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string ExcludePatterns { get; init; } = FileExclusions.DEFAULT_PATTERNS;
|
||||
public string ExcludePatterns { get; set; } = FileExclusions.DEFAULT_PATTERNS;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public int MaxFileSizeMegabytes { get; init; } = FolderFileSelection.DEFAULT_MAX_FILE_SIZE_MEGABYTES;
|
||||
public int MaxFileSizeMegabytes { get; set; } = FolderFileSelection.DEFAULT_MAX_FILE_SIZE_MEGABYTES;
|
||||
|
||||
/// <summary>
|
||||
/// Whether files in folders below the path are considered for indexing.
|
||||
/// </summary>
|
||||
public bool IncludeSubdirectories { get; init; } = DEFAULT_INCLUDE_SUBDIRECTORIES;
|
||||
public bool IncludeSubdirectories { get; set; } = DEFAULT_INCLUDE_SUBDIRECTORIES;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public bool IncludeHiddenFilesAndFolders { get; init; } = DEFAULT_INCLUDE_HIDDEN_FILES_AND_FOLDERS;
|
||||
public bool IncludeHiddenFilesAndFolders { get; set; } = DEFAULT_INCLUDE_HIDDEN_FILES_AND_FOLDERS;
|
||||
|
||||
/// <summary>
|
||||
/// The patterns used at runtime. An invalid value can only come from malformed or hand-edited
|
||||
|
||||
@@ -8,53 +8,53 @@ namespace AIStudio.Settings.DataModel;
|
||||
/// <summary>
|
||||
/// Represents one local file as a data source.
|
||||
/// </summary>
|
||||
public readonly record struct DataSourceLocalFile : IInternalDataSource
|
||||
public record struct DataSourceLocalFile: IInternalDataSource
|
||||
{
|
||||
public DataSourceLocalFile()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint Num { get; init; }
|
||||
public uint Num { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Id { get; init; } = Guid.Empty.ToString();
|
||||
public string Id { get; set; } = Guid.Empty.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The description of the data source. What kind of data does it contain?
|
||||
/// What is the data source used for?
|
||||
/// </summary>
|
||||
public string Description { get; init; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataSourceType Type { get; init; } = DataSourceType.NONE;
|
||||
public DataSourceType Type { get; set; } = DataSourceType.NONE;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string EmbeddingId { get; init; } = Guid.Empty.ToString();
|
||||
public string EmbeddingId { get; set; } = Guid.Empty.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public int MaxChunkTokenLength { get; init; }
|
||||
public int MaxChunkTokenLength { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ChunkOverlapTokenLength { get; init; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH;
|
||||
public int ChunkOverlapTokenLength { get; set; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConfidenceLevel ConfidenceLevel { get; init; } = ConfidenceLevel.UNKNOWN;
|
||||
public ConfidenceLevel ConfidenceLevel { get; set; } = ConfidenceLevel.UNKNOWN;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnterpriseConfiguration { get; init; }
|
||||
public bool IsEnterpriseConfiguration { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty;
|
||||
public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ushort MaxMatches { get; init; } = 10;
|
||||
public ushort MaxMatches { get; set; } = 10;
|
||||
|
||||
/// <inheritdoc />
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; init; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION;
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; set; } = DataSourceOutboundDataExtensions.DEFAULT_OUTBOUND_DATA_RESTRICTION;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<IRetrievalContext>> RetrieveDataAsync(IContent lastUserPrompt, ChatThread thread, CancellationToken token = default) =>
|
||||
@@ -67,5 +67,5 @@ public readonly record struct DataSourceLocalFile : IInternalDataSource
|
||||
/// <summary>
|
||||
/// The path to the file.
|
||||
/// </summary>
|
||||
public string FilePath { get; init; } = string.Empty;
|
||||
public string FilePath { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -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.
|
||||
/// </remarks>
|
||||
public readonly record struct DataSourceMailbox : IIndexedDataSource, ISecretId
|
||||
public record struct DataSourceMailbox: IIndexedDataSource, ISecretId
|
||||
{
|
||||
public DataSourceMailbox()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public uint Num { get; init; }
|
||||
public uint Num { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Id { get; init; } = Guid.Empty.ToString();
|
||||
public string Id { get; set; } = Guid.Empty.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataSourceType Type { get; init; } = DataSourceType.MAILBOX;
|
||||
public DataSourceType Type { get; set; } = DataSourceType.MAILBOX;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnterpriseConfiguration { get; init; }
|
||||
public bool IsEnterpriseConfiguration { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid EnterpriseConfigurationPluginId { get; init; } = Guid.Empty;
|
||||
public Guid EnterpriseConfigurationPluginId { get; set; } = Guid.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string EmbeddingId { get; init; } = Guid.Empty.ToString();
|
||||
public string EmbeddingId { get; set; } = Guid.Empty.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public int MaxChunkTokenLength { get; init; }
|
||||
public int MaxChunkTokenLength { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ChunkOverlapTokenLength { get; init; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH;
|
||||
public int ChunkOverlapTokenLength { get; set; } = DataSourceEmbeddingService.DEFAULT_CHUNK_OVERLAP_TOKEN_LENGTH;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public ConfidenceLevel ConfidenceLevel { get; init; } = ConfidenceLevel.NONE;
|
||||
public ConfidenceLevel ConfidenceLevel { get; set; } = ConfidenceLevel.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// The host name of the IMAP server, e.g., imap.example.org.
|
||||
/// </summary>
|
||||
public string Host { get; init; } = string.Empty;
|
||||
public string Host { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The port of the IMAP server. The default is the one for IMAP with TLS from the first byte on.
|
||||
/// </summary>
|
||||
public int Port { get; init; } = MailboxTransportSecurityExtensions.SSL_ON_CONNECT_PORT;
|
||||
public int Port { get; set; } = MailboxTransportSecurityExtensions.SSL_ON_CONNECT_PORT;
|
||||
|
||||
/// <summary>
|
||||
/// How the connection to the IMAP server is encrypted.
|
||||
/// </summary>
|
||||
public MailboxTransportSecurity TransportSecurity { get; init; } = MailboxTransportSecurity.SSL_ON_CONNECT;
|
||||
public MailboxTransportSecurity TransportSecurity { get; set; } = MailboxTransportSecurity.SSL_ON_CONNECT;
|
||||
|
||||
/// <summary>
|
||||
/// How AI Studio signs in to the IMAP server.
|
||||
/// </summary>
|
||||
public MailboxAuthMethod AuthMethod { get; init; } = MailboxAuthMethod.PASSWORD;
|
||||
public MailboxAuthMethod AuthMethod { get; set; } = MailboxAuthMethod.PASSWORD;
|
||||
|
||||
/// <summary>
|
||||
/// The username to sign in with, often the e-mail address.
|
||||
/// </summary>
|
||||
public string Username { get; init; } = string.Empty;
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public string RootFolder { get; init; } = string.Empty;
|
||||
public string RootFolder { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public bool IncludeSentAndDrafts { get; init; } = true;
|
||||
public bool IncludeSentAndDrafts { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// How far back the index reaches. Flagged mails and drafts are indexed regardless of their age.
|
||||
/// </summary>
|
||||
public MailboxMaxAge MaxAge { get; init; } = MailboxMaxAge.LAST_12_MONTHS;
|
||||
public MailboxMaxAge MaxAge { get; set; } = MailboxMaxAge.LAST_12_MONTHS;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the text of attached documents is indexed as well.
|
||||
/// </summary>
|
||||
public bool IndexAttachments { get; init; } = true;
|
||||
public bool IndexAttachments { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// The size in megabytes up to which the text of an attachment is indexed. Of a larger one, only the name is.
|
||||
/// </summary>
|
||||
public int MaxAttachmentSizeMegabytes { get; init; } = 10;
|
||||
public int MaxAttachmentSizeMegabytes { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <remarks>
|
||||
/// An organization may demand a stricter one, see DataMailboxes.MinimumOutboundDataRestriction.
|
||||
/// </remarks>
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; init; } = OutboundDataRestriction.ONLY_CONFIGURED_SERVICES;
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; set; } = OutboundDataRestriction.ONLY_CONFIGURED_SERVICES;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of mails one search returns. Searched page by page, it is the size of a page.
|
||||
/// </summary>
|
||||
public ushort MaxMatches { get; init; } = 10;
|
||||
public ushort MaxMatches { get; set; } = 10;
|
||||
|
||||
#region Implementation of ISecretId
|
||||
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
public Version Version { get; init; } = Version.V3;
|
||||
public Version Version { get; set; } = Version.V3;
|
||||
|
||||
/// <summary>
|
||||
/// List of configured providers.
|
||||
/// </summary>
|
||||
public List<Provider> Providers { get; init; } = [];
|
||||
public List<Provider> Providers { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The next provider number to use.
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
public Version Version { get; init; }
|
||||
public Version Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of configured providers.
|
||||
/// </summary>
|
||||
public List<Provider> Providers { get; init; } = [];
|
||||
public List<Provider> Providers { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Settings concerning the LLM providers.
|
||||
/// </summary>
|
||||
public DataLLMProvidersV5 LLMProviders { get; init; } = new();
|
||||
public DataLLMProvidersV5 LLMProviders { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of configured profiles.
|
||||
/// </summary>
|
||||
public List<Profile> Profiles { get; init; } = [];
|
||||
public List<Profile> Profiles { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The next provider number to use.
|
||||
@@ -33,27 +33,27 @@ public sealed class DataV4
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
public Version Version { get; init; } = Version.V5;
|
||||
public Version Version { get; set; } = Version.V5;
|
||||
|
||||
/// <summary>
|
||||
/// List of configured providers.
|
||||
/// </summary>
|
||||
public List<AIStudio.Settings.Provider> Providers { get; init; } = [];
|
||||
public List<AIStudio.Settings.Provider> Providers { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Settings concerning the LLM providers.
|
||||
/// </summary>
|
||||
public DataLLMProvidersV5 LLMProviders { get; init; } = new();
|
||||
public DataLLMProvidersV5 LLMProviders { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// A collection of embedding providers configured.
|
||||
/// </summary>
|
||||
public List<EmbeddingProvider> EmbeddingProviders { get; init; } = [];
|
||||
public List<EmbeddingProvider> EmbeddingProviders { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A collection of speech providers configured.
|
||||
/// </summary>
|
||||
public List<TranscriptionProvider> TranscriptionProviders { get; init; } = [];
|
||||
public List<TranscriptionProvider> TranscriptionProviders { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// A collection of data sources configured.
|
||||
@@ -38,12 +38,12 @@ public sealed class DataV5
|
||||
/// <summary>
|
||||
/// List of configured profiles.
|
||||
/// </summary>
|
||||
public List<Profile> Profiles { get; init; } = [];
|
||||
public List<Profile> Profiles { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// List of configured chat templates.
|
||||
/// </summary>
|
||||
public List<ChatTemplate> ChatTemplates { get; init; } = [];
|
||||
public List<ChatTemplate> ChatTemplates { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// List of enabled plugins.
|
||||
@@ -95,55 +95,55 @@ public sealed class DataV5
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public interface IDataSource : IDataSourceBase
|
||||
/// <summary>
|
||||
/// The maximum number of matches one retrieval returns. Searched page by page, it is the size of a page.
|
||||
/// </summary>
|
||||
public ushort MaxMatches { get; init; }
|
||||
public ushort MaxMatches { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; init; }
|
||||
public OutboundDataRestriction OutboundDataRestriction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The last page which can be retrieved from this data source.
|
||||
|
||||
@@ -17,5 +17,5 @@ public interface IDataSourceBase : IConfigurationObject
|
||||
/// <summary>
|
||||
/// Which type of data source is this?
|
||||
/// </summary>
|
||||
public DataSourceType Type { get; init; }
|
||||
public DataSourceType Type { get; set; }
|
||||
}
|
||||
@@ -9,35 +9,35 @@ public interface IERIDataSource : IExternalDataSource
|
||||
/// <summary>
|
||||
/// The hostname of the ERI server.
|
||||
/// </summary>
|
||||
public string Hostname { get; init; }
|
||||
public string Hostname { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The port of the ERI server.
|
||||
/// </summary>
|
||||
public int Port { get; init; }
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The authentication method to use.
|
||||
/// </summary>
|
||||
public AuthMethod AuthMethod { get; init; }
|
||||
public AuthMethod AuthMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The username to use for authentication, when the auth. method is USERNAME_PASSWORD.
|
||||
/// </summary>
|
||||
public string Username { get; init; }
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How username/password authentication should obtain the username.
|
||||
/// </summary>
|
||||
public DataSourceERIUsernamePasswordMode UsernamePasswordMode { get; init; }
|
||||
public DataSourceERIUsernamePasswordMode UsernamePasswordMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ERI specification to use.
|
||||
/// </summary>
|
||||
public ERIVersion Version { get; init; }
|
||||
public ERIVersion Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the selected retrieval process.
|
||||
/// </summary>
|
||||
public string SelectedRetrievalId { get; init; }
|
||||
public string SelectedRetrievalId { get; set; }
|
||||
}
|
||||
@@ -9,7 +9,7 @@ public interface IExternalDataSource : IDataSource, ISecretId
|
||||
/// <summary>
|
||||
/// Which data security policy is applied to this external data source?
|
||||
/// </summary>
|
||||
public DataSourceSecurity SecurityPolicy { get; init; }
|
||||
public DataSourceSecurity SecurityPolicy { get; set; }
|
||||
|
||||
#region Implementation of ISecretId
|
||||
|
||||
|
||||
@@ -15,21 +15,21 @@ public interface IIndexedDataSource : IDataSourceBase
|
||||
/// <summary>
|
||||
/// Which provider confidence level is required by this data source?
|
||||
/// </summary>
|
||||
public ConfidenceLevel ConfidenceLevel { get; init; }
|
||||
public ConfidenceLevel ConfidenceLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique identifier of the embedding provider used by this data source.
|
||||
/// </summary>
|
||||
public string EmbeddingId { get; init; }
|
||||
public string EmbeddingId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional maximum number of tokens per embedding chunk for this data source.
|
||||
/// A value of 0 means the embedding provider's setting is used.
|
||||
/// </summary>
|
||||
public int MaxChunkTokenLength { get; init; }
|
||||
public int MaxChunkTokenLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional number of tokens to overlap between consecutive chunks.
|
||||
/// </summary>
|
||||
public int ChunkOverlapTokenLength { get; init; }
|
||||
public int ChunkOverlapTokenLength { get; set; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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<Provider> LOGGER = Program.LOGGER_FACTORY.CreateLogger<Provider>();
|
||||
|
||||
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
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// How many tokens this installation reads and writes, or null to keep the automatic answer.
|
||||
@@ -131,21 +131,21 @@ public sealed record ProviderCapabilityOverrides
|
||||
/// </remarks>
|
||||
[JsonPropertyName(CONTEXT_WINDOW_KEY)]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public int? ContextWindowTokens { get; init; }
|
||||
public int? ContextWindowTokens { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How many images one message may carry, or null to keep the automatic answer.
|
||||
/// </summary>
|
||||
[JsonPropertyName(MAX_IMAGES_PER_MESSAGE_KEY)]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public int? MaxImagesPerMessage { get; init; }
|
||||
public int? MaxImagesPerMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// How many images one request may carry, or null to keep the automatic answer.
|
||||
/// </summary>
|
||||
[JsonPropertyName(MAX_IMAGES_PER_REQUEST_KEY)]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public int? MaxImagesPerRequest { get; init; }
|
||||
public int? MaxImagesPerRequest { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool HasOverrides =>
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// The contracts with which System.Text.Json reads and writes the settings, built at compile time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.<br/><br/>
|
||||
///
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||
[JsonSerializable(typeof(Data))]
|
||||
[JsonSerializable(typeof(DataV1V3))]
|
||||
[JsonSerializable(typeof(DataV4))]
|
||||
[JsonSerializable(typeof(DataV5))]
|
||||
[JsonSerializable(typeof(ISet<string>))]
|
||||
[JsonSerializable(typeof(ISet<PreviewFeatures>))]
|
||||
[JsonSerializable(typeof(ISet<LLMProviders>))]
|
||||
[JsonSerializable(typeof(ISet<ConfigurableAssistant>))]
|
||||
[JsonSerializable(typeof(IList<string>))]
|
||||
[JsonSerializable(typeof(IList<DataAssistantPluginEnterpriseApproval>))]
|
||||
[JsonSerializable(typeof(IDictionary<string, string>))]
|
||||
[JsonSerializable(typeof(ProviderV4), TypeInfoPropertyName = "ProviderV4")]
|
||||
[JsonSerializable(typeof(List<ProviderV4>), TypeInfoPropertyName = "ListProviderV4")]
|
||||
[JsonSerializable(typeof(EMailWritingStyles), TypeInfoPropertyName = "EMailWritingStyles")]
|
||||
[JsonSerializable(typeof(RewriteImproveWritingStyles), TypeInfoPropertyName = "RewriteImproveWritingStyles")]
|
||||
[JsonSerializable(typeof(ERIContentType), TypeInfoPropertyName = "ERIContentType")]
|
||||
internal sealed partial class SettingsJsonContext : JsonSerializerContext;
|
||||
@@ -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();
|
||||
|
||||
/// <summary>
|
||||
/// From this duration on, storing the settings is reported as slow.
|
||||
|
||||
@@ -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<TranscriptionProvider> LOGGER = Program.LOGGER_FACTORY.CreateLogger<TranscriptionProvider>();
|
||||
|
||||
public static readonly TranscriptionProvider NONE = new();
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
/// <summary>
|
||||
/// File types which are compressed already. Compressing them once more costs time and makes the
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Tools.DataBackup;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||
[JsonSerializable(typeof(DataBackupManifest))]
|
||||
[JsonSerializable(typeof(DataRestorePlan))]
|
||||
[JsonSerializable(typeof(DataRestoreReport))]
|
||||
internal sealed partial class DataBackupJsonContext : JsonSerializerContext;
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Tools.Databases.ProvenanceStore;
|
||||
|
||||
/// <summary>
|
||||
/// The contracts with which System.Text.Json reads and writes the content around a passage, built at compile time.
|
||||
/// </summary>
|
||||
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||
[JsonSerializable(typeof(IReadOnlyList<string>))]
|
||||
[JsonSerializable(typeof(List<string>))]
|
||||
internal sealed partial class ProvenanceJsonContext : JsonSerializerContext;
|
||||
+6
-2
@@ -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.
|
||||
/// </summary>
|
||||
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<List<string>>(row.SurroundingContent) ?? [];
|
||||
return JsonSerializer.Deserialize<List<string>>(row.SurroundingContent, JSON_OPTIONS) ?? [];
|
||||
}
|
||||
catch (JsonException exception)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public static class JsonSerializerOptionsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes the options read-only and returns them, so that a static field gets its options in one expression.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <param name="options">The options, with their TypeInfoResolver set.</param>
|
||||
/// <returns>The same options, now read-only.</returns>
|
||||
public static JsonSerializerOptions AsReadOnly(this JsonSerializerOptions options)
|
||||
{
|
||||
options.MakeReadOnly();
|
||||
return options;
|
||||
}
|
||||
}
|
||||
@@ -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<AssistantAuditFinding> Findings { get; set; } = [];
|
||||
|
||||
@@ -5,19 +5,19 @@ public abstract record ConfigurationBaseObject : IConfigurationObject
|
||||
#region Implementation of IConfigurationObject
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string Id { get; init; }
|
||||
public abstract string Id { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract uint Num { get; init; }
|
||||
public abstract uint Num { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string Name { get; init; }
|
||||
public abstract string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool IsEnterpriseConfiguration { get; init; }
|
||||
public abstract bool IsEnterpriseConfiguration { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract Guid EnterpriseConfigurationPluginId { get; init; }
|
||||
public abstract Guid EnterpriseConfigurationPluginId { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public sealed class RetrievalImageContext : IRetrievalContext, IImageSource
|
||||
/// <remarks>
|
||||
/// Is the image source a URL, a local file path, a base64 string, etc.?
|
||||
/// </remarks>
|
||||
public required ContentImageSource SourceType { get; init; }
|
||||
public required ContentImageSource SourceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The image source.
|
||||
|
||||
@@ -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.
|
||||
/// </remarks>
|
||||
public string SourceUrl { get; init; } = string.Empty;
|
||||
public string SourceUrl { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The name of where the passage was found, e.g., a document or a database.
|
||||
/// </summary>
|
||||
public string DataSourceName { get; init; } = string.Empty;
|
||||
public string DataSourceName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The path to the content, e.g., a file path or a URL.
|
||||
/// </summary>
|
||||
public string Path { get; init; } = string.Empty;
|
||||
public string Path { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The category of the content, like text or image.
|
||||
/// </summary>
|
||||
public RetrievalContentCategory Category { get; init; }
|
||||
public RetrievalContentCategory Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What type of content the passage is, like a document or a transcript.
|
||||
/// </summary>
|
||||
public RetrievalContentType Type { get; init; }
|
||||
public RetrievalContentType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public Guid? ProvenanceId { get; init; }
|
||||
public Guid? ProvenanceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The matched text, filtered for prompt injections, as long as it is not stored, see ProvenanceId.
|
||||
@@ -65,22 +65,22 @@ public sealed record SourcePassage
|
||||
/// <remarks>
|
||||
/// Empty for an image which matched: the AI is only told that it did, never shown the image.
|
||||
/// </remarks>
|
||||
public string Text { get; init; } = string.Empty;
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The content around the matched text, filtered for prompt injections, as long as it is not stored, see ProvenanceId.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> SurroundingContent { get; init; } = [];
|
||||
public IReadOnlyList<string> SurroundingContent { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The page the passage starts on, or null when it has none.
|
||||
/// </summary>
|
||||
public int? PageNumber { get; init; }
|
||||
public int? PageNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The page the passage ends on, or null when it is not known.
|
||||
/// </summary>
|
||||
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.
|
||||
/// </summary>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? SheetName { get; init; }
|
||||
public string? SheetName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The row the passage starts at, counted from one: a row of its sheet, or of a table such as a CSV file.
|
||||
/// </summary>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public int? StartRow { get; init; }
|
||||
public int? StartRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The row the passage ends at, or null when it is not known.
|
||||
/// </summary>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public int? EndRow { get; init; }
|
||||
public int? EndRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The line the passage starts at, counted from one, for text and source code, or null.
|
||||
/// </summary>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public int? StartLine { get; init; }
|
||||
public int? StartLine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The line the passage ends at, or null when it is not known.
|
||||
/// </summary>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public int? EndLine { get; init; }
|
||||
public int? EndLine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public int? ChunkNumber { get; init; }
|
||||
public int? ChunkNumber { get; set; }
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 _);
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
["Vor dem Treffer: <b>Größe</b> & \"Zitat\" für C# 14","Nach dem Treffer: 50 % – fertig, 'oder?'"]
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the values of one JSON document which another one lacks.
|
||||
/// </summary>
|
||||
public static class JsonContainment
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the path of every value in the expected document which the actual one lacks or holds otherwise.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
/// <param name="expected">The document whose values have to be there.</param>
|
||||
/// <param name="actual">The document which has to hold them.</param>
|
||||
/// <returns>One line per value, with its path and both values.</returns>
|
||||
public static IReadOnlyList<string> FindMissing(JsonNode? expected, JsonNode? actual)
|
||||
{
|
||||
var missing = new List<string>();
|
||||
Compare(expected, actual, "$", missing);
|
||||
return missing;
|
||||
}
|
||||
|
||||
private static void Compare(JsonNode? expected, JsonNode? actual, string path, List<string> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static class JsonContractDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes everything in a contract which decides what gets written and what a read accepts.
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">The contract.</param>
|
||||
/// <returns>One line for the type, and one line per property, polymorphism, and callback.</returns>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether System.Text.Json neither writes nor reads a property, e.g., one marked with JsonIgnore.
|
||||
/// </summary>
|
||||
/// <param name="property">The property of a contract.</param>
|
||||
public static bool IsIgnored(JsonPropertyInfo property) => property is { Get: null, Set: null, AssociatedParameter: null };
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of a type the way C# writes it, e.g., Dictionary<String, Int32>.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the contracts AI Studio uses for its JSON match those reflection builds.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Checks that every contract AI Studio uses for its JSON comes from source generation, never from reflection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Checks that a property missing in the JSON keeps the value a new object starts with.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.<br/><br/>
|
||||
///
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[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<string>();
|
||||
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";
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// A stored file in the fixtures, together with the type and the options AI Studio reads it with.
|
||||
/// </summary>
|
||||
/// <param name="FileName">The name of the file in the folder Fixtures next to the tests.</param>
|
||||
/// <param name="Type">The type AI Studio reads the file as.</param>
|
||||
/// <param name="Options">The options AI Studio reads and writes the file with.</param>
|
||||
public sealed record JsonGoldenFile(string FileName, Type Type, JsonSerializerOptions Options)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => this.FileName;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
public sealed class JsonGoldenFileTests
|
||||
{
|
||||
private static IEnumerable<JsonGoldenFile> 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<string>), 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<SettingsManager>.Instance, version, json, SettingsManager.JSON_OPTIONS);
|
||||
var migratedByReflection = SettingsMigrations.Migrate(NullLogger<SettingsManager>.Instance, version, json, baseline);
|
||||
|
||||
Assert.That(JsonSerializer.Serialize(migrated, SettingsManager.JSON_OPTIONS), Is.EqualTo(JsonSerializer.Serialize(migratedByReflection, baseline)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a file from the fixtures next to this test.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Read from the source tree, the way the other fixtures are, so the file needs no entry in the project file.
|
||||
/// </remarks>
|
||||
private static string ReadFixture(string fileName, [CallerFilePath] string sourceFilePath = "") => File.ReadAllText(Path.Combine(Path.GetDirectoryName(sourceFilePath)!, "Fixtures", fileName));
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// The options of AI Studio as System.Text.Json uses them without source generation: the same settings, but
|
||||
/// with contracts which reflection builds.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public static class JsonReflectionBaseline
|
||||
{
|
||||
private static readonly ConditionalWeakTable<JsonSerializerOptions, JsonSerializerOptions> BASELINES = new();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the given options, whose contracts reflection builds.
|
||||
/// </summary>
|
||||
/// <param name="options">The options AI Studio uses.</param>
|
||||
public static JsonSerializerOptions Of(JsonSerializerOptions options) => BASELINES.GetValue(options, static original => new JsonSerializerOptions(original)
|
||||
{
|
||||
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// A type which AI Studio reads or writes as JSON on its own, together with the options it uses for that.
|
||||
/// </summary>
|
||||
/// <param name="Type">The type which gets serialized. Whatever it holds gets checked along with it.</param>
|
||||
/// <param name="Options">The options AI Studio passes to System.Text.Json for this type.</param>
|
||||
public sealed record JsonTestRoot(Type Type, JsonSerializerOptions Options)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string ToString() => JsonContractDescription.TypeName(this.Type);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// The types which AI Studio reads or writes as JSON on their own, for the tests which compare the contracts of
|
||||
/// System.Text.Json.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public static class JsonTestRoots
|
||||
{
|
||||
/// <summary>
|
||||
/// The data AI Studio keeps on the disk of the user.
|
||||
/// </summary>
|
||||
public static IEnumerable<TestCaseData> 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<string>, 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<string>), SqliteProvenanceStoreClientImplementation.JSON_OPTIONS));
|
||||
yield return Case(new(typeof(List<string>), 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<Type> 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AIStudio.Tests.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Collects every type System.Text.Json meets while it reads or writes a root.
|
||||
/// </summary>
|
||||
public static class JsonTypeGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="root">The type to start with.</param>
|
||||
/// <param name="options">The options whose contracts tell what a type holds.</param>
|
||||
public static IReadOnlyList<Type> Collect(Type root, JsonSerializerOptions options)
|
||||
{
|
||||
var types = new List<Type>();
|
||||
Visit(root, options, [], types);
|
||||
return types;
|
||||
}
|
||||
|
||||
private static void Visit(Type type, JsonSerializerOptions options, HashSet<Type> visited, List<Type> 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);
|
||||
}
|
||||
}
|
||||
+10
-1
@@ -12,7 +12,16 @@ namespace AIStudio.Tests;
|
||||
public sealed class TestHost
|
||||
{
|
||||
[OneTimeSetUp]
|
||||
public void PrepareStaticApplicationState()
|
||||
public void PrepareStaticApplicationState() => PrepareStaticState();
|
||||
|
||||
/// <summary>
|
||||
/// Fills the static state of the app which the tests need.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public static void PrepareStaticState()
|
||||
{
|
||||
//
|
||||
// A number of types in the app hold a static logger field that is initialized from
|
||||
|
||||
@@ -234,7 +234,7 @@ public sealed class EmbeddingSignatureTests
|
||||
/// <summary>
|
||||
/// A data source which is embedded but not kept in DataSources, the way mailboxes are.
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user