-
Couldn't load subscription status.
- Fork 840
Use the same JsonSerializerOptions default in all locations. #5507
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,17 +23,12 @@ namespace Microsoft.Extensions.AI; | |
| /// <summary>Provides factory methods for creating commonly-used implementations of <see cref="AIFunction"/>.</summary> | ||
| public static class AIFunctionFactory | ||
| { | ||
| internal const string UsesReflectionJsonSerializerMessage = | ||
| "This method uses the reflection-based JsonSerializer which can break in trimmed or AOT applications."; | ||
|
|
||
| /// <summary>Lazily-initialized default options instance.</summary> | ||
| private static AIFunctionFactoryCreateOptions? _defaultOptions; | ||
|
|
||
| /// <summary>Creates an <see cref="AIFunction"/> instance for a method, specified via a delegate.</summary> | ||
| /// <param name="method">The method to be represented via the created <see cref="AIFunction"/>.</param> | ||
| /// <returns>The created <see cref="AIFunction"/> for invoking <paramref name="method"/>.</returns> | ||
| [RequiresUnreferencedCode(UsesReflectionJsonSerializerMessage)] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A number of these overloads are now redundant, but I defer to @stephentoub when and when they should be removed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which are redundant / would you want to remove? Do you mean making the AIFunctionFactoryCreateOptions optional on the other overload? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be ok if you wanted to consolidate them here. I'm more concerned about batching up breaking binary changes to the object model, as those are the things we'd expect nuget packages to be impacted by. AIFunctionFactory is less relevant there. |
||
| [RequiresDynamicCode(UsesReflectionJsonSerializerMessage)] | ||
| public static AIFunction Create(Delegate method) => Create(method, _defaultOptions ??= new()); | ||
|
|
||
| /// <summary>Creates an <see cref="AIFunction"/> instance for a method, specified via a delegate.</summary> | ||
|
|
@@ -52,8 +47,6 @@ public static AIFunction Create(Delegate method, AIFunctionFactoryCreateOptions | |
| /// <param name="name">The name to use for the <see cref="AIFunction"/>.</param> | ||
| /// <param name="description">The description to use for the <see cref="AIFunction"/>.</param> | ||
| /// <returns>The created <see cref="AIFunction"/> for invoking <paramref name="method"/>.</returns> | ||
| [RequiresUnreferencedCode("Reflection is used to access types from the supplied Delegate.")] | ||
| [RequiresDynamicCode("Reflection is used to access types from the supplied Delegate.")] | ||
| public static AIFunction Create(Delegate method, string? name, string? description = null) | ||
| => Create(method, (_defaultOptions ??= new()).SerializerOptions, name, description); | ||
|
|
||
|
|
@@ -80,8 +73,6 @@ public static AIFunction Create(Delegate method, JsonSerializerOptions options, | |
| /// This should be <see langword="null"/> if and only if <paramref name="method"/> is a static method. | ||
| /// </param> | ||
| /// <returns>The created <see cref="AIFunction"/> for invoking <paramref name="method"/>.</returns> | ||
| [RequiresUnreferencedCode("Reflection is used to access types from the supplied MethodInfo.")] | ||
| [RequiresDynamicCode("Reflection is used to access types from the supplied MethodInfo.")] | ||
| public static AIFunction Create(MethodInfo method, object? target = null) | ||
| => Create(method, target, _defaultOptions ??= new()); | ||
|
|
||
|
|
@@ -107,8 +98,8 @@ private sealed class ReflectionAIFunction : AIFunction | |
| { | ||
| private readonly MethodInfo _method; | ||
| private readonly object? _target; | ||
| private readonly Func<IReadOnlyDictionary<string, object?>, AIFunctionContext?, object?>[] _parameterMarshalers; | ||
| private readonly Func<object?, ValueTask<object?>> _returnMarshaler; | ||
| private readonly Func<IReadOnlyDictionary<string, object?>, AIFunctionContext?, object?>[] _parameterMarshallers; | ||
| private readonly Func<object?, ValueTask<object?>> _returnMarshaller; | ||
stephentoub marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| private readonly JsonTypeInfo? _returnTypeInfo; | ||
| private readonly bool _needsAIFunctionContext; | ||
|
|
||
|
|
@@ -185,11 +176,11 @@ static bool IsAsyncMethod(MethodInfo method) | |
|
|
||
| // Get marshaling delegates for parameters and build up the parameter metadata. | ||
| var parameters = method.GetParameters(); | ||
| _parameterMarshalers = new Func<IReadOnlyDictionary<string, object?>, AIFunctionContext?, object?>[parameters.Length]; | ||
| _parameterMarshallers = new Func<IReadOnlyDictionary<string, object?>, AIFunctionContext?, object?>[parameters.Length]; | ||
| bool sawAIContextParameter = false; | ||
| for (int i = 0; i < parameters.Length; i++) | ||
| { | ||
| if (GetParameterMarshaler(options.SerializerOptions, parameters[i], ref sawAIContextParameter, out _parameterMarshalers[i]) is AIFunctionParameterMetadata parameterView) | ||
| if (GetParameterMarshaller(options.SerializerOptions, parameters[i], ref sawAIContextParameter, out _parameterMarshallers[i]) is AIFunctionParameterMetadata parameterView) | ||
| { | ||
| parameterMetadata?.Add(parameterView); | ||
| } | ||
|
|
@@ -198,7 +189,7 @@ static bool IsAsyncMethod(MethodInfo method) | |
| _needsAIFunctionContext = sawAIContextParameter; | ||
|
|
||
| // Get the return type and a marshaling func for the return value. | ||
| Type returnType = GetReturnMarshaler(method, out _returnMarshaler); | ||
| Type returnType = GetReturnMarshaller(method, out _returnMarshaller); | ||
| _returnTypeInfo = returnType != typeof(void) ? options.SerializerOptions.GetTypeInfo(returnType) : null; | ||
|
|
||
| Metadata = new AIFunctionMetadata(functionName) | ||
|
|
@@ -224,8 +215,8 @@ static bool IsAsyncMethod(MethodInfo method) | |
| IEnumerable<KeyValuePair<string, object?>>? arguments, | ||
| CancellationToken cancellationToken) | ||
| { | ||
| var paramMarshalers = _parameterMarshalers; | ||
| object?[] args = paramMarshalers.Length != 0 ? new object?[paramMarshalers.Length] : []; | ||
| var paramMarshallers = _parameterMarshallers; | ||
| object?[] args = paramMarshallers.Length != 0 ? new object?[paramMarshallers.Length] : []; | ||
|
|
||
| IReadOnlyDictionary<string, object?> argDict = | ||
| arguments is null || args.Length == 0 ? EmptyReadOnlyDictionary<string, object?>.Instance : | ||
|
|
@@ -242,10 +233,10 @@ static bool IsAsyncMethod(MethodInfo method) | |
|
|
||
| for (int i = 0; i < args.Length; i++) | ||
| { | ||
| args[i] = paramMarshalers[i](argDict, context); | ||
| args[i] = paramMarshallers[i](argDict, context); | ||
| } | ||
|
|
||
| object? result = await _returnMarshaler(ReflectionInvoke(_method, _target, args)).ConfigureAwait(false); | ||
| object? result = await _returnMarshaller(ReflectionInvoke(_method, _target, args)).ConfigureAwait(false); | ||
|
|
||
| switch (_returnTypeInfo) | ||
| { | ||
|
|
@@ -271,11 +262,11 @@ static bool IsAsyncMethod(MethodInfo method) | |
| /// <summary> | ||
| /// Gets a delegate for handling the marshaling of a parameter. | ||
| /// </summary> | ||
| private static AIFunctionParameterMetadata? GetParameterMarshaler( | ||
| private static AIFunctionParameterMetadata? GetParameterMarshaller( | ||
| JsonSerializerOptions options, | ||
| ParameterInfo parameter, | ||
| ref bool sawAIFunctionContext, | ||
| out Func<IReadOnlyDictionary<string, object?>, AIFunctionContext?, object?> marshaler) | ||
| out Func<IReadOnlyDictionary<string, object?>, AIFunctionContext?, object?> marshaller) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(parameter.Name)) | ||
| { | ||
|
|
@@ -292,20 +283,20 @@ static bool IsAsyncMethod(MethodInfo method) | |
|
|
||
| sawAIFunctionContext = true; | ||
|
|
||
| marshaler = static (_, ctx) => | ||
| marshaller = static (_, ctx) => | ||
| { | ||
| Debug.Assert(ctx is not null, "Expected a non-null context object."); | ||
| return ctx; | ||
| }; | ||
| return null; | ||
| } | ||
|
|
||
| // Resolve the contract used to marshall the value from JSON -- can throw if not supported or not found. | ||
| // Resolve the contract used to marshal the value from JSON -- can throw if not supported or not found. | ||
| Type parameterType = parameter.ParameterType; | ||
| JsonTypeInfo typeInfo = options.GetTypeInfo(parameterType); | ||
|
|
||
| // Create a marshaler that simply looks up the parameter by name in the arguments dictionary. | ||
| marshaler = (IReadOnlyDictionary<string, object?> arguments, AIFunctionContext? _) => | ||
| // Create a marshaller that simply looks up the parameter by name in the arguments dictionary. | ||
| marshaller = (IReadOnlyDictionary<string, object?> arguments, AIFunctionContext? _) => | ||
| { | ||
| // If the parameter has an argument specified in the dictionary, return that argument. | ||
| if (arguments.TryGetValue(parameter.Name, out object? value)) | ||
|
|
@@ -368,15 +359,15 @@ static bool IsAsyncMethod(MethodInfo method) | |
| /// <summary> | ||
| /// Gets a delegate for handling the result value of a method, converting it into the <see cref="Task{FunctionResult}"/> to return from the invocation. | ||
| /// </summary> | ||
| private static Type GetReturnMarshaler(MethodInfo method, out Func<object?, ValueTask<object?>> marshaler) | ||
| private static Type GetReturnMarshaller(MethodInfo method, out Func<object?, ValueTask<object?>> marshaller) | ||
| { | ||
| // Handle each known return type for the method | ||
| Type returnType = method.ReturnType; | ||
|
|
||
| // Task | ||
| if (returnType == typeof(Task)) | ||
| { | ||
| marshaler = async static result => | ||
| marshaller = async static result => | ||
| { | ||
| await ((Task)ThrowIfNullResult(result)).ConfigureAwait(false); | ||
| return null; | ||
|
|
@@ -387,7 +378,7 @@ private static Type GetReturnMarshaler(MethodInfo method, out Func<object?, Valu | |
| // ValueTask | ||
| if (returnType == typeof(ValueTask)) | ||
| { | ||
| marshaler = async static result => | ||
| marshaller = async static result => | ||
| { | ||
| await ((ValueTask)ThrowIfNullResult(result)).ConfigureAwait(false); | ||
| return null; | ||
|
|
@@ -401,7 +392,7 @@ private static Type GetReturnMarshaler(MethodInfo method, out Func<object?, Valu | |
| if (returnType.GetGenericTypeDefinition() == typeof(Task<>)) | ||
| { | ||
| MethodInfo taskResultGetter = GetMethodFromGenericMethodDefinition(returnType, _taskGetResult); | ||
| marshaler = async result => | ||
| marshaller = async result => | ||
| { | ||
| await ((Task)ThrowIfNullResult(result)).ConfigureAwait(false); | ||
| return ReflectionInvoke(taskResultGetter, result, null); | ||
|
|
@@ -414,7 +405,7 @@ private static Type GetReturnMarshaler(MethodInfo method, out Func<object?, Valu | |
| { | ||
| MethodInfo valueTaskAsTask = GetMethodFromGenericMethodDefinition(returnType, _valueTaskAsTask); | ||
| MethodInfo asTaskResultGetter = GetMethodFromGenericMethodDefinition(valueTaskAsTask.ReturnType, _taskGetResult); | ||
| marshaler = async result => | ||
| marshaller = async result => | ||
| { | ||
| var task = (Task)ReflectionInvoke(valueTaskAsTask, ThrowIfNullResult(result), null)!; | ||
| await task.ConfigureAwait(false); | ||
|
|
@@ -425,7 +416,7 @@ private static Type GetReturnMarshaler(MethodInfo method, out Func<object?, Valu | |
| } | ||
|
|
||
| // For everything else, just use the result as-is. | ||
| marshaler = result => new ValueTask<object?>(result); | ||
| marshaller = result => new ValueTask<object?>(result); | ||
| return returnType; | ||
|
|
||
| // Throws an exception if a result is found to be null unexpectedly | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.