Skip to content

.NET: IChatClient's service provider lost when using ChatAgentClient, breaking custom AITool implementations #505

@cgillum

Description

@cgillum

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.

Metadata

Metadata

Assignees

Labels

.NETagentsIssues related to single agentsneed infoAdditional info/context is needed to proceed

Type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions