Add extensible chat client routing - #7662
#7662Add extensible chat client routing#7662joshuajyue wants to merge 4 commits intodotnet:maindotnet/extensions:mainfrom joshuajyue:routing-behaviorsjoshuajyue/extensions:routing-behaviorsCopy head branch name to clipboard
Conversation
Introduce one-shot and failover routing primitives, ordered and semantic routing implementations, lifecycle reporting, streaming safeguards, and focused routing tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
|
@dotnet-policy-service agree |
There was a problem hiding this comment.
Pull request overview
Adds new experimental chat-routing primitives to the Microsoft.Extensions.AI.* stack to enable one-shot client selection, failover with attempt tracking, ordered fallback, and semantic (embedding-based) routing, along with extensive unit test coverage.
Changes:
- Introduces
RoutingChatClient/RoutingContextin AI.Abstractions for pluggable per-request client selection. - Adds
FailoverChatClient,FailoverChatClientAttempt,OrderedFailoverChatClient, andSemanticRoutingChatClientin Microsoft.Extensions.AI. - Updates experimental diagnostic IDs and public API JSON manifests; adds comprehensive routing/failover/semantic tests.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/Libraries/Microsoft.Extensions.AI.Tests/ChatRouting/RoutingChatClientTests.cs | Adds broad unit coverage for routing, failover, ordered fallback, streaming behaviors, and semantic routing. |
| src/Shared/DiagnosticIds/DiagnosticIds.cs | Adds a new experimental diagnostic ID for routing chat APIs. |
| src/Libraries/Microsoft.Extensions.AI/Microsoft.Extensions.AI.json | Updates public API manifest for new experimental routing/failover types. |
| src/Libraries/Microsoft.Extensions.AI/ChatRouting/SemanticRoutingChatClient.cs | Implements embedding-based client selection with cached profile embeddings and top-K aggregation. |
| src/Libraries/Microsoft.Extensions.AI/ChatRouting/OrderedFailoverChatClient.cs | Implements ordered client failover policy as a FailoverChatClient. |
| src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClientAttempt.cs | Adds an attempt record type capturing duration/commitment/completion/exception details. |
| src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs | Adds failover orchestration for non-streaming + streaming, including commitment rules and attempt limits. |
| src/Libraries/Microsoft.Extensions.AI.Abstractions/Microsoft.Extensions.AI.Abstractions.json | Updates public API manifest for new experimental routing primitives. |
| src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatRouting/RoutingContext.cs | Introduces mutable per-request routing context (messages/options). |
| src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatRouting/RoutingChatClient.cs | Introduces the base routing client (and callback factory) for one-shot selection and dispatch. |
Comments suppressed due to low confidence (2)
src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatRouting/RoutingChatClient.cs:71
SelectClientAsyncreturning null will currently cause a null-dereference when attempting to stream from the selected client. Consider throwing a clearInvalidOperationExceptioninstead.
var context = new RoutingContext(messages, options);
IChatClient client = await SelectClientAsync(context, cancellationToken).ConfigureAwait(false);
await foreach (ChatResponseUpdate update in
client.GetStreamingResponseAsync(context.Messages, context.ChatOptions, cancellationToken)
.WithCancellation(cancellationToken)
.ConfigureAwait(false))
src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs:220
- Streaming pre-output failures treat cancellation as terminal only when
cancellationToken.IsCancellationRequestedis true. If the failure is anOperationCanceledExceptionnot tied to the caller token, failover may incorrectly reselect another client.
bool isTerminal = cancellationToken.IsCancellationRequested || reachedAttemptLimit;
await OnRoutingUpdateAsync(context, attempt, isTerminal, cancellationToken);
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Treat provider-thrown cancellation as terminal, materialize semantic routing messages before inspection, and enforce non-null selection consistently for streaming. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs:247
- In streaming failover,
outputCommittedis set totruebefore accessingenumerator.Current. IfCurrentthrows (possible for a faulty async enumerator), the attempt will incorrectly record output as committed and will not capture the exception inFailoverChatClientAttempt.Exception, and routing may become terminal when it should be eligible for failover before any update is exposed.
while (hasCurrent)
{
outputCommitted = true;
yield return enumerator.Current;
Summary
Adds experimental chat-routing primitives for selecting configured
IChatClientinstances without imposing a particular routing policy.RoutingChatClientsupports one-shot client selection.FailoverChatClientretries uncanceled failures that occur before output is exposed.OrderedFailoverChatClientprovides ordered fallback.SemanticRoutingChatClientselects clients using cached profile embeddings.RoutingContextexposes mutable request messages and options.FailoverChatClientAttemptreports invocation outcome, duration, time to first update, completion, and output commitment.Related to #7647.
API shape
Routing policies have two seams:
SelectClientAsyncruns before every invocation.OnRoutingUpdateAsyncreports each attempt so policy state can influence the next selection.isTerminalmeans the base will not select another client after the callback completes. A null attempt represents selection terminating before a client was invoked.Failover behavior
Semantic routing
SemanticRoutingChatClient:topK: 1.leaveOpen.Tests
Coverage includes selection failures, retries, cancellation, attempt limits, streaming commitment, early disposal, enumerator failures, concurrent requests, state cleanup, semantic thresholds, top-K aggregation, caching, and ownership.
net10.0net472test project builds successfullyMicrosoft Reviewers: Open in CodeFlow