Skip to content

Commit 30578be

Browse files
authored
feat: add support for whisper on openai (#251)
1 parent 4ab0b2a commit 30578be

File tree

6 files changed

+130
-0
lines changed

6 files changed

+130
-0
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ $embeddings = new Embeddings();
6969
* Embeddings Models
7070
* [OpenAI's Text Embeddings](https://platform.openai.com/docs/guides/embeddings/embedding-models) with [OpenAI](https://platform.openai.com/docs/overview) and [Azure](https://learn.microsoft.com/azure/ai-services/openai/concepts/models) as Platform
7171
* [Voyage's Embeddings](https://docs.voyageai.com/docs/embeddings) with [Voyage](https://www.voyageai.com/) as Platform
72+
* Other Models
73+
* [OpenAI's Dall·E](https://platform.openai.com/docs/guides/image-generation) with [OpenAI](https://platform.openai.com/docs/overview) as Platform
74+
* [OpenAI's Whisper](https://platform.openai.com/docs/guides/speech-to-text) with [OpenAI](https://platform.openai.com/docs/overview) as Platform
7275

7376
See [issue #28](https://github.com/php-llm/llm-chain/issues/28) for planned support of other models and platforms.
7477

examples/audio-transcript-whisper.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
use PhpLlm\LlmChain\Bridge\OpenAI\PlatformFactory;
4+
use PhpLlm\LlmChain\Bridge\OpenAI\Whisper;
5+
use PhpLlm\LlmChain\Bridge\OpenAI\Whisper\File;
6+
use Symfony\Component\Dotenv\Dotenv;
7+
8+
require_once dirname(__DIR__).'/vendor/autoload.php';
9+
(new Dotenv())->loadEnv(dirname(__DIR__).'/.env');
10+
11+
if (empty($_ENV['OPENAI_API_KEY'])) {
12+
echo 'Please set the OPENAI_API_KEY environment variable.'.PHP_EOL;
13+
exit(1);
14+
}
15+
16+
$platform = PlatformFactory::create($_ENV['OPENAI_API_KEY']);
17+
$model = new Whisper();
18+
$file = new File(dirname(__DIR__).'/tests/Fixture/audio.mp3');
19+
20+
$response = $platform->request($model, $file);
21+
22+
echo $response->getContent().PHP_EOL;

src/Bridge/OpenAI/PlatformFactory.php

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings\ResponseConverter as EmbeddingsResponseConverter;
1010
use PhpLlm\LlmChain\Bridge\OpenAI\GPT\ModelClient as GPTModelClient;
1111
use PhpLlm\LlmChain\Bridge\OpenAI\GPT\ResponseConverter as GPTResponseConverter;
12+
use PhpLlm\LlmChain\Bridge\OpenAI\Whisper\ModelClient as WhisperModelClient;
1213
use PhpLlm\LlmChain\Platform;
1314
use Symfony\Component\HttpClient\EventSourceHttpClient;
1415
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -23,17 +24,20 @@ public static function create(
2324
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
2425

2526
$dallEModelClient = new DallEModelClient($httpClient, $apiKey);
27+
$whisperModelClient = new WhisperModelClient($httpClient, $apiKey);
2628

2729
return new Platform(
2830
[
2931
new GPTModelClient($httpClient, $apiKey),
3032
new EmbeddingsModelClient($httpClient, $apiKey),
3133
$dallEModelClient,
34+
$whisperModelClient,
3235
],
3336
[
3437
new GPTResponseConverter(),
3538
new EmbeddingsResponseConverter(),
3639
$dallEModelClient,
40+
$whisperModelClient,
3741
],
3842
);
3943
}

src/Bridge/OpenAI/Whisper.php

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Bridge\OpenAI;
6+
7+
use PhpLlm\LlmChain\Model\Model;
8+
9+
final readonly class Whisper implements Model
10+
{
11+
public const WHISPER_1 = 'whisper-1';
12+
13+
/**
14+
* @param array<string, mixed> $options
15+
*/
16+
public function __construct(
17+
private string $version = self::WHISPER_1,
18+
private array $options = [],
19+
) {
20+
}
21+
22+
public function getVersion(): string
23+
{
24+
return $this->version;
25+
}
26+
27+
public function getOptions(): array
28+
{
29+
return $this->options;
30+
}
31+
}

src/Bridge/OpenAI/Whisper/File.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Bridge\OpenAI\Whisper;
6+
7+
use PhpLlm\LlmChain\Exception\InvalidArgumentException;
8+
9+
final readonly class File
10+
{
11+
public function __construct(
12+
public string $path,
13+
) {
14+
if (!is_readable($path) || false === file_get_contents($path)) {
15+
throw new InvalidArgumentException(sprintf('The file "%s" does not exist or is not readable.', $path));
16+
}
17+
}
18+
}
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Bridge\OpenAI\Whisper;
6+
7+
use PhpLlm\LlmChain\Bridge\OpenAI\Whisper;
8+
use PhpLlm\LlmChain\Model\Model;
9+
use PhpLlm\LlmChain\Model\Response\ResponseInterface as LlmResponse;
10+
use PhpLlm\LlmChain\Model\Response\TextResponse;
11+
use PhpLlm\LlmChain\Platform\ModelClient as PlatformResponseFactory;
12+
use PhpLlm\LlmChain\Platform\ResponseConverter as PlatformResponseConverter;
13+
use Symfony\Contracts\HttpClient\HttpClientInterface;
14+
use Symfony\Contracts\HttpClient\ResponseInterface;
15+
use Webmozart\Assert\Assert;
16+
17+
final readonly class ModelClient implements PlatformResponseFactory, PlatformResponseConverter
18+
{
19+
public function __construct(
20+
private HttpClientInterface $httpClient,
21+
#[\SensitiveParameter]
22+
private string $apiKey,
23+
) {
24+
Assert::stringNotEmpty($apiKey, 'The API key must not be empty.');
25+
}
26+
27+
public function supports(Model $model, object|array|string $input): bool
28+
{
29+
return $model instanceof Whisper && $input instanceof File;
30+
}
31+
32+
public function request(Model $model, object|array|string $input, array $options = []): ResponseInterface
33+
{
34+
assert($input instanceof File);
35+
36+
return $this->httpClient->request('POST', 'https://api.openai.com/v1/audio/transcriptions', [
37+
'auth_bearer' => $this->apiKey,
38+
'headers' => ['Content-Type' => 'multipart/form-data'],
39+
'body' => array_merge($options, $model->getOptions(), [
40+
'model' => $model->getVersion(),
41+
'file' => fopen($input->path, 'r'),
42+
]),
43+
]);
44+
}
45+
46+
public function convert(ResponseInterface $response, array $options = []): LlmResponse
47+
{
48+
$data = $response->toArray();
49+
50+
return new TextResponse($data['text']);
51+
}
52+
}

0 commit comments

Comments
 (0)