Skip to content

Commit 61f4b60

Browse files
committed
feat: test build out for realtime
1 parent 6e5bdfb commit 61f4b60

10 files changed

Lines changed: 230 additions & 0 deletions

File tree

src/Contracts/ClientContract.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use OpenAI\Contracts\Resources\ImagesContract;
1616
use OpenAI\Contracts\Resources\ModelsContract;
1717
use OpenAI\Contracts\Resources\ModerationsContract;
18+
use OpenAI\Contracts\Resources\RealtimeContract;
1819
use OpenAI\Contracts\Resources\ResponsesContract;
1920
use OpenAI\Contracts\Resources\ThreadsContract;
2021
use OpenAI\Contracts\Resources\VectorStoresContract;
@@ -36,6 +37,13 @@ public function completions(): CompletionsContract;
3637
*/
3738
public function responses(): ResponsesContract;
3839

40+
/**
41+
* Communicate with a GPT-4o class model in real time using WebRTC or WebSockets. Supports text and audio inputs and outputs, along with audio transcriptions.
42+
*
43+
* @see https://platform.openai.com/docs/api-reference/realtime-sessions
44+
*/
45+
public function realtime(): RealtimeContract;
46+
3947
/**
4048
* Given a chat conversation, the model will return a chat completion response.
4149
*

src/Resources/Realtime.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ public function token(array $parameters = []): SessionResponse
3535
return SessionResponse::from($response->data());
3636
}
3737

38+
/**
39+
* Create an ephemeral API token for real time transcription sessions.
40+
*
41+
* @see https://platform.openai.com/docs/api-reference/realtime-sessions/create-transcription
42+
*
43+
* @param array<string, mixed> $parameters
44+
*/
3845
public function transcribeToken(array $parameters = []): TranscriptionSessionResponse
3946
{
4047
$payload = Payload::create('realtime/transcription_sessions', $parameters);

src/Testing/ClientFake.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use OpenAI\Testing\Resources\ImagesTestResource;
2222
use OpenAI\Testing\Resources\ModelsTestResource;
2323
use OpenAI\Testing\Resources\ModerationsTestResource;
24+
use OpenAI\Testing\Resources\RealtimeTestResource;
2425
use OpenAI\Testing\Resources\ResponsesTestResource;
2526
use OpenAI\Testing\Resources\ThreadsTestResource;
2627
use OpenAI\Testing\Resources\VectorStoresTestResource;
@@ -138,6 +139,11 @@ public function responses(): ResponsesTestResource
138139
return new ResponsesTestResource($this);
139140
}
140141

