Add live audio transcription streaming support to Foundry Local JS SDK#486
Add live audio transcription streaming support to Foundry Local JS SDK#486
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
… API, sample restructure (#538) Resolves all 23 review comments on the live audio transcription PR (`ruiren/audio-streaming-support-sdk`), including merge conflict resolution. Covers namespace fixes, a removed-but-needed public method, test file restoration, and sample reorganization. ## SDK fixes (`sdk_v2/cs/src/`) - **`OpenAI/AudioClient.cs`**: Restored `TranscribeAudioStreamingAsync` public method — was accidentally removed; `AudioTranscriptionExample` depends on it - **`OpenAI/LiveAudioTranscriptionClient.cs`** + **`LiveAudioTranscriptionTypes.cs`**: Changed namespace `Microsoft.AI.Foundry.Local` → `Microsoft.AI.Foundry.Local.OpenAI` (consistent with `ToolCallingExtensions.cs`, `AudioTranscriptionRequestResponseTypes.cs`); added required `using Microsoft.AI.Foundry.Local;` - **`OpenAI/LiveAudioTranscriptionClient.cs`**: Removed unused `using System.Runtime.InteropServices` (would fail build with `TreatWarningsAsErrors=true`); fixed XML doc `PushAudioAsync` → `AppendAsync`; removed leftover `#pragma warning disable` directives; cleaned up double blank lines - **`OpenAI/LiveAudioTranscriptionTypes.cs`**: Removed `Confidence` property — not populated by any code path - **`AssemblyInfo.cs`**: Removed `InternalsVisibleTo("AudioStreamTest")` — local dev artifact, not for shipped SDK ## Test fix (`sdk_v2/cs/test/`) - **`Utils.cs`**: Restored original `Microsoft.AI.Foundry.Local.Tests.Utils` class from main — file was completely overwritten with a top-level executable test script, breaking all existing tests that reference `Utils.CoreInterop`, `Utils.IsRunningInCI`, etc. ## Sample restructure (`samples/cs/`) - Removed standalone `samples/cs/LiveAudioTranscription/` (csproj, Program.cs, README) - Added `samples/cs/GettingStarted/src/LiveAudioTranscriptionExample/Program.cs` — follows `HelloFoundryLocalSdk` pattern using `Utils.GetAppLogger()`, `Utils.RunWithSpinner()`, `catalog.GetModelAsync()`; removed hardcoded DLL paths, model cache dir override, `BitsPerSample=16` (property doesn't exist), and debug diagnostics - Added cross-platform and Windows `.csproj` files under `GettingStarted/cross-platform/` and `GettingStarted/windows/` matching the structure of `AudioTranscriptionExample` > [!WARNING] > > <details> > <summary>Firewall rules blocked me from connecting to one or more addresses (expand for details)</summary> > > #### I tried to connect to the following addresses, but was blocked by firewall rules: > > - `0t3vsblobprodcus362.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/B2063432E236EB2499F756DC7AEAC028/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force ng/emptyFakeDotnetRoot ing/emptyFakeDotnetRoot` (dns block) > - `1javsblobprodcus364.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/Microsoft.AI.Foundry.Local.SDK.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/CDD8923456756250B6AF4E42CA6F8DFB/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force ng/emptyFakeDotnetRoot ing/emptyFakeDotnetRoot` (dns block) > - `1s1vsblobprodcus386.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/EFEB4E95C962CAA7DA01DE9B7C9E5F4D/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force` (dns block) > - `4zjvsblobprodcus390.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/EFEB4E95C962CAA7DA01DE9B7C9E5F4D/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/79820580DC01B1F2024CE1D67DCA3751/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force ng/emptyFakeDotnetRoot ing/emptyFakeDotnetRoot` (dns block) > - `51yvsblobprodcus36.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/Microsoft.AI.Foundry.Local.SDK.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/CDD8923456756250B6AF4E42CA6F8DFB/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force ng/emptyFakeDotnetRoot ing/emptyFakeDotnetRoot` (dns block) > - `80zvsblobprodcus35.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/EFEB4E95C962CAA7DA01DE9B7C9E5F4D/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force` (dns block) > - `aiinfra.pkgs.visualstudio.com` > - Triggering command: `/opt/hostedtoolcache/CodeQL/2.24.3/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp /opt/hostedtoolcache/CodeQL/2.24.3/x64/codeql/csharp/tools/linux64/Semmle.Autobuild.CSharp` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/samples/cs/GettingStarted/cross-platform/FoundrySamplesXPlatform.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/samples/cs/GettingStarted/cross-platform/AudioTranscriptionExample/AudioTranscriptionExample.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - `c50vsblobprodcus330.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/Microsoft.AI.Foundry.Local.SDK.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - `frdvsblobprodcus327.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - `i1qvsblobprodcus353.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/Microsoft.AI.Foundry.Local.SDK.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - `imzvsblobprodcus368.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/Microsoft.AI.Foundry.Local.SDK.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - `k0ivsblobprodcus356.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/B2063432E236EB2499F756DC7AEAC028/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force ng/emptyFakeDotnetRoot ing/emptyFakeDotnetRoot` (dns block) > - `kxqvsblobprodcus376.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/Microsoft.AI.Foundry.Local.SDK.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - `m16vsblobprodcus374.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/EFEB4E95C962CAA7DA01DE9B7C9E5F4D/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force` (dns block) > - `s8mvsblobprodcus38.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/Microsoft.AI.Foundry.Local.SDK.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/EFEB4E95C962CAA7DA01DE9B7C9E5F4D/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force` (dns block) > - `se1vsblobprodcus349.vsblob.vsassets.io` > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/Microsoft.AI.Foundry.Local.SDK.sln --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Foundry-Local/Foundry-Local/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true` (dns block) > - Triggering command: `/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/63E6685CBF8FE43B2889F9BB97016C00/missingpackages_workingdir --packages /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-1a696f058c3bb324/dbs/csharp/working/nugetconfig/nuget.config --force` (dns block) > > If you need me to access, download, or install something from one of these locations, you can either: > > - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled > - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/microsoft/Foundry-Local/settings/copilot/coding_agent) (admins only) > > </details> <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> ## Context PR #485 (branch `ruiren/audio-streaming-support-sdk` targeting `main`) in microsoft/Foundry-Local adds live audio transcription streaming support to the Foundry Local C# SDK. It currently has merge conflicts with `main` and 23 review comments from Copilot bot and @kunal-vaishnavi that all need to be resolved. ## Task 1: Merge main branch and resolve conflicts The PR's `mergeable_state` is "dirty". Merge `main` into `ruiren/audio-streaming-support-sdk` and resolve all conflicts, ensuring the PR author's new code is preserved while incorporating any changes from main. ## Task 2: Resolve ALL of the following review comments ### SDK Source Code Fixes: 1. **`sdk/cs/src/Detail/JsonSerializationContext.cs`**: The file is in namespace `Microsoft.AI.Foundry.Local.Detail` but references `LiveAudioTranscriptionResult` and `CoreErrorResponse` which will be in namespace `Microsoft.AI.Foundry.Local.OpenAI` (see fix #8 below). Add a `using Microsoft.AI.Foundry.Local.OpenAI;` statement (this using may already exist from main, just ensure the types resolve correctly after the namespace change). 2. **`sdk/cs/src/OpenAI/AudioClient.cs`**: The public `TranscribeAudioStreamingAsync(...)` method was removed in the PR but the private `TranscribeAudioStreamingImplAsync(...)` still exists. **Restore the public `TranscribeAudioStreamingAsync` method** that wraps the private impl. This is used by speech-to-text models like Whisper and must NOT be removed. The original version from main is: ```csharp public async IAsyncEnumerable<AudioCreateTranscriptionResponse> TranscribeAudioStreamingAsync( string audioFilePath, [EnumeratorCancellation] CancellationToken ct) { var enumerable = Utils.CallWithExceptionHandling( () => TranscribeAudioStreamingImplAsync(audioFilePath, ct), "Error during streaming audio transcription.", _logger).ConfigureAwait(false); await foreach (var item in enumerable) { yield return item; } } ``` 3. **`sdk/cs/src/OpenAI/LiveAudioTranscriptionClient.cs`**: - Remove `using System.Runtime.InteropServices;` — it is unused and `TreatWarningsAsErrors=true` means this will cause CS8019 build failure. - Fix the XML doc comment that says "Thread safety: PushAudioAsync can be called from any thread" — change it to reference `AppendAsync` instead of `PushAudioAsync`. - Remove `#pragma warning disable` directives if they are not necessary. The reviewer asked why they're needed — they appear to be from development and should be removed for a clean PR. 4. **`sdk/cs/src/OpenAI/LiveAudioTranscriptionTypes.cs`**: - Change namespace from `Microsoft.AI.Foundry.Local` to `Microsoft.AI.Foundry.Local.OpenAI` (since the file is in the OpenAI folder, it should match the folder-based namespace convention used by the rest of the codebase). - Remove the `Confidence` property from `LiveAudioTranscriptionResult` if it is not being calculated/populated. The reviewer asked and it appears not to be calculated. 5. **`sdk/cs/src/OpenAI/LiveAudioTranscriptionClient.cs`**: - Also change namespace from `Microsoft.AI.Foundry.Local` to `Microsoft.AI.Foundry.Local.OpenAI` (same reason as above — the file is in the OpenAI folder). 6. **`sdk/cs/src/Microsoft.AI.Foundry.Local.csproj`**: Remove the `InternalsVisibleTo("AudioStreamTest")` attribute/assembly attribute. This was only needed for local experimentation and should not be in the shipped SDK. 7. **Remove trailing blank lines** in any files that have extra trailing blank lines added by this PR. ### Test File Fix: 8. **`sdk/cs/test/FoundryLocal.Tests/Utils.cs`**: This file was completely rewritten in the PR with top-level executable code and a hardcoded Core DLL path. It must be **restored to its original content from main**. The original file defines the `Microsoft.AI.Foundry.Local.Tests.Utils` helper class with `TestCatalogInfo`, `AssemblyInit`, `CoreInterop`, `CreateCapturingLoggerMock`, `CreateCoreInteropWithIntercept`, `IsRunningInCI`, `BuildTestCatalog`, `GetRepoRoot` etc. Multiple tests reference `Utils.*` (e.g., `Utils.CoreInterop`, `Utils.IsRunningInCI`), so the test project won't compile without it. Restore it to match the version on `main` exactly. ### Sample Restructuring: 9. **Move the sample from `samples/cs/LiveAudioTranscription/`** to `samples/cs/GettingStarted/src/LiveAudioTranscriptionExample/`. The sample Program.cs should be placed there. 10. **Remove the standalone `samples/cs/LiveAudioTranscription/` directory** entirely (including the README.md in it — reviewer says it's good for internal docs but these samples are public-facing, and the existing GettingStarted README covers it). 11. **Create cross-platform `.csproj`** at `samples/cs/GettingStarted/cross-platform/LiveAudioTranscriptionExample/LiveAudioTranscriptionExample.csproj` following the format of the existing cross-platform AudioTranscriptionExample: ```xml <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe<... </details> <!-- START COPILOT CODING AGENT SUFFIX --> *This pull request was created from Copilot chat.* > <!-- START COPILOT CODING AGENT TIPS --> --- 🔒 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.](https://gh.io/cca-advanced-security) --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rui-ren <15321482+rui-ren@users.noreply.github.com>
…dio-streaming-support-sdk-js # Conflicts: # sdk/js/src/openai/liveAudioTranscriptionClient.ts # sdk/js/src/openai/liveAudioTranscriptionTypes.ts # sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj # sdk_v2/js/src/index.ts
There was a problem hiding this comment.
Pull request overview
This PR introduces live (microphone-style) audio transcription streaming support, adding a new streaming client to the JS SDK and a parallel end-to-end implementation in the C# SDK (interop, types, tests, and samples). It also bumps Foundry Local Core package versions to 0.9.0 in JS install scripts and C# projects.
Changes:
- JS: Add
LiveAudioTranscriptionClient/types and wire acreateLiveTranscriptionClient()factory throughIModel/Model/ModelVariant, plus export from the package entrypoint. - C#: Add
LiveAudioTranscriptionSession+ types, CoreInterop binary push plumbing, tests, docs, and sample projects. - Versioning: Update Foundry Local Core package versions from
0.9.0.8-rc3to0.9.0in relevant JS/C# build/install locations.
Reviewed changes
Copilot reviewed 23 out of 25 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/js/src/openai/liveAudioTranscriptionClient.ts | New JS streaming transcription client implementation (session lifecycle, push queue, async stream). |
| sdk/js/src/openai/liveAudioTranscriptionTypes.ts | New JS result/error interfaces and error parsing helper. |
| sdk/js/src/imodel.ts | Adds createLiveTranscriptionClient() to the JS model interface. |
| sdk/js/src/model.ts | Exposes createLiveTranscriptionClient() on Model by delegating to the selected variant. |
| sdk/js/src/modelVariant.ts | Implements createLiveTranscriptionClient() for a specific model variant. |
| sdk/js/src/index.ts | Exports live transcription client/settings/types from the JS SDK entrypoint. |
| sdk/js/script/install.cjs | Bumps core artifact versions used by the JS install flow to 0.9.0. |
| sdk/cs/src/OpenAI/LiveAudioTranscriptionClient.cs | Adds C# live transcription session (push loop, stream results, lifecycle). |
| sdk/cs/src/OpenAI/LiveAudioTranscriptionTypes.cs | Adds C# response/error types + JSON mapping for streaming transcription. |
| sdk/cs/src/OpenAI/AudioClient.cs | Adds CreateLiveTranscriptionSession() to the C# audio client. |
| sdk/cs/src/OpenAI/ChatClient.cs | Adjusts streaming chat completion implementation (notably error propagation behavior). |
| sdk/cs/src/Detail/ICoreInterop.cs | Extends interop contract with audio streaming methods and a binary request buffer struct. |
| sdk/cs/src/Detail/CoreInterop.cs | Implements binary push via execute_command_with_binary and adds audio streaming helpers. |
| sdk/cs/src/Detail/JsonSerializationContext.cs | Registers streaming types for System.Text.Json source generation. |
| sdk/cs/src/Microsoft.AI.Foundry.Local.csproj | Bumps core package versions to 0.9.0. |
| sdk/cs/test/FoundryLocal.Tests/LiveAudioTranscriptionTests.cs | Adds unit tests for streaming types/options and session guards. |
| sdk/cs/test/FoundryLocal.Tests/Microsoft.AI.Foundry.Local.Tests.csproj | Adds a conditional package reference related to ORT Linux GPU package versioning. |
| sdk/cs/test/FoundryLocal.Tests/ModelTests.cs | Minor formatting adjustment (closing brace). |
| sdk/cs/README.md | Documents the new C# live audio transcription streaming API and lifecycle. |
| samples/cs/GettingStarted/src/LiveAudioTranscriptionExample/Program.cs | Adds a console sample showing microphone streaming transcription. |
| samples/cs/GettingStarted/windows/LiveAudioTranscriptionExample/LiveAudioTranscriptionExample.csproj | Adds Windows sample project configuration and dependencies (WinML + NAudio). |
| samples/cs/GettingStarted/cross-platform/LiveAudioTranscriptionExample/LiveAudioTranscriptionExample.csproj | Adds cross-platform sample project configuration and dependencies. |
| sdk_v2/js/src/index.ts | Adds a new sdk_v2/js entrypoint exporting the JS surface (including live transcription). |
| sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj | Adds/updates a sdk_v2/cs project file with core package version settings and dependencies. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** | ||
| * Structured error response from native core audio streaming commands. | ||
| * @internal | ||
| */ | ||
| export interface CoreErrorResponse { | ||
| /** Machine-readable error code. */ | ||
| code: string; | ||
| /** Human-readable error message. */ | ||
| message: string; | ||
| /** Whether this error is transient and may succeed on retry. */ | ||
| isTransient: boolean; | ||
| } |
There was a problem hiding this comment.
CoreErrorResponse is marked @internal but is exported from the package entrypoint (sdk/js/src/index.ts). Either remove the @internal tag (if it’s intended to be public API) or stop exporting it from the public surface to keep docs/API consistent.
| @@ -196,17 +196,6 @@ private async IAsyncEnumerable<ChatCompletionCreateResponse> ChatStreamingImplAs | |||
| ct | |||
| ).ConfigureAwait(false); | |||
|
|
|||
| // If the native layer returned an error (e.g. missing model, invalid input) | |||
| // without invoking any callbacks, propagate it so the caller sees an exception | |||
| // instead of an empty stream. | |||
| if (!failed && response.Error != null) | |||
| { | |||
| channel.Writer.TryComplete( | |||
| new FoundryLocalException($"Error from chat_completions command: {response.Error}", _logger)); | |||
| failed = true; | |||
| return; | |||
| } | |||
|
|
|||
| // use TryComplete as an exception in the callback may have already closed the channel | |||
| _ = channel.Writer.TryComplete(); | |||
| } | |||
There was a problem hiding this comment.
ExecuteCommandWithCallbackAsync returns a Response that can contain Error even when no callbacks were invoked. By discarding the returned response and removing the response.Error check, callers can now receive an empty stream instead of an exception on immediate native failures. Please restore the error propagation (or change ExecuteCommandWithCallbackAsync to throw when Error is set).
| * Creates a LiveAudioTranscriptionClient for real-time audio streaming ASR. | ||
| * @returns A LiveAudioTranscriptionClient instance. | ||
| */ | ||
| createLiveTranscriptionClient(): LiveAudioTranscriptionClient; | ||
| /** |
There was a problem hiding this comment.
The interface declares createLiveTranscriptionClient() twice with identical signatures/JSDoc. Even if TypeScript merges them, this is redundant and can lead to duplicated docs or confusing hover text; please keep only a single declaration (and consolidate the JSDoc).
| * Creates a LiveAudioTranscriptionClient for real-time audio streaming ASR. | |
| * @returns A LiveAudioTranscriptionClient instance. | |
| */ | |
| createLiveTranscriptionClient(): LiveAudioTranscriptionClient; | |
| /** |
| resolve({ value: item, done: false }); | ||
| return true; | ||
| } | ||
|
|
There was a problem hiding this comment.
tryWrite() ignores maxCapacity entirely and can grow the queue without bound even when the queue is meant to be bounded (pushQueueCapacity). If you keep tryWrite, it should return false when at capacity (or implement the same backpressure semantics as write).
| if (this.queue.length >= this.maxCapacity) { | |
| return false; | |
| } |
| try { | ||
| this.coreInterop.executeCommand("audio_stream_stop", { | ||
| Params: { SessionHandle: this.sessionHandle! } | ||
| }); |
There was a problem hiding this comment.
audio_stream_stop return value is ignored. If native core returns a final transcription payload on stop (as the C# implementation does), it should be parsed and delivered to consumers via outputQueue before completing the stream; otherwise callers may miss the final result.
sdk_v2/js/src/index.ts
Outdated
| export { FoundryLocalManager } from './foundryLocalManager.js'; | ||
| export type { FoundryLocalConfig } from './configuration.js'; | ||
| export { Catalog } from './catalog.js'; | ||
| export { Model } from './model.js'; | ||
| export { ModelVariant } from './modelVariant.js'; | ||
| export type { IModel } from './imodel.js'; | ||
| export { ChatClient, ChatClientSettings } from './openai/chatClient.js'; | ||
| export { AudioClient, AudioClientSettings } from './openai/audioClient.js'; | ||
| export { LiveAudioTranscriptionClient, LiveAudioTranscriptionSettings } from './openai/liveAudioTranscriptionClient.js'; | ||
| export type { LiveAudioTranscriptionResult, CoreErrorResponse } from './openai/liveAudioTranscriptionTypes.js'; | ||
| export { ResponsesClient, ResponsesClientSettings, getOutputText } from './openai/responsesClient.js'; |
There was a problem hiding this comment.
PR description focuses on the active JS SDK under sdk/js/..., but this change also adds a new sdk_v2/js/src/index.ts entrypoint (and includes substantial C# changes). If sdk_v2/js is still shipped, the description should mention it; if not, consider dropping this file to avoid maintaining two parallel entrypoints.
| this.coreInterop.executeCommand("audio_stream_push", { | ||
| Params: { | ||
| SessionHandle: this.sessionHandle!, | ||
| AudioDataLength: audioData.length.toString() | ||
| } | ||
| }); |
There was a problem hiding this comment.
audio_stream_push is invoked without sending the actual audioData bytes (only AudioDataLength is passed). With the current JS CoreInterop there is no execute_command_with_binary wrapper, so native core will never receive PCM and the stream will not function. Add a binary-capable CoreInterop method and use it here to pass the PCM buffer, then parse any returned transcription JSON and enqueue it onto outputQueue for getTranscriptionStream() to yield.
| this.coreInterop.executeCommand("audio_stream_push", { | |
| Params: { | |
| SessionHandle: this.sessionHandle!, | |
| AudioDataLength: audioData.length.toString() | |
| } | |
| }); | |
| const coreAny = this.coreInterop as any; | |
| const response = await coreAny.executeCommandWithBinary( | |
| "audio_stream_push", | |
| { | |
| Params: { | |
| SessionHandle: this.sessionHandle!, | |
| AudioDataLength: audioData.length.toString() | |
| } | |
| }, | |
| audioData | |
| ); | |
| if (typeof response === 'string' && response.trim().length > 0) { | |
| try { | |
| const parsed = JSON.parse(response) as LiveAudioTranscriptionResult; | |
| await this.outputQueue?.write(parsed); | |
| } catch (parseError) { | |
| const parseMsg = parseError instanceof Error ? parseError.message : String(parseError); | |
| console.error('Failed to parse transcription response from audio_stream_push:', parseMsg); | |
| } | |
| } |
| if (this.queue.length >= this.maxCapacity) { | ||
| await new Promise<void>((resolve) => { | ||
| this.backpressureResolve = resolve; | ||
| }); | ||
| } |
There was a problem hiding this comment.
AsyncQueue.write() backpressure is not safe with multiple concurrent writers: when at capacity, each writer overwrites backpressureResolve, so earlier writers can wait forever. Use a FIFO of pending writer resolvers (or reuse the existing streaming queue pattern used elsewhere in the JS SDK) so all blocked writers are eventually released.
| }); | ||
| } catch (error) { | ||
| const errorMsg = error instanceof Error ? error.message : String(error); | ||
| const errorInfo = tryParseCoreError(errorMsg); |
There was a problem hiding this comment.
tryParseCoreError(errorMsg) is very unlikely to succeed because CoreInterop.executeCommand() throws messages prefixed with "Command 'X' failed: ...", which is not valid JSON. Either change CoreInterop to throw the raw native error string (without prefix) or make tryParseCoreError robust (e.g., extract the JSON substring after the prefix) before attempting JSON.parse.
| const errorInfo = tryParseCoreError(errorMsg); | |
| const coreErrorMsg = extractCoreErrorMessage(errorMsg); | |
| const errorInfo = tryParseCoreError(coreErrorMsg); |
| console.error('Terminating push loop due to push failure:', errorMsg); | ||
| this.outputQueue?.complete(fatalError); |
There was a problem hiding this comment.
This library code writes directly to console.error/console.warn. The rest of the JS SDK avoids emitting console output; errors should be surfaced via exceptions/stream completion and let the host application decide how to log. Consider removing these calls or injecting a logger via configuration/settings.
Here's the updated PR description with the renamed types:
Title: Add live audio transcription streaming support to Foundry Local JS SDK
Description:
Adds real-time audio streaming support to the Foundry Local JS SDK, enabling live microphone-to-text transcription via ONNX Runtime GenAI ASR.
The existing
AudioClientonly supports file-based transcription. This PR introducesLiveAudioTranscriptionClientthat accepts continuous PCM audio chunks (e.g., from a microphone) and returns partial/final transcription results as an async iterable.What's included
New files
src/openai/liveAudioTranscriptionClient.ts— Streaming client withstart(),pushAudioData(),getTranscriptionStream(),stop(),dispose()src/openai/liveAudioTranscriptionTypes.ts—LiveAudioTranscriptionResultandCoreErrorResponseinterfaces,tryParseCoreError()helperModified files
src/imodel.ts— AddedcreateLiveTranscriptionClient()to interfacesrc/model.ts— Delegates toselectedVariant.createLiveTranscriptionClient()src/modelVariant.ts— Implementation (createsnew LiveAudioTranscriptionClient(modelId, coreInterop))src/index.ts— ExportsLiveAudioTranscriptionClient,LiveAudioTranscriptionSettings,LiveAudioTranscriptionResult,CoreErrorResponseAPI surface
Design highlights
AsyncQueue<T>serializes audio pushes from any context (safe for mic callbacks) and provides backpressure. Mirrors C#'sChannel<T>pattern.Object.freeze()d atstart(), immutable during the sessionpushAudioData()copies the inputUint8Arraybefore queueing, safe when caller reuses buffersstop()completes the push queue, waits for the push loop to drain, then calls native stopdispose()wrapsstop()in try/catch, never throwsNative core dependency
This PR adds the JS SDK surface. The 3 native commands (
audio_stream_start,audio_stream_push,audio_stream_stop) are routed through the existingexecute_command/execute_command_with_binaryexports. The code compiles with zero TypeScript errors without the native library.Testing
Parity with C# SDK
This implementation mirrors the C#
LiveAudioTranscriptionSession(branchruiren/audio-streaming-support-sdk) with identical logic:start→push→getStream→stopLiveAudioTranscription*(matching C# rename)