NativeAOT and Trimming Guidelines - gunpal5/Google_GenerativeAI GitHub Wiki
This page outlines the guidelines for using the Google Generative AI SDK with NativeAOT and trimming. NativeAOT and trimming can significantly reduce the size and startup time of your application, but they require careful consideration of reflection and dynamic code generation.
The core features and code-generated function tools of the Google Generative AI SDK are designed to be compatible with NativeAOT and trimming without requiring manual changes. However, features that rely on JsonMode and reflection-based QuickTools necessitate explicit configuration of JsonSerializerContext.
When using JsonMode, you must create a JsonSerializerContext that includes all the types used in your JSON schema. This allows the NativeAOT compiler to generate the necessary serialization code at compile time, eliminating the need for runtime reflection.
using System.Text.Json.Serialization;
using Google.GenerativeAI;
// Example Data Class
public class MyData
{
public string Name { get; set; }
public int Age { get; set; }
}
[JsonSerializable(typeof(MyData))]
public partial class MyDataJsonContext : JsonSerializerContext { }
//Option 1 add to global type resolvers
DefaultSerializerOptions.CustomTypeResolvers.Add(MyDataJsonContext.Default);
//Option 2 add to GenerativeModel class instance
generativeModel.GenerateObjectJsonSerializerOptions = MyDataJsonContext.Default.Options
//Option 3 pass it as argument
GenerateContentRequest request = new GenerateContentRequest()
request.UseJsonMode<MyData>(MyDataJsonContext.Default.Options)For QuickTools that rely on reflection, you must also provide a JsonSerializerContext that includes all class type arguments and return types used in the tools.
public class ToolInput
{
public string InputText { get; set; }
}
public class ToolOutput
{
public int Result { get; set; }
}
public ToolOutput MyToolMethod(ToolInput input)
{
return new ToolOutput { Result = input.InputText.Length };
}
[JsonSerializable(typeof(ToolInput))]
[JsonSerializable(typeof(ToolOutput))]
public partial class MyToolJsonContext : JsonSerializerContext { }
//Option 1 add to global type resolvers
DefaultSerializerOptions.CustomTypeResolvers.Add(MyDataJsonContext.Default);
//Option 2 pass it into the constructor
var quickTool = new QuickTool(MyToolMethod,options:MyToolJsonContext.Default.Options)You can pass the JsonSerializerContext in three ways:
-
Global Configuration: Add the
JsonSerializerContexttoDefaultSerializerOptions.CustomJsonTypeResolversduring application initialization. This makes the context available throughout the application.using System.Text.Json; using Google.GenerativeAI; public static void ConfigureJsonContext() { DefaultSerializerOptions.CustomJsonTypeResolvers.Add(MyDataJsonContext.Default); }
-
GenerativeModel Scope: Set the
GenerateObjectJsonSerializerOptionsproperty of aGenerativeAIinstance. This makes the context available within the scope of theGenerativeModelclass.using System.Text.Json; using Google.GenerativeAI; public async Task UseModelScopedContext(string apiKey, string modelName, string prompt) { var googleAI = new GenerativeAI(apiKey); var model = googleAI.GenerativeModel(modelName); model.GenerateObjectJsonSerializerOptions = MyDataJsonContext.Default.Options; //Use JsonMode specific overloads var obj = await model.GenerateObjectAsync<T>(...); }
-
Request/Response Level: Pass the
JsonSerializerOptionstoGenerateContentRequest.UseJsonMode<T>(options)andGenerateContentResponse.ToObject<T>(options).using Google.GenerativeAI; public async Task UseRequestSpecificContext(GenerativeModel model, string prompt) { var request = new GenerateContentRequest(); request.AddText(prompt); request.UseJsonMode<MyData>(MyDataJsonContext.Default.Options); var response = await model.GenerateContentAsync(request); var myData = response.ToObject<MyData>(MyDataJsonContext.Default.Options); Console.WriteLine($"Name: {myData.Name}, Age: {myData.Age}"); }
-
Complete Type Registration: Ensure that all types used in
JsonModeand QuickTools are registered in theJsonSerializerContext. Failure to do so will result in runtime errors. -
Performance: Pre-generating serialization code using
JsonSerializerContextimproves performance and reduces startup time in NativeAOT environments. -
Trimming: When trimming, the compiler will remove unused code. Providing a
JsonSerializerContextensures that necessary serialization code is preserved. - Reflection Limitations: NativeAOT and trimming significantly restrict the use of runtime reflection. Rely on code generation and explicit type registration to avoid issues.