Skip to content

feat: Pass in serializer options to be AOT compliant#351

Open
copybara-service[bot] wants to merge 1 commit into
mainfrom
copybara_927504092
Open

feat: Pass in serializer options to be AOT compliant#351
copybara-service[bot] wants to merge 1 commit into
mainfrom
copybara_927504092

Conversation

@copybara-service

Copy link
Copy Markdown
Contributor

feat: Pass in serializer options to be AOT compliant

@copybara-service copybara-service Bot force-pushed the copybara_927504092 branch 4 times, most recently from 7dc7a81 to 3f856e0 Compare June 8, 2026 18:33
@matthew29tang

Copy link
Copy Markdown
Collaborator

@tarekgh could you take a look at this PR to see if it includes the right changes to the auto-generated files for AOT compliance?

It seems like I'd need to re-record all of the tests because we're passing in the serializer options for all the http calls, so that will take some additional time on my end.

@tarekgh

tarekgh commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Thanks for putting this together! Passing explicit, source-generated serializer options through these call sites is a great step in the right direction, and the GenAIJsonContext coverage looks solid. I took a closer look to see whether this gets the library all the way to full AOT compatibility, and I think there are a few remaining items worth considering before we can rely on it end to end. Sharing them here in case they are helpful.

1. The shared options still chain a reflection-based resolver

In Google.GenAI/JsonConfig.cs (lines 54-55), the options are built as:

options.TypeInfoResolverChain.Insert(0, GenAIJsonContext.Default);
options.TypeInfoResolverChain.Add(new DefaultJsonTypeInfoResolver());

DefaultJsonTypeInfoResolver's parameterless constructor is annotated with [RequiresUnreferencedCode] and [RequiresDynamicCode], so as long as it stays in the chain the options are not AOT-safe: any type that is not in GenAIJsonContext will resolve through reflection at runtime, and an AOT/trim build would flag it. Since this PR routes everything through these options, this fallback effectively remains on the hot path. To close the loop, it would need to be removed and every serialized type covered by a source-generated resolver.

2. A couple of call sites still use the reflection default options

These two were not updated and still rely on JsonSerializerOptions.Default (reflection):

  • Google.GenAI/Files.cs:860JsonSerializer.Deserialize<Google.GenAI.Types.File>(fileNode.ToString())
  • Google.GenAI/Tokens.cs:201JsonSerializer.SerializeToNode(config)

3. Polymorphic object values fall back to reflection for unregistered types

Google.GenAI/GoogleGenAIChatClient.cs:504 stores a boxed object into the function response:

funcResponse.Response = new() { ["result"] = functionResultContent.Result };

When the graph is serialized, Result can hold Microsoft.Extensions.AI content types (e.g. AIContent/List<AIContent>), which are not part of GenAIJsonContext, so they go through the reflection fallback. The updated expectation in GoogleGenAIExtensionsTest.cs reflects this (the output switched to camelCase via the shared options, but the type metadata still comes from the fallback resolver). Common.cs:289 (ParseToJsonNode(object?)) has the same characteristic for arbitrary boxed values. Chaining in AIJsonUtilities.DefaultOptions's resolver (or otherwise registering these types) would likely be needed here.

4. There is currently no AOT/trim enforcement in the build

Google.GenAI/Google.GenAI.csproj does not set IsAotCompatible (nor the trim/AOT analyzers), there is no Directory.Build.props, and CI does not run an AOT publish. Without enforcement, reflection paths like the ones above will not surface as warnings, which makes it easy for them to creep back in. Enabling <IsAotCompatible>true</IsAotCompatible> for the net8.0 target (netstandard2.0 is naturally exempt) and adding a small PublishAot smoke-test app to CI would give us a guardrail to verify and keep this working. The current AotJsonContextTest is a nice coverage check, but it runs under the JIT and does not exercise an actual AOT publish.

Summary: items 1, 2, and 4 feel like must-haves before we can call the library fully AOT compatible, and item 3 is the trickier piece because of the open-ended object values. Happy to help with any of these if it is useful, and thanks again for driving this forward!

@copybara-service copybara-service Bot force-pushed the copybara_927504092 branch from 3f856e0 to 6ab5207 Compare June 10, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants