'session.updated']), (string) json_encode(['type' => 'response.output_audio_transcript.delta', 'delta' => 'Roger, ']), (string) json_encode(['type' => 'response.output_audio.delta', 'delta' => base64_encode('AB')]), (string) json_encode(['type' => 'response.output_audio_transcript.delta', 'delta' => 'wilco.']), (string) json_encode(['type' => 'response.output_audio.delta', 'delta' => base64_encode('CD')]), (string) json_encode(['type' => 'response.done']), ]; $fake = new ScriptedRealtimeTransport($inbound); $bridge = new OpenAiRealtimeBridge(fn ($url, $headers, $timeout) => $fake); $turn = $bridge->exchange('PILOTPCM', ['api_key' => 'sk-test', 'voice' => 'echo', 'instructions' => 'be ATC']); $this->assertNull($turn->error); $this->assertTrue($turn->ok()); $this->assertSame('ABCD', $turn->pcm); $this->assertSame('Roger, wilco.', $turn->transcript); $this->assertTrue($fake->closed); // Correct GA protocol on the wire, in order. $types = $fake->sentTypes(); $this->assertSame('session.update', $types[0]); $this->assertContains('input_audio_buffer.append', $types); $this->assertContains('input_audio_buffer.commit', $types); $this->assertContains('response.create', $types); // Session config carries the voice + instructions and audio-only modality. $session = json_decode($fake->sent[0], true)['session']; $this->assertSame(['audio'], $session['output_modalities']); $this->assertSame('echo', $session['audio']['output']['voice']); $this->assertSame('be ATC', $session['instructions']); } public function test_it_validates_before_dialing(): void { $bridge = new OpenAiRealtimeBridge(function () { throw new \RuntimeException('should not dial'); }); $this->assertSame('missing api_key', $bridge->exchange('pcm', [])->error); $this->assertSame('empty input audio', $bridge->exchange('', ['api_key' => 'k'])->error); } public function test_it_surfaces_provider_errors(): void { $inbound = [(string) json_encode(['type' => 'error', 'error' => ['message' => 'boom']])]; $bridge = new OpenAiRealtimeBridge(fn ($url, $headers, $timeout) => new ScriptedRealtimeTransport($inbound)); $turn = $bridge->exchange('PCM', ['api_key' => 'k']); $this->assertSame('boom', $turn->error); $this->assertFalse($turn->ok()); $this->assertSame('', $turn->pcm); } }