diff --git a/docs/ai/how-to/snippets/switch-model-providers/BatchEvaluation/BatchEvaluation.csproj b/docs/ai/how-to/snippets/switch-model-providers/BatchEvaluation/BatchEvaluation.csproj
new file mode 100644
index 0000000000000..1ada20920a31f
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/BatchEvaluation/BatchEvaluation.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/BatchEvaluation/Program.cs b/docs/ai/how-to/snippets/switch-model-providers/BatchEvaluation/Program.cs
new file mode 100644
index 0000000000000..2e6ac285a4671
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/BatchEvaluation/Program.cs
@@ -0,0 +1,122 @@
+using Microsoft.Extensions.AI;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using OllamaSharp;
+using System.Diagnostics;
+
+// Define providers for evaluation
+var providers = new Dictionary
+{
+ ["Ollama-Llama3.1"] = new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1"),
+ ["Azure-GPT4"] = new AzureOpenAIClient(
+ new Uri("YOUR-AZURE-OPENAI-ENDPOINT"),
+ new DefaultAzureCredential())
+ .GetChatClient("gpt-4")
+ .AsIChatClient(),
+ ["Azure-GPT35"] = new AzureOpenAIClient(
+ new Uri("YOUR-AZURE-OPENAI-ENDPOINT"),
+ new DefaultAzureCredential())
+ .GetChatClient("gpt-35-turbo")
+ .AsIChatClient()
+};
+
+// Test prompts for evaluation
+string[] testPrompts = [
+ "Explain quantum computing in simple terms.",
+ "Write a haiku about artificial intelligence.",
+ "What are the pros and cons of renewable energy?",
+ "How does machine learning work?",
+ "Describe the water cycle."
+];
+
+// Evaluation results
+var results = new List();
+
+Console.WriteLine("Starting batch evaluation across providers...\n");
+
+foreach (var prompt in testPrompts)
+{
+ Console.WriteLine($"Evaluating prompt: \"{prompt}\"");
+ Console.WriteLine(new string('-', 50));
+
+ foreach (var (providerName, client) in providers)
+ {
+ try
+ {
+ var stopwatch = Stopwatch.StartNew();
+ var response = await client.GetResponseAsync(prompt);
+ stopwatch.Stop();
+
+ var result = new EvaluationResult
+ {
+ Prompt = prompt,
+ Provider = providerName,
+ Response = response.ToString(),
+ ResponseTime = stopwatch.Elapsed,
+ InputTokens = (int)(response.Usage?.InputTokenCount ?? 0),
+ OutputTokens = (int)(response.Usage?.OutputTokenCount ?? 0),
+ Success = true
+ };
+
+ results.Add(result);
+
+ Console.WriteLine($"{providerName}:");
+ Console.WriteLine($" Response time: {stopwatch.ElapsedMilliseconds}ms");
+ Console.WriteLine($" Tokens: {result.InputTokens} in, {result.OutputTokens} out");
+ Console.WriteLine($" Response: {response.ToString()[..Math.Min(response.ToString().Length, 100)]}...");
+ }
+ catch (Exception ex)
+ {
+ var result = new EvaluationResult
+ {
+ Prompt = prompt,
+ Provider = providerName,
+ Response = "",
+ ResponseTime = TimeSpan.Zero,
+ InputTokens = 0,
+ OutputTokens = 0,
+ Success = false,
+ ErrorMessage = ex.Message
+ };
+
+ results.Add(result);
+
+ Console.WriteLine($"{providerName}: ERROR - {ex.Message}");
+ }
+ }
+
+ Console.WriteLine();
+}
+
+// Generate summary report
+Console.WriteLine("EVALUATION SUMMARY");
+Console.WriteLine(new string('=', 50));
+
+var groupedResults = results.GroupBy(r => r.Provider);
+
+foreach (var group in groupedResults)
+{
+ var successfulResults = group.Where(r => r.Success).ToList();
+
+ Console.WriteLine($"\n{group.Key}:");
+ Console.WriteLine($" Success rate: {successfulResults.Count}/{group.Count()} ({(double)successfulResults.Count / group.Count() * 100:F1}%)");
+
+ if (successfulResults.Any())
+ {
+ Console.WriteLine($" Avg response time: {successfulResults.Average(r => r.ResponseTime.TotalMilliseconds):F0}ms");
+ Console.WriteLine($" Avg output tokens: {successfulResults.Average(r => r.OutputTokens):F0}");
+ }
+}
+
+// Data model for evaluation results
+public class EvaluationResult
+{
+ public string Prompt { get; set; } = "";
+ public string Provider { get; set; } = "";
+ public string Response { get; set; } = "";
+ public TimeSpan ResponseTime { get; set; }
+ public int InputTokens { get; set; }
+ public int OutputTokens { get; set; }
+ public bool Success { get; set; }
+ public string? ErrorMessage { get; set; }
+}
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/ConfigurationBased.csproj b/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/ConfigurationBased.csproj
new file mode 100644
index 0000000000000..18f55fb8578e7
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/ConfigurationBased.csproj
@@ -0,0 +1,29 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/Program.cs b/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/Program.cs
new file mode 100644
index 0000000000000..5e30012fc1e66
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/Program.cs
@@ -0,0 +1,79 @@
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.Configuration;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using OllamaSharp;
+
+// Build configuration
+var configuration = new ConfigurationBuilder()
+ .SetBasePath(Directory.GetCurrentDirectory())
+ .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
+ .AddEnvironmentVariables()
+ .Build();
+
+// Get AI provider configuration
+var aiConfig = configuration.GetSection("AIProviders");
+var defaultProvider = aiConfig["DefaultProvider"] ?? "Ollama";
+
+// Create provider factory
+IChatClient CreateChatClient(string providerName)
+{
+ var providerSection = aiConfig.GetSection($"Providers:{providerName}");
+ var providerType = providerSection["Type"];
+
+ return providerType switch
+ {
+ "Ollama" => new OllamaApiClient(
+ new Uri(providerSection["Endpoint"] ?? "http://localhost:11434"),
+ providerSection["Model"] ?? "llama3.1"),
+
+ "AzureOpenAI" => new AzureOpenAIClient(
+ new Uri(providerSection["Endpoint"] ?? throw new InvalidOperationException("Azure endpoint required")),
+ new DefaultAzureCredential())
+ .GetChatClient(providerSection["Model"] ?? "gpt-35-turbo")
+ .AsIChatClient(),
+
+ "OpenAI" => throw new NotImplementedException("OpenAI provider not implemented in this example"),
+
+ _ => throw new NotSupportedException($"Provider type '{providerType}' is not supported")
+ };
+}
+
+// Get the configured providers
+var enabledProviders = aiConfig.GetSection("EnabledProviders").Get() ?? [defaultProvider];
+
+Console.WriteLine($"Default provider: {defaultProvider}");
+Console.WriteLine($"Enabled providers: {string.Join(", ", enabledProviders)}");
+Console.WriteLine();
+
+// Test with each enabled provider
+foreach (var providerName in enabledProviders)
+{
+ try
+ {
+ Console.WriteLine($"Testing provider: {providerName}");
+
+ var client = CreateChatClient(providerName);
+ var response = await client.GetResponseAsync("What is artificial intelligence?");
+
+ Console.WriteLine($"Response from {providerName}: {response.ToString()[..Math.Min(response.ToString().Length, 100)]}...");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error with provider {providerName}: {ex.Message}");
+ }
+
+ Console.WriteLine();
+}
+
+// Example of switching based on configuration flags
+var useAdvancedFeatures = configuration.GetValue("AIProviders:UseAdvancedFeatures");
+var providerForComplexQueries = useAdvancedFeatures ? "AzureOpenAI" : defaultProvider;
+
+Console.WriteLine($"For complex queries, using: {providerForComplexQueries}");
+
+var complexQueryClient = CreateChatClient(providerForComplexQueries);
+var complexResponse = await complexQueryClient.GetResponseAsync(
+ "Analyze the impact of quantum computing on modern cryptography and suggest mitigation strategies.");
+
+Console.WriteLine($"Complex query response: {complexResponse.ToString()[..Math.Min(complexResponse.ToString().Length, 150)]}...");
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/appsettings.json b/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/appsettings.json
new file mode 100644
index 0000000000000..6ecb046dd9c54
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/ConfigurationBased/appsettings.json
@@ -0,0 +1,30 @@
+{
+ "AIProviders": {
+ "DefaultProvider": "Ollama",
+ "UseAdvancedFeatures": false,
+ "EnabledProviders": [ "Ollama", "AzureOpenAI" ],
+ "Providers": {
+ "Ollama": {
+ "Type": "Ollama",
+ "Endpoint": "http://localhost:11434",
+ "Model": "llama3.1"
+ },
+ "AzureOpenAI": {
+ "Type": "AzureOpenAI",
+ "Endpoint": "https://your-resource.openai.azure.com/",
+ "Model": "gpt-35-turbo"
+ },
+ "AzureOpenAI-Advanced": {
+ "Type": "AzureOpenAI",
+ "Endpoint": "https://your-resource.openai.azure.com/",
+ "Model": "gpt-4"
+ }
+ }
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/DependencyInjection/DependencyInjection.csproj b/docs/ai/how-to/snippets/switch-model-providers/DependencyInjection/DependencyInjection.csproj
new file mode 100644
index 0000000000000..2c2b94d42b189
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/DependencyInjection/DependencyInjection.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/DependencyInjection/Program.cs b/docs/ai/how-to/snippets/switch-model-providers/DependencyInjection/Program.cs
new file mode 100644
index 0000000000000..4b34b2524c498
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/DependencyInjection/Program.cs
@@ -0,0 +1,115 @@
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using OllamaSharp;
+
+// Create host builder
+var builder = Host.CreateApplicationBuilder();
+
+// Register multiple chat clients with different keys
+builder.Services.AddKeyedSingleton("local", (sp, key) =>
+ new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1"));
+
+builder.Services.AddKeyedSingleton("cloud", (sp, key) =>
+ new AzureOpenAIClient(
+ new Uri("YOUR-AZURE-OPENAI-ENDPOINT"),
+ new DefaultAzureCredential())
+ .GetChatClient("gpt-35-turbo")
+ .AsIChatClient());
+
+builder.Services.AddKeyedSingleton("premium", (sp, key) =>
+ new AzureOpenAIClient(
+ new Uri("YOUR-AZURE-OPENAI-ENDPOINT"),
+ new DefaultAzureCredential())
+ .GetChatClient("gpt-4")
+ .AsIChatClient());
+
+// Register a provider selector service
+builder.Services.AddScoped();
+
+// Register a service that uses the chat providers
+builder.Services.AddScoped();
+
+var host = builder.Build();
+
+// Get and use the AI service
+var aiService = host.Services.GetRequiredService();
+
+await aiService.ProcessQueriesAsync([
+ "Hello, how are you?",
+ "Explain the theory of relativity in detail with mathematical formulations",
+ "What's the weather like today?"
+]);
+
+// Interface for provider selection
+public interface IChatProviderSelector
+{
+ IChatClient SelectProvider(string query);
+}
+
+// Implementation of provider selector
+public class ChatProviderSelector : IChatProviderSelector
+{
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ILogger _logger;
+
+ public ChatProviderSelector(IServiceProvider serviceProvider, ILogger logger)
+ {
+ _serviceProvider = serviceProvider;
+ _logger = logger;
+ }
+
+ public IChatClient SelectProvider(string query)
+ {
+ // Simple logic for demonstration
+ // In practice, this could be much more sophisticated
+ string selectedProvider = query.Length switch
+ {
+ < 50 => "local",
+ < 200 => "cloud",
+ _ => "premium"
+ };
+
+ _logger.LogInformation("Selected provider '{Provider}' for query of length {Length}",
+ selectedProvider, query.Length);
+
+ return _serviceProvider.GetRequiredKeyedService(selectedProvider);
+ }
+}
+
+// Service that uses AI providers
+public class AIService
+{
+ private readonly IChatProviderSelector _providerSelector;
+ private readonly ILogger _logger;
+
+ public AIService(IChatProviderSelector providerSelector, ILogger logger)
+ {
+ _providerSelector = providerSelector;
+ _logger = logger;
+ }
+
+ public async Task ProcessQueriesAsync(string[] queries)
+ {
+ foreach (var query in queries)
+ {
+ _logger.LogInformation("Processing query: {Query}", query[..Math.Min(query.Length, 50)]);
+
+ try
+ {
+ var client = _providerSelector.SelectProvider(query);
+ var response = await client.GetResponseAsync(query);
+
+ _logger.LogInformation("Response received: {Response}",
+ response.ToString()[..Math.Min(response.ToString().Length, 100)]);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error processing query: {Query}", query);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/EnvironmentSwitch.csproj b/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/EnvironmentSwitch.csproj
new file mode 100644
index 0000000000000..1ada20920a31f
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/EnvironmentSwitch.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/Program.cs b/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/Program.cs
new file mode 100644
index 0000000000000..8227a56ab6e19
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/Program.cs
@@ -0,0 +1,37 @@
+using Microsoft.Extensions.AI;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using OllamaSharp;
+
+// Get the current environment
+string environment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Development";
+
+// Create chat client based on environment
+IChatClient chatClient = environment == "Development"
+ ? new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1")
+ : new AzureOpenAIClient(
+ new Uri("YOUR-AZURE-OPENAI-ENDPOINT"),
+ new DefaultAzureCredential())
+ .GetChatClient("gpt-4")
+ .AsIChatClient();
+
+// Send a chat request
+await foreach (var message in chatClient.GetStreamingResponseAsync("What is AI?"))
+{
+ Console.Write($"{message.Text}");
+}
+Console.WriteLine();
+
+// Create embedding generator based on environment
+IEmbeddingGenerator> embeddingGenerator =
+ environment == "Development"
+ ? new OllamaApiClient(new Uri("http://localhost:11434"), "all-minilm")
+ : new AzureOpenAIClient(
+ new Uri("YOUR-AZURE-OPENAI-ENDPOINT"),
+ new DefaultAzureCredential())
+ .GetEmbeddingClient("text-embedding-3-small")
+ .AsIEmbeddingGenerator();
+
+// Generate embeddings
+var embedding = await embeddingGenerator.GenerateAsync("What is AI?");
+Console.WriteLine($"Generated embedding with {embedding.Vector.Length} dimensions");
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/README.md b/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/README.md
new file mode 100644
index 0000000000000..30117a39c9c49
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/EnvironmentSwitch/README.md
@@ -0,0 +1,23 @@
+# Environment-based Provider Switching
+
+This example demonstrates how to switch between local and cloud AI providers based on the current environment.
+
+To run this example:
+
+1. For local development with Ollama:
+ - Install Ollama from https://ollama.ai
+ - Run `ollama pull llama3.1` and `ollama pull all-minilm`
+ - Set environment variable: `DOTNET_ENVIRONMENT=Development`
+
+2. For production with Azure OpenAI:
+ - Set up Azure OpenAI resource
+ - Configure authentication (managed identity or API key)
+ - Set environment variable: `DOTNET_ENVIRONMENT=Production`
+
+## Usage
+
+```bash
+dotnet run
+```
+
+The application will automatically detect the environment and use the appropriate provider.
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/SmartRouting/Program.cs b/docs/ai/how-to/snippets/switch-model-providers/SmartRouting/Program.cs
new file mode 100644
index 0000000000000..17fedd5e9cb58
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/SmartRouting/Program.cs
@@ -0,0 +1,64 @@
+using Microsoft.Extensions.AI;
+using Azure.AI.OpenAI;
+using Azure.Identity;
+using OllamaSharp;
+
+// Create different clients for different complexity levels
+var simpleClient = new OllamaApiClient(new Uri("http://localhost:11434"), "llama3.1");
+var complexClient = new AzureOpenAIClient(
+ new Uri("YOUR-AZURE-OPENAI-ENDPOINT"),
+ new DefaultAzureCredential())
+ .GetChatClient("gpt-4")
+ .AsIChatClient();
+
+// Smart routing function
+IChatClient ChooseProvider(string query)
+{
+ // Simple heuristics for demonstration
+ // In practice, you might use more sophisticated analysis
+ int complexityScore = CalculateComplexity(query);
+
+ return complexityScore > 50 ? complexClient : simpleClient;
+}
+
+int CalculateComplexity(string query)
+{
+ int score = 0;
+
+ // Add points for various complexity indicators
+ score += query.Length > 100 ? 20 : 0;
+ score += query.Split(' ').Length > 20 ? 15 : 0;
+
+ // Complex keywords
+ string[] complexKeywords = { "analyze", "compare", "synthesize", "reasoning", "step-by-step" };
+ score += complexKeywords.Count(keyword =>
+ query.Contains(keyword, StringComparison.OrdinalIgnoreCase)) * 15;
+
+ // Technical terms
+ string[] technicalTerms = { "algorithm", "database", "architecture", "implementation" };
+ score += technicalTerms.Count(term =>
+ query.Contains(term, StringComparison.OrdinalIgnoreCase)) * 10;
+
+ return Math.Min(score, 100);
+}
+
+// Test queries with different complexity levels
+string[] testQueries = [
+ "What is AI?",
+ "Analyze the architectural implications of implementing a distributed microservices system with event sourcing, considering the trade-offs between consistency, availability, and partition tolerance in the context of the CAP theorem.",
+ "Compare machine learning algorithms for natural language processing tasks and provide a step-by-step reasoning for choosing the best approach for sentiment analysis in social media data."
+];
+
+foreach (var query in testQueries)
+{
+ var client = ChooseProvider(query);
+ var complexity = CalculateComplexity(query);
+
+ Console.WriteLine($"Query: {query[..Math.Min(query.Length, 50)]}...");
+ Console.WriteLine($"Complexity Score: {complexity}");
+ Console.WriteLine($"Using: {(client == simpleClient ? "Local Model" : "Cloud Model")}");
+
+ var response = await client.GetResponseAsync(query);
+ Console.WriteLine($"Response: {response.ToString()[..Math.Min(response.ToString().Length, 100)]}...");
+ Console.WriteLine();
+}
\ No newline at end of file
diff --git a/docs/ai/how-to/snippets/switch-model-providers/SmartRouting/SmartRouting.csproj b/docs/ai/how-to/snippets/switch-model-providers/SmartRouting/SmartRouting.csproj
new file mode 100644
index 0000000000000..1ada20920a31f
--- /dev/null
+++ b/docs/ai/how-to/snippets/switch-model-providers/SmartRouting/SmartRouting.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/ai/how-to/switch-model-providers.md b/docs/ai/how-to/switch-model-providers.md
new file mode 100644
index 0000000000000..69b6713bc02ca
--- /dev/null
+++ b/docs/ai/how-to/switch-model-providers.md
@@ -0,0 +1,136 @@
+---
+title: Switch model providers with Microsoft.Extensions.AI
+description: Learn how to dynamically switch between different AI model providers using Microsoft.Extensions.AI in various scenarios such as development vs production, smart routing, and batch evaluations.
+author: IEvangelist
+ms.author: dapine
+ms.topic: how-to
+ms.date: 01/16/2025
+ai-usage: ai-generated
+#customer intent: As a .NET developer, I want to switch between different AI model providers based on environment, complexity, or other criteria using Microsoft.Extensions.AI.
+---
+
+# Switch model providers with Microsoft.Extensions.AI
+
+This article demonstrates how to dynamically switch between different AI model providers using Microsoft.Extensions.AI. The Microsoft.Extensions.AI libraries provide abstractions that enable seamless portability across different AI services and models.
+
+## Why switch model providers
+
+There are several scenarios where you might want to switch between different AI model providers:
+
+- **Environment-based switching**: Use local models during development and cloud models in production
+- **Smart routing**: Choose different providers based on query complexity, cost, or performance requirements
+- **Batch evaluations**: Test prompts across multiple providers to compare results and find the best performing model
+- **Fallback scenarios**: Automatically switch to an alternative provider if the primary one is unavailable
+- **Cost optimization**: Route simpler queries to less expensive models and complex ones to premium models
+
+## Prerequisites
+
+- [.NET 9 SDK](https://dotnet.microsoft.com/download/dotnet/9.0) or later
+- Basic understanding of [Microsoft.Extensions.AI](../microsoft-extensions-ai.md)
+- Access to at least one AI service (Azure OpenAI, OpenAI, Ollama, etc.)
+
+## Environment-based provider switching
+
+One of the most common scenarios is switching between local models during development and cloud-hosted models in production. This approach allows you to develop and test locally without incurring cloud costs or requiring internet connectivity.
+
+### Switching chat clients
+
+The following example shows how to conditionally create different `IChatClient` implementations based on the current environment:
+
+:::code language="csharp" source="snippets/switch-model-providers/EnvironmentSwitch/Program.cs" range="1-25":::
+
+### Switching embedding generators
+
+Similarly, you can switch embedding generators based on environment:
+
+:::code language="csharp" source="snippets/switch-model-providers/EnvironmentSwitch/Program.cs" range="27-37":::
+
+## Smart routing based on query complexity
+
+You can implement smart routing to choose different providers based on the complexity or type of query. This approach helps optimize cost and performance.
+
+:::code language="csharp" source="snippets/switch-model-providers/SmartRouting/Program.cs":::
+
+## Batch evaluation across providers
+
+For evaluation purposes, you might want to test the same prompt across multiple providers to compare results:
+
+:::code language="csharp" source="snippets/switch-model-providers/BatchEvaluation/Program.cs":::
+
+## Configuration-based provider switching
+
+For more complex scenarios, you can use configuration to define which providers to use:
+
+:::code language="csharp" source="snippets/switch-model-providers/ConfigurationBased/Program.cs":::
+
+With the corresponding configuration in `appsettings.json`:
+
+:::code language="json" source="snippets/switch-model-providers/ConfigurationBased/appsettings.json":::
+
+## Dependency injection with multiple providers
+
+When using dependency injection, you can register multiple providers and resolve them by name or type:
+
+:::code language="csharp" source="snippets/switch-model-providers/DependencyInjection/Program.cs":::
+
+## Best practices
+
+When switching between model providers, consider the following best practices:
+
+### Use abstractions consistently
+
+Always program against the `IChatClient` and `IEmbeddingGenerator` interfaces rather than concrete implementations. This ensures your code remains portable across different providers.
+
+### Handle provider-specific differences
+
+Different providers might have varying capabilities, rate limits, or response formats. Implement error handling and fallback mechanisms:
+
+```csharp
+try
+{
+ var response = await chatClient.GetResponseAsync(prompt, options);
+ return response;
+}
+catch (Exception ex) when (IsRateLimitException(ex))
+{
+ // Switch to fallback provider or implement retry logic
+ var fallbackClient = GetFallbackClient();
+ return await fallbackClient.GetResponseAsync(prompt, options);
+}
+```
+
+### Configure options appropriately
+
+Different providers might require different configuration options. Use the `ChatOptions` parameter to specify provider-specific settings:
+
+```csharp
+var options = new ChatOptions
+{
+ ModelId = provider == "openai" ? "gpt-4" : "llama3.1",
+ Temperature = 0.7f,
+ MaxOutputTokens = provider == "local" ? 1000 : 2000
+};
+```
+
+### Monitor and log provider usage
+
+Track which providers are being used and their performance characteristics:
+
+```csharp
+using var activity = ActivitySource.StartActivity("chat-request");
+activity?.SetTag("provider", providerName);
+activity?.SetTag("model", modelId);
+
+var response = await chatClient.GetResponseAsync(prompt, options);
+
+activity?.SetTag("tokens.input", response.Usage?.InputTokenCount);
+activity?.SetTag("tokens.output", response.Usage?.OutputTokenCount);
+```
+
+## See also
+
+- [Microsoft.Extensions.AI libraries](../microsoft-extensions-ai.md)
+- [Sample implementations of IChatClient and IEmbeddingGenerator](../advanced/sample-implementations.md)
+- [Authentication for Azure-hosted apps and services](../azure-ai-services-authentication.md)
+- [Configuration in .NET](../../core/extensions/configuration.md)
+- [Dependency injection in .NET](../../core/extensions/dependency-injection.md)
diff --git a/docs/ai/toc.yml b/docs/ai/toc.yml
index c53a1e1b86223..9977a8a521edc 100644
--- a/docs/ai/toc.yml
+++ b/docs/ai/toc.yml
@@ -65,6 +65,10 @@ items:
href: tutorials/tutorial-ai-vector-search.md
- name: Scale Azure OpenAI with Azure Container Apps
href: get-started-app-chat-scaling-with-azure-container-apps.md
+- name: How-to guides
+ items:
+ - name: Switch model providers
+ href: how-to/switch-model-providers.md
- name: Security and content safety
items:
- name: Authentication for Azure-hosted apps and services