Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
57bfb8f
Add zip/code-deploy POC for Hosted-ChatClientAgent (.NET)
rogerbarreto Jul 23, 2026
a4e02e0
.NET: Auto-bind Foundry hosted port for zip/code deploy; migrate Host…
rogerbarreto Jul 24, 2026
c760aa3
.NET: Bind Foundry hosted port unconditionally; drop container files …
rogerbarreto Jul 28, 2026
ea1b14a
Pin hosted agent listen port in azure.yaml
rogerbarreto Jul 28, 2026
b331394
Use the documented env map in azure.yaml
rogerbarreto Jul 28, 2026
c9c70e8
Make the contributor flow an extra step inside the end-user flow
rogerbarreto Jul 28, 2026
a8b505d
Keep contributor scaffolding out of the sample project file
rogerbarreto Jul 28, 2026
e374758
Document the full deploy walkthrough and add a bash contributor script
rogerbarreto Jul 28, 2026
335e822
Trim troubleshooting detail from the sample README
rogerbarreto Jul 28, 2026
01d050c
Pass the model deployment name to the hosted container
rogerbarreto Jul 28, 2026
ab98c2f
Add --local and --remote flags to the SimpleAgent REPL
rogerbarreto Jul 28, 2026
c42887e
Use central package management in the hosted sample
rogerbarreto Jul 28, 2026
ce1d993
Clarify where the contributor step fits in the deploy walkthrough
rogerbarreto Jul 28, 2026
fce7016
Restore the HTTP scheme rewrite for local AIProjectClient runs
rogerbarreto Jul 28, 2026
7b2b72f
Keep the sample package versions in the project file
rogerbarreto Jul 28, 2026
f3e3a81
Drop the sample Directory.Packages.props
rogerbarreto Jul 28, 2026
114af90
Add a container deploy variant of the hosted chat client agent sample
rogerbarreto Jul 28, 2026
d892540
Treat a blank model deployment variable as unset
rogerbarreto Jul 28, 2026
568bcc5
Let azd prompt for the Foundry project and expand the contributor sec…
rogerbarreto Jul 28, 2026
2e3b46f
Document the stale conversation 404 in the hosted agent samples
rogerbarreto Jul 28, 2026
3ac87c3
Remove using directives already covered by global usings
rogerbarreto Jul 28, 2026
15a7235
Bind the Foundry listen port only inside a hosted container
rogerbarreto Jul 28, 2026
8b0dbd5
Resolve the Foundry listen port from IConfiguration
rogerbarreto Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,9 @@
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-ChatClientAgent/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-ChatClientAgent/HostedChatClientAgent.csproj" />
</Folder>
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-ChatClientAgent-Dockerfile/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-ChatClientAgent-Dockerfile/HostedChatClientAgentDocker.csproj" />
</Folder>
<Folder Name="/Samples/04-hosting/FoundryHostedAgents/responses/Hosted-FoundryAgent/">
<Project Path="samples/04-hosting/FoundryHostedAgents/responses/Hosted-FoundryAgent/HostedFoundryAgent.csproj" />
</Folder>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,17 @@ For end-to-end hosted agent deployment guidance, see the [official deployment gu
## NuGet package users

If you are consuming the Agent Framework as a NuGet package (not building from source), use the standard `Dockerfile` instead of `Dockerfile.contributor`. See the commented section in `HostedAzureSearchRag.csproj` for the `PackageReference` alternative.

## Troubleshooting

**`azd ai agent invoke` fails with `404 not_found: Conversation '<id>' not found`**

`azd` saves the session and conversation per agent and reuses them on the next invoke. Once the
agent is redeployed, deleted, or restarted, that saved conversation no longer exists on the server,
so every following invoke fails even though the agent itself is healthy. Start a fresh one:

```
azd ai agent invoke --new-conversation "Hello!"
```

Add `--new-session` as well if the failure persists.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Keeps local-only files out of the image build context. Without this, `COPY . .` in the Dockerfile
# would copy the local .env into a build layer, so local credentials would ship inside the image.
.env
.env.*
.azure/
.git/

# Build output: the image builds from source, so shipping host binaries only bloats the context and
# risks copying binaries built for a different platform into the container.
bin/
obj/
*.user
*.suo
.vs/

# Agent session state written during local runs.
.checkpoints/

# Note: local-feed/ and nuget.config are deliberately NOT excluded. When present (contributor mode)
# the `dotnet restore` inside the image build resolves the Agent Framework from them.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Foundry project endpoint (shape: https://<host>/api/projects/<project>)
FOUNDRY_PROJECT_ENDPOINT=<your-azure-ai-project-endpoint>

# Model deployment name in your Foundry project.
AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o

# Local development only. Bind the app to the port Foundry probes for readiness, which is the
# port the Using-Samples REPLs expect. The Dockerfile sets this for the container; a plain
# `dotnet run` on the host does not go through the Dockerfile, so set it here too.
ASPNETCORE_URLS=http://+:8088

# Local development only. Restrict DefaultAzureCredential to developer credentials
# (Azure CLI, Visual Studio, azd) and skip the Managed Identity probe. Without this,
# on a machine with no managed identity DefaultAzureCredential hangs for a long time
# probing the IMDS endpoint (169.254.169.254) before every model call. Not set in
# Foundry, where the platform-injected managed identity is used.
AZURE_TOKEN_CREDENTIALS=dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Foundry builds this image and runs it as the hosted agent. The build restores and publishes the
# project inside the container, so a contributor feed dropped into this folder (local-feed/ plus
# nuget.config) is picked up by the `dotnet restore` below without any change here.
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app
COPY --from=build /app/publish .

# Foundry probes port 8088 for readiness. The .NET base image defaults ASPNETCORE_URLS to port 80,
# so without this the probe never succeeds and every invoke fails with HTTP 424 session_not_ready.
EXPOSE 8088
ENV ASPNETCORE_URLS=http://+:8088

ENTRYPOINT ["dotnet", "HostedChatClientAgentDocker.dll"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<Project>

<!--
Container deploy sample. Foundry builds the Dockerfile in this folder and runs the resulting
image, so unlike the source (ZIP) path there is no server-side `dotnet restore` on a bare
folder: the restore happens inside the container build.

ImportDirectoryPackagesProps has to be set before the SDK props are imported, hence the explicit
Sdk imports below instead of the usual Sdk attribute on the Project element. It stops MSBuild
from walking up to the repository's dotnet/Directory.Packages.props, which does two things this
sample must avoid: it turns on central package management, and it injects analyzer
PackageReference items whose versions it also supplies. Neither exists inside the container
build context, so without this the in-repo build would resolve differently from the image build.
-->

<PropertyGroup>
<ImportDirectoryPackagesProps>false</ImportDirectoryPackagesProps>
</PropertyGroup>

<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk.Web" />

<PropertyGroup>
<!-- Single target: the Dockerfile publishes without -f, so the project must not multi-target.
The empty TargetFrameworks clears the value inherited from the repo's samples
Directory.Build.props for in-repo builds. -->
<TargetFramework>net10.0</TargetFramework>
<TargetFrameworks></TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>HostedChatClientAgentDocker</RootNamespace>
<AssemblyName>HostedChatClientAgentDocker</AssemblyName>
<UserSecretsId>7b1c3f04-24a1-4f0e-9a5e-0d2f6b8c1e57</UserSecretsId>
<AgentFrameworkVersion>1.15.0-preview.260722.1</AgentFrameworkVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Agents.AI.Foundry" Version="$(AgentFrameworkVersion)" />
<PackageReference Include="Microsoft.Agents.AI.Foundry.Hosting" Version="$(AgentFrameworkVersion)" />
<PackageReference Include="Azure.AI.Projects" Version="2.1.0-beta.4" />
<PackageReference Include="Azure.Identity" Version="1.21.0" />
<PackageReference Include="DotNetEnv" Version="3.1.1" />
</ItemGroup>

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk.Web" />

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) Microsoft. All rights reserved.

// Sample: a minimal general-purpose AI assistant hosted as a Foundry Hosted Agent
// using the Responses protocol. It is deployed to Foundry as a container image built
// from the Dockerfile in this folder.
//
// The sibling Hosted-ChatClientAgent sample is the same agent deployed the other way,
// straight from source with no container image. Compare the two folders to see exactly
// what the container path adds.

using Azure.AI.Projects;
using Azure.Identity;
using DotNetEnv;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry.Hosting;

// Load a local .env file when present (local development only). In Foundry the
// platform injects the required environment variables at runtime.
Env.TraversePath().Load();

var projectEndpoint = new Uri(System.Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set."));

// Environment variables can arrive set but blank: azd substitutes an empty string when the azd
// environment does not define the variable referenced from azure.yaml. An empty string is not
// null, so a plain ?? chain would pass the blank straight through and fail deep inside the SDK.
var model = FirstNonBlank(
System.Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME"),
System.Environment.GetEnvironmentVariable("FOUNDRY_MODEL"),
"gpt-4o");

var agentName = System.Environment.GetEnvironmentVariable("AGENT_NAME") ?? "hosted-chat-client-agent-docker";

// WARNING: DefaultAzureCredential is convenient for development but requires careful
// consideration in production. Consider a specific credential (for example
// ManagedIdentityCredential) to avoid latency, unintended credential probing, and
// fallback security risks.
AIAgent agent = new AIProjectClient(projectEndpoint, new DefaultAzureCredential())
.AsAIAgent(
model: model,
instructions: """
You are a helpful AI assistant hosted as a Foundry Hosted Agent.
You can help with a wide range of tasks including answering questions,
providing explanations, brainstorming ideas, and offering guidance.
Be concise, clear, and helpful in your responses.
""",
name: agentName,
description: "A simple general-purpose AI assistant");

// Host the agent using the Responses protocol.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFoundryResponses(agent);

var app = builder.Build();
app.MapFoundryResponses();

app.Run();

// Returns the first candidate that has an actual value, ignoring null and blank entries.
static string FirstNonBlank(params string?[] candidates) =>
Array.Find(candidates, c => !string.IsNullOrWhiteSpace(c))!;
Loading
Loading