Fix: Propagate native errors from ExecuteCommandWithCallbackAsync in streaming paths#466
Fix: Propagate native errors from ExecuteCommandWithCallbackAsync in streaming paths#466
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…ient and AudioClient streaming paths" - Capture Response from ExecuteCommandWithCallbackAsync and propagate Response.Error as FoundryLocalException - Add internal constructors to OpenAIChatClient and OpenAIAudioClient for dependency injection in tests - Extend Utils.CreateCoreInteropWithIntercept to also mock ExecuteCommandWithCallbackAsync - Add ChatClientStreamingErrorTests and AudioClientStreamingErrorTests unit test classes Co-authored-by: baijumeswani <12852605+baijumeswani@users.noreply.github.com>
|
@copilot can you please address the pipeline failures? |
TUnit 0.63.3 doesn't support the zero-argument ThrowsAsync<T>() generic overload that was used in ChatClientStreamingErrorTests and AudioClientStreamingErrorTests. Replace with the explicit try-catch + IsNotNull assertion pattern used elsewhere. Co-authored-by: baijumeswani <12852605+baijumeswani@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a bug where streaming methods in ChatClient and AudioClient were not checking the Response.Error field returned by ExecuteCommandWithCallbackAsync. When the native layer reported an error without invoking any callbacks, the streaming methods would silently complete with an empty stream instead of propagating the error to the caller.
Changes:
- Fixed error propagation in streaming methods by capturing and checking
Response.ErrorfromExecuteCommandWithCallbackAsync - Added internal constructors with dependency injection support to enable unit testing
- Added comprehensive unit tests for error propagation scenarios in streaming methods
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk_v2/cs/src/OpenAI/ChatClient.cs | Added DI constructor; captured response from ExecuteCommandWithCallbackAsync; added error check to complete channel with exception when response.Error is set |
| sdk_v2/cs/src/OpenAI/AudioClient.cs | Added DI constructor; captured response from ExecuteCommandWithCallbackAsync; added error check to complete channel with exception when response.Error is set |
| sdk_v2/cs/test/FoundryLocal.Tests/Utils.cs | Extended CreateCoreInteropWithIntercept to mock ExecuteCommandWithCallbackAsync for pass-through behavior |
| sdk_v2/cs/test/FoundryLocal.Tests/ChatClientStreamingErrorTests.cs | New unit tests verifying ChatClient streaming properly propagates native errors via exceptions |
| sdk_v2/cs/test/FoundryLocal.Tests/AudioClientStreamingErrorTests.cs | New unit tests verifying AudioClient streaming properly propagates native errors via exceptions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
sdk_v2/cs/src/OpenAI/ChatClient.cs
Outdated
| if (response.Error != null) | ||
| { | ||
| channel.Writer.TryComplete( | ||
| new FoundryLocalException($"Error from chat_completions command: {response.Error}", _logger)); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Consider checking the failed flag before attempting to complete the channel with response.Error. If a callback exception has already completed the channel (failed=true), attempting to complete it again with response.Error could be confusing. While TryComplete makes it safe, checking if (!failed && response.Error != null) would make the intent clearer and avoid redundant error handling.
There was a problem hiding this comment.
Done in 6db84d7. Added !failed && guard and failed = true after completing the channel with the error — now matches the pattern already used in AudioClient.cs.
sdk_v2/cs/test/FoundryLocal.Tests/ChatClientStreamingErrorTests.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Align ChatClient.cs with AudioClient.cs: only complete the channel with response.Error if a callback exception has not already closed it, making the intent clearer and consistent across both streaming clients. Co-authored-by: baijumeswani <12852605+baijumeswani@users.noreply.github.com>
|
Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
ChatStreamingImplAsyncinChatClient.csto capture and checkResponse.ErrorfromExecuteCommandWithCallbackAsyncTranscribeAudioStreamingImplAsyncinAudioClient.csto capture and checkResponse.ErrorfromExecuteCommandWithCallbackAsync!failedguard beforeresponse.Errorcheck in bothChatClient.csandAudioClient.csfor clarity and consistencyOpenAIChatClientfor dependency injection in testsOpenAIAudioClientfor dependency injection in testsUtils.CreateCoreInteropWithInterceptto also mockExecuteCommandWithCallbackAsyncChatClientstreaming (ChatClientStreamingErrorTests.cs)AudioClientstreaming (AudioClientStreamingErrorTests.cs)ThrowsAsync<T>()calls with try-catch +IsNotNullassertionOriginal prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.