Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.AI;
Expand All @@ -11,6 +12,9 @@ namespace Microsoft.Extensions.AI;
/// Represents a request for user input.
/// </summary>
[Experimental("MEAI001")]
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
[JsonDerivedType(typeof(FunctionApprovalRequestContent), "functionApprovalRequest")]
[JsonDerivedType(typeof(McpServerToolApprovalRequestContent), "mcpServerToolApprovalRequest")]
public class UserInputRequestContent : AIContent
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.AI;
Expand All @@ -11,6 +12,9 @@ namespace Microsoft.Extensions.AI;
/// Represents the response to a request for user input.
/// </summary>
[Experimental("MEAI001")]
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
[JsonDerivedType(typeof(FunctionApprovalResponseContent), "functionApprovalResponse")]
[JsonDerivedType(typeof(McpServerToolApprovalResponseContent), "mcpServerToolApprovalResponse")]
public class UserInputResponseContent : AIContent
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ private static JsonSerializerOptions CreateDefaultOptions()

// Temporary workaround: These should be implicitly added in once they're no longer [Experimental]
// and are included via [JsonDerivedType] on AIContent.
[JsonSerializable(typeof(UserInputRequestContent))]
[JsonSerializable(typeof(UserInputResponseContent))]
[JsonSerializable(typeof(FunctionApprovalRequestContent))]
[JsonSerializable(typeof(FunctionApprovalResponseContent))]
[JsonSerializable(typeof(McpServerToolCallContent))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI.Contents;
Expand Down Expand Up @@ -51,4 +53,19 @@ public void CreateResponse_ReturnsExpectedResponse(bool approved)
Assert.Equal(approved, response.Approved);
Assert.Same(functionCall, response.FunctionCall);
}

[Fact]
public void Serialization_Roundtrips()
{
var content = new FunctionApprovalRequestContent("request123", new FunctionCallContent("call123", "functionName", new Dictionary<string, object?> { { "param1", 123 } }));

var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<FunctionApprovalRequestContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.Id, deserializedContent.Id);
Assert.NotNull(deserializedContent.FunctionCall);
Assert.Equal(content.FunctionCall.CallId, deserializedContent.FunctionCall.CallId);
Assert.Equal(content.FunctionCall.Name, deserializedContent.FunctionCall.Name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI.Contents;
Expand Down Expand Up @@ -33,4 +34,20 @@ public void Constructor_Roundtrips(string id, bool approved)
Assert.Equal(approved, content.Approved);
Assert.Same(functionCall, content.FunctionCall);
}

[Fact]
public void Serialization_Roundtrips()
{
var content = new FunctionApprovalResponseContent("request123", true, new FunctionCallContent("call123", "functionName"));

var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<FunctionApprovalResponseContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.Id, deserializedContent.Id);
Assert.Equal(content.Approved, deserializedContent.Approved);
Assert.NotNull(deserializedContent.FunctionCall);
Assert.Equal(content.FunctionCall.CallId, deserializedContent.FunctionCall.CallId);
Assert.Equal(content.FunctionCall.Name, deserializedContent.FunctionCall.Name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI;
Expand Down Expand Up @@ -49,4 +50,16 @@ public void Constructor_PropsRoundtrip()
c.AdditionalProperties = props;
Assert.Same(props, c.AdditionalProperties);
}

[Fact]
public void Serialization_Roundtrips()
{
var content = new HostedFileContent("file123");

var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<HostedFileContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.FileId, deserializedContent.FileId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI;
Expand Down Expand Up @@ -49,4 +50,16 @@ public void Constructor_PropsRoundtrip()
c.AdditionalProperties = props;
Assert.Same(props, c.AdditionalProperties);
}

[Fact]
public void Serialization_Roundtrips()
{
var content = new HostedVectorStoreContent("vectorstore123");

var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<HostedVectorStoreContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.VectorStoreId, deserializedContent.VectorStoreId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI;
Expand Down Expand Up @@ -48,4 +49,20 @@ public void Constructor_Throws()
Assert.Throws<ArgumentException>("callId", () => new McpServerToolResultContent(string.Empty));
Assert.Throws<ArgumentNullException>("callId", () => new McpServerToolResultContent(null!));
}

[Fact]
public void Serialization_Roundtrips()
{
var content = new McpServerToolResultContent("call123")
{
Output = new List<AIContent> { new TextContent("result") }
};

var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<McpServerToolResultContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.CallId, deserializedContent.CallId);
Assert.NotNull(deserializedContent.Output);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI;
Expand Down Expand Up @@ -47,4 +48,16 @@ public void Constructor_PropsRoundtrip()
Assert.Equal(string.Empty, c.Text);
Assert.Equal(string.Empty, c.ToString());
}

[Fact]
public void Serialization_Roundtrips()
{
var content = new TextContent("Hello, world!");

var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<TextContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.Text, deserializedContent.Text);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI;
Expand Down Expand Up @@ -54,4 +55,17 @@ public void Constructor_PropsRoundtrip()
c.ProtectedData = null;
Assert.Null(c.ProtectedData);
}

