Introduction
Many AI applications begin with a single model provider. The first integration is usually straightforward: configure an API key, send a prompt, and return the response.
The architecture becomes more difficult when an application needs multiple providers. Different models may be suitable for different tasks, while providers may have different latency, pricing, rate limits, and availability.
A small gateway layer can centralize these decisions. The application sends one request to the gateway, and the gateway selects a provider, applies policy, and records the result.
This article demonstrates a simplified routing pattern in ASP.NET Core. The example is intentionally provider-neutral and is designed to explain the architecture rather than provide a production SDK.
Request model
The request contains the prompt and the type of work being performed.
public sealed record LlmRequest(
string Prompt,
string TaskType);TaskType could be values such as interactive, reasoning, or classification. In a real application, this value might come from a workflow or service policy.
Provider abstraction
The gateway should not depend directly on one vendor SDK. An interface keeps provider-specific code behind an adapter.
public interface ILlmProvider
{
string Name { get; }
Task<string> CompleteAsync(
string prompt,
CancellationToken cancellationToken);
}Each provider adapter implements this interface. This makes it possible to replace or reorder providers without changing application code.
Routing service
The router selects a provider based on the task type. The example below also demonstrates a simple fallback sequence.
public sealed class LlmRouter
{
private readonly IReadOnlyDictionary<string, ILlmProvider> _providers;
public LlmRouter(IEnumerable<ILlmProvider> providers)
{
_providers = providers.ToDictionary(
provider => provider.Name,
StringComparer.OrdinalIgnoreCase);
}
public async Task<string> RouteAsync(
LlmRequest request,
CancellationToken cancellationToken)
{
var route = request.TaskType.ToLowerInvariant() switch
{
"reasoning" => new[] { "primary-reasoning", "backup-general" },
"classification" => new[] { "low-cost", "backup-general" },
_ => new[] { "fast", "backup-general" }
};
foreach (var providerName in route)
{
if (!_providers.TryGetValue(providerName, out var provider))
{
continue;
}
try
{
return await provider.CompleteAsync(
request.Prompt,
cancellationToken);
}
catch (HttpRequestException)
{
// Continue to the next provider when the current one fails.
}
}
throw new InvalidOperationException(
"No configured LLM provider could process the request.");
}
}
Join the conversation! Your thoughts help the community grow.