FlexBase provides unified abstractions for external service providers, enabling you to switch implementations without changing application code. All providers follow consistent patterns for configuration, usage, and error handling.
Connect to multiple AI/LLM services with one interface:
Provider
Best For
Features
Azure OpenAI
Enterprise, compliance
GPT-4o, embeddings, SLA
OpenAI
Latest models
GPT-4o, o1, latest features
Anthropic Claude
Long context, analysis
200K context, reasoning
Google Gemini
Multimodal
Text, images, competitive pricing
Ollama
Local/offline
Privacy, no API costs
// Works with ANY AI providerpublicclassChatService{privatereadonlyIFlexAIProvider _aiProvider;// Could be OpenAI, Claude, Ollama, etc.publicasyncTask<string>AskAsync(string question){returnawait_aiProvider.ChatAsync(question);}}
Email Providers
Send emails through multiple services:
Provider
Best For
Features
Console
Development
No real emails, console output
SMTP
Standard servers
Office 365, Gmail, custom
SMTP + Templates
Branded emails
Built-in templates, variables
SendGrid
Transactional
Delivery tracking, analytics
Message Providers
Send messages across multiple channels:
Channel
Use Cases
Providers
SMS
Verification codes, alerts
Twilio, AWS SNS
Push
Mobile notifications
Firebase, AWS SNS
WhatsApp
Customer support
Twilio
Slack/Teams
Internal alerts
Native APIs
Console
Development
Testing
Common Patterns
Automatic Setup via Flex Studio
Providers are automatically added to your application through Flex Studio. When you add a provider:
Provider files are created in Infrastructure/Providers/{YourApplication}.DataStoreProviders/{Category}/{Provider}/
NuGet package is referenced automatically
Default configuration is added to Application/EndPoints/{YourApplication}.EndPoint.CommonConfigs/AppSettings/{Category}/{Provider}.json
README documentation is included in the provider folder
You can find all provider files, configuration examples, and documentation in the generated infrastructure folder.
Configuration
Providers use the IOptions pattern. Configuration files are automatically created when you add providers via Flex Studio:
// Register multiple AI providers
builder.Services.AddKeyedSingleton<IFlexAIProvider>("openai", (sp, _) =>
new FlexOpenAIProvider(Configuration["OpenAI:ApiKey"]!));
builder.Services.AddKeyedSingleton<IFlexAIProvider>("local", (sp, _) =>
new OllamaProvider());
// Use in service
public class AIService
{
private readonly IFlexAIProvider _cloud;
private readonly IFlexAIProvider _local;
public AIService(
[FromKeyedServices("openai")] IFlexAIProvider cloud,
[FromKeyedServices("local")] IFlexAIProvider local)
{
_cloud = cloud;
_local = local;
}
public async Task<string> ProcessAsync(string input, bool useLocal = false)
{
var provider = useLocal ? _local : _cloud;
return await provider.ChatAsync(input);
}
}
// Development
builder.Services.AddFlexConsoleEmailProvider();
builder.Services.AddFlexConsoleMessageProvider();
// Production
builder.Services.AddFlexSmtpEmailProvider();
builder.Services.AddTwilioMessageProvider();
public class CostOptimizedService
{
private readonly IFlexAIProvider _cloudProvider; // GPT-4o for complex
private readonly IFlexAIProvider _localProvider; // Ollama for simple
public async Task<string> ProcessAsync(string input)
{
if (IsComplexQuery(input))
{
return await _cloudProvider.ChatAsync(input);
}
else
{
return await _localProvider.ChatAsync(input); // Free!
}
}
}
// Application code stays the same
public class MyService
{
private readonly IFlexAIProvider _ai;
private readonly IFlexEmailProvider _email;
// Works with ANY provider implementation
}
public async Task<bool> SendWithErrorHandlingAsync()
{
try
{
var result = await _provider.SendAsync(message);
if (!result.IsSuccess)
{
_logger.LogError("Operation failed: {Error}", result.ErrorMessage);
// Handle specific error codes
switch (result.ErrorCode)
{
case "RATE_LIMITED":
await Task.Delay(TimeSpan.FromSeconds(5));
return await SendWithErrorHandlingAsync(); // Retry
case "INVALID_INPUT":
return false; // Don't retry
default:
throw new Exception(result.ErrorMessage);
}
}
return true;
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
_logger.LogError("Invalid credentials");
throw;
}
}
if (builder.Environment.IsDevelopment())
{
builder.Services.AddFlexConsoleEmailProvider();
builder.Services.AddFlexConsoleMessageProvider();
builder.Services.AddSingleton<IFlexAIProvider>(new OllamaProvider());
}
else
{
builder.Services.AddFlexSmtpEmailProvider(/* production config */);
builder.Services.AddTwilioMessageProvider(/* production config */);
builder.Services.AddSingleton<IFlexAIProvider>(new FlexOpenAIProvider(/* production config */));
}
// In Program.cs after building
using var scope = app.Services.CreateScope();
var aiProvider = scope.ServiceProvider.GetRequiredService<IFlexAIProvider>();
if (!await aiProvider.TestConnectionAsync())
{
throw new InvalidOperationException("AI provider not configured correctly");
}
var emailProvider = scope.ServiceProvider.GetRequiredService<IFlexEmailProvider>();
if (!await emailProvider.TestConnectionAsync())
{
throw new InvalidOperationException("Email provider not configured correctly");
}