Skip to content
Open
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
2 changes: 2 additions & 0 deletions dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step21_WebSearch/Agent_Step21_WebSearch.csproj" />
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step22_MemorySearch/Agent_Step22_MemorySearch.csproj" />
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step23_LocalMCP/Agent_Step23_LocalMCP.csproj" />
<Project Path="samples/02-agents/AgentsWithFoundry/Agent_Step24_CodeInterpreterFileDownload/Agent_Step24_CodeInterpreterFileDownload.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/AgentWithMemory/">
<File Path="samples/02-agents/AgentWithMemory/README.md" />
Expand All @@ -167,6 +168,7 @@
<Project Path="samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/Agent_OpenAI_Step03_CreateFromChatClient.csproj" />
<Project Path="samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient.csproj" />
<Project Path="samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Agent_OpenAI_Step05_Conversation.csproj" />
<Project Path="samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step06_CodeInterpreterFileDownload/Agent_OpenAI_Step06_CodeInterpreterFileDownload.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/AgentWithRAG/">
<File Path="samples/02-agents/AgentWithRAG/README.md" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>

<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

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

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

// This sample shows how to download files generated by Code Interpreter using the Containers API.
// Code Interpreter generates files inside containers (cfile_ / cntr_ IDs) which cannot be
// downloaded via the standard Files API. Use ContainerClient instead.

#pragma warning disable OPENAI001

using System.ClientModel;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Containers;
using OpenAI.Responses;

string apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new InvalidOperationException("OPENAI_API_KEY is not set.");
string model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini";

var openAIClient = new OpenAIClient(new ApiKeyCredential(apiKey));

// Create an agent with Code Interpreter tool enabled
AIAgent agent = openAIClient
.GetResponsesClient()
.AsAIAgent(
model: model,
instructions: "You are a helpful assistant that can generate files using code.",
name: "CodeInterpreterAgent",
tools: [new HostedCodeInterpreterTool()]);

// Ask the agent to generate a file
AgentResponse response = await agent.RunAsync(
"Create a CSV file with the multiplication times tables from 1 to 12. Include headers.");

// Display the text response
foreach (ChatMessage message in response.Messages)
{
foreach (AIContent content in message.Contents)
{
if (content is TextContent textContent)
{
Console.WriteLine(textContent.Text);
}
}
}

// Extract container file citations from response annotations and download
ContainerClient containerClient = openAIClient.GetContainerClient();

HashSet<string> downloadedFiles = [];
bool foundContainerFiles = false;

foreach (ChatMessage message in response.Messages)
{
foreach (AIContent content in message.Contents)
{
if (content.Annotations is null)
{
continue;
}

foreach (AIAnnotation annotation in content.Annotations)
{
// Container files from Code Interpreter have ContainerFileCitationMessageAnnotation as raw representation
if (annotation is CitationAnnotation citation
&& citation.RawRepresentation is ContainerFileCitationMessageAnnotation containerCitation)
{
foundContainerFiles = true;

// Deduplicate by container+file ID in case the same file is cited multiple times
string key = $"{containerCitation.ContainerId}/{containerCitation.FileId}";
if (!downloadedFiles.Add(key))
{
continue;
}

Console.WriteLine($"\nDownloading container file: {containerCitation.Filename}");
Console.WriteLine($" Container ID: {containerCitation.ContainerId}");
Console.WriteLine($" File ID: {containerCitation.FileId}");

BinaryData fileData = await containerClient.DownloadContainerFileAsync(
containerCitation.ContainerId,
containerCitation.FileId);

// Sanitize filename to prevent path traversal
string safeFilename = Path.GetFileName(containerCitation.Filename);
string outputPath = Path.Combine(Directory.GetCurrentDirectory(), safeFilename);
await File.WriteAllBytesAsync(outputPath, fileData.ToArray());
Console.WriteLine($" Saved to: {outputPath}");
}
}
}
}

