Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.

Commit 5fb3a3d

Browse files
Copilotchr-hertel
andcommitted
Implement consistent Whisper task option for OpenAI and Azure bridges
Co-authored-by: chr-hertel <2852185+chr-hertel@users.noreply.github.com>
1 parent b5df809 commit 5fb3a3d

File tree

6 files changed

+278
-2
lines changed

6 files changed

+278
-2
lines changed

src/Platform/Bridge/Azure/OpenAI/WhisperModelClient.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PhpLlm\LlmChain\Platform\Bridge\Azure\OpenAI;
66

77
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper;
8+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper\Task;
89
use PhpLlm\LlmChain\Platform\Model;
910
use PhpLlm\LlmChain\Platform\ModelClientInterface;
1011
use Symfony\Component\HttpClient\EventSourceHttpClient;
@@ -41,7 +42,17 @@ public function supports(Model $model): bool
4142

4243
public function request(Model $model, array|string $payload, array $options = []): ResponseInterface
4344
{
44-
$url = \sprintf('https://%s/openai/deployments/%s/audio/translations', $this->baseUrl, $this->deployment);
45+
// Extract task from options if provided, default to transcription for backward compatibility
46+
$task = $options['task'] ?? Task::TRANSCRIPTION;
47+
unset($options['task']);
48+
49+
$endpoint = match ($task) {
50+
Task::TRANSCRIPTION => 'transcriptions',
51+
Task::TRANSLATION => 'translations',
52+
default => 'transcriptions',
53+
};
54+
55+
$url = \sprintf('https://%s/openai/deployments/%s/audio/%s', $this->baseUrl, $this->deployment, $endpoint);
4556

4657
return $this->httpClient->request('POST', $url, [
4758
'headers' => [

src/Platform/Bridge/OpenAI/Whisper/ModelClient.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper;
66

77
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper;
8+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper\Task;
89
use PhpLlm\LlmChain\Platform\Model;
910
use PhpLlm\LlmChain\Platform\ModelClientInterface as BaseModelClient;
1011
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -31,7 +32,17 @@ public function supports(Model $model): bool
3132

3233
public function request(Model $model, array|string $payload, array $options = []): ResponseInterface
3334
{
34-
return $this->httpClient->request('POST', 'https://api.openai.com/v1/audio/transcriptions', [
35+
// Extract task from options if provided, default to transcription for backward compatibility
36+
$task = $options['task'] ?? Task::TRANSCRIPTION;
37+
unset($options['task']);
38+
39+
$endpoint = match ($task) {
40+
Task::TRANSCRIPTION => 'transcriptions',
41+
Task::TRANSLATION => 'translations',
42+
default => 'transcriptions',
43+
};
44+
45+
return $this->httpClient->request('POST', "https://api.openai.com/v1/audio/{$endpoint}", [
3546
'auth_bearer' => $this->apiKey,
3647
'headers' => ['Content-Type' => 'multipart/form-data'],
3748
'body' => array_merge($options, $payload, ['model' => $model->getName()]),
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper;
6+
7+
/**
8+
* @author Christopher Hertel <mail@christopher-hertel.de>
9+
*/
10+
interface Task
11+
{
12+
public const TRANSCRIPTION = 'transcription';
13+
public const TRANSLATION = 'translation';
14+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Platform\Bridge\Azure\OpenAI;
6+
7+
use PhpLlm\LlmChain\Platform\Bridge\Azure\OpenAI\WhisperModelClient;
8+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper;
9+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper\Task;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\Attributes\Small;
12+
use PHPUnit\Framework\Attributes\Test;
13+
use PHPUnit\Framework\TestCase;
14+
use Symfony\Component\HttpClient\MockHttpClient;
15+
use Symfony\Component\HttpClient\Response\MockResponse;
16+
17+
#[CoversClass(WhisperModelClient::class)]
18+
#[Small]
19+
final class WhisperModelClientTest extends TestCase
20+
{
21+
#[Test]
22+
public function itSupportsWhisperModel(): void
23+
{
24+
$client = new WhisperModelClient(
25+
new MockHttpClient(),
26+
'test.openai.azure.com',
27+
'whisper-deployment',
28+
'2023-12-01-preview',
29+
'test-key'
30+
);
31+
$model = new Whisper();
32+
33+
self::assertTrue($client->supports($model));
34+
}
35+
36+
#[Test]
37+
public function itUsesTranscriptionEndpointByDefault(): void
38+
{
39+
$httpClient = new MockHttpClient([
40+
new MockResponse('{"text": "Hello World"}'),
41+
]);
42+
43+
$client = new WhisperModelClient(
44+
$httpClient,
45+
'test.openai.azure.com',
46+
'whisper-deployment',
47+
'2023-12-01-preview',
48+
'test-key'
49+
);
50+
$model = new Whisper();
51+
$payload = ['file' => 'audio-data'];
52+
53+
$client->request($model, $payload);
54+
55+
$requestInfo = $httpClient->getRequestsCount() > 0 ?
56+
$httpClient->getRequestsHistory()[0] : null;
57+
58+
self::assertNotNull($requestInfo);
59+
self::assertSame('POST', $requestInfo['method']);
60+
self::assertStringContains('/audio/transcriptions', $requestInfo['url']);
61+
}
62+
63+
#[Test]
64+
public function itUsesTranscriptionEndpointWhenTaskIsSpecified(): void
65+
{
66+
$httpClient = new MockHttpClient([
67+
new MockResponse('{"text": "Hello World"}'),
68+
]);
69+
70+
$client = new WhisperModelClient(
71+
$httpClient,
72+
'test.openai.azure.com',
73+
'whisper-deployment',
74+
'2023-12-01-preview',
75+
'test-key'
76+
);
77+
$model = new Whisper();
78+
$payload = ['file' => 'audio-data'];
79+
$options = ['task' => Task::TRANSCRIPTION];
80+
81+
$client->request($model, $payload, $options);
82+
83+
$requestInfo = $httpClient->getRequestsCount() > 0 ?
84+
$httpClient->getRequestsHistory()[0] : null;
85+
86+
self::assertNotNull($requestInfo);
87+
self::assertSame('POST', $requestInfo['method']);
88+
self::assertStringContains('/audio/transcriptions', $requestInfo['url']);
89+
}
90+
91+
#[Test]
92+
public function itUsesTranslationEndpointWhenTaskIsSpecified(): void
93+
{
94+
$httpClient = new MockHttpClient([
95+
new MockResponse('{"text": "Hello World"}'),
96+
]);
97+
98+
$client = new WhisperModelClient(
99+
$httpClient,
100+
'test.openai.azure.com',
101+
'whisper-deployment',
102+
'2023-12-01-preview',
103+
'test-key'
104+
);
105+
$model = new Whisper();
106+
$payload = ['file' => 'audio-data'];
107+
$options = ['task' => Task::TRANSLATION];
108+
109+
$client->request($model, $payload, $options);
110+
111+
$requestInfo = $httpClient->getRequestsCount() > 0 ?
112+
$httpClient->getRequestsHistory()[0] : null;
113+
114+
self::assertNotNull($requestInfo);
115+
self::assertSame('POST', $requestInfo['method']);
116+
self::assertStringContains('/audio/translations', $requestInfo['url']);
117+
}
118+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Platform\Bridge\OpenAI\Whisper;
6+
7+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper;
8+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper\ModelClient;
9+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper\Task;
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\Attributes\Small;
12+
use PHPUnit\Framework\Attributes\Test;
13+
use PHPUnit\Framework\TestCase;
14+
use Symfony\Component\HttpClient\MockHttpClient;
15+
use Symfony\Component\HttpClient\Response\MockResponse;
16+
17+
#[CoversClass(ModelClient::class)]
18+
#[Small]
19+
final class ModelClientTest extends TestCase
20+
{
21+
#[Test]
22+
public function itSupportsWhisperModel(): void
23+
{
24+
$client = new ModelClient(new MockHttpClient(), 'test-key');
25+
$model = new Whisper();
26+
27+
self::assertTrue($client->supports($model));
28+
}
29+
30+
#[Test]
31+
public function itUsesTranscriptionEndpointByDefault(): void
32+
{
33+
$httpClient = new MockHttpClient([
34+
new MockResponse('{"text": "Hello World"}'),
35+
]);
36+
37+
$client = new ModelClient($httpClient, 'test-key');
38+
$model = new Whisper();
39+
$payload = ['file' => 'audio-data'];
40+
41+
$client->request($model, $payload);
42+
43+
$requestInfo = $httpClient->getRequestsCount() > 0 ?
44+
$httpClient->getRequestsHistory()[0] : null;
45+
46+
self::assertNotNull($requestInfo);
47+
self::assertSame('POST', $requestInfo['method']);
48+
self::assertSame('https://api.openai.com/v1/audio/transcriptions', $requestInfo['url']);
49+
}
50+
51+
#[Test]
52+
public function itUsesTranscriptionEndpointWhenTaskIsSpecified(): void
53+
{
54+
$httpClient = new MockHttpClient([
55+
new MockResponse('{"text": "Hello World"}'),
56+
]);
57+
58+
$client = new ModelClient($httpClient, 'test-key');
59+
$model = new Whisper();
60+
$payload = ['file' => 'audio-data'];
61+
$options = ['task' => Task::TRANSCRIPTION];
62+
63+
$client->request($model, $payload, $options);
64+
65+
$requestInfo = $httpClient->getRequestsCount() > 0 ?
66+
$httpClient->getRequestsHistory()[0] : null;
67+
68+
self::assertNotNull($requestInfo);
69+
self::assertSame('POST', $requestInfo['method']);
70+
self::assertSame('https://api.openai.com/v1/audio/transcriptions', $requestInfo['url']);
71+
}
72+
73+
#[Test]
74+
public function itUsesTranslationEndpointWhenTaskIsSpecified(): void
75+
{
76+
$httpClient = new MockHttpClient([
77+
new MockResponse('{"text": "Hello World"}'),
78+
]);
79+
80+
$client = new ModelClient($httpClient, 'test-key');
81+
$model = new Whisper();
82+
$payload = ['file' => 'audio-data'];
83+
$options = ['task' => Task::TRANSLATION];
84+
85+
$client->request($model, $payload, $options);
86+
87+
$requestInfo = $httpClient->getRequestsCount() > 0 ?
88+
$httpClient->getRequestsHistory()[0] : null;
89+
90+
self::assertNotNull($requestInfo);
91+
self::assertSame('POST', $requestInfo['method']);
92+
self::assertSame('https://api.openai.com/v1/audio/translations', $requestInfo['url']);
93+
}
94+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Platform\Bridge\OpenAI\Whisper;
6+
7+
use PhpLlm\LlmChain\Platform\Bridge\OpenAI\Whisper\Task;
8+
use PHPUnit\Framework\Attributes\CoversClass;
9+
use PHPUnit\Framework\Attributes\Small;
10+
use PHPUnit\Framework\Attributes\Test;
11+
use PHPUnit\Framework\TestCase;
12+
13+
#[CoversClass(Task::class)]
14+
#[Small]
15+
final class TaskTest extends TestCase
16+
{
17+
#[Test]
18+
public function itDefinesTranscriptionTask(): void
19+
{
20+
self::assertSame('transcription', Task::TRANSCRIPTION);
21+
}
22+
23+
#[Test]
24+
public function itDefinesTranslationTask(): void
25+
{
26+
self::assertSame('translation', Task::TRANSLATION);
27+
}
28+
}

0 commit comments

Comments
 (0)