Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

Chat Client Provider

The IChatClientProvider interface is the bridge between the AI conversation service and your AI backend.

By default, a built-in InjectedChatClientProvider is registered that resolves IChatClient from your DI container. For most apps, simply register an IChatClient and you’re done:

builder.Services.AddChatClient(new OpenAIClient("your-api-key").GetChatClient("gpt-4o").AsIChatClient());
builder.Services.AddShinyAiConversation(opts =>
{
// No need to call SetChatClientProvider — the default resolves IChatClient from DI
});

If no IChatClient is registered and no custom provider is set, an InvalidOperationException is thrown at runtime.

Providers also declare how their endpoint can be asked for a structured turn. StructuredOutputMode is a default interface member, so you only override it when the default is wrong for your endpoint:

public class MyChatClientProvider : IChatClientProvider
{
public Task<IChatClient> GetChatClient(CancellationToken cancelToken = default) => ...;
public AiStructuredOutputMode StructuredOutputMode => AiStructuredOutputMode.Json;
}
Mode Request Use for
JsonSchema (default) Native schema-constrained response format OpenAI and compatible endpoints
Json JSON response format, shape described in the prompt GitHub Copilot, models without schema support
Prompt Shape in the prompt only, no format constraint Endpoints that reject both of the above
None Plain text — opts out entirely Providers that can’t do any of it

Both GitHub Copilot providers default to Json because the Copilot proxy passes json_schema support through per-model. A wrong value degrades rather than breaks — the service retries unstructured and falls back to plain text.

Two ready-made providers are available as separate NuGet packages so you don’t have to implement IChatClientProvider yourself.

A static provider for any OpenAI-compatible endpoint (OpenAI, Azure OpenAI, Ollama, etc.). Creates the client once at startup with logging and function invocation middleware.

Terminal window
dotnet add package Shiny.AiConversation.OpenAi
builder.Services.AddShinyAiConversation(opts =>
{
opts.AddStaticOpenAIChatClient(
apiToken: "your-api-key",
endpointUri: "https://api.openai.com/v1",
modelName: "gpt-4o"
);
});

Works great for Blazor WASM, server apps, or any MAUI app where you have an API key up front.

A MAUI-specific provider that authenticates via the GitHub OAuth device code flow and uses the Copilot API for chat completions. The entire authentication experience is self-contained:

  1. When authentication is needed, a popup appears with the device code
  2. The code is automatically copied to the clipboard
  3. The user presses OK and a browser opens to GitHub’s verification page
  4. The provider polls in the background until the user authorizes
  5. The OAuth token is exchanged for a Copilot API token and cached
  6. Tokens are persisted in SecureStorage across app restarts
Terminal window
dotnet add package Shiny.AiConversation.Maui.GithubCopilot
builder.Services.AddShinyAiConversation(opts =>
{
opts.AddGithubCopilotChatClient();
});

No login page, token storage, or event wiring needed — one line of registration handles everything.

Additional API on GitHubCopilotChatClientProvider:

Member Description
IsAuthenticated Whether a stored token exists
StartAuthentication(ct) Manually trigger the device code flow
CancelAuthentication() Cancel an in-progress auth flow
SignOut() Clear stored tokens
AccessTokenChanged Event fired when auth state changes (token or null)

For scenarios not covered by the built-in providers, implement IChatClientProvider:

public interface IChatClientProvider
{
Task<IChatClient> GetChatClient(CancellationToken cancelToken = default);
}

The service calls GetChatClient() before each chat request, so your implementation can handle token refresh, re-authentication, or client rotation.

builder.Services.AddShinyAiConversation(opts =>
{
opts.SetChatClientProvider<MyChatClientProvider>();
});
using Microsoft.Extensions.AI;
using Shiny.AiConversation;
public class OllamaChatClientProvider : IChatClientProvider
{
public Task<IChatClient> GetChatClient(CancellationToken cancelToken = default)
{
var client = new OllamaChatClient(
new Uri("http://localhost:11434"),
"llama3.1"
);
return Task.FromResult<IChatClient>(client);
}
}
Pattern Provider
API key from config AddStaticOpenAIChatClient() or register IChatClient in DI
GitHub Copilot (device code flow) AddGithubCopilotChatClient()
Managed identity / custom Implement IChatClientProvider
Token refresh with caching Implement IChatClientProvider