Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
</Folder>
<Folder Name="/Samples/02-agents/Agents/">
<File Path="samples/02-agents/Agents/README.md" />
<Project Path="samples/02-agents/Agents/Agent_ContainerAppsDynamicSessions/Agent_ContainerAppsDynamicSessions.csproj" />
<Project Path="samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Agent_Step01_UsingFunctionToolsWithApprovals.csproj" />
<Project Path="samples/02-agents/Agents/Agent_Step02_StructuredOutput/Agent_Step02_StructuredOutput.csproj" />
<Project Path="samples/02-agents/Agents/Agent_Step03_PersistedConversations/Agent_Step03_PersistedConversations.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.Projects" />
<PackageReference Include="Azure.Identity" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Foundry\Microsoft.Agents.AI.Foundry.csproj" />
</ItemGroup>

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

// Expose Azure Container Apps Dynamic Sessions as an agent function tool.

using System.ComponentModel;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Azure.AI.Projects;
using Azure.Core;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

string projectEndpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string model = Environment.GetEnvironmentVariable("FOUNDRY_MODEL")
?? throw new InvalidOperationException("FOUNDRY_MODEL is not set.");
string poolEndpoint = Environment.GetEnvironmentVariable("POOL_MANAGEMENT_ENDPOINT")
?? throw new InvalidOperationException("POOL_MANAGEMENT_ENDPOINT is not set.");
Uri poolUri = new(poolEndpoint);
if (poolUri.Scheme != Uri.UriSchemeHttps || poolUri.UserInfo.Length != 0 || poolEndpoint.Contains('?') || poolEndpoint.Contains('#'))
{
throw new InvalidOperationException("POOL_MANAGEMENT_ENDPOINT must be a trusted HTTPS pool URL without credentials or a query.");
}

// One application-owned session per run. Reuse it for this agent's tool calls only.
string sessionId = Guid.NewGuid().ToString("N");
Uri executionUri = new($"{poolEndpoint.TrimEnd('/')}/executions?api-version=2025-10-02-preview&identifier={sessionId}");
AzureCliCredential credential = new();
using HttpClient httpClient = new(new HttpClientHandler { AllowAutoRedirect = false, CheckCertificateRevocationList = true }) { Timeout = TimeSpan.FromSeconds(70) };

AIAgent agent = new AIProjectClient(new Uri(projectEndpoint), credential).AsAIAgent(
model: model,
instructions: "Use ExecutePython for calculations. Check its status and errors before reporting a result.",
tools: [AIFunctionFactory.Create(ExecutePythonAsync, "ExecutePython")]);

Console.WriteLine(await agent.RunAsync("Use Python to calculate the total of 19.95, 42.50, and 8.75, then add 8% tax."));

[Description("Run Python in the conversation's cloud sandbox and return its output and execution status.")]
async Task<JsonElement> ExecutePythonAsync([Description("Python code to execute in the remote session.")] string code, CancellationToken cancellationToken)
{
AccessToken token = await credential.GetTokenAsync(new TokenRequestContext(["https://dynamicsessions.io/.default"]), cancellationToken);
using HttpRequestMessage request = new(HttpMethod.Post, executionUri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);
request.Content = JsonContent.Create(new
{
codeInputType = "Inline",
executionType = "Synchronous",
code,
timeoutInSeconds = 30,
outputStreamsMaxLength = 4096,
});
using HttpResponseMessage response = await httpClient.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<JsonElement>(cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Azure Container Apps Dynamic Sessions

This sample gives an Agent Framework agent a function tool that runs Python in an
Azure Container Apps code-interpreter session. It uses the existing HTTP and Azure
identity libraries to call the [code execution REST API](https://learn.microsoft.com/rest/api/data-plane/containerapps/code-execution/execute).
Generated code runs in the cloud session, not on the machine running this sample.

## Setup

Use .NET 10 and an existing Microsoft Foundry model deployment. Create a Python
code-interpreter session pool using the [Dynamic Sessions setup guide](https://learn.microsoft.com/azure/container-apps/sessions-usage).
The signed-in identity needs `Azure ContainerApps Session Executor` on the pool
and permission to invoke the Foundry model. Sign in with `az login`.

Retrieve the pool management endpoint from
`az containerapp sessionpool show --name <pool> --resource-group <group> --query properties.poolManagementEndpoint --output tsv`.
Then configure and run:

```powershell
$env:FOUNDRY_PROJECT_ENDPOINT = "<project-endpoint>"
$env:FOUNDRY_MODEL = "<model-deployment>"
$env:POOL_MANAGEMENT_ENDPOINT = "<pool-management-endpoint>"
dotnet run
```

The agent asks the tool to calculate an order total with tax. The tool returns
the execution status and result, including standard output and errors. HTTP
failures are surfaced to the agent's function invocation pipeline. Each execution
is limited to 30 seconds with a bounded output stream; the HTTP client allows
additional time for transport and pool startup.

## Session ownership and lifecycle

The application generates a new session identifier for each run and reuses it
for that run's tool calls, allowing Python state to persist between calls. The
model supplies only code, not the endpoint or session identifier. For a multi-user
host, bind a separate tool/session identifier to each authorized conversation.
Do not share a session across unrelated users.

Use only a trusted pool management endpoint. Configure the pool's network access
for your workload and do not pass application credentials or confidential data
into generated code. This example uses interactive developer authentication;
deployed applications should use an identity configured for their host.

The sample does not provision or delete Azure resources. The pool's configured
cooldown manages idle session cleanup. Model and pool usage incur costs.
1 change: 1 addition & 0 deletions dotnet/samples/02-agents/Agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Before you begin, ensure you have the following prerequisites:

|Sample|Description|
|---|---|
|[Container Apps Dynamic Sessions](./Agent_ContainerAppsDynamicSessions/)|Run agent-generated Python in a cloud code-interpreter session using a function tool|
|[Using OpenAPI function tools with a simple agent](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples/AgentFrameworkMigration/AzureOpenAI/Step04_ToolCall_WithOpenAPI)|This sample demonstrates how to create function tools from an OpenAPI spec and use them with a simple agent (note that this sample is in the Semantic Kernel repository)|
|[Using function tools with approvals](./Agent_Step01_UsingFunctionToolsWithApprovals/)|This sample demonstrates how to use function tools where approvals require human in the loop approvals before execution|
|[Structured output with a simple agent](./Agent_Step02_StructuredOutput/)|This sample demonstrates how to use structured output with a simple agent|
Expand Down
Loading