Skip to content

Add live audio transcription streaming support to Foundry Local JS SDK#486

Open
rui-ren wants to merge 69 commits intomainfrom
ruiren/audio-streaming-support-sdk-js
Open

Add live audio transcription streaming support to Foundry Local JS SDK#486
rui-ren wants to merge 69 commits intomainfrom
ruiren/audio-streaming-support-sdk-js

Conversation

@rui-ren
Copy link
Copy Markdown

@rui-ren rui-ren commented Mar 5, 2026

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 AudioClient only supports file-based transcription. This PR introduces LiveAudioTranscriptionClient that 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 with start(), pushAudioData(), getTranscriptionStream(), stop(), dispose()
  • src/openai/liveAudioTranscriptionTypes.tsLiveAudioTranscriptionResult and CoreErrorResponse interfaces, tryParseCoreError() helper

Modified files

  • src/imodel.ts — Added createLiveTranscriptionClient() to interface
  • src/model.ts — Delegates to selectedVariant.createLiveTranscriptionClient()
  • src/modelVariant.ts — Implementation (creates new LiveAudioTranscriptionClient(modelId, coreInterop))
  • src/index.ts — Exports LiveAudioTranscriptionClient, LiveAudioTranscriptionSettings, LiveAudioTranscriptionResult, CoreErrorResponse

API surface

const audioClient = model.createAudioClient();
const session = model.createLiveTranscriptionClient();

session.settings.sampleRate = 16000;
session.settings.channels = 1;
session.settings.language = "en";

await session.start();

// Push audio from microphone callback
await session.pushAudioData(pcmBytes);

// Read results as async iterable
for await (const result of session.getTranscriptionStream()) {
    console.log(result.text);
}

await session.stop();

Design highlights

  • Internal async push queue — Bounded AsyncQueue<T> serializes audio pushes from any context (safe for mic callbacks) and provides backpressure. Mirrors C#'s Channel<T> pattern.
  • Retry policy — Transient native errors retried with exponential backoff (3 attempts); permanent errors terminate the session
  • Settings freeze — Audio format settings are snapshot-copied and Object.freeze()d at start(), immutable during the session
  • Buffer copypushAudioData() copies the input Uint8Array before queueing, safe when caller reuses buffers
  • Drain-on-stopstop() completes the push queue, waits for the push loop to drain, then calls native stop
  • Dispose safetydispose() wraps stop() in try/catch, never throws

Native 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 existing execute_command / execute_command_with_binary exports. The code compiles with zero TypeScript errors without the native library.

Testing

  • ✅ TypeScript compilation — 0 errors across all source files
  • ⏳ Integration tests pending native core delivery

Parity with C# SDK

This implementation mirrors the C# LiveAudioTranscriptionSession (branch ruiren/audio-streaming-support-sdk) with identical logic:

  • Same session lifecycle: startpushgetStreamstop
  • Same push loop with retry and permanent error handling
  • Same settings freeze and buffer copy semantics
  • Same drain-before-stop ordering
  • Same renamed types: LiveAudioTranscription* (matching C# rename)

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
foundry-local Ready Ready Preview, Comment Mar 31, 2026 4:22am

Request Review

@rui-ren rui-ren changed the title Add real-time audio streaming support (Microphone ASR) - JS Add live audio transcription streaming support to Foundry Local JS SDK Mar 13, 2026
@rui-ren rui-ren requested a review from prathikr March 16, 2026 21:47
ruiren_microsoft and others added 11 commits March 17, 2026 20:42
… 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
Copilot AI review requested due to automatic review settings March 24, 2026 18:58
ruiren_microsoft added 6 commits March 26, 2026 09:29
…g-support-sdk

# Conflicts:
#	sdk/js/test/openai/chatClient.test.ts
…treaming-support-sdk-js

# Conflicts:
#	sdk/js/test/openai/chatClient.test.ts
#	sdk/rust/build.rs
ruiren_microsoft and others added 2 commits March 27, 2026 14:24
…ionItem pattern (#561)

### Description

Redesigns `LiveAudioTranscriptionResponse` to follow the OpenAI Realtime
API's `ConversationItem` shape, enabling forward compatibility with a
future WebSocket-based architecture.

**Motivation:**
- Customers using OpenAI's Realtime API access transcription via
`result.content[0].transcript`
- By adopting this pattern now, customers who write
`result.Content[0].Text` won't need to change their code when we migrate
to WebSocket transport
- Aligns with the team's plan to move toward OpenAI Realtime API
compatibility

**Before:**
```csharp
// Extended AudioCreateTranscriptionResponse from Betalgo
await foreach (var result in session.GetTranscriptionStream())
{
    Console.Write(result.Text);           // inherited from base
    bool final = result.IsFinal;          // custom field
    var segments = result.Segments;       // inherited from base
}
```

**After:**
```csharp
// Own type shaped like OpenAI Realtime ConversationItem
await foreach (var result in session.GetTranscriptionStream())
{
    Console.Write(result.Content[0].Text);       // ConversationItem pattern
    Console.Write(result.Content[0].Transcript); // alias for Text (Realtime compat)
    bool final = result.IsFinal;
    double? start = result.StartTime;
}
```

**Changes:**

| File | Change |
|------|--------|
| LiveAudioTranscriptionTypes.cs | Removed
`AudioCreateTranscriptionResponse` inheritance. New standalone
`LiveAudioTranscriptionResponse` with `Content` list + new
`TranscriptionContentPart` type |
| LiveAudioTranscriptionClient.cs | Updated text checks: `.Text` →
`.Content?[0]?.Text` |
| JsonSerializationContext.cs | Registered `TranscriptionContentPart`,
removed `AudioCreateTranscriptionResponse.Segment` |
| LiveAudioTranscriptionTests.cs | Updated assertions to match new type
shape |
| Program.cs (sample) | Updated result reading to
`result.Content?[0]?.Text` |
| README.md | Updated docs and output type table |

**Key design decisions:**
- `TranscriptionContentPart` has both `Text` and `Transcript` (set to
the same value) for maximum compatibility with both Whisper and Realtime
API patterns
- `StartTime`/`EndTime` are top-level on the response (not nested in
Segments) — simpler access, maps to Realtime's
`audio_start_ms`/`audio_end_ms`
- No dependency on Betalgo's `ConversationItem` — we own the type to
avoid carrying unused chat/tool-calling fields
- `LiveAudioTranscriptionRaw` (Core JSON deserialization) is unchanged —
this is purely an SDK presentation change, no Core/neutron-server impact

**No breaking changes to:** Core API, native interop, audio pipeline,
session lifecycle

---------

Co-authored-by: ruiren_microsoft <ruiren@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants