fix: unify pyannote validation toggle

This commit is contained in:
2026-09-02 13:46:51 +02:00
parent 70ffaa7357
commit b1365b202d
14 changed files with 329 additions and 81 deletions
+7 -5
View File
@@ -183,10 +183,8 @@ public sealed class WhisperLocalOptions
public PyannoteDiarizationOptions Diarization { get; set; } = new();
}
public sealed class PyannoteDiarizationOptions
public class PyannoteRuntimeOptions
{
public bool Enabled { get; set; } = true;
public string DockerCommand { get; set; } = "docker";
public string BaseImage { get; set; } = "python:3.11-slim";
@@ -210,6 +208,11 @@ public sealed class PyannoteDiarizationOptions
public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromMinutes(30);
}
public sealed class PyannoteDiarizationOptions : PyannoteRuntimeOptions
{
public bool Enabled { get; set; } = true;
}
public sealed class AzureSpeechOptions
{
public string Endpoint { get; set; } = "";
@@ -367,9 +370,8 @@ public sealed class SpeakerIdentityPyannoteValidationOptions
public double MinimumMatchingKnownSnippetRatio { get; set; } = 0.50;
public PyannoteDiarizationOptions Diarization { get; set; } = new()
public PyannoteRuntimeOptions Diarization { get; set; } = new()
{
Enabled = true,
CommandTimeout = TimeSpan.FromHours(1),
AlignmentMode = PyannoteAlignmentMode.PyannoteTurns
};
@@ -190,7 +190,7 @@ public sealed class PyannoteSpeakerIdentityMatchValidator : ISpeakerIdentityMatc
return [];
}
return await finalizer.FinalizeAsync(
return await finalizer.FinalizeEnabledAsync(
wavPath,
[new TranscriptionSegment(TimeSpan.Zero, duration, "Unknown", "speaker identity validation sample")],
options.Diarization,
@@ -96,14 +96,18 @@ public sealed class PyannoteDiarizationWarmupHostedService : IHostedService
}
}
private IEnumerable<PyannoteDiarizationOptions> GetEnabledDiarizationOptions()
private IEnumerable<PyannoteRuntimeOptions> GetEnabledDiarizationOptions()
{
return launchProfiles.GetProfiles()
.SelectMany(profile => GetEnabledDiarizationOptions(profile.Options))
var transcriptionRuntimes = launchProfiles.GetProfiles()
.SelectMany(profile => GetEnabledTranscriptionDiarizationOptions(profile.Options));
var speakerValidationRuntimes = GetEnabledSpeakerValidationDiarizationOptions(
launchProfiles.GetRequiredProfile(null).Options);
return transcriptionRuntimes
.Concat(speakerValidationRuntimes)
.DistinctBy(CreateWarmUpKey);
}
private static IEnumerable<PyannoteDiarizationOptions> GetEnabledDiarizationOptions(
private static IEnumerable<PyannoteRuntimeOptions> GetEnabledTranscriptionDiarizationOptions(
MeetingAssistantOptions options)
{
if (options.Recording.TranscriptionProvider.Equals("whisper-local", StringComparison.OrdinalIgnoreCase) &&
@@ -111,15 +115,18 @@ public sealed class PyannoteDiarizationWarmupHostedService : IHostedService
{
yield return options.WhisperLocal.Diarization;
}
}
if (options.SpeakerIdentification.PyannoteValidation.Enabled &&
options.SpeakerIdentification.PyannoteValidation.Diarization.Enabled)
private static IEnumerable<PyannoteRuntimeOptions> GetEnabledSpeakerValidationDiarizationOptions(
MeetingAssistantOptions options)
{
if (options.SpeakerIdentification.PyannoteValidation.Enabled)
{
yield return options.SpeakerIdentification.PyannoteValidation.Diarization;
}
}
private static string CreateWarmUpKey(PyannoteDiarizationOptions diarization)
private static string CreateWarmUpKey(PyannoteRuntimeOptions diarization)
{
return string.Join(
'\u001f',
@@ -28,7 +28,12 @@ public sealed class PyannoteTranscriptFinalizer
SpeechRecognitionPipelineOptions pipelineOptions,
CancellationToken cancellationToken)
{
return await FinalizeAsync(
if (!options.WhisperLocal.Diarization.Enabled)
{
return [];
}
return await FinalizeEnabledAsync(
audioPath,
liveSegments,
options.WhisperLocal.Diarization,
@@ -36,14 +41,14 @@ public sealed class PyannoteTranscriptFinalizer
cancellationToken);
}
public async Task<IReadOnlyList<TranscriptionSegment>> FinalizeAsync(
internal async Task<IReadOnlyList<TranscriptionSegment>> FinalizeEnabledAsync(
string audioPath,
IReadOnlyList<TranscriptionSegment> liveSegments,
PyannoteDiarizationOptions diarization,
PyannoteRuntimeOptions diarization,
SpeechRecognitionPipelineOptions pipelineOptions,
CancellationToken cancellationToken)
{
if (!diarization.Enabled || liveSegments.Count == 0)
if (liveSegments.Count == 0)
{
return [];
}
@@ -83,14 +88,9 @@ public sealed class PyannoteTranscriptFinalizer
}
public async Task WarmUpAsync(
PyannoteDiarizationOptions diarization,
PyannoteRuntimeOptions diarization,
CancellationToken cancellationToken)
{
if (!diarization.Enabled)
{
return;
}
var token = ResolveToken(diarization);
if (string.IsNullOrWhiteSpace(token))
{
@@ -137,7 +137,7 @@ public sealed class PyannoteTranscriptFinalizer
private async Task<CommandResult> RunDiarizationAsync(
string fullAudioPath,
string token,
PyannoteDiarizationOptions diarization,
PyannoteRuntimeOptions diarization,
SpeechRecognitionPipelineOptions pipelineOptions,
CancellationToken cancellationToken)
{
@@ -171,7 +171,7 @@ public sealed class PyannoteTranscriptFinalizer
}
private async Task EnsureDockerImageAsync(
PyannoteDiarizationOptions diarization,
PyannoteRuntimeOptions diarization,
string modelsFolder,
CancellationToken cancellationToken)
{
@@ -198,7 +198,7 @@ public sealed class PyannoteTranscriptFinalizer
}
}
private static string BuildDockerfile(PyannoteDiarizationOptions diarization)
private static string BuildDockerfile(PyannoteRuntimeOptions diarization)
{
return
$"FROM {diarization.BaseImage}\n"
@@ -211,7 +211,7 @@ public sealed class PyannoteTranscriptFinalizer
}
private string[] BuildDockerArguments(
PyannoteDiarizationOptions diarization,
PyannoteRuntimeOptions diarization,
string fullAudioPath,
string modelsFolder,
SpeechRecognitionPipelineOptions pipelineOptions)
@@ -242,7 +242,7 @@ public sealed class PyannoteTranscriptFinalizer
}
private string[] BuildWarmUpDockerArguments(
PyannoteDiarizationOptions diarization,
PyannoteRuntimeOptions diarization,
string modelsFolder)
{
return
@@ -269,7 +269,7 @@ public sealed class PyannoteTranscriptFinalizer
}
private static string BuildPythonCommand(
PyannoteDiarizationOptions diarization,
PyannoteRuntimeOptions diarization,
SpeechRecognitionPipelineOptions pipelineOptions)
{
var model = diarization.Model;
@@ -296,7 +296,7 @@ public sealed class PyannoteTranscriptFinalizer
+ "python /tmp/meeting_assistant_pyannote.py";
}
private static string BuildWarmUpPythonCommand(PyannoteDiarizationOptions diarization)
private static string BuildWarmUpPythonCommand(PyannoteRuntimeOptions diarization)
{
var model = diarization.Model;
return
@@ -475,7 +475,7 @@ public sealed class PyannoteTranscriptFinalizer
return turns;
}
private static string ResolveToken(PyannoteDiarizationOptions options)
private static string ResolveToken(PyannoteRuntimeOptions options)
{
if (!string.IsNullOrWhiteSpace(options.Token))
{
+1 -2
View File
@@ -127,11 +127,10 @@
"MergeRecentIdentityAge": "14.00:00:00",
"MatchTimeout": "00:03:00",
"PyannoteValidation": {
"Enabled": true,
"Enabled": false,
"MinimumSingleSpeakerCoverage": 0.9,
"MinimumMatchingKnownSnippetRatio": 0.5,
"Diarization": {
"Enabled": false,
"DockerCommand": "docker",
"BaseImage": "python:3.11-slim",
"Image": "meeting-assistant-pyannote:local",