fix(macos): preserve audio when transcription fails

This commit is contained in:
dh
2026-09-01 11:55:15 +02:00
parent 68db6955a2
commit 84dc696ce9
6 changed files with 132 additions and 5 deletions
@@ -1378,6 +1378,45 @@ public sealed class RecordingCoordinatorTests
Assert.False(audioArchive.Deleted);
}
[Fact]
public async Task StopQueuesAzureBacklogAndRetainsAudioWhenTranscriptionFails()
{
var audioSource = new ControlledAudioSource();
var provider = new FailingAfterFirstAudioProvider();
var audioArchive = new InMemoryRecordedAudioStore();
var backlog = new InMemoryOfflineTranscriptionBacklog();
var coordinator = new MeetingRecordingCoordinator(
audioSource,
new TestSpeechRecognitionPipelineFactory(provider),
new InMemoryTranscriptStore(),
new InMemoryMeetingNoteStore(),
new CapturingMeetingNoteOpener(),
new InMemoryMeetingArtifactStore(),
audioArchive,
new CapturingMeetingSummaryPipeline(),
Options.Create(new MeetingAssistantOptions
{
Recording = new RecordingOptions
{
TranscriptionProvider = "azure-speech"
}
}),
NullLogger<MeetingRecordingCoordinator>.Instance,
offlineTranscriptionBacklog: backlog);
await coordinator.StartAsync(CancellationToken.None);
await audioSource.WriteAsync(new AudioChunk([1, 0], 16000, 1), CancellationToken.None);
await provider.WaitUntilFailureObservedAsync();
var stopped = await coordinator.StopAsync(CancellationToken.None);
Assert.False(stopped.IsRecording);
var item = Assert.Single(backlog.Items);
Assert.Equal("memory-recording.wav", item.AudioPath);
Assert.True(audioArchive.Completed);
Assert.False(audioArchive.Deleted);
}
[Fact]
public async Task OfflineBacklogReplaysQueuedRecordingAndCompletesMeetingArtifacts()
{
@@ -4988,6 +5027,31 @@ public sealed class RecordingCoordinatorTests
}
}
private sealed class FailingAfterFirstAudioProvider : IStreamingTranscriptionProvider
{
private readonly TaskCompletionSource failureObserved =
new(TaskCreationOptions.RunContinuationsAsynchronously);
public Task WaitUntilFailureObservedAsync()
{
return failureObserved.Task.WaitAsync(TimeSpan.FromSeconds(5));
}
public async IAsyncEnumerable<TranscriptionSegment> TranscribeAsync(
IAsyncEnumerable<AudioChunk> audio,
SpeechRecognitionPipelineOptions options,
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
{
await foreach (var _ in audio.WithCancellation(cancellationToken))
{
failureObserved.TrySetResult();
throw new InvalidOperationException("Configured transcription backend is unavailable.");
}
yield break;
}
}
private static byte[] Samples(params short[] samples)
{
var bytes = new byte[samples.Length * sizeof(short)];
@@ -4995,6 +5059,3 @@ public sealed class RecordingCoordinatorTests
return bytes;
}
}