Skip to content

Commit 52303a8

Browse files
.NET: Add Code Interpreter container file download samples (#5014)
* Add Code Interpreter container file download samples (#3081) - Add Agent_OpenAI_Step06_CodeInterpreterFileDownload (Public OpenAI) - Add Agent_Step24_CodeInterpreterFileDownload (Microsoft Foundry) - Both samples demonstrate downloading cfile_/cntr_ container files via ContainerClient instead of the standard Files API - Update solution file and parent READMEs * Address review feedback: flatten nested foreach loops using SelectMany Addresses #5014 (comment) and #5014 (comment) --------- Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Co-authored-by: rogerbarreto <rogerbarreto@users.noreply.github.com>
1 parent c85d24d commit 52303a8

9 files changed

Lines changed: 326 additions & 1 deletion

File tree

dotnet/agent-framework-dotnet.slnx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj" />
153153
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj" />
154154
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj" />
155+
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step24_CodeInterpreterFileDownload/Agent_Step24_CodeInterpreterFileDownload.csproj" />
155156
</Folder>
156157
<Folder Name="/Samples/02-agents/Evaluation/">
157158
<Project Path="samples/02-agents/Evaluation/Evaluation_SimpleEval/Evaluation_SimpleEval.csproj" />
@@ -173,6 +174,7 @@
173174
<Project Path="samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Agent_OpenAI_Step03_CreateFromChatClient.csproj" />
174175
<Project Path="samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient.csproj" />
175176
<Project Path="samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Agent_OpenAI_Step05_Conversation.csproj" />
177+
<Project Path="samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step06_CodeInterpreterFileDownload/Agent_OpenAI_Step06_CodeInterpreterFileDownload.csproj" />
176178
</Folder>
177179
<Folder Name="/Samples/02-agents/AgentWithRAG/">
178180
<File Path="samples/02-agents/AgentWithRAG/README.md" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>net10.0</TargetFrameworks>
6+
7+
<Nullable>enable</Nullable>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
// This sample shows how to download files generated by Code Interpreter using the Containers API.
4+
// Code Interpreter generates files inside containers (cfile_ / cntr_ IDs) which cannot be
5+
// downloaded via the standard Files API. Use ContainerClient instead.
6+
7+
#pragma warning disable OPENAI001
8+
9+
using System.ClientModel;
10+
using Microsoft.Agents.AI;
11+
using Microsoft.Extensions.AI;
12+
using OpenAI;
13+
using OpenAI.Containers;
14+
using OpenAI.Responses;
15+
16+
string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
17+
string model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini";
18+
19+
var openAIClient = new OpenAIClient(new ApiKeyCredential(apiKey));
20+
21+
// Create an agent with Code Interpreter tool enabled
22+
AIAgent agent = openAIClient
23+
.GetResponsesClient()
24+
.AsAIAgent(
25+
model: model,
26+
instructions: "You are a helpful assistant that can generate files using code.",
27+
name: "CodeInterpreterAgent",
28+
tools: [new HostedCodeInterpreterTool()]);
29+
30+
// Ask the agent to generate a file
31+
AgentResponse response = await agent.RunAsync(
32+
"Create a CSV file with the multiplication times tables from 1 to 12. Include headers.");
33+
34+
// Display the text response
35+
foreach (TextContent textContent in response.Messages.SelectMany(x => x.Contents).OfType<TextContent>())
36+
{
37+
Console.WriteLine(textContent.Text);
38+
}
39+
40+
// Extract container file citations from response annotations and download
41+
ContainerClient containerClient = openAIClient.GetContainerClient();
42+
43+
HashSet<string> downloadedFiles = [];
44+
bool foundContainerFiles = false;
45+
46+
foreach (AIContent content in response.Messages.SelectMany(x => x.Contents))
47+
{
48+
if (content.Annotations is null)
49+
{
50+
continue;
51+
}
52+
53+
foreach (AIAnnotation annotation in content.Annotations)
54+
{
55+
// Container files from Code Interpreter have ContainerFileCitationMessageAnnotation as raw representation
56+
if (annotation is CitationAnnotation citation
57+
&& citation.RawRepresentation is ContainerFileCitationMessageAnnotation containerCitation)
58+
{
59+
foundContainerFiles = true;
60+
61+
// Deduplicate by container+file ID in case the same file is cited multiple times
62+
string key = $"{containerCitation.ContainerId}/{containerCitation.FileId}";
63+
if (!downloadedFiles.Add(key))
64+
{
65+
continue;
66+
}
67+
68+
Console.WriteLine($"\nDownloading container file: {containerCitation.Filename}");
69+
Console.WriteLine($" Container ID: {containerCitation.ContainerId}");
70+
Console.WriteLine($" File ID: {containerCitation.FileId}");
71+
72+
BinaryData fileData = await containerClient.DownloadContainerFileAsync(
73+
containerCitation.ContainerId,
74+
containerCitation.FileId);
75+
76+
// Sanitize filename to prevent path traversal
77+
string safeFilename = Path.GetFileName(containerCitation.Filename);
78+
string outputPath = Path.Combine(Directory.GetCurrentDirectory(), safeFilename);
79+
await File.WriteAllBytesAsync(outputPath, fileData.ToArray());
80+
Console.WriteLine($" Saved to: {outputPath}");
81+
}
82+
}
83+
}
84+
85+
if (!foundContainerFiles)
86+
{
87+
Console.WriteLine("\nNo container file citations found in the response.");
88+
Console.WriteLine("The model may not have generated a downloadable file for this prompt.");
89+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Code Interpreter File Download (OpenAI)
2+
3+
This sample demonstrates how to download files generated by Code Interpreter when using the OpenAI Responses API.
4+
5+
## What this sample demonstrates
6+
7+
- Creating an agent with Code Interpreter tool using `ResponsesClient.AsAIAgent()`
8+
- Generating files through Code Interpreter (e.g., CSV, Excel, images)
9+
- Extracting container file citations from agent response annotations
10+
- Downloading container files using the `ContainerClient` API
11+
12+
## Container files vs regular files
13+
14+
When Code Interpreter generates a file, the file is stored inside a **container** with a `cntr_` prefixed ID. The file itself gets a `cfile_` prefixed ID.
15+
16+
These container files **cannot** be downloaded using the standard Files API (`GetOpenAIFileClient`), which returns 404 for `cfile_` IDs. Instead, you must use the **Containers API** (`GetContainerClient`) to download them:
17+
18+
```csharp
19+
// ❌ This does NOT work for container files
20+
var filesClient = openAIClient.GetOpenAIFileClient();
21+
await filesClient.DownloadFileAsync("cfile_..."); // Returns 404
22+
23+
// ✅ Use ContainerClient instead
24+
var containerClient = openAIClient.GetContainerClient();
25+
await containerClient.DownloadContainerFileAsync("cntr_...", "cfile_...");
26+
```
27+
28+
The container ID and file ID are available from the `ContainerFileCitationMessageAnnotation` annotation in the response, accessible via `CitationAnnotation.RawRepresentation`.
29+
30+
## Prerequisites
31+
32+
- .NET 10 SDK or later
33+
- OpenAI API key with access to a model that supports Code Interpreter
34+
35+
Set the following environment variables:
36+
37+
```powershell
38+
$env:OPENAI_API_KEY="sk-..."
39+
$env:OPENAI_CHAT_MODEL_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
40+
```
41+
42+
## Run the sample
43+
44+
```powershell
45+
dotnet run
46+
```
47+
48+
## See also
49+
50+
- [Code Interpreter File Download with Foundry](../../../02-agents/AgentsWithFoundry/Agent_Step24_CodeInterpreterFileDownload/) — same scenario using Microsoft Foundry
51+
- [Code Interpreter](../../../02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/) — Code Interpreter without file download

dotnet/samples/02-agents/AgentWithOpenAI/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ Agent Framework provides additional support to allow OpenAI developers to use th
1414
|[Using Reasoning Capabilities](./Agent_OpenAI_Step02_Reasoning/)|This sample demonstrates how to create an AI agent with reasoning capabilities using OpenAI's reasoning models and response types.|
1515
|[Creating an Agent from a ChatClient](./Agent_OpenAI_Step03_CreateFromChatClient/)|This sample demonstrates how to create an AI agent directly from an OpenAI.Chat.ChatClient instance using OpenAIChatClientAgent.|
1616
|[Creating an Agent from an OpenAIResponseClient](./Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/)|This sample demonstrates how to create an AI agent directly from an OpenAI.Responses.OpenAIResponseClient instance using OpenAIResponseClientAgent.|
17-
|[Managing Conversation State](./Agent_OpenAI_Step05_Conversation/)|This sample demonstrates how to maintain conversation state across multiple turns using the AgentSession for context continuity.|
17+
|[Managing Conversation State](./Agent_OpenAI_Step05_Conversation/)|This sample demonstrates how to maintain conversation state across multiple turns using the AgentSession for context continuity.|
18+
|[Code Interpreter File Download](./Agent_OpenAI_Step06_CodeInterpreterFileDownload/)|This sample demonstrates how to download files generated by Code Interpreter using the Containers API (`cfile_`/`cntr_` IDs).|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>net10.0</TargetFrameworks>
6+
7+
<Nullable>enable</Nullable>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Azure.Identity" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Foundry\Microsoft.Agents.AI.Foundry.csproj" />
17+
</ItemGroup>
18+
19+
</Project>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
// This sample shows how to download files generated by Code Interpreter using Microsoft Foundry.
4+
// Code Interpreter generates files inside containers (cfile_ / cntr_ IDs) which cannot be
5+
// downloaded via the standard Files API. Use ContainerClient from the project's OpenAI client instead.
6+
7+
#pragma warning disable OPENAI001
8+
9+
using Azure.AI.Projects;
10+
using Azure.Identity;
11+
using Microsoft.Agents.AI;
12+
using Microsoft.Extensions.AI;
13+
using OpenAI.Responses;
14+
15+
string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
16+
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
17+
18+
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
19+
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
20+
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
21+
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());
22+
23+
// Create an agent with Code Interpreter tool enabled
24+
AIAgent agent = aiProjectClient.AsAIAgent(
25+
deploymentName,
26+
instructions: "You are a helpful assistant that can generate files using code.",
27+
name: "CodeInterpreterAgent",
28+
tools: [new HostedCodeInterpreterTool()]);
29+
30+
// Ask the agent to generate a file
31+
AgentResponse response = await agent.RunAsync(
32+
"Create a CSV file with the multiplication times tables from 1 to 12. Include headers.");
33+
34+
// Display the text response
35+
foreach (TextContent textContent in response.Messages.SelectMany(x => x.Contents).OfType<TextContent>())
36+
{
37+
Console.WriteLine(textContent.Text);
38+
}
39+
40+
// Extract container file citations from response annotations and download.
41+
// AIProjectClient.GetProjectOpenAIClient() returns a ProjectOpenAIClient (inherits from OpenAI.OpenAIClient)
42+
// which supports GetContainerClient(), unlike AzureOpenAIClient which does not.
43+
var containerClient = aiProjectClient.GetProjectOpenAIClient().GetContainerClient();
44+
45+
HashSet<string> downloadedFiles = [];
46+
bool foundContainerFiles = false;
47+
48+
foreach (AIContent content in response.Messages.SelectMany(x => x.Contents))
49+
{
50+
if (content.Annotations is null)
51+
{
52+
continue;
53+
}
54+
55+
foreach (AIAnnotation annotation in content.Annotations)
56+
{
57+
// Container files from Code Interpreter have ContainerFileCitationMessageAnnotation as raw representation
58+
if (annotation is CitationAnnotation citation
59+
&& citation.RawRepresentation is ContainerFileCitationMessageAnnotation containerCitation)
60+
{
61+
foundContainerFiles = true;
62+
63+
// Deduplicate by container+file ID in case the same file is cited multiple times
64+
string key = $"{containerCitation.ContainerId}/{containerCitation.FileId}";
65+
if (!downloadedFiles.Add(key))
66+
{
67+
continue;
68+
}
69+
70+
Console.WriteLine($"\nDownloading container file: {containerCitation.Filename}");
71+
Console.WriteLine($" Container ID: {containerCitation.ContainerId}");
72+
Console.WriteLine($" File ID: {containerCitation.FileId}");
73+
74+
BinaryData fileData = await containerClient.DownloadContainerFileAsync(
75+
containerCitation.ContainerId,
76+
containerCitation.FileId);
77+
78+
// Sanitize filename to prevent path traversal
79+
string safeFilename = Path.GetFileName(containerCitation.Filename);
80+
string outputPath = Path.Combine(Directory.GetCurrentDirectory(), safeFilename);
81+
await File.WriteAllBytesAsync(outputPath, fileData.ToArray());
82+
Console.WriteLine($" Saved to: {outputPath}");
83+
}
84+
}
85+
}
86+
87+
if (!foundContainerFiles)
88+
{
89+
Console.WriteLine("\nNo container file citations found in the response.");
90+
Console.WriteLine("The model may not have generated a downloadable file for this prompt.");
91+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Code Interpreter File Download (Microsoft Foundry)
2+
3+
This sample demonstrates how to download files generated by Code Interpreter when using Microsoft Foundry.
4+
5+
## What this sample demonstrates
6+
7+
- Creating an agent with Code Interpreter tool using `AIProjectClient.AsAIAgent()`
8+
- Generating files through Code Interpreter (e.g., CSV, Excel, images)
9+
- Extracting container file citations from agent response annotations
10+
- Downloading container files using the `ContainerClient` via `AIProjectClient.GetProjectOpenAIClient()`
11+
12+
## Container files vs regular files
13+
14+
When Code Interpreter generates a file, the file is stored inside a **container** with a `cntr_` prefixed ID. The file itself gets a `cfile_` prefixed ID.
15+
16+
These container files **cannot** be downloaded using the standard Files API (`GetOpenAIFileClient`), which returns 404 for `cfile_` IDs. Instead, you must use the **Containers API** to download them.
17+
18+
### Getting the ContainerClient with Foundry
19+
20+
`AzureOpenAIClient.GetContainerClient()` is not supported and throws `InvalidOperationException`. Instead, use the project's OpenAI client which inherits directly from `OpenAI.OpenAIClient`:
21+
22+
```csharp
23+
// ❌ AzureOpenAIClient does not support ContainerClient
24+
var azureClient = new AzureOpenAIClient(endpoint, credential);
25+
azureClient.GetContainerClient(); // Throws InvalidOperationException
26+
27+
// ✅ Use AIProjectClient's project OpenAI client
28+
var containerClient = aiProjectClient.GetProjectOpenAIClient().GetContainerClient();
29+
await containerClient.DownloadContainerFileAsync("cntr_...", "cfile_...");
30+
```
31+
32+
The container ID and file ID are available from the `ContainerFileCitationMessageAnnotation` annotation in the response, accessible via `CitationAnnotation.RawRepresentation`.
33+
34+
## Prerequisites
35+
36+
- .NET 10 SDK or later
37+
- Microsoft Foundry service endpoint and deployment configured
38+
- Azure CLI installed and authenticated (`az login`)
39+
40+
Set the following environment variables:
41+
42+
```powershell
43+
$env:AZURE_AI_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project"
44+
$env:AZURE_AI_MODEL_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
45+
```
46+
47+
## Run the sample
48+
49+
```powershell
50+
dotnet run
51+
```
52+
53+
## See also
54+
55+
- [Code Interpreter File Download with OpenAI](../../../02-agents/AgentWithOpenAI/Agent_OpenAI_Step06_CodeInterpreterFileDownload/) — same scenario using Public OpenAI
56+
- [Code Interpreter](../Agent_Step14_CodeInterpreter/) — Code Interpreter without file download

dotnet/samples/02-agents/AgentsWithFoundry/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ Some samples require extra tool-specific environment variables. See each sample
7272
| [Web search](./Agent_Step21_WebSearch/) | Web search tool |
7373
| [Memory search](./Agent_Step22_MemorySearch/) | Memory search tool |
7474
| [Local MCP](./Agent_Step23_LocalMCP/) | Local MCP client with HTTP transport |
75+
| [Code interpreter file download](./Agent_Step24_CodeInterpreterFileDownload/) | Download container files generated by code interpreter |
7576

7677
## Running the samples
7778

0 commit comments

Comments
 (0)