[Fact]
public void Serialization_Roundtrips()
{
var content = new TextReasoningContent("reasoning text") { ProtectedData = "protected" };

var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<TextReasoningContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.Text, deserializedContent.Text);
Assert.Equal("protected", deserializedContent.ProtectedData);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI;
Expand Down Expand Up @@ -57,4 +58,24 @@ public void Details_SetNull_Throws()

Assert.Same(d, c.Details);
}

[Fact]
public void Serialization_Roundtrips()
{
var content = new UsageContent(new UsageDetails
{
InputTokenCount = 10,
OutputTokenCount = 20,
TotalTokenCount = 30
});

var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<UsageContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.NotNull(deserializedContent.Details);
Assert.Equal(content.Details.InputTokenCount, deserializedContent.Details.InputTokenCount);
Assert.Equal(content.Details.OutputTokenCount, deserializedContent.Details.OutputTokenCount);
Assert.Equal(content.Details.TotalTokenCount, deserializedContent.Details.TotalTokenCount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI.Contents;
Expand All @@ -27,6 +30,33 @@ public void Constructor_Roundtrips(string id)
Assert.Equal(id, content.Id);
}

[Fact]
public void Serialization_DerivedTypes_Roundtrips()
{
UserInputRequestContent content = new FunctionApprovalRequestContent("request123", new FunctionCallContent("call123", "functionName", new Dictionary<string, object?> { { "param1", 123 } }));
var serializedContent = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<UserInputRequestContent>(serializedContent, AIJsonUtilities.DefaultOptions);
Assert.NotNull(deserializedContent);
Assert.Equal(content.GetType(), deserializedContent.GetType());

UserInputRequestContent[] contents =
[
new FunctionApprovalRequestContent("request123", new FunctionCallContent("call123", "functionName", new Dictionary<string, object?> { { "param1", 123 } })),
new McpServerToolApprovalRequestContent("request123", new McpServerToolCallContent("call123", "myTool", "myServer")),
];

var serializedContents = JsonSerializer.Serialize(contents, TestJsonSerializerContext.Default.UserInputRequestContentArray);
var deserializedContents = JsonSerializer.Deserialize(serializedContents, TestJsonSerializerContext.Default.UserInputRequestContentArray);
Assert.NotNull(deserializedContents);

Assert.Equal(contents.Count(), deserializedContents.Length);
for (int i = 0; i < deserializedContents.Length; i++)
{
Assert.NotNull(contents.ElementAt(i));
Assert.Equal(contents.ElementAt(i).GetType(), deserializedContents[i].GetType());
}
}

private sealed class TestUserInputRequestContent : UserInputRequestContent
{
public TestUserInputRequestContent(string id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Text.Json;
using Xunit;

namespace Microsoft.Extensions.AI.Contents;
Expand All @@ -27,6 +28,33 @@ public void Constructor_Roundtrips(string id)
Assert.Equal(id, content.Id);
}

[Fact]
public void Serialization_DerivedTypes_Roundtrips()
{
UserInputResponseContent content = new FunctionApprovalResponseContent("request123", true, new FunctionCallContent("call123", "functionName"));
var serializedContent = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
var deserializedContent = JsonSerializer.Deserialize<UserInputResponseContent>(serializedContent, AIJsonUtilities.DefaultOptions);
Assert.NotNull(deserializedContent);
Assert.Equal(content.GetType(), deserializedContent.GetType());

UserInputResponseContent[] contents =
[
new FunctionApprovalResponseContent("request123", true, new FunctionCallContent("call123", "functionName")),
new McpServerToolApprovalResponseContent("request123", true),
];

var serializedContents = JsonSerializer.Serialize(contents, TestJsonSerializerContext.Default.UserInputResponseContentArray);
var deserializedContents = JsonSerializer.Deserialize(serializedContents, TestJsonSerializerContext.Default.UserInputResponseContentArray);
Assert.NotNull(deserializedContents);

Assert.Equal(contents.Length, deserializedContents.Length);
for (int i = 0; i < deserializedContents.Length; i++)
{
Assert.NotNull(contents[i]);
Assert.Equal(contents[i].GetType(), deserializedContents[i].GetType());
}
}

private class TestUserInputResponseContent : UserInputResponseContent
{
public TestUserInputResponseContent(string id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ namespace Microsoft.Extensions.AI;
[JsonSerializable(typeof(ChatResponseFormatTests.SomeType))]
[JsonSerializable(typeof(ChatResponseFormatTests.TypeWithDisplayName))]
[JsonSerializable(typeof(ResponseContinuationToken))]
[JsonSerializable(typeof(UserInputRequestContent[]))]
[JsonSerializable(typeof(UserInputResponseContent[]))]
internal sealed partial class TestJsonSerializerContext : JsonSerializerContext;
Loading