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

feat: add whisper azure support #252

Merged
merged 1 commit into from
Mar 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ $embeddings = new Embeddings();
* [Voyage's Embeddings](https://docs.voyageai.com/docs/embeddings) with [Voyage](https://www.voyageai.com/) as Platform
* Other Models
* [OpenAI's Dall·E](https://platform.openai.com/docs/guides/image-generation) with [OpenAI](https://platform.openai.com/docs/overview) as Platform
* [OpenAI's Whisper](https://platform.openai.com/docs/guides/speech-to-text) with [OpenAI](https://platform.openai.com/docs/overview) as Platform
* [OpenAI's Whisper](https://platform.openai.com/docs/guides/speech-to-text) with [OpenAI](https://platform.openai.com/docs/overview) and [Azure](https://learn.microsoft.com/azure/ai-services/openai/concepts/models) as Platform

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

Expand Down
6 changes: 4 additions & 2 deletions src/Bridge/Azure/OpenAI/PlatformFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings;
use PhpLlm\LlmChain\Bridge\OpenAI\GPT\ResponseConverter;
use PhpLlm\LlmChain\Bridge\OpenAI\Whisper;
use PhpLlm\LlmChain\Platform;
use Symfony\Component\HttpClient\EventSourceHttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
Expand All @@ -23,10 +24,11 @@ public static function create(
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
$embeddingsResponseFactory = new EmbeddingsModelClient($httpClient, $baseUrl, $deployment, $apiVersion, $apiKey);
$GPTResponseFactory = new GPTModelClient($httpClient, $baseUrl, $deployment, $apiVersion, $apiKey);
$whisperResponseFactory = new WhisperModelClient($httpClient, $baseUrl, $deployment, $apiVersion, $apiKey);

return new Platform(
[$GPTResponseFactory, $embeddingsResponseFactory],
[new ResponseConverter(), new Embeddings\ResponseConverter()],
[$GPTResponseFactory, $embeddingsResponseFactory, $whisperResponseFactory],
[new ResponseConverter(), new Embeddings\ResponseConverter(), new Whisper\ResponseConverter()],
);
}
}
58 changes: 58 additions & 0 deletions src/Bridge/Azure/OpenAI/WhisperModelClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Bridge\Azure\OpenAI;

use PhpLlm\LlmChain\Bridge\OpenAI\Whisper;
use PhpLlm\LlmChain\Bridge\OpenAI\Whisper\File;
use PhpLlm\LlmChain\Model\Model;
use PhpLlm\LlmChain\Platform\ModelClient;
use Symfony\Component\HttpClient\EventSourceHttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Webmozart\Assert\Assert;

final readonly class WhisperModelClient implements ModelClient
{
private EventSourceHttpClient $httpClient;

public function __construct(
HttpClientInterface $httpClient,
private string $baseUrl,
private string $deployment,
private string $apiVersion,
#[\SensitiveParameter] private string $apiKey,
) {
$this->httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
Assert::notStartsWith($baseUrl, 'http://', 'The base URL must not contain the protocol.');
Assert::notStartsWith($baseUrl, 'https://', 'The base URL must not contain the protocol.');
Assert::stringNotEmpty($deployment, 'The deployment must not be empty.');
Assert::stringNotEmpty($apiVersion, 'The API version must not be empty.');
Assert::stringNotEmpty($apiKey, 'The API key must not be empty.');
}

public function supports(Model $model, object|array|string $input): bool
{
return $model instanceof Whisper && $input instanceof File;
}

public function request(Model $model, object|array|string $input, array $options = []): ResponseInterface
{
assert($input instanceof File);

$url = sprintf('https://%s/openai/deployments/%s/audio/translations', $this->baseUrl, $this->deployment);

return $this->httpClient->request('POST', $url, [
'headers' => [
'api-key' => $this->apiKey,
'Content-Type' => 'multipart/form-data',
],
'query' => ['api-version' => $this->apiVersion],
'body' => array_merge($options, $model->getOptions(), [
'model' => $model->getVersion(),
'file' => fopen($input->path, 'r'),
]),
]);
}
}
6 changes: 3 additions & 3 deletions src/Bridge/OpenAI/PlatformFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PhpLlm\LlmChain\Bridge\OpenAI\GPT\ModelClient as GPTModelClient;
use PhpLlm\LlmChain\Bridge\OpenAI\GPT\ResponseConverter as GPTResponseConverter;
use PhpLlm\LlmChain\Bridge\OpenAI\Whisper\ModelClient as WhisperModelClient;
use PhpLlm\LlmChain\Bridge\OpenAI\Whisper\ResponseConverter as WhisperResponseConverter;
use PhpLlm\LlmChain\Platform;
use Symfony\Component\HttpClient\EventSourceHttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
Expand All @@ -24,20 +25,19 @@ public static function create(
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);

$dallEModelClient = new DallEModelClient($httpClient, $apiKey);
$whisperModelClient = new WhisperModelClient($httpClient, $apiKey);

return new Platform(
[
new GPTModelClient($httpClient, $apiKey),
new EmbeddingsModelClient($httpClient, $apiKey),
$dallEModelClient,
$whisperModelClient,
new WhisperModelClient($httpClient, $apiKey),
],
[
new GPTResponseConverter(),
new EmbeddingsResponseConverter(),
$dallEModelClient,
$whisperModelClient,
new WhisperResponseConverter(),
],
);
}
Expand Down
14 changes: 2 additions & 12 deletions src/Bridge/OpenAI/Whisper/ModelClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@

use PhpLlm\LlmChain\Bridge\OpenAI\Whisper;
use PhpLlm\LlmChain\Model\Model;
use PhpLlm\LlmChain\Model\Response\ResponseInterface as LlmResponse;
use PhpLlm\LlmChain\Model\Response\TextResponse;
use PhpLlm\LlmChain\Platform\ModelClient as PlatformResponseFactory;
use PhpLlm\LlmChain\Platform\ResponseConverter as PlatformResponseConverter;
use PhpLlm\LlmChain\Platform\ModelClient as BaseModelClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Webmozart\Assert\Assert;

final readonly class ModelClient implements PlatformResponseFactory, PlatformResponseConverter
final readonly class ModelClient implements BaseModelClient
{
public function __construct(
private HttpClientInterface $httpClient,
Expand Down Expand Up @@ -42,11 +39,4 @@ public function request(Model $model, object|array|string $input, array $options
]),
]);
}

public function convert(ResponseInterface $response, array $options = []): LlmResponse
{
$data = $response->toArray();

return new TextResponse($data['text']);
}
}
27 changes: 27 additions & 0 deletions src/Bridge/OpenAI/Whisper/ResponseConverter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Bridge\OpenAI\Whisper;

use PhpLlm\LlmChain\Bridge\OpenAI\Whisper;
use PhpLlm\LlmChain\Model\Model;
use PhpLlm\LlmChain\Model\Response\ResponseInterface as LlmResponse;
use PhpLlm\LlmChain\Model\Response\TextResponse;
use PhpLlm\LlmChain\Platform\ResponseConverter as BaseResponseConverter;
use Symfony\Contracts\HttpClient\ResponseInterface as HttpResponse;

final class ResponseConverter implements BaseResponseConverter
{
public function supports(Model $model, object|array|string $input): bool
{
return $model instanceof Whisper && $input instanceof File;
}

public function convert(HttpResponse $response, array $options = []): LlmResponse
{
$data = $response->toArray();

return new TextResponse($data['text']);
}
}