.NET: Bump Azure.AI.Projects to 2.1.0-beta.2 and add agent-endpoint AsAIAgent path#5899
.NET: Bump Azure.AI.Projects to 2.1.0-beta.2 and add agent-endpoint AsAIAgent path#5899rogerbarreto wants to merge 2 commits into
Conversation
…sAIAgent path
Bumps Azure.AI.Projects to 2.1.0-beta.2 with the matching transitive pins (Azure.Core 1.55.0, System.ClientModel 1.11.0).
Foundry agent endpoint plumbing:
* FoundryAgent now routes the agent-endpoint constructor through the new GetProjectResponsesClientForAgentEndpoint helper.
* Adds an internal FoundryAgent ctor that takes an existing AIProjectClient plus a parsed agent endpoint so the public extension does not need to construct a second project client.
* Adds public AIProjectClient.AsAIAgent(Uri agentEndpoint, ...) extension. This is the path consumer samples are expected to use for hosted agents because version selection happens server-side.
* Trims the dangling "If you want to construct a FoundryAgent against a project endpoint..." sentence from ParseAgentEndpoint.
Unit tests:
* Four new tests in AzureAIProjectChatClientExtensionsTests cover the AIProjectClient.AsAIAgent(Uri agentEndpoint, ...) overload. 263/263 pass.
Consumer samples (Using-Samples):
* SimpleAgent and SessionFilesClient now read AZURE_AI_PROJECT_ENDPOINT and AZURE_AI_AGENT_NAME (both required, throw on missing), derive the agent endpoint with new Uri($"{projectEndpoint}/agents/{agentName}/endpoint/protocols/openai"), then call aiProjectClient.AsAIAgent(agentEndpoint, ...).
* SessionFilesClient README updated.
Contributor samples (responses/*):
* New HostedContributorRouteExtensions.MapDevTemporaryLocalAgentEndpoint() wildcard route extension so localhost contributor servers accept the per-agent OpenAI endpoint shape the production Hosted runtime exposes.
* All 11 contributor Program.cs files call MapDevTemporaryLocalAgentEndpoint() with a contributor-only warning comment.
* Hosted-Files and Hosted-AzureSearchRag were importing Hosted_Shared_Contributor_Setup but never calling AddDevTemporaryLocalContributorSetup(). Both now call it so HostedSessionIsolationKeyProvider resolves correctly in dev.
* Hosted-AzureSearchRag, Hosted-Files, Hosted-MemoryAgent csprojs drop stale VersionOverride="2.1.0-beta.1" pins.
* Hosted-AzureSearchRag and Hosted-Files csprojs add ProjectReference to Hosted_Shared_Contributor_Setup.
* Hosted-Observability/.dockerignore removed the out/ exclusion that was blocking COPY out/ . in Dockerfile.contributor.
Verified:
* Full solution-scoped build of changed projects: green.
* Scoped CI-parity dotnet format via WSL2 + Docker (mcr.microsoft.com/dotnet/sdk:10.0) over every changed csproj: clean.
* Foundry unit tests: 263/263.
* Contributor docker smoke for 8 hosted samples (publish + docker build + docker run + curl POST to the wildcard route): HTTP 200 / 500 with route matched.
* End-to-end smoke against the real Azure Foundry project with a fresh bearer token: Hosted-Files contributor container served HTTP 200, the agent invoked ListBundledFiles, and returned the expected file name.
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 89%
✓ Correctness
The PR correctly bumps Azure.AI.Projects to 2.1.0-beta.2 and refactors FoundryAgent to route agent-endpoint traffic through the new GetProjectResponsesClientForAgentEndpoint SDK API instead of manually building a ProjectOpenAIClient with AgentName set. The _aiProjectClient field is properly promoted from nullable to non-nullable—all four constructors now assign it. Removal of GetService() is safe since no production code depends on it (only tests, which are updated). The new AsAIAgent(Uri agentEndpoint) extension method and internal constructor are straightforward wiring. Sample updates consistently replace the old openai/v1 dev route with the new per-agent endpoint route shape via MapDevTemporaryLocalAgentEndpoint(). The CreateProjectClientOptions helper omits RetryPolicy/NetworkTimeout when mapping from ProjectOpenAIClientOptions to AIProjectClientOptions, which is appropriate since these types have incompatible retry configuration hierarchies (System.ClientModel vs Azure.Core). No correctness issues found.
✓ Security Reliability
This PR is clean from a security and reliability standpoint. All new code paths have proper null-guard validation via
Throw.IfNull. TheParseAgentEndpointmethod thoroughly validates the URI shape before using it. The dev-only route mapping inMapDevTemporaryLocalAgentEndpointis correctly gated behindIsDevelopment(). TheDevTemporaryLocalSessionIsolationKeyProviderstill prioritizes platform-supplied isolation headers when present, falling back to dev defaults only when absent — so even its unconditional registration in sample code doesn't bypass production security. The field change from nullableAIProjectClient?to non-nullableAIProjectClientis safe because all constructor paths now set it. TheCreateConversationSessionAsyncswitch to_aiProjectClient.ProjectOpenAIClientis equivalent — the SDK property is always non-null on a properly constructedAIProjectClient. No secrets, injection risks, resource leaks, or unhandled failure modes were found.
✓ Test Coverage
The PR adds four new unit tests for the
AsAIAgent(AIProjectClient, Uri)extension and updates existing tests to match the removal ofGetService<ProjectOpenAIClient>(). Test coverage for null-guard, name/ID parsing, client reuse, and client factory is solid. Two gaps: (1) the rewrittenAgentEndpointConstructor_PreservesUserAgentApplicationIdtest is now tautological—it only asserts that a local options object's property wasn't mutated, verifying nothing about the agent's behavior; (2) none of the newAsAIAgent(Uri)tests exercise thetoolsparameter, unlike the thorough tool coverage present for the otherAsAIAgentoverloads.
✗ Design Approach
I found one design issue in the new agent-endpoint overload: the public
AsAIAgent(AIProjectClient, Uri)path can silently target the wrong Foundry project when the supplied client and endpoint do not belong to the same project. The implementation parses the endpoint but then discards the parsed project root and builds the responses client from the already-rootedAIProjectClient, so a mismatched(client, endpoint)pair is accepted and routed incorrectly instead of failing fast.
Suggestions
- Validate that the supplied AIProjectClient's project root matches the project root parsed from agentEndpoint (via ParseAgentEndpoint) before calling GetProjectResponsesClientForAgentEndpoint, and throw ArgumentException on mismatch.
- The AsAIAgent(AIProjectClient, Uri) overload accepts a tools parameter but none of the four new tests exercise it. Add at least one test passing tools through this overload to verify they flow into ChatOptions.Tools, matching the coverage depth of the other AsAIAgent overloads.
Automated review by rogerbarreto's agents
There was a problem hiding this comment.
Pull request overview
Updates the .NET Foundry integration to use Azure.AI.Projects 2.1.0-beta.2 and adds a supported consumption path for hosted agents via an agent-specific endpoint (AIProjectClient.AsAIAgent(Uri agentEndpoint, ...)). This aligns hosted-agent calls with the per-agent endpoint routing where version selection is managed server-side.
Changes:
- Bump Azure SDK package versions (Azure.AI.Projects, Azure.Core, System.ClientModel) and remove per-project VersionOverride entries in favor of central package management.
- Refactor
FoundryAgenthosted-agent (agent-endpoint) construction to reuse/build anAIProjectClientand route per-agent traffic viaGetProjectResponsesClientForAgentEndpoint(...); add an internal ctor to reuse an existingAIProjectClient. - Add
AIProjectClient.AsAIAgent(Uri agentEndpoint, ...)and update tests + hosting samples (including a contributor-only dev route mapper) to use the agent-endpoint shape.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/FoundryAgentTests.cs | Updates unit tests for new service exposure expectations and agent-endpoint routing behavior. |
| dotnet/tests/Microsoft.Agents.AI.Foundry.UnitTests/AzureAIProjectChatClientExtensionsTests.cs | Adds unit tests for the new AsAIAgent(Uri agentEndpoint) extension overload. |
| dotnet/tests/Foundry.Hosting.IntegrationTests/Foundry.Hosting.IntegrationTests.csproj | Removes Azure.AI.Projects VersionOverride to use centrally managed version. |
| dotnet/src/Microsoft.Agents.AI.Foundry/FoundryAgent.cs | Reworks agent-endpoint flow to use AIProjectClient + per-agent responses client factory and adjusts service exposure. |
| dotnet/src/Microsoft.Agents.AI.Foundry/AzureAIProjectChatClientExtensions.cs | Adds AsAIAgent(AIProjectClient, Uri agentEndpoint, ...) overload for hosted agents. |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Using-Samples/SimpleAgent/Program.cs | Updates sample to derive agent endpoint from project endpoint + agent name and call AsAIAgent(agentEndpoint). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Using-Samples/SessionFilesClient/README.md | Updates sample configuration env var names and explains agent-endpoint derivation. |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Using-Samples/SessionFilesClient/Program.cs | Updates sample to derive agent endpoint and call AsAIAgent(agentEndpoint). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Workflow-Simple/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Toolbox/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-TextRag/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Observability/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Observability/.dockerignore | Fixes contributor Docker build by no longer excluding out/. |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-MemoryAgent/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-MemoryAgent/HostedMemoryAgent.csproj | Removes Azure.AI.Projects VersionOverride to use centrally managed version. |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-McpTools/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-LocalTools/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-FoundryAgent/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Files/Program.cs | Adds contributor setup registration and uses MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-Files/HostedFiles.csproj | Removes VersionOverride and adds project reference to contributor setup helper. |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-ChatClientAgent/Program.cs | Replaces prior dev-only route mapping with MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-AzureSearchRag/Program.cs | Adds contributor setup registration and uses MapDevTemporaryLocalAgentEndpoint(). |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-AzureSearchRag/HostedAzureSearchRag.csproj | Removes VersionOverride and adds project reference to contributor setup helper. |
| dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted_Shared_Contributor_Setup/HostedContributorRouteExtensions.cs | Adds dev-only routing helper to expose the per-agent endpoint route shape locally. |
| dotnet/Directory.Packages.props | Bumps central package versions for Azure.AI.Projects/Azure.Core/System.ClientModel. |
- CreateProjectClientOptions also carries RetryPolicy, NetworkTimeout, ClientLoggingOptions, MessageLoggingPolicy (was Transport+UserAgentApplicationId only). - Make CreateProjectClientOptions internal so tests can verify the copy directly. - Add AsAIAgent(Uri) UTs covering tools forwarding to inner ChatOptions and null tools handling. - Add CreateProjectClientOptions UTs covering null caller and full pipeline-settings copy.
| .env | ||
| bin/ | ||
| obj/ | ||
| out/ |
There was a problem hiding this comment.
nit: Did you mean to remove this?
Summary
Bumps
Azure.AI.Projectsto2.1.0-beta.2and wires the new per-agent endpoint flow throughFoundryAgent.Changes
Directory.Packages.props:Azure.AI.Projects 2.1.0-beta.2,Azure.Core 1.55.0,System.ClientModel 1.11.0.FoundryAgent: agent-endpoint ctor now usesGetProjectResponsesClientForAgentEndpoint. Adds an internal ctor that takes an existingAIProjectClientplus parsed endpoint to avoid building a second project client.AIProjectClient.AsAIAgent(Uri agentEndpoint, ...)extension added. This is the supported path for hosted agents because version selection happens server side.Samples
Using samples (
SimpleAgent,SessionFilesClient) now requireAZURE_AI_PROJECT_ENDPOINTandAZURE_AI_AGENT_NAME, derive the agent endpoint with a singlenew Uri(...)interpolation, and callaiProjectClient.AsAIAgent(agentEndpoint).Contributor samples (
responses/*) get a newMapDevTemporaryLocalAgentEndpoint()route extension and all 11Program.csfiles register it.Hosted-FilesandHosted-AzureSearchRagwere importing the contributor setup helper but never callingAddDevTemporaryLocalContributorSetup(); both now do.Fixes a pre-existing
Hosted-Observability/.dockerignorebug that excludedout/and brokeDockerfile.contributor.Validation
--warnaserror) across 17 changed projects: green.dotnet format --verify-no-changes(WSL2 + Dockermcr.microsoft.com/dotnet/sdk:10.0): clean.Hosted-Filesanswered HTTP 200, tool invocation succeeded, agent returned the expected file name.dotnet-foundry-hosted-itjob on the branch run (workflow_dispatch 25926366098): success.