Skip to content

Commit dd69cab

Browse files
westey-mCopilot
andauthored
.NET: Seal factory contexts and add non JSO deserialize overloads (#3066)
* Seal factory contexts and add non JSO deserialize overloads * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent 2e1189c commit dd69cab

File tree

4 files changed

+68
-3
lines changed

4 files changed

+68
-3
lines changed

dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,15 @@ public AgentRunResponseUpdate[] ToAgentRunResponseUpdates()
291291
return updates;
292292
}
293293

294+
/// <summary>
295+
/// Deserializes the response text into the given type.
296+
/// </summary>
297+
/// <typeparam name="T">The output type to deserialize into.</typeparam>
298+
/// <returns>The result as the requested type.</returns>
299+
/// <exception cref="InvalidOperationException">The result is not parsable into the requested type.</exception>
300+
public T Deserialize<T>() =>
301+
this.Deserialize<T>(AgentAbstractionsJsonUtilities.DefaultOptions);
302+
294303
/// <summary>
295304
/// Deserializes the response text into the given type using the specified serializer options.
296305
/// </summary>
@@ -311,6 +320,15 @@ public T Deserialize<T>(JsonSerializerOptions serializerOptions)
311320
};
312321
}
313322

323+
/// <summary>
324+
/// Tries to deserialize response text into the given type.
325+
/// </summary>
326+
/// <typeparam name="T">The output type to deserialize into.</typeparam>
327+
/// <param name="structuredOutput">The parsed structured output.</param>
328+
/// <returns><see langword="true" /> if parsing was successful; otherwise, <see langword="false" />.</returns>
329+
public bool TryDeserialize<T>([NotNullWhen(true)] out T? structuredOutput) =>
330+
this.TryDeserialize(AgentAbstractionsJsonUtilities.DefaultOptions, out structuredOutput);
331+
314332
/// <summary>
315333
/// Tries to deserialize response text into the given type using the specified serializer options.
316334
/// </summary>

dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public ChatClientAgentOptions Clone()
8080
/// <summary>
8181
/// Context object passed to the <see cref="AIContextProviderFactory"/> to create a new instance of <see cref="AIContextProvider"/>.
8282
/// </summary>
83-
public class AIContextProviderFactoryContext
83+
public sealed class AIContextProviderFactoryContext
8484
{
8585
/// <summary>
8686
/// Gets or sets the serialized state of the <see cref="AIContextProvider"/>, if any.
@@ -97,7 +97,7 @@ public class AIContextProviderFactoryContext
9797
/// <summary>
9898
/// Context object passed to the <see cref="ChatMessageStoreFactory"/> to create a new instance of <see cref="ChatMessageStore"/>.
9999
/// </summary>
100-
public class ChatMessageStoreFactoryContext
100+
public sealed class ChatMessageStoreFactoryContext
101101
{
102102
/// <summary>
103103
/// Gets or sets the serialized state of the chat message store, if any.

dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunResponse{T}.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public ChatClientAgentRunResponse(ChatResponse<T> response) : base(response)
4040
/// </summary>
4141
/// <remarks>
4242
/// If the response did not contain JSON, or if deserialization fails, this property will throw.
43-
/// To avoid exceptions, use <see cref="AgentRunResponse.TryDeserialize{T}"/> instead.
4443
/// </remarks>
4544
public override T Result => this._response.Result;
4645
}

dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentRunResponseTests.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,37 @@ public void ToAgentRunResponseUpdatesProducesUpdates()
214214
Assert.Equal(100, usageContent.Details.TotalTokenCount);
215215
}
216216

217+
#if NETFRAMEWORK
218+
/// <summary>
219+
/// Since Json Serialization using reflection is disabled in .net core builds, and we are using a custom type here that wouldn't
220+
/// be registered with the default source generated serializer, this test will only pass in .net framework builds where reflection-based
221+
/// serialization is available.
222+
/// </summary>
217223
[Fact]
218224
public void ParseAsStructuredOutputSuccess()
219225
{
220226
// Arrange.
221227
var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger };
222228
var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal)));
223229

230+
// Act.
231+
var animal = response.Deserialize<Animal>();
232+
233+
// Assert.
234+
Assert.NotNull(animal);
235+
Assert.Equal(expectedResult.Id, animal.Id);
236+
Assert.Equal(expectedResult.FullName, animal.FullName);
237+
Assert.Equal(expectedResult.Species, animal.Species);
238+
}
239+
#endif
240+
241+
[Fact]
242+
public void ParseAsStructuredOutputWithJSOSuccess()
243+
{
244+
// Arrange.
245+
var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger };
246+
var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal)));
247+
224248
// Act.
225249
var animal = response.Deserialize<Animal>(TestJsonSerializerContext.Default.Options);
226250

@@ -262,13 +286,37 @@ public void ParseAsStructuredOutputFailsWithIncorrectTypedJson()
262286
Assert.Throws<JsonException>(() => response.Deserialize<Animal>(TestJsonSerializerContext.Default.Options));
263287
}
264288

289+
#if NETFRAMEWORK
290+
/// <summary>
291+
/// Since Json Serialization using reflection is disabled in .net core builds, and we are using a custom type here that wouldn't
292+
/// be registered with the default source generated serializer, this test will only pass in .net framework builds where reflection-based
293+
/// serialization is available.
294+
/// </summary>
265295
[Fact]
266296
public void TryParseAsStructuredOutputSuccess()
267297
{
268298
// Arrange.
269299
var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger };
270300
var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal)));
271301

302+
// Act.
303+
response.TryDeserialize(out Animal? animal);
304+
305+
// Assert.
306+
Assert.NotNull(animal);
307+
Assert.Equal(expectedResult.Id, animal.Id);
308+
Assert.Equal(expectedResult.FullName, animal.FullName);
309+
Assert.Equal(expectedResult.Species, animal.Species);
310+
}
311+
#endif
312+
313+
[Fact]
314+
public void TryParseAsStructuredOutputWithJSOSuccess()
315+
{
316+
// Arrange.
317+
var expectedResult = new Animal { Id = 1, FullName = "Tigger", Species = Species.Tiger };
318+
var response = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, JsonSerializer.Serialize(expectedResult, TestJsonSerializerContext.Default.Animal)));
319+
272320
// Act.
273321
response.TryDeserialize(TestJsonSerializerContext.Default.Options, out Animal? animal);
274322

0 commit comments

Comments
 (0)