142+
public function realtime(): RealtimeTestResource
143+
{
144+
return new RealtimeTestResource($this);
145+
}
146+
141147
public function completions(): CompletionsTestResource
142148
{
143149
return new CompletionsTestResource($this);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace OpenAI\Testing\Resources;
4+
5+
use OpenAI\Contracts\Resources\RealtimeContract;
6+
use OpenAI\Resources\Realtime;
7+
use OpenAI\Responses\Realtime\SessionResponse;
8+
use OpenAI\Responses\Realtime\TranscriptionSessionResponse;
9+
use OpenAI\Testing\Resources\Concerns\Testable;
10+
11+
final class RealtimeTestResource implements RealtimeContract
12+
{
13+
use Testable;
14+
15+
public function resource(): string
16+
{
17+
return Realtime::class;
18+
}
19+
20+
public function token(array $parameters = []): SessionResponse
21+
{
22+
return $this->record(__FUNCTION__, func_get_args());
23+
}
24+
25+
public function transcribeToken(array $parameters = []): TranscriptionSessionResponse
26+
{
27+
return $this->record(__FUNCTION__, func_get_args());
28+
}
29+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace OpenAI\Testing\Responses\Fixtures\Realtime;
4+
5+
final class SessionResponseFixture
6+
{
7+
public const ATTRIBUTES = [
8+
'client_secret' => [
9+
'expires_at' => 1735680000,
10+
'value' => 'ek_secret_123',
11+
],
12+
'input_audio_format' => 'pcm16',
13+
'input_audio_transcription' => null,
14+
'instructions' => 'Your knowledge cutoff is 2023-10. You are a helpful assistant.',
15+
'max_response_output_tokens' => 'inf',
16+
'modalities' => [
17+
'audio',
18+
'text',
19+
],
20+
'output_audio_format' => 'pcm16',
21+
'temperature' => 0.7,
22+
'tool_choice' => 'auto',
23+
'tools' => [],
24+
'turn_detection' => [
25+
'prefix_padding_ms' => 100,
26+
'silence_duration_ms' => 500,
27+
'threshold' => 0.5,
28+
'type' => 'server_vad',
29+
],
30+
'voice' => 'alloy',
31+
];
32+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace OpenAI\Testing\Responses\Fixtures\Realtime;
4+
5+
final class TranscriptionSessionResponseFixture
6+
{
7+
public const ATTRIBUTES = [
8+
'client_secret' => [
9+
'expires_at' => 1735680000,
10+
'value' => 'ek_secret_123',
11+
],
12+
'input_audio_format' => 'pcm16',
13+
'input_audio_transcription' => null,
14+
'modalities' => null,
15+
'turn_detection' => [
16+
'prefix_padding_ms' => 300,
17+
'silence_duration_ms' => 200,
18+
'threshold' => 0.5,
19+
'type' => 'server_vad',
20+
],
21+
];
22+
}

tests/Fixtures/Realtime.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
/**
4+
* @return array<string, mixed>
5+
*/
6+
function sessionResponseResource(): array
7+
{
8+
return [
9+
'client_secret' => [
10+
'expires_at' => 1735680000,
11+
'value' => 'ek_secret_123',
12+
],
13+
'input_audio_format' => 'pcm16',
14+
'input_audio_transcription' => null,
15+
'instructions' => 'Your knowledge cutoff is 2023-10. You are a helpful assistant.',
16+
'max_response_output_tokens' => 'inf',
17+
'modalities' => [
18+
'audio',
19+
'text',
20+
],
21+
'output_audio_format' => 'pcm16',
22+
'temperature' => 0.7,
23+
'tool_choice' => 'auto',
24+
'tools' => [],
25+
'turn_detection' => [
26+
'prefix_padding_ms' => 100,
27+
'silence_duration_ms' => 500,
28+
'threshold' => 0.5,
29+
'type' => 'server_vad',
30+
],
31+
'voice' => 'alloy',
32+
];
33+
}
34+
35+
/**
36+
* @return array<string, mixed>
37+
*/
38+
function transcriptionSessionResponseResource(): array
39+
{
40+
return [
41+
'client_secret' => [
42+
'expires_at' => 1735680000,
43+
'value' => 'ek_secret_123',
44+
],
45+
'input_audio_format' => 'pcm16',
46+
'input_audio_transcription' => null,
47+
'modalities' => null,
48+
'turn_detection' => [
49+
'prefix_padding_ms' => 300,
50+
'silence_duration_ms' => 200,
51+
'threshold' => 0.5,
52+
'type' => 'server_vad',
53+
],
54+
];
55+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
use OpenAI\Responses\Realtime\Session\ClientSecret;
4+
use OpenAI\Responses\Realtime\Session\TurnDetection;
5+
use OpenAI\Responses\Realtime\SessionResponse;
6+
7+
test('from', function () {
8+
$response = SessionResponse::from(sessionResponseResource());
9+
10+
expect($response)
11+
->toBeInstanceOf(SessionResponse::class)
12+
->clientSecret->toBeInstanceOf(ClientSecret::class)
13+
->inputAudioFormat->toBe('pcm16')
14+
->inputAudioTranscription->toBeNull()
15+
->instructions->toBe('Your knowledge cutoff is 2023-10. You are a helpful assistant.')
16+
->maxResponseOutputTokens->toBe('inf')
17+
->modalities->toBe(['audio', 'text'])
18+
->outputAudioFormat->toBe('pcm16')
19+
->temperature->toBe(0.7)
20+
->toolChoice->toBe('auto')
21+
->tools->toBeArray()
22+
->turnDetection->toBeInstanceOf(TurnDetection::class)
23+
->voice->toBe('alloy');
24+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
use OpenAI\Responses\Realtime\Session\ClientSecret;
4+
use OpenAI\Responses\Realtime\Session\TurnDetection;
5+
use OpenAI\Responses\Realtime\TranscriptionSessionResponse;
6+
7+
test('from', function () {
8+
$response = TranscriptionSessionResponse::from(transcriptionSessionResponseResource());
9+
10+
expect($response)
11+
->toBeInstanceOf(TranscriptionSessionResponse::class)
12+
->clientSecret->toBeInstanceOf(ClientSecret::class)
13+
->inputAudioFormat->toBe('pcm16')
14+
->inputAudioTranscription->toBeNull()
15+
->modalities->toBeNull()
16+
->turnDetection->toBeInstanceOf(TurnDetection::class);
17+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
use OpenAI\Resources\Realtime;
4+
use OpenAI\Responses\Realtime\SessionResponse;
5+
use OpenAI\Responses\Realtime\TranscriptionSessionResponse;
6+
use OpenAI\Testing\ClientFake;
7+
8+
it('records a realtime token request', function () {
9+
$fake = new ClientFake([
10+
SessionResponse::fake(),
11+
]);
12+
13+
$fake->realtime()->token();
14+
15+
$fake->assertSent(Realtime::class, function ($method) {
16+
return $method === 'token';
17+
});
18+
});
19+
20+
it('records a realtime token transcription request', function () {
21+
$fake = new ClientFake([
22+
TranscriptionSessionResponse::fake(),
23+
]);
24+
25+
$fake->realtime()->transcribeToken();
26+
27+
$fake->assertSent(Realtime::class, function ($method) {
28+
return $method === 'transcribeToken';
29+
});
30+
});

0 commit comments

Comments
 (0)