.NET: Add Code Interpreter container file download samples#5014
.NET: Add Code Interpreter container file download samples#5014chetantoshniwal wants to merge 1 commit intomicrosoft:mainfrom
Conversation
chetantoshniwal
left a comment
There was a problem hiding this comment.
Automated Code Review
Reviewers: 4 | Confidence: 86%
✓ Correctness
The PR adds a thin extension method on OpenAIClient to download container files, plus comprehensive input-validation unit tests. The implementation is clean: argument guards use the shared Throw helper consistently, the CancellationToken is forwarded correctly, the namespace follows existing conventions (OpenAI extensions live in the OpenAI namespace), and the [Experimental] attribute matches other extension classes. The tests correctly expect ArgumentNullException for null inputs and ArgumentException for empty/whitespace, matching the Throw.IfNullOrWhitespace behavior. No correctness issues found.
✓ Security Reliability
This PR adds a clean extension method for downloading OpenAI container files, with proper input validation via the established Throw helpers, correct CancellationToken propagation, and ConfigureAwait(false) for library code. The method follows the same patterns used by other extension methods in the project (e.g., OpenAIResponseClientExtensions). Tests cover all guard-clause branches. No security or reliability concerns were identified — there are no injection risks, resource leaks, secrets, or unhandled failure modes.
✗ Test Coverage
The test file thoroughly covers all input-validation guard clauses (null, empty, and whitespace for both containerId and fileId, plus null client), which is good. However, there is no happy-path test verifying that when valid arguments are supplied, the method actually delegates to
client.GetContainerClient().DownloadContainerFileAsync(...)with the correct arguments and propagates the CancellationToken. The tests only exercise the threeThrow.*lines (lines 39-41 of the production code) and never reach the actual business logic on lines 43-44. This is a significant gap: without a happy-path test, a regression that breaks the delegation or drops the CancellationToken would go undetected.
✓ Design Approach
The extension follows the established pattern in this codebase (thin validated wrapper over an SDK method, placed in the SDK's namespace) and the implementation itself is sound. The only meaningful gap is the complete absence of a happy-path test: all seven tests exercise pre-condition validation that fires before the method reaches
GetContainerClient().DownloadContainerFileAsync(), so the actual delegation logic—the one line of real behaviour—is entirely untested. If the underlying SDK method is mockable or the OpenAIClient subclass approach already used in the test file can be extended to stub the container client, a success-case test should be added.
Flagged Issues
- No happy-path test: the core behavior of the method — obtaining a ContainerClient and calling DownloadContainerFileAsync with the correct containerId, fileId, and CancellationToken (lines 43-44) — is completely untested. All seven tests only cover argument-validation guard clauses.
Suggestions
- Add a happy-path test that extends the existing TestOpenAIClient stub (similar to the pattern in OpenAIAssistantClientExtensionsTests) to intercept GetContainerClient().DownloadContainerFileAsync, then verify that the extension correctly delegates with the expected containerId, fileId, and CancellationToken and returns the expected ClientResult.
- Consider adding a test that verifies CancellationToken propagation — e.g., passing a pre-cancelled token and asserting it is forwarded to the underlying SDK call.
Automated review by chetantoshniwal's agents
.../Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIClientContainerFileExtensionsTests.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Adds a .NET extension on OpenAIClient to download Code Interpreter container-scoped files (cfile_ within cntr_) via the Containers API, addressing the inability to download these files through the standard Files API.
Changes:
- Added
OpenAIClientContainerFileExtensions.DownloadContainerFileAsyncto route downloads throughGetContainerClient(). - Added unit tests covering null/empty/whitespace argument guards for
client,containerId, andfileId.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIClientContainerFileExtensions.cs | New OpenAIClient extension method to download container-scoped cfile_ outputs via Containers API. |
| dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIClientContainerFileExtensionsTests.cs | New unit tests validating argument guard behavior for the new extension method. |
dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIClientContainerFileExtensions.cs
Outdated
Show resolved
Hide resolved
dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIClientContainerFileExtensions.cs
Outdated
Show resolved
Hide resolved
dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIClientContainerFileExtensions.cs
Outdated
Show resolved
Hide resolved
.../Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIClientContainerFileExtensionsTests.cs
Outdated
Show resolved
Hide resolved
.../Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/OpenAIClientContainerFileExtensionsTests.cs
Outdated
Show resolved
Hide resolved
- 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
2eb8f78 to
794ef85
Compare
| foreach (ChatMessage message in response.Messages) | ||
| { | ||
| foreach (AIContent content in message.Contents) | ||
| { | ||
| if (content is TextContent textContent) | ||
| { | ||
| Console.WriteLine(textContent.Text); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Suggestion
| 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); | |
| } |
| foreach (ChatMessage message in response.Messages) | ||
| { | ||
| foreach (AIContent content in message.Contents) |
There was a problem hiding this comment.
This could also be flattened into foreach (ChatMessage message in response.Messages.SelectMany(x => x.Contents))
Motivation and Context
Azure OpenAI Responses API Code Interpreter generates files with
cfile_IDs that live inside containers (cntr_IDs). The standard Files API (GetOpenAIFileClient) cannot download these container-scoped files, leaving users unable to retrieve Code Interpreter output.Fixes #3081
Description
Revised approach: Instead of adding a wrapper extension method (which is not our responsibility — the OpenAI SDK already provides
ContainerClientdirectly), this PR adds two end-to-end samples that demonstrate the correct pattern for downloading Code Interpreter generated files.What changed
OpenAIClientContainerFileExtensionsand its unit tests — the OpenAI SDK already providesOpenAIClient.GetContainerClient()andContainerClient.DownloadContainerFileAsync()natively.Agent_OpenAI_Step06_CodeInterpreterFileDownload— Public OpenAI sample usingOpenAIClient→GetResponsesClient().AsAIAgent()with Code Interpreter → downloads container files viaopenAIClient.GetContainerClient().Agent_Step24_CodeInterpreterFileDownload— Microsoft Foundry sample usingAIProjectClient.AsAIAgent()withHostedCodeInterpreterTool→ downloads viaaiProjectClient.GetProjectOpenAIClient().GetContainerClient().Key finding
AzureOpenAIClient.GetContainerClient()throwsInvalidOperationException, butAIProjectClient.GetProjectOpenAIClient()returns aProjectOpenAIClientwhich inherits fromOpenAI.OpenAIClient(notAzureOpenAIClient), soGetContainerClient()works on it. This is the correct path for Foundry users.Container files vs regular files
The samples and their READMEs explain the difference:
cfile_IDs and live insidecntr_containersGetOpenAIFileClient) returns 404 for these IDsContainerClient(GetContainerClient()) to download themContainerIdandFileIdare available fromContainerFileCitationMessageAnnotationviaCitationAnnotation.RawRepresentationVerified end-to-end
Both samples were tested against real services and successfully downloaded generated CSV files:
multiplication_times_table.csv(1378 bytes)multiplication_times_tables_1_to_12.csv(498 bytes)Contribution Checklist