Microsoft.Extensions.AI: Common Mistakes with Renamed Methods, Deprecated Ollama Packages, Keyed Registration & Tool Calling

Verified against .NET 10 and Microsoft.Extensions.AI 10.x. Last reviewed: July 23, 2026. These APIs changed significantly between preview and stable releases, so compare copied samples with the current names shown below.

Your Sample Code Compiles Against a Version That No Longer Exists

An older Microsoft.Extensions.AI sample can look perfectly reasonable and still fail on the first build. The package stabilised quickly, and several preview-era method names were replaced before the current 10.x line. Blog posts, videos, GitHub snippets, and AI-generated answers often preserve those older names long after the package has moved on.

The failures usually fall into the same clusters: CompleteAsync no longer exists, .AsChatClient() cannot be found, a NuGet warning says the Ollama adapter is deprecated, keyed clients have no middleware, or a tool is offered to the model but never executes. These are not five unrelated problems. They are signs that the sample mixes preview APIs, outdated provider adapters, and an incomplete client pipeline.

Mistake 1 & 2: Obsolete Method and Extension Names

The most obvious symptom is a compile error on code copied from an older sample. Current clients use GetResponseAsync for a normal response and GetStreamingResponseAsync for streamed updates. Provider SDK clients are adapted with .AsIChatClient(), not the older .AsChatClient() extension.

There is another stale assumption nearby: IChatClient does not expose a Metadata property. The current interface is intentionally small. Its main members are GetResponseAsync, GetStreamingResponseAsync, GetService, and Dispose. Use GetService when a client or wrapper exposes an underlying service.

The returned framework type is Microsoft.Extensions.AI.ChatResponse. Its useful values include Text, Usage, FinishReason, ModelId, and ConversationId. Those names are a useful quick test when checking whether a snippet matches the stable API.

ObsoleteApiNames.cs
// WRONG: preview-era names
ChatResponse response = await client.CompleteAsync(messages);
await foreach (var update in client.CompleteStreamingAsync(messages)) { }

IChatClient chatClient = openAiChatClient.AsChatClient();
object? metadata = chatClient.Metadata;

// FIX: current Microsoft.Extensions.AI surface
IChatClient currentClient = openAiChatClient.AsIChatClient();

ChatResponse currentResponse =
    await currentClient.GetResponseAsync(messages, cancellationToken: ct);

Console.WriteLine(currentResponse.Text);
Console.WriteLine(currentResponse.FinishReason);
Console.WriteLine(currentResponse.Usage);

await foreach (ChatResponseUpdate update in
    currentClient.GetStreamingResponseAsync(messages, cancellationToken: ct))
{
    Console.Write(update.Text);
}

// Ask the client or middleware for an exposed service.
OpenAI.Chat.ChatClient? inner =
    currentClient.GetService<OpenAI.Chat.ChatClient>();

Mistake 3: Installing the Deprecated Ollama Package

A second common problem begins before any code is written: the project installs Microsoft.Extensions.AI.Ollama because an older tutorial uses OllamaChatClient. That package is deprecated. Its NuGet page states that no further updates, features, or fixes are planned and recommends OllamaSharp instead.

The replacement is not merely a package-name change. OllamaApiClient from OllamaSharp implements IChatClient directly, so it can be registered as the provider client and wrapped with the same middleware used for cloud providers. New projects should not deliberately adopt an adapter that has already reached the end of its update path.

Deprecation warning: Do not treat a successful restore as proof that a package is suitable for new development. NuGet can restore a deprecated package while also warning that it will receive no further fixes.

The same warning applies to local embeddings. If another article shows an old Microsoft Ollama embedding adapter, check the current IEmbeddingGenerator guidance and use the supported OllamaSharp integration instead. The detailed hybrid-search tutorial linked later uses that current approach.

OllamaMigration.cs
// DEPRECATED — avoid in new code
// IChatClient client = new OllamaChatClient(
//     new Uri("http://localhost:11434"),
//     "llama3.2");

// CURRENT — OllamaSharp
using OllamaSharp;

IChatClient ollama = new OllamaApiClient(
    new Uri("http://localhost:11434"),
    "llama3.2");

// Compose it like any other IChatClient.
IChatClient pipeline = new ChatClientBuilder(ollama)
    .UseFunctionInvocation()
    .UseOpenTelemetry()
    .Build();

Mistake 4: Registration That Silently Drops Your Middleware

Keyed dependency injection is useful when an application has several providers, but the registration method matters. AddKeyedSingleton<IChatClient> stores a keyed client and stops there. It does not return the ChatClientBuilder used to add function invocation, OpenTelemetry, logging, caching, or other delegating middleware.

AddKeyedChatClient is designed for this scenario. It registers the keyed client and returns a builder, allowing each provider to receive its own pipeline. An OpenAI client can enable tools and telemetry while another provider can use a smaller pipeline based on its supported capabilities.

A related source of confusion is naming the application's public DTO ChatResponse. That collides with Microsoft.Extensions.AI.ChatResponse and produces ambiguous references. Use names such as GatewayChatRequest and GatewayChatResponse, while reusing the framework's ChatMessage and ChatRole types internally.

