Skip to content

Commit 090d7a2

Browse files
authored
Add NativeAOT testapp project for M.E.AI (#5573)
* Add NativeAOT testapp project for M.E.AI * Address PR feedback
1 parent 443dc6e commit 090d7a2

File tree

5 files changed

+104
-8
lines changed

5 files changed

+104
-8
lines changed

src/Libraries/Microsoft.Extensions.AI.AzureAIInference/JsonContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ private static JsonSerializerOptions CreateDefaultToolJsonOptions()
4848
{
4949
// If reflection-based serialization is enabled by default, use it, as it's the most permissive in terms of what it can serialize,
5050
// and we want to be flexible in terms of what can be put into the various collections in the object model.
51-
// Otherwise, use the source-generated options to enable Native AOT.
51+
// Otherwise, use the source-generated options to enable trimming and Native AOT.
5252

5353
if (JsonSerializer.IsReflectionEnabledByDefault)
5454
{
55-
// Keep in sync with the JsonSourceGenerationOptions on JsonContext below.
55+
// Keep in sync with the JsonSourceGenerationOptions attribute on JsonContext above.
5656
JsonSerializerOptions options = new(JsonSerializerDefaults.Web)
5757
{
5858
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),

src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIChatClient.cs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Reflection;
78
using System.Runtime.CompilerServices;
89
using System.Text;
910
using System.Text.Json;
1011
using System.Text.Json.Serialization;
12+
using System.Text.Json.Serialization.Metadata;
1113
using System.Threading;
1214
using System.Threading.Tasks;
1315
using Microsoft.Shared.Diagnostics;
@@ -587,10 +589,9 @@ private sealed class OpenAIChatToolJson
587589
string? result = resultContent.Result as string;
588590
if (result is null && resultContent.Result is not null)
589591
{
590-
JsonSerializerOptions options = ToolCallJsonSerializerOptions ?? JsonContext.Default.Options;
591592
try
592593
{
593-
result = JsonSerializer.Serialize(resultContent.Result, options.GetTypeInfo(typeof(object)));
594+
result = JsonSerializer.Serialize(resultContent.Result, JsonContext.GetTypeInfo(typeof(object), ToolCallJsonSerializerOptions));
594595
}
595596
catch (NotSupportedException)
596597
{
@@ -617,7 +618,9 @@ private sealed class OpenAIChatToolJson
617618
ChatToolCall.CreateFunctionToolCall(
618619
callRequest.CallId,
619620
callRequest.Name,
620-
BinaryData.FromObjectAsJson(callRequest.Arguments, ToolCallJsonSerializerOptions)));
621+
new(JsonSerializer.SerializeToUtf8Bytes(
622+
callRequest.Arguments,
623+
JsonContext.GetTypeInfo(typeof(IDictionary<string, object?>), ToolCallJsonSerializerOptions)))));
621624
}
622625
}
623626

@@ -670,8 +673,53 @@ private static FunctionCallContent ParseCallContentFromBinaryData(BinaryData ut8
670673
argumentParser: static json => JsonSerializer.Deserialize(json, JsonContext.Default.IDictionaryStringObject)!);
671674

672675
/// <summary>Source-generated JSON type information.</summary>
676+
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web,
677+
UseStringEnumConverter = true,
678+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
679+
WriteIndented = true)]
673680
[JsonSerializable(typeof(OpenAIChatToolJson))]
674681
[JsonSerializable(typeof(IDictionary<string, object?>))]
675682
[JsonSerializable(typeof(JsonElement))]
676-
private sealed partial class JsonContext : JsonSerializerContext;
683+
private sealed partial class JsonContext : JsonSerializerContext
684+
{
685+
/// <summary>Gets the <see cref="JsonSerializerOptions"/> singleton used as the default in JSON serialization operations.</summary>
686+
private static readonly JsonSerializerOptions _defaultToolJsonOptions = CreateDefaultToolJsonOptions();
687+
688+
/// <summary>Gets JSON type information for the specified type.</summary>
689+
/// <remarks>
690+
/// This first tries to get the type information from <paramref name="firstOptions"/>,
691+
/// falling back to <see cref="_defaultToolJsonOptions"/> if it can't.
692+
/// </remarks>
693+
public static JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions? firstOptions) =>
694+
firstOptions?.TryGetTypeInfo(type, out JsonTypeInfo? info) is true ?
695+
info :
696+
_defaultToolJsonOptions.GetTypeInfo(type);
697+
698+
/// <summary>Creates the default <see cref="JsonSerializerOptions"/> to use for serialization-related operations.</summary>
699+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050", Justification = "DefaultJsonTypeInfoResolver is only used when reflection-based serialization is enabled")]
700+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "DefaultJsonTypeInfoResolver is only used when reflection-based serialization is enabled")]
701+
private static JsonSerializerOptions CreateDefaultToolJsonOptions()
702+
{
703+
// If reflection-based serialization is enabled by default, use it, as it's the most permissive in terms of what it can serialize,
704+
// and we want to be flexible in terms of what can be put into the various collections in the object model.
705+
// Otherwise, use the source-generated options to enable trimming and Native AOT.
706+
707+
if (JsonSerializer.IsReflectionEnabledByDefault)
708+
{
709+
// Keep in sync with the JsonSourceGenerationOptions attribute on JsonContext above.
710+
JsonSerializerOptions options = new(JsonSerializerDefaults.Web)
711+
{
712+
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
713+
Converters = { new JsonStringEnumConverter() },
714+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
715+
WriteIndented = true,
716+
};
717+
718+
options.MakeReadOnly();
719+
return options;
720+
}
721+
722+
return Default.Options;
723+
}
724+
}
677725
}

src/Libraries/Microsoft.Extensions.AI/Utilities/AIJsonUtilities.Defaults.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ private static JsonSerializerOptions CreateDefaultOptions()
2323
{
2424
// If reflection-based serialization is enabled by default, use it, as it's the most permissive in terms of what it can serialize,
2525
// and we want to be flexible in terms of what can be put into the various collections in the object model.
26-
// Otherwise, use the source-generated options to enable Native AOT.
26+
// Otherwise, use the source-generated options to enable trimming and Native AOT.
2727

2828
if (JsonSerializer.IsReflectionEnabledByDefault)
2929
{
30-
// Keep in sync with the JsonSourceGenerationOptions on JsonContext below.
30+
// Keep in sync with the JsonSourceGenerationOptions attribute on JsonContext below.
3131
JsonSerializerOptions options = new(JsonSerializerDefaults.Web)
3232
{
3333
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>$(LatestTargetFramework)</TargetFrameworks>
6+
<PublishAot>true</PublishAot>
7+
<TrimmerSingleWarn>false</TrimmerSingleWarn>
8+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<TrimmerRootAssembly Include="Microsoft.Extensions.AI" />
13+
<TrimmerRootAssembly Include="Microsoft.Extensions.AI.Abstractions" />
14+
<TrimmerRootAssembly Include="Microsoft.Extensions.AI.Ollama" />
15+
<!-- Azure.AI.Inference produces many warnings
16+
<TrimmerRootAssembly Include="Microsoft.Extensions.AI.AzureAIInference" />
17+
-->
18+
<!-- OpenAI produces a few warnings
19+
<TrimmerRootAssembly Include="Microsoft.Extensions.AI.OpenAI" />
20+
-->
21+
22+
<TrimmerRootAssembly Update="@(TrimmerRootAssembly)" Path="$(RepoRoot)\src\Libraries\%(Identity)\%(Identity).csproj" />
23+
<ProjectReference Include="@(TrimmerRootAssembly->'%(Path)')" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#pragma warning disable S125 // Remove this commented out code
5+
6+
using Microsoft.Extensions.AI;
7+
8+
// Use types from each library.
9+
10+
// Microsoft.Extensions.AI.Ollama
11+
using var b = new OllamaChatClient("http://localhost:11434", "llama3.2");
12+
13+
// Microsoft.Extensions.AI.AzureAIInference
14+
// using var a = new Azure.AI.Inference.ChatCompletionClient(new Uri("http://localhost"), new("apikey")); // uncomment once warnings in Azure.AI.Inference are addressed
15+
16+
// Microsoft.Extensions.AI.OpenAI
17+
// using var c = new OpenAI.OpenAIClient("apikey").AsChatClient("gpt-4o-mini"); // uncomment once warnings in OpenAI are addressed
18+
19+
// Microsoft.Extensions.AI
20+
AIFunctionFactory.Create(() => { });
21+
22+
System.Console.WriteLine("Success!");

0 commit comments

Comments
 (0)