Stream Whisper verbose metadata live without overflowing Reverb.
Persist accumulated verbose_json on the recording and broadcast only transcript/whisper deltas so the show page can render language, segments, word timestamps, and confidence while a run is in progress.
This commit is contained in:
@@ -44,6 +44,74 @@ class WhisperTranscriptionStreamTest extends TestCase
|
||||
$this->assertTrue($event['legacy']);
|
||||
$this->assertSame('First segment', $event['append']);
|
||||
$this->assertSame(12.5, $event['end']);
|
||||
$this->assertSame('First segment', $event['whisper']['segment']['text']);
|
||||
$this->assertSame(12.5, $event['whisper']['segment']['end']);
|
||||
}
|
||||
|
||||
public function test_parses_typed_segment_events_as_legacy_chunks(): void
|
||||
{
|
||||
$event = $this->stream->parseEvent(json_encode([
|
||||
'type' => 'segment',
|
||||
'start' => 0.0,
|
||||
'end' => 2.4,
|
||||
'text' => 'Hello, how are you?',
|
||||
'avg_logprob' => -0.12,
|
||||
'words' => [
|
||||
['word' => 'Hello', 'start' => 0.0, 'end' => 0.5, 'probability' => 0.99],
|
||||
],
|
||||
]));
|
||||
|
||||
$this->assertTrue($event['legacy']);
|
||||
$this->assertSame('Hello, how are you?', $event['append']);
|
||||
$this->assertSame(2.4, $event['end']);
|
||||
$this->assertSame(-0.12, $event['whisper']['segment']['avg_logprob']);
|
||||
$this->assertSame('Hello', $event['whisper']['segment']['words'][0]['word']);
|
||||
}
|
||||
|
||||
public function test_parses_verbose_segment_words_language_and_logprobs(): void
|
||||
{
|
||||
$json = json_encode([
|
||||
'language' => 'en',
|
||||
'duration' => 8.5,
|
||||
'text' => 'Hello world',
|
||||
'id' => 0,
|
||||
'start' => 0.0,
|
||||
'end' => 1.6,
|
||||
'tokens' => [50364, 2425, 1002],
|
||||
'avg_logprob' => -0.21,
|
||||
'compression_ratio' => 1.15,
|
||||
'no_speech_prob' => 0.01,
|
||||
'temperature' => 0.0,
|
||||
'words' => [
|
||||
['word' => 'Hello', 'start' => 0.0, 'end' => 0.6, 'probability' => 0.94],
|
||||
['word' => 'world', 'start' => 0.7, 'end' => 1.5, 'probability' => 0.88],
|
||||
],
|
||||
]);
|
||||
|
||||
$event = $this->stream->parseEvent($json);
|
||||
|
||||
$this->assertSame('en', $event['whisper']['language']);
|
||||
$this->assertSame(8.5, $event['whisper']['duration']);
|
||||
$this->assertSame([50364, 2425, 1002], $event['whisper']['segment']['tokens']);
|
||||
$this->assertSame(-0.21, $event['whisper']['segment']['avg_logprob']);
|
||||
$this->assertCount(2, $event['whisper']['segment']['words']);
|
||||
$this->assertSame('Hello', $event['whisper']['segment']['words'][0]['word']);
|
||||
}
|
||||
|
||||
public function test_parses_openai_delta_logprobs_without_treating_them_as_a_segment(): void
|
||||
{
|
||||
$event = $this->stream->parseEvent(json_encode([
|
||||
'type' => 'transcript.text.delta',
|
||||
'delta' => 'Hel',
|
||||
'logprobs' => [
|
||||
['token' => 'Hel', 'logprob' => -0.05],
|
||||
],
|
||||
]));
|
||||
|
||||
$this->assertSame('Hel', $event['append']);
|
||||
$this->assertArrayNotHasKey('segment', $event['whisper']);
|
||||
$this->assertSame('Hel', $event['whisper']['logprobs'][0]['token']);
|
||||
$this->assertSame(-0.05, $event['whisper']['logprobs'][0]['logprob']);
|
||||
}
|
||||
|
||||
public function test_applies_delta_then_done_replace(): void
|
||||
@@ -58,6 +126,35 @@ class WhisperTranscriptionStreamTest extends TestCase
|
||||
$this->assertSame('Hello world', $text);
|
||||
}
|
||||
|
||||
public function test_parses_verbose_json_segments_array(): void
|
||||
{
|
||||
$event = $this->stream->parseEvent(json_encode([
|
||||
'task' => 'transcribe',
|
||||
'language' => 'en',
|
||||
'duration' => 4.2,
|
||||
'text' => 'Hello world',
|
||||
'segments' => [
|
||||
[
|
||||
'id' => 0,
|
||||
'start' => 0.0,
|
||||
'end' => 4.2,
|
||||
'text' => ' Hello world',
|
||||
'avg_logprob' => -0.3,
|
||||
'tokens' => [1, 2],
|
||||
'words' => [
|
||||
['word' => ' Hello', 'start' => 0.0, 'end' => 0.5, 'probability' => 0.9],
|
||||
],
|
||||
],
|
||||
],
|
||||
]));
|
||||
|
||||
$this->assertSame('Hello world', $event['replace']);
|
||||
$this->assertSame(4.2, $event['end']);
|
||||
$this->assertSame('en', $event['whisper']['language']);
|
||||
$this->assertCount(1, $event['whisper']['segments']);
|
||||
$this->assertSame(-0.3, $event['whisper']['segments'][0]['avg_logprob']);
|
||||
}
|
||||
|
||||
public function test_joins_legacy_segments_with_spaces(): void
|
||||
{
|
||||
$first = $this->stream->parseEvent('{"text":"Hello from","end":10}');
|
||||
@@ -88,4 +185,30 @@ class WhisperTranscriptionStreamTest extends TestCase
|
||||
$this->assertSame(['{"text":"Last"}'], $payloads);
|
||||
$this->assertSame('', $buffer);
|
||||
}
|
||||
|
||||
public function test_openai_delta_uses_word_end_as_audio_position(): void
|
||||
{
|
||||
$event = $this->stream->parseEvent(json_encode([
|
||||
'type' => 'transcript.text.delta',
|
||||
'delta' => 'Hello',
|
||||
'words' => [
|
||||
['word' => 'Hello', 'start' => 0.0, 'end' => 1.6, 'probability' => 0.9],
|
||||
],
|
||||
]));
|
||||
|
||||
$this->assertSame(1.6, $event['end']);
|
||||
$this->assertSame('Hello', $event['whisper']['words'][0]['word']);
|
||||
}
|
||||
|
||||
public function test_file_duration_is_not_used_as_audio_position(): void
|
||||
{
|
||||
$event = $this->stream->parseEvent(json_encode([
|
||||
'type' => 'transcript.text.delta',
|
||||
'delta' => 'Hello',
|
||||
'duration' => 40.0,
|
||||
]));
|
||||
|
||||
$this->assertNull($event['end']);
|
||||
$this->assertSame(40.0, $event['whisper']['duration']);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user