Context
I have a custom AITool implementation that needs to resolve method parameters from an IServiceProvider (using the AIFunctionFactoryOptions.ParameterBindingOptions extensibility in Microsoft.Extensions.AI.Abstractions). I won't go into the details in case it distracts from the core issue.
This scenario used to work well in earlier builds (last one I tried was commit 3ee9ddd, about 1 month old). However, starting today, I noticed that my custom AITool implementation is no longer working because AIFunctionArguments.Services always returns EmptyServiceProvider. Before it would return the IServiceProvider that gets passed to ChatClientBuilder.Build(IServiceProvider).
Repro Steps
Here's the code I had before, where the IChatClient infrastructure correctly used my provided IServiceProvider for binding to AITool parameters.
IServiceProvider sp = // ...
IChatClient chatClient = new AzureOpenAIClient(new Uri(azureOpenAiEndpoint), credential)
.GetChatClient(azureOpenAiDeploymentName)
.AsIChatClient()
.UseFunctionInvocation()
.Build(sp); // <-- add my service provider
ChatClientAgent agentClient = new(chatClient, agentOptions);
await agentClient.RunStreamingAsync(...); // <-- invokes AITool infrastructure
Investigation
The issue seems to be here:
|
chatBuilder.Use((IChatClient innerClient, IServiceProvider services) => |
|
{ |
|
var loggerFactory = services.GetService<ILoggerFactory>(); |
|
|
|
return new NewFunctionInvokingChatClient(innerClient, loggerFactory, services); |
|
}); |
For some reason, the NewFunctionInvokingChatClient is always given EmptyServiceProvider as the services parameter. It somehow loses the IServiceProvider that I gave to my IChatClient originally.
Workaround
I worked around this by creating a custom IChatClient that implements NewFunctionInvokingChatClient and passing in my IServiceProvider there. That causes the code I highlighted above to get skipped, allowing my scenario to start working again. It took quite a bit of debugging to figure this out, though and it feels very fragile.
Hoping there is a simple fix to ensure that I don't have to rely on fragile workarounds. But it seems that the Agent Framework wrappers are breaking existing stable Microsoft.Extensions.AI behavior/expectations (custom parameter binding, in my case), so probably worth fixing.
Context
I have a custom
AIToolimplementation that needs to resolve method parameters from anIServiceProvider(using theAIFunctionFactoryOptions.ParameterBindingOptionsextensibility in Microsoft.Extensions.AI.Abstractions). I won't go into the details in case it distracts from the core issue.This scenario used to work well in earlier builds (last one I tried was commit 3ee9ddd, about 1 month old). However, starting today, I noticed that my custom
AIToolimplementation is no longer working becauseAIFunctionArguments.Servicesalways returnsEmptyServiceProvider. Before it would return theIServiceProviderthat gets passed toChatClientBuilder.Build(IServiceProvider).Repro Steps
Here's the code I had before, where the IChatClient infrastructure correctly used my provided
IServiceProviderfor binding toAIToolparameters.Investigation
The issue seems to be here:
agent-framework/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientExtensions.cs
Lines 22 to 27 in 78125f0
For some reason, the
NewFunctionInvokingChatClientis always givenEmptyServiceProvideras theservicesparameter. It somehow loses theIServiceProviderthat I gave to myIChatClientoriginally.Workaround
I worked around this by creating a custom
IChatClientthat implementsNewFunctionInvokingChatClientand passing in myIServiceProviderthere. That causes the code I highlighted above to get skipped, allowing my scenario to start working again. It took quite a bit of debugging to figure this out, though and it feels very fragile.Hoping there is a simple fix to ensure that I don't have to rely on fragile workarounds. But it seems that the Agent Framework wrappers are breaking existing stable Microsoft.Extensions.AI behavior/expectations (custom parameter binding, in my case), so probably worth fixing.