Skip to content

Commit

Permalink
.Net - Remove empty properties for content serialization. (#5644)
Browse files Browse the repository at this point in the history
### Motivation and Context

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->
A lot of extra bytes for empty and duplicated content when serializing
`ChatHistory`.

```
 [
  {
    "Role": {
      "Label": "user"
    },
    "Content": "Discuss the potential long-term consequences for the Earth\u0027s ecosystem as well.",
    "Items": [
      {
        "$type": "TextContent",
        "Text": "Discuss the potential long-term consequences for the Earth\u0027s ecosystem as well.",
        "ModelId": null,
        "Metadata": null,
        "MimeType": null
      },
      {
        "$type": "ImageContent",
        "Uri": "https://fake-random-test-host:123",
        "Data": null,
        "ModelId": null,
        "Metadata": null,
        "MimeType": null
      },
      {
        "$type": "BinaryContent",
        "Content": "WzEsMiwzXQ==",
        "ModelId": null,
        "Metadata": null,
        "MimeType": null
      },
      {
        "$type": "AudioContent",
        "Data": "WzEsMiwzXQ==",
        "ModelId": null,
        "Metadata": null,
        "MimeType": null
      }
    ],
    "ModelId": null,
    "Metadata": null,
    "MimeType": null
  }
]
```
### Description

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

Fixed - no data loss with current version (also added json output to
test for visibility):

```
[
  {
    "Role": {
      "Label": "user"
    },
    "Items": [
      {
        "$type": "TextContent",
        "Text": "Discuss the potential long-term consequences for the Earth\u0027s ecosystem as well."
      },
      {
        "$type": "ImageContent",
        "Uri": "https://fake-random-test-host:123"
      },
      {
        "$type": "BinaryContent",
        "Content": "WzEsMiwzXQ=="
      },
      {
        "$type": "AudioContent",
        "Data": "WzEsMiwzXQ=="
      }
    ]
  }
]
```

> NOTE: There exists a small window of time in the evolution of
`ChatHistory` where it only contained a simple `Content` property and no
`Items` property. In the outside chance this version of `ChatHistory`
have been serialized and now attempted to be deserialized, the data
stored in `Content` will not be present in the deserialized object
instance.

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄
  • Loading branch information
crickman committed Mar 27, 2024
1 parent 8f732b9 commit 7fd2ac3
Show file tree
Hide file tree
Showing 4 changed files with 13 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace Examples;

public class Example87_ChatHistorySerialization : BaseTest
{
private static readonly JsonSerializerOptions s_options = new() { WriteIndented = true };

/// <summary>
/// Demonstrates how to serialize and deserialize <see cref="ChatHistory"/> class
/// with <see cref="ChatMessageContent"/> having SK various content types as items.
Expand All @@ -36,7 +38,7 @@ public void SerializeChatHistoryWithSKContentTypes()

var chatHistory = new ChatHistory(new[] { message });

var chatHistoryJson = JsonSerializer.Serialize(chatHistory);
var chatHistoryJson = JsonSerializer.Serialize(chatHistory, s_options);

var deserializedHistory = JsonSerializer.Deserialize<ChatHistory>(chatHistoryJson);

Expand All @@ -52,6 +54,8 @@ public void SerializeChatHistoryWithSKContentTypes()
WriteLine($"Binary content: {Encoding.UTF8.GetString((deserializedMessage.Items![2]! as BinaryContent)!.Content!.Value.Span)}");

WriteLine($"Audio content: {Encoding.UTF8.GetString((deserializedMessage.Items![3]! as AudioContent)!.Data!.Value.Span)}");

WriteLine($"JSON:\n{chatHistoryJson}");
}

/// <summary>
Expand All @@ -72,7 +76,8 @@ public void SerializeChatWithHistoryWithCustomContentType()
// The custom resolver should be used to serialize and deserialize the chat history with custom .
var options = new JsonSerializerOptions
{
TypeInfoResolver = new CustomResolver()
TypeInfoResolver = new CustomResolver(),
WriteIndented = true,
};

var chatHistoryJson = JsonSerializer.Serialize(chatHistory, options);
Expand All @@ -87,6 +92,7 @@ public void SerializeChatWithHistoryWithCustomContentType()
WriteLine($"Text content: {(deserializedMessage.Items![0]! as TextContent)!.Text}");

WriteLine($"Custom content: {(deserializedMessage.Items![1]! as CustomContent)!.Content}");
WriteLine($"JSON:\n{chatHistoryJson}");
}

public Example87_ChatHistorySerialization(ITestOutputHelper output) : base(output)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class ChatMessageContent : KernelContent
/// A convenience property to get or set the text of the first item in the <see cref="Items" /> collection of <see cref="TextContent"/> type.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[JsonIgnore]
public string? Content
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public sealed class ImageContent : KernelContent
/// <summary>
/// The image data.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public ReadOnlyMemory<byte>? Data { get; set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ public abstract class KernelContent
/// <summary>
/// The model ID used to generate the content.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? ModelId { get; set; }

/// <summary>
/// The metadata associated with the content.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IReadOnlyDictionary<string, object?>? Metadata { get; set; }

/// <summary>
/// MIME type of the content.
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? MimeType { get; set; }

/// <summary>
Expand Down

0 comments on commit 7fd2ac3

Please sign in to comment.