Skip to content

Connecting to Each from .NET

Key Points

  • Microsoft.Extensions.AI.OpenAI — first-party for OpenAI + Azure OpenAI.
  • Anthropic, Google, AWS Bedrock: community NuGets or OpenAI-compatible endpoints.
  • OpenAI-compatible is the universal wire format — Together, Groq, Fireworks, Ollama, vLLM, Mistral all support.
  • Wrap into IChatClient for vendor-portable code.

OpenAI / Azure OpenAI

<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.*" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.*" />
// OpenAI direct
IChatClient chat = new OpenAIClient(apiKey).AsChatClient("gpt-4o-mini");

// Azure OpenAI with managed identity
IChatClient azureChat = new AzureOpenAIClient(
        new Uri(endpoint),
        new DefaultAzureCredential())
    .AsChatClient(deploymentName);

Anthropic Claude

No first-party MS SDK. Options:

1. Community Anthropic SDK + adapter

// Anthropic.SDK
public class AnthropicChatClientAdapter(AnthropicClient inner) : IChatClient
{
    public async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken ct = default)
    {
        // Map ChatMessage → Anthropic Message
        // Call inner.Messages.GetClaudeMessageAsync
        // Map response back
    }
}

2. AWS Bedrock (Claude hosted)

// AWSSDK.BedrockRuntime
var bedrock = new AmazonBedrockRuntimeClient(region);
// Adapter to IChatClient

3. Vertex AI (Claude available there too)

// Google.Cloud.AIPlatform.V1

Google Gemini

// Mscc.GenerativeAI (community)
var gemini = new GoogleAI(apiKey);
var model = gemini.GenerativeModel(Model.Gemini25Pro);
var resp = await model.GenerateContent("Hello");

Vertex AI:

// Google.Cloud.AIPlatform.V1
var client = new PredictionServiceClientBuilder { Endpoint = "us-central1-aiplatform.googleapis.com" }.Build();

AWS Bedrock

Hosts Anthropic, Meta Llama, Mistral, Amazon Nova, Stability AI.

// AWSSDK.BedrockRuntime
var client = new AmazonBedrockRuntimeClient();
var resp = await client.InvokeModelAsync(new InvokeModelRequest { ModelId = "anthropic.claude-sonnet-4-...", Body = ... });

Adapter to IChatClient worth building once.

OpenAI-compatible endpoints

Many providers expose OpenAI-compatible API: same SDK, different baseUrl + key.

// Together AI
new OpenAIClient(
    new ApiKeyCredential(togetherKey),
    new OpenAIClientOptions { Endpoint = new Uri("https://api.together.xyz/v1") })
    .AsChatClient("meta-llama/Llama-3.3-70B-Instruct-Turbo");

// Groq
new OpenAIClient(
    new ApiKeyCredential(groqKey),
    new OpenAIClientOptions { Endpoint = new Uri("https://api.groq.com/openai/v1") })
    .AsChatClient("llama-3.3-70b-versatile");

// Mistral
new OpenAIClient(
    new ApiKeyCredential(mistralKey),
    new OpenAIClientOptions { Endpoint = new Uri("https://api.mistral.ai/v1") })
    .AsChatClient("mistral-large-latest");

// Fireworks AI
new OpenAIClient(/* same pattern */);

Massive portability via OpenAI-compatible.

Ollama (local)

<PackageReference Include="Microsoft.Extensions.AI.Ollama" Version="9.*" />
IChatClient chat = new OllamaChatClient(new Uri("http://localhost:11434"), "llama3.3");

ONNX Runtime GenAI (Phi)

<PackageReference Include="Microsoft.ML.OnnxRuntimeGenAI" />
var model = new Model("/path/to/phi-4-onnx");
// (Custom IChatClient adapter; community packages exist)

Pipeline pattern

Same regardless of provider:

chat = chat.AsBuilder()
    .UseFunctionInvocation()                              // tool calls
    .UseLogging(loggerFactory)
    .UseOpenTelemetry(/* GenAI conventions */)
    .UseDistributedCache(redisCache)
    .Build();

Switch provider → keep pipeline.

Multi-provider routing

public class MultiChatClient(IDictionary<string, IChatClient> clients) : IChatClient
{
    public Task<ChatResponse> GetResponseAsync(/* ... */, ChatOptions? options, CancellationToken ct)
    {
        var provider = options?.AdditionalProperties?["provider"]?.ToString() ?? "default";
        return clients[provider].GetResponseAsync(/* ... */, options, ct);
    }
}

builder.Services.AddSingleton<IChatClient>(new MultiChatClient(new Dictionary<string, IChatClient>
{
    ["openai"] = openAiClient,
    ["anthropic"] = anthropicClient,
    ["default"] = openAiClient
}));

Or fallback:

chat = chat.AsBuilder()
    .UseFallback(secondaryChat)   // hypothetical; or hand-rolled
    .Build();

Authentication patterns

Provider Auth
OpenAI API key
Azure OpenAI API key OR managed identity (preferred)
Anthropic API key
Google AI Studio API key
Vertex AI Service Account / Workload Identity
AWS Bedrock IAM role / SDK
Together / Groq API key
Ollama (local) none

Configuration

"AI": {
  "Provider": "azure-openai",
  "AzureOpenAI": { "Endpoint": "https://...", "Deployment": "gpt-4o-mini" },
  "OpenAI": { "ApiKey": "@KeyVault(...)", "Model": "gpt-4o-mini" },
  "Anthropic": { "ApiKey": "@KeyVault(...)", "Model": "claude-4-sonnet" }
}

Senior consideration

Build the provider abstraction once. Use config to swap. Test against multiple providers in CI to catch breakage.


Cross-references