-
Notifications
You must be signed in to change notification settings - Fork 1.9k
.NET: Bump MEAI to 10.5.1 and add Foundry per-call x-client header support #5652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
rogerbarreto
merged 6 commits into
microsoft:main
from
rogerbarreto:feature/hosted-user-agent-meai-10.5.1
May 6, 2026
Merged
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
9f31bb6
Bump MEAI to 10.5.1 and add per-call x-client header support
rogerbarreto a4c8f91
Address PR review: pre-wire AsAIAgent path and dedup TryApplyUserAgent
rogerbarreto 4865332
Add tests covering AsAIAgent pre-wire and TryApplyUserAgent dedup
rogerbarreto 7fd5bcc
Move tests next to their SUT
rogerbarreto 5c6e21e
Remove redundant WithCancellation on inner streaming call
rogerbarreto 66f4a6c
Address PR review: surface downstream MEAI experimental ID
rogerbarreto File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 0 additions & 113 deletions
113
dotnet/src/Microsoft.Agents.AI.Foundry.Hosting/UserAgentResponsesClient.cs
This file was deleted.
Oops, something went wrong.
105 changes: 105 additions & 0 deletions
105
dotnet/src/Microsoft.Agents.AI.Foundry/ClientHeadersAgent.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| // Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| using System.Collections.Generic; | ||
| using System.Runtime.CompilerServices; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Extensions.AI; | ||
|
|
||
| namespace Microsoft.Agents.AI.Foundry; | ||
|
|
||
| /// <summary> | ||
| /// Delegating <see cref="AIAgent"/> that captures any <c>x-client-*</c> headers stored on | ||
| /// <see cref="ChatClientAgentRunOptions.ChatOptions"/> by callers of | ||
| /// <see cref="ClientHeadersExtensions.WithClientHeader(ChatOptions, string, string)"/> and pushes | ||
| /// them onto a <see cref="ClientHeadersScope"/> for the lifetime of the run. The scope is read by | ||
| /// <see cref="ClientHeadersPolicy"/> inside the SCM transport pipeline and stamped onto the | ||
| /// outbound request. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// <para> | ||
| /// The decorator snapshots the header dictionary at scope-push time so concurrent runs that share | ||
| /// the same <see cref="ChatOptions"/> reference are isolated; mutating the source dictionary after | ||
| /// <c>RunAsync</c> begins does not leak into in-flight requests. | ||
| /// </para> | ||
| /// <para> | ||
| /// Streaming uses the async-iterator pattern so the AsyncLocal scope stays alive across yields, | ||
| /// which is required because the underlying HTTP send happens during enumeration. | ||
| /// </para> | ||
| /// </remarks> | ||
| internal sealed class ClientHeadersAgent : DelegatingAIAgent | ||
| { | ||
| public ClientHeadersAgent(AIAgent innerAgent) | ||
| : base(innerAgent) | ||
| { | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| protected override Task<AgentResponse> RunCoreAsync( | ||
| IEnumerable<ChatMessage> messages, | ||
| AgentSession? session = null, | ||
| AgentRunOptions? options = null, | ||
| CancellationToken cancellationToken = default) | ||
| { | ||
| var snapshot = TrySnapshot(options); | ||
| if (snapshot is null) | ||
| { | ||
| return this.InnerAgent.RunAsync(messages, session, options, cancellationToken); | ||
| } | ||
|
|
||
| return RunAsyncCoreAsync(messages, session, options, snapshot, cancellationToken); | ||
|
|
||
| async Task<AgentResponse> RunAsyncCoreAsync( | ||
| IEnumerable<ChatMessage> innerMessages, | ||
| AgentSession? innerSession, | ||
| AgentRunOptions? innerOptions, | ||
| Dictionary<string, string> innerSnapshot, | ||
| CancellationToken innerCt) | ||
| { | ||
| using var _ = ClientHeadersScope.Push(innerSnapshot); | ||
| return await this.InnerAgent.RunAsync(innerMessages, innerSession, innerOptions, innerCt).ConfigureAwait(false); | ||
| } | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
| protected override async IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync( | ||
| IEnumerable<ChatMessage> messages, | ||
| AgentSession? session = null, | ||
| AgentRunOptions? options = null, | ||
| [EnumeratorCancellation] CancellationToken cancellationToken = default) | ||
| { | ||
| var snapshot = TrySnapshot(options); | ||
| using var _ = snapshot is null ? default : ClientHeadersScope.Push(snapshot); | ||
|
|
||
| await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken) | ||
| .WithCancellation(cancellationToken) | ||
| .ConfigureAwait(false)) | ||
| { | ||
| yield return update; | ||
| } | ||
| } | ||
|
|
||
| /// <summary>Reads the header dictionary stamped by <c>WithClientHeader(s)</c> and returns an immutable snapshot, or <see langword="null"/> if none.</summary> | ||
| private static Dictionary<string, string>? TrySnapshot(AgentRunOptions? options) | ||
| { | ||
| if (options is not ChatClientAgentRunOptions { ChatOptions: { } chatOptions }) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| var headers = chatOptions.GetClientHeaders(); | ||
| if (headers is null || headers.Count == 0) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| // Copy to defeat caller mutation after RunAsync starts. | ||
| var copy = new Dictionary<string, string>(headers.Count, System.StringComparer.OrdinalIgnoreCase); | ||
| foreach (var kvp in headers) | ||
| { | ||
| copy[kvp.Key] = kvp.Value; | ||
| } | ||
|
|
||
| return copy; | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.