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