70 lines
2.9 KiB
PHP
70 lines
2.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Blax\WebRtc\Tests;
|
|
|
|
use Blax\WebRtc\Realtime\OpenAiRealtimeBridge;
|
|
use Blax\WebRtc\Tests\Fixtures\ScriptedRealtimeTransport;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class OpenAiRealtimeBridgeTest extends TestCase
|
|
{
|
|
public function test_exchange_drives_the_session_and_assembles_the_reply(): void
|
|
{
|
|
$inbound = [
|
|
(string) json_encode(['type' => '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);
|
|
}
|
|
}
|