Capability warning: A keyed registration identifies a client; it does not prove that its model supports tools, structured output, usage reporting, or the same context size. Keep capability metadata in your provider registry and validate requests before sending them.
KeyedRegistration.cs
// FLAT REGISTRATION: the keyed client exists, but no builder pipeline follows.
builder.Services.AddKeyedSingleton<IChatClient>("ollama", (_, _) =>
    new OllamaApiClient(new Uri(ollamaEndpoint), ollamaModel));

// BUILDER-BASED REGISTRATION: middleware belongs to this provider.
builder.Services
    .AddKeyedChatClient("ollama", _ =>
        new OllamaApiClient(new Uri(ollamaEndpoint), ollamaModel))
    .UseFunctionInvocation()
    .UseOpenTelemetry();

// Avoid a framework type-name collision.
public sealed record GatewayChatRequest(
    string Message,
    string? Provider);

public sealed record GatewayChatResponse(
    string Content,
    string Provider,
    string? Model,
    string? FinishReason);

Mistake 5: Tool Calls the Model Requests but Nothing Executes

Creating an AIFunction and adding it to ChatOptions.Tools makes the function visible to the model. It does not, by itself, execute the function. Without function-invocation middleware, the provider can return a tool-call request and the application simply receives that request as the end of the exchange.

UseFunctionInvocation() adds FunctionInvokingChatClient to the pipeline. That wrapper detects the requested function, invokes it, adds the result to the conversation, and continues the model call. This is why a tool can appear to be correctly registered while never firing: the definition exists, but the execution loop does not.

Treat every generated tool argument as untrusted input. A model can invent a customer ID, date, file name, or quantity. Validate ranges, identifiers, authorization, and side effects exactly as you would for an HTTP request. Also set sensible iteration limits so a tool loop cannot continue indefinitely.

Security warning: Tool calling is not an authorization system. A valid-looking model argument does not prove the user is allowed to perform the requested operation.
ToolCalling.cs
AIFunction getOrder = AIFunctionFactory.Create(
    (int orderId) =>
    {
        if (orderId <= 0)
        {
            throw new ArgumentOutOfRangeException(
                nameof(orderId), "Order id must be positive.");
        }

        return orderLookup.Find(orderId);
    },
    name: "get_order");

// INCOMPLETE: the model sees the tool, but nothing executes it.
var options = new ChatOptions
{
    Tools = [getOrder]
};

ChatResponse stoppedAtToolRequest =
    await rawClient.GetResponseAsync(messages, options, ct);

// FIX: add the function-invocation loop to the client pipeline.
IChatClient toolEnabledClient = new ChatClientBuilder(rawClient)
    .UseFunctionInvocation()
    .Build();

ChatResponse completed =
    await toolEnabledClient.GetResponseAsync(messages, options, ct);

How to Tell If a Snippet Is Current

Before copying an AI integration sample into a current .NET project, scan it for four warning signs. Any one of them suggests that the code was written against an older preview or a deprecated adapter. Check the package's official README and API reference before trying to repair the surrounding code.

Current code should normally show GetResponseAsync, GetStreamingResponseAsync, .AsIChatClient(), and OllamaSharp for local Ollama access. It should use a ChatClientBuilder when middleware is required and GetService rather than a nonexistent Metadata property.

CurrentApiCheatSheet.txt
// OLD / STALE                              // CURRENT
CompleteAsync(...)                          GetResponseAsync(...)
CompleteStreamingAsync(...)                 GetStreamingResponseAsync(...)
providerClient.AsChatClient()               providerClient.AsIChatClient()
Microsoft.Extensions.AI.Ollama              OllamaSharp
OllamaChatClient                            OllamaApiClient
chatClient.Metadata                         chatClient.GetService<T>()
AddKeyedSingleton<IChatClient>(...)          AddKeyedChatClient(...)
// when middleware is required:
ChatOptions.Tools only                      .UseFunctionInvocation() + Tools

What Developers Want to Know

Why does CompleteAsync not exist on IChatClient?

CompleteAsync was used by older preview-era samples. Current Microsoft.Extensions.AI applications use GetResponseAsync for ordinary responses and GetStreamingResponseAsync for streaming responses. If copied code also uses AsChatClient, replace that with AsIChatClient.

Should I still use the Microsoft.Extensions.AI.Ollama package?

No for new applications. The Microsoft.Extensions.AI.Ollama package is deprecated and no further updates, features, or fixes are planned. Use OllamaSharp instead. Its OllamaApiClient implements IChatClient and can be composed through ChatClientBuilder.

Why is my AI function never called even though I registered it?

Adding an AIFunction to ChatOptions.Tools only tells the model that the tool exists. The client pipeline must also include UseFunctionInvocation so FunctionInvokingChatClient executes requested calls and continues the conversation. Validate every generated argument before performing work.

Do I need AddKeyedChatClient, or is AddKeyedSingleton enough?

AddKeyedSingleton can register an IChatClient, but it does not give you the ChatClientBuilder pipeline used for function invocation, OpenTelemetry, caching, and other middleware. AddKeyedChatClient is the better fit when each provider needs its own composed pipeline.

Back to Articles