Skip to content

Add extensible chat client routing - #7662

Open
joshuajyue wants to merge 16 commits into
dotnet:mainfrom
joshuajyue:routing-behaviors
Open

Add extensible chat client routing#7662
joshuajyue wants to merge 16 commits into
dotnet:mainfrom
joshuajyue:routing-behaviors

Conversation

@joshuajyue

@joshuajyue joshuajyue commented Jul 27, 2026

Copy link
Copy Markdown
Member

Resolves #7647

Summary

Adds experimental chat-routing primitives for selecting configured IChatClient instances without imposing a particular routing policy.

  • RoutingChatClient supports one-shot client selection.
  • FailoverChatClient retries uncanceled failures that occur before output is exposed.
  • OrderedFailoverChatClient provides ordered fallback.
  • SemanticRoutingChatClient selects clients using cached profile embeddings.
  • RoutingContext exposes mutable request messages and options.
  • FailoverChatClientAttempt reports invocation outcome, duration, time to first update, completion, and output commitment.

API shape

Routing policies have two seams:

protected override ValueTask<IChatClient> SelectClientAsync(
    RoutingContext context,
    CancellationToken cancellationToken);

protected override ValueTask OnRoutingUpdateAsync(
    RoutingContext context,
    FailoverChatClientAttempt? attempt,
    bool isTerminal,
    CancellationToken cancellationToken);

SelectClientAsync runs before every invocation. OnRoutingUpdateAsync reports each attempt so policy state can influence the next selection.

isTerminal means the base will not select another client after the callback completes. A null attempt represents selection terminating before a client was invoked.

Failover behavior

  • Cancellation never triggers reselection.
  • Streaming fallback occurs only before the first update is exposed.
  • Mid-stream failures and caller abandonment are terminal.
  • Enumerator disposal failures are included in the attempt.
  • Attempt limits are request-local.
  • Null selector results are treated as terminal selection failures.
  • Ordered failover retains request state only between a nonterminal update and the immediately following selection; it holds no state while a provider invocation or stream is active.
  • Streaming callers must dispose active enumerators for terminal observation and inner-resource cleanup.

Semantic routing

SemanticRoutingChatClient:

  • Lazily embeds and caches profile utterances.
  • Embeds the final user message for selection.
  • Supports global top-K profile matching.
  • Aggregates matches per client using mean or sum.
  • Preserves the existing single-best-match behavior with topK: 1.
  • Uses a required default client when no profile clears the threshold.
  • Supports caller-controlled ownership through 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.

  • 753 tests passed on net10.0
  • net472 test project builds successfully
Microsoft Reviewers: Open in CodeFlow

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
Copilot AI review requested due to automatic review settings July 27, 2026 22:31
@joshuajyue
joshuajyue requested review from a team as code owners July 27, 2026 22:31
@joshuajyue

This comment was marked as resolved.

This comment was marked as resolved.

joshuajyue and others added 3 commits July 27, 2026 15:43
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

This comment was marked as resolved.

Capture Current access failures before committing output so pre-output failover and attempt reporting remain accurate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74

This comment was marked as resolved.

joshuajyue and others added 2 commits July 28, 2026 14:22
Apply ConfigureAwait(false) consistently across selection, invocation, streaming, disposal, and routing update awaits.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated

@joshuajyue joshuajyue left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 0abbfd1, along with some cancellation races

Clarify RoutingContext input semantics, add optional message buffering, centralize validation, simplify timing and disposal, and separate caller cancellation from provider failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/OrderedFailoverChatClient.cs Outdated
Clone request options for selector shaping and restrict routing updates to concrete client attempts, leaving selection-failure cleanup to selectors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Follow MEAI's repeatable-enumerable convention and propagate selector failures without cancellation rewriting.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/OrderedFailoverChatClient.cs Outdated
Use ConcurrentDictionary atomic operations instead of explicit lock blocks while preserving the narrow request-state lifetime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
joshuajyue and others added 2 commits July 30, 2026 19:42
Keep client selection simple while preventing selector and failed-client option mutations from affecting downstream attempts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
Document client-reference identity and keep semantic scoring parameters together in the constructor signature.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74

@jozkee jozkee left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good.

Comment thread src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatRouting/RoutingContext.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClientAttempt.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/FailoverChatClient.cs Outdated
Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/OrderedFailoverChatClient.cs Outdated
Align routing code with MEAI formatting and documentation conventions, simplify disposal, and split routing tests by component and assembly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
@joshuajyue

Copy link
Copy Markdown
Member Author

Thank you David for the feedback, addressed in 8b79ae4

Comment thread src/Libraries/Microsoft.Extensions.AI/ChatRouting/SemanticRoutingChatClient.cs Outdated
joshuajyue and others added 2 commits July 31, 2026 16:36
Consolidate shared streaming and non-streaming cases, reuse routing test helpers, and simplify semantic router disposal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 74d04840-2379-4615-93f7-84f2299ada74
@joshuajyue
joshuajyue requested a review from jozkee July 31, 2026 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API Proposal]: Add extensible request routing for IChatClient

4 participants