mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
* .NET: Foundry agent-endpoint constructor uses ProjectOpenAIClient directly to fix hosted-agent URL routing
Fixes the experimental FoundryAgent(Uri agentEndpoint, AuthenticationTokenProvider, ...)
constructor so it actually works against Foundry hosted agents.
The previous implementation routed through AzureAIProjectChatClient, which
internally called aiProjectClient.GetProjectOpenAIClient().GetProjectResponsesClientForAgent(...).
For an agent-endpoint URL of the canonical shape
https://<host>/api/projects/<project>/agents/<agentName>/endpoint/protocols/openai
the chain produced
POST https://<host>/api/projects/<project>/openai/v1/responses
(project-level path, no /agents/ segment). The Foundry service rejects this with
HTTP 400 "Hosted agents can only be called through the agent endpoint:
.../agents/<agentName>/endpoint/protocols/openai/responses".
The constructor also extracted the agent name via
agentEndpoint.Segments[^1].TrimEnd('/'), which returns "openai" (the last segment),
not the agent name.
What changed
- Public ctor signature: clientOptions parameter type changed from
AIProjectClientOptions? to ProjectOpenAIClientOptions?. The constructor is
fundamentally building a ProjectOpenAIClient; accepting AIProjectClientOptions
was a leaky abstraction whose translation silently dropped any pipeline
policies the caller added via AddPolicy(...). With the direct type, caller
policies pass through to the per-agent traffic verbatim.
- Per-agent client construction: `new ProjectOpenAIClient(BearerTokenPolicy, ProjectOpenAIClientOptions)`
with Endpoint and AgentName set, then `GetProjectResponsesClient().AsIChatClient()`.
The SDK auto-appends ?api-version=v1 when AgentName is set.
- New private static ParseAgentEndpoint helper: single source of truth for both
agent-name extraction and project-root derivation. Tolerates trailing slash,
case variants on /agents/ and the suffix segment, strips query/fragment, and
throws ArgumentException with paramName=nameof(agentEndpoint) for malformed input.
- Project-level client (used by CreateConversationSessionAsync) is built fresh
from the derived project root with primitive properties copied
(RetryPolicy/NetworkTimeout/Transport/UserAgentApplicationId) plus MEAI UA.
- New GetService<ProjectOpenAIClient>() entry alongside the existing
GetService<AIProjectClient>() (the latter returns null in agent-endpoint mode
since no AIProjectClient is constructed on that path).
- Endpoint and AgentName on caller-supplied ProjectOpenAIClientOptions are
overridden by values derived from agentEndpoint.
Compatibility
- FoundryAgent is [Experimental(OPENAI001)]. No GA surface touched. The Foundry
project does not maintain PublicAPI.*.txt baselines so there is no shipped
baseline to update.
- The Microsoft.Agents.AI.Foundry csproj pins
Azure.AI.Projects to VersionOverride 2.1.0-beta.1 (matching what the IT and
hosting projects already use); the central pin in Directory.Packages.props
stays at 2.0.0.
- WireClientHeaders from PR #5652 is invoked on the agent-endpoint path so
per-call x-client-* headers behave identically across both ctors.
Tests
- 23 new unit tests in FoundryAgentTests.cs:
- 12 for the agent-endpoint constructor (URL routing for non-streaming and
streaming, conversations URL shape, MEAI UA stamping, caller-policy
passthrough on the per-agent pipeline, Endpoint/AgentName override
semantics, GetService matrix, ProjectOpenAIClient propagation,
UserAgentApplicationId propagation, null-arg validation, ID/Name slug)
- 9 for ParseAgentEndpoint (standard shape, trailing slash, casing,
sovereign-cloud host without /api/projects/ literal prefix, special chars
in agent name, query/fragment stripping, three negative cases)
- 2 null-arg tests for the public ctor
- All 250 Microsoft.Agents.AI.Foundry.UnitTests pass (was 221 baseline plus
29 from PR #5652 plus 23 new in this PR equals 273; pre-existing tests
collapsed by the rebase merge keep the total at 250).
- All 225 Microsoft.Agents.AI.Foundry.Hosting.UnitTests pass; no behavioral
change to the hosting layer.
- dotnet build clean across net8/9/10/netstandard2.0/net472 with
TreatWarningsAsErrors=true.
- dotnet format --verify-no-changes clean for the touched src and test projects.
* .NET: Bump central Azure.AI.Projects pin to 2.1.0-beta.1 and flip Microsoft.Agents.AI.Foundry to preview
Required to fix the NU1109 downgrade chain that broke CI on the agent-endpoint
constructor rewire (#5677). Microsoft.Agents.AI.Foundry now depends on
ProjectOpenAIClientOptions.AgentName and the (AuthenticationPolicy, options)
constructor that only exist in Azure.AI.Projects 2.1.0-beta.1.
Changes:
* Directory.Packages.props: Azure.AI.Projects 2.0.0 -> 2.1.0-beta.1.
* Microsoft.Agents.AI.Foundry.csproj: drop IsReleased=true so the package ships
as preview (matches the beta SDK we now depend on). Add a comment noting the
flip is temporary and should revert once Azure.AI.Projects ships a stable
2.1.0.
* Drop redundant VersionOverride="2.1.0-beta.1" from the 10 csprojs that had it
as a workaround; the central pin now suffices.
Verified:
* dotnet build agent-framework-dotnet.slnx --warnaserror clean across all TFMs.
* Microsoft.Agents.AI.Foundry.UnitTests 250/250 pass.
* Microsoft.Agents.AI.Foundry.Hosting.UnitTests 211/211 pass.
* dotnet format --verify-no-changes clean for the touched src and test projects.
Getting started with Foundry Agents
These samples demonstrate how to use Microsoft Foundry with Agent Framework.
Quick start
The simplest way to create a Foundry agent is using the FoundryAgent type directly:
FoundryAgent agent = new(
new Uri(endpoint),
new AzureCliCredential(),
model: "gpt-5.4-mini",
instructions: "You are good at telling jokes.",
name: "JokerAgent");
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate."));
Or using the AIProjectClient.AsAIAgent(...) extensions:
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
FoundryAgent agent = aiProjectClient.AsAIAgent(
model: deploymentName,
instructions: "You are good at telling jokes.",
name: "JokerAgent");
Prerequisites
- .NET 10 SDK or later
- Foundry project endpoint
- Azure CLI installed and authenticated
Set:
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-5.4-mini"
Some samples require extra tool-specific environment variables. See each sample for details.
Samples
| Sample | Description |
|---|---|
| FoundryAgent lifecycle | Create a FoundryAgent directly with endpoint and credentials |
| Basics (Responses API) | Create and run an agent using AsAIAgent extensions |
| Multi-turn conversation | Multi-turn using sessions and response ID chaining |
| Multi-turn with server conversations | Server-side conversations visible in Foundry UI |
| Using function tools | Function tools |
| Function tools with approvals | Human-in-the-loop approval |
| Structured output | Structured output with JSON schema |
| Persisted conversations | Persisting and resuming conversations |
| Observability | OpenTelemetry observability |
| Dependency injection | DI with a hosted service |
| Using MCP client as tools | MCP client tools |
| Using images | Image multi-modality |
| Agent as function tool | Agent as a function tool for another |
| Middleware | Multiple middleware layers |
| Plugins | Plugins with dependency injection |
| Code interpreter | Code interpreter tool |
| Computer use | Computer use tool |
| File search | File search tool |
| OpenAPI tools | OpenAPI tools |
| Bing custom search | Bing Custom Search tool |
| SharePoint | SharePoint grounding tool |
| Microsoft Fabric | Microsoft Fabric tool |
| Web search | Web search tool |
| Memory search | Memory search tool |
| Local MCP | Local MCP client with HTTP transport |
| Code interpreter file download | Download container files generated by code interpreter |
Running the samples
cd dotnet/samples/02-agents/AgentsWithFoundry
dotnet run --project .\FoundryAgent_Step01