mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 21:27:21 +00:00
Generated the JSON contracts of the stored data at compile time (#1148)
This commit is contained in:
1 parent
fc7399e81b
commit
e954881fda
75 files changed
+5437
-298
No files matched your search
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user