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:
@@ -14,9 +14,21 @@ class RecordingTranscriptionUpdated implements ShouldBroadcastNow
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
* Reverb's default max payload is 10KB. Keep streamed text well under that.
|
||||
*/
|
||||
public function __construct(public Recording $recording) {}
|
||||
public const MAX_DELTA_BYTES = 8_000;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param array<string, mixed>|null $whisperDelta
|
||||
*/
|
||||
public function __construct(
|
||||
public Recording $recording,
|
||||
public ?string $transcriptDelta = null,
|
||||
public bool $transcriptReplace = false,
|
||||
public ?array $whisperDelta = null,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the channels the event should broadcast on.
|
||||
@@ -41,11 +53,89 @@ class RecordingTranscriptionUpdated implements ShouldBroadcastNow
|
||||
*/
|
||||
public function broadcastWith(): array
|
||||
{
|
||||
return array_merge(
|
||||
$payload = array_merge(
|
||||
$this->recording->transcriptionStatusPayload(),
|
||||
[
|
||||
'word_count' => $this->recording->word_count,
|
||||
],
|
||||
);
|
||||
|
||||
unset($payload['transcript'], $payload['whisper']);
|
||||
|
||||
$delta = $this->transcriptDelta;
|
||||
|
||||
if ($delta !== null && $delta !== '' && strlen($delta) <= self::MAX_DELTA_BYTES) {
|
||||
$payload['transcript_delta'] = $delta;
|
||||
$payload['transcript_replace'] = $this->transcriptReplace;
|
||||
}
|
||||
|
||||
$whisper = $this->compactWhisperDelta($this->whisperDelta);
|
||||
|
||||
if ($whisper !== null) {
|
||||
$payload['whisper_delta'] = $whisper;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>|null $whisper
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function compactWhisperDelta(?array $whisper): ?array
|
||||
{
|
||||
if ($whisper === null || $whisper === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Accumulated segment lists belong in HTTP hydrate, not on Reverb.
|
||||
unset($whisper['segments']);
|
||||
|
||||
if ($whisper === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (['tokens', 'logprobs', 'words'] as $drop) {
|
||||
$encoded = json_encode($whisper);
|
||||
|
||||
if (! is_string($encoded) || strlen($encoded) <= self::MAX_DELTA_BYTES) {
|
||||
return $whisper;
|
||||
}
|
||||
|
||||
$whisper = $this->dropWhisperField($whisper, $drop);
|
||||
}
|
||||
|
||||
$encoded = json_encode($whisper);
|
||||
|
||||
if (! is_string($encoded) || strlen($encoded) > self::MAX_DELTA_BYTES) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $whisper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $whisper
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function dropWhisperField(array $whisper, string $field): array
|
||||
{
|
||||
unset($whisper[$field]);
|
||||
|
||||
if (isset($whisper['segment']) && is_array($whisper['segment'])) {
|
||||
unset($whisper['segment'][$field]);
|
||||
}
|
||||
|
||||
if (isset($whisper['segments']) && is_array($whisper['segments'])) {
|
||||
$whisper['segments'] = array_map(function (mixed $segment) use ($field): mixed {
|
||||
if (is_array($segment)) {
|
||||
unset($segment[$field]);
|
||||
}
|
||||
|
||||
return $segment;
|
||||
}, $whisper['segments']);
|
||||
}
|
||||
|
||||
return $whisper;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user