if (!foundContainerFiles)
{
Console.WriteLine("\nNo container file citations found in the response.");
Console.WriteLine("The model may not have generated a downloadable file for this prompt.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Code Interpreter File Download (OpenAI)

This sample demonstrates how to download files generated by Code Interpreter when using the OpenAI Responses API.

## What this sample demonstrates

- Creating an agent with Code Interpreter tool using `ResponsesClient.AsAIAgent()`
- Generating files through Code Interpreter (e.g., CSV, Excel, images)
- Extracting container file citations from agent response annotations
- Downloading container files using the `ContainerClient` API

## Container files vs regular files

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.

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:

```csharp
// ❌ This does NOT work for container files
var filesClient = openAIClient.GetOpenAIFileClient();
await filesClient.DownloadFileAsync("cfile_..."); // Returns 404

// ✅ Use ContainerClient instead
var containerClient = openAIClient.GetContainerClient();
await containerClient.DownloadContainerFileAsync("cntr_...", "cfile_...");
```

The container ID and file ID are available from the `ContainerFileCitationMessageAnnotation` annotation in the response, accessible via `CitationAnnotation.RawRepresentation`.

## Prerequisites

- .NET 10 SDK or later
- OpenAI API key with access to a model that supports Code Interpreter

Set the following environment variables:

```powershell
$env:OPENAI_API_KEY="sk-..."
$env:OPENAI_CHAT_MODEL_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
```

## Run the sample

```powershell
dotnet run
```

## See also

- [Code Interpreter File Download with Foundry](../../../02-agents/AgentsWithFoundry/Agent_Step24_CodeInterpreterFileDownload/) — same scenario using Microsoft Foundry
- [Code Interpreter](../../../02-agents/AgentsWithFoundry/Agent_Step14_CodeInterpreter/) — Code Interpreter without file download
3 changes: 2 additions & 1 deletion dotnet/samples/02-agents/AgentWithOpenAI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Agent Framework provides additional support to allow OpenAI developers to use th
|[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.|
|[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.|
|[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.|
|[Managing Conversation State](./Agent_OpenAI_Step05_Conversation/)|This sample demonstrates how to maintain conversation state across multiple turns using the AgentSession for context continuity.|
|[Managing Conversation State](./Agent_OpenAI_Step05_Conversation/)|This sample demonstrates how to maintain conversation state across multiple turns using the AgentSession for context continuity.|
|[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 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.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,100 @@
// Copyright (c) Microsoft. All rights reserved.

// This sample shows how to download files generated by Code Interpreter using Microsoft Foundry.
// Code Interpreter generates files inside containers (cfile_ / cntr_ IDs) which cannot be
// downloaded via the standard Files API. Use ContainerClient from the project's OpenAI client instead.

#pragma warning disable OPENAI001

using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI.Responses;

string endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini";

// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential());

// Create an agent with Code Interpreter tool enabled
AIAgent agent = aiProjectClient.AsAIAgent(
deploymentName,
instructions: "You are a helpful assistant that can generate files using code.",
name: "CodeInterpreterAgent",
tools: [new HostedCodeInterpreterTool()]);

// Ask the agent to generate a file
AgentResponse response = await agent.RunAsync(
"Create a CSV file with the multiplication times tables from 1 to 12. Include headers.");

// Display the text response
foreach (ChatMessage message in response.Messages)
{
foreach (AIContent content in message.Contents)
{
if (content is TextContent textContent)
{
Console.WriteLine(textContent.Text);
}
}
}
Comment on lines +35 to +44
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion

Suggested change
foreach (ChatMessage message in response.Messages)
{
foreach (AIContent content in message.Contents)
{
if (content is TextContent textContent)
{
Console.WriteLine(textContent.Text);
}
}
}
foreach (TextContent textContent in response.Messages.SelectMany(x => x.Contents).OfType<TextContent>())
{
Console.WriteLine(textContent.Text);
}


// Extract container file citations from response annotations and download.
// AIProjectClient.GetProjectOpenAIClient() returns a ProjectOpenAIClient (inherits from OpenAI.OpenAIClient)
// which supports GetContainerClient(), unlike AzureOpenAIClient which does not.
var containerClient = aiProjectClient.GetProjectOpenAIClient().GetContainerClient();

HashSet<string> downloadedFiles = [];
bool foundContainerFiles = false;

foreach (ChatMessage message in response.Messages)
{
foreach (AIContent content in message.Contents)
Comment on lines +54 to +56
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be flattened into foreach (ChatMessage message in response.Messages.SelectMany(x => x.Contents))

{
if (content.Annotations is null)
{
continue;
}

foreach (AIAnnotation annotation in content.Annotations)
{
// Container files from Code Interpreter have ContainerFileCitationMessageAnnotation as raw representation
if (annotation is CitationAnnotation citation
&& citation.RawRepresentation is ContainerFileCitationMessageAnnotation containerCitation)
{
foundContainerFiles = true;

// Deduplicate by container+file ID in case the same file is cited multiple times
string key = $"{containerCitation.ContainerId}/{containerCitation.FileId}";
if (!downloadedFiles.Add(key))
{
continue;
}

Console.WriteLine($"\nDownloading container file: {containerCitation.Filename}");
Console.WriteLine($" Container ID: {containerCitation.ContainerId}");
Console.WriteLine($" File ID: {containerCitation.FileId}");

BinaryData fileData = await containerClient.DownloadContainerFileAsync(
containerCitation.ContainerId,
containerCitation.FileId);

// Sanitize filename to prevent path traversal
string safeFilename = Path.GetFileName(containerCitation.Filename);
string outputPath = Path.Combine(Directory.GetCurrentDirectory(), safeFilename);
await File.WriteAllBytesAsync(outputPath, fileData.ToArray());
Console.WriteLine($" Saved to: {outputPath}");
}
}
}
}

if (!foundContainerFiles)
{
Console.WriteLine("\nNo container file citations found in the response.");
Console.WriteLine("The model may not have generated a downloadable file for this prompt.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Code Interpreter File Download (Microsoft Foundry)

This sample demonstrates how to download files generated by Code Interpreter when using Microsoft Foundry.

## What this sample demonstrates

- Creating an agent with Code Interpreter tool using `AIProjectClient.AsAIAgent()`
- Generating files through Code Interpreter (e.g., CSV, Excel, images)
- Extracting container file citations from agent response annotations
- Downloading container files using the `ContainerClient` via `AIProjectClient.GetProjectOpenAIClient()`

## Container files vs regular files

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.

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.

### Getting the ContainerClient with Foundry

`AzureOpenAIClient.GetContainerClient()` is not supported and throws `InvalidOperationException`. Instead, use the project's OpenAI client which inherits directly from `OpenAI.OpenAIClient`:

```csharp
// ❌ AzureOpenAIClient does not support ContainerClient
var azureClient = new AzureOpenAIClient(endpoint, credential);
azureClient.GetContainerClient(); // Throws InvalidOperationException

// ✅ Use AIProjectClient's project OpenAI client
var containerClient = aiProjectClient.GetProjectOpenAIClient().GetContainerClient();
await containerClient.DownloadContainerFileAsync("cntr_...", "cfile_...");
```

The container ID and file ID are available from the `ContainerFileCitationMessageAnnotation` annotation in the response, accessible via `CitationAnnotation.RawRepresentation`.

## Prerequisites

- .NET 10 SDK or later
- Microsoft Foundry service endpoint and deployment configured
- Azure CLI installed and authenticated (`az login`)

Set the following environment variables:

```powershell
$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-4o-mini" # Optional, defaults to gpt-4o-mini
```

## Run the sample

```powershell
dotnet run
```

## See also

- [Code Interpreter File Download with OpenAI](../../../02-agents/AgentWithOpenAI/Agent_OpenAI_Step06_CodeInterpreterFileDownload/) — same scenario using Public OpenAI
- [Code Interpreter](../Agent_Step14_CodeInterpreter/) — Code Interpreter without file download
1 change: 1 addition & 0 deletions dotnet/samples/02-agents/AgentsWithFoundry/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Some samples require extra tool-specific environment variables. See each sample
| [Web search](./Agent_Step21_WebSearch/) | Web search tool |
| [Memory search](./Agent_Step22_MemorySearch/) | Memory search tool |
| [Local MCP](./Agent_Step23_LocalMCP/) | Local MCP client with HTTP transport |
| [Code interpreter file download](./Agent_Step24_CodeInterpreterFileDownload/) | Download container files generated by code interpreter |

## Running the samples

Expand Down
Loading