Skip to content

Commit 3d60f40

Browse files
authored
Merge pull request #64 from moe-mizrak/feat/add-input-audio
Feat/add input audio
2 parents a9173fb + be57629 commit 3d60f40

File tree

5 files changed

+222
-0
lines changed

5 files changed

+222
-0
lines changed

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ This Laravel package provides an easy-to-use interface for integrating **[OpenRo
2929
- [Stream Chat Request](#stream-chat-request)
3030
- [Maintaining Conversation Continuity](#maintaining-conversation-continuity)
3131
- [Structured Output](#structured-output)
32+
- [Audio Inputs](#audio-inputs)
3233
- [Cost Request](#cost-request)
3334
- [Limit Request](#limit-request)
3435
- [Using OpenRouterRequest Class](#using-openrouterrequest-class)
@@ -536,6 +537,51 @@ $chatData = new ChatData(
536537
> [!TIP]
537538
> You can also use **prompt engineering** to obtain structured output and control the format of responses.
538539
540+
- #### Audio Inputs
541+
(Please also refer to [OpenRouter Document Audio Inputs](https://openrouter.ai/docs/features/multimodal/audio) for models supporting audio inputs, also for more details)
542+
543+
Audio input is supported by some models in OpenRouter. You can provide audio input by using the `AudioContentData` DTO class as following:
544+
545+
```php
546+
$model = 'mistralai/voxtral-small-24b-2507'; // Audio input supported models: https://openrouter.ai/models?fmt=cards&input_modalities=audio
547+
$data = base64_encode('path/of/audio/file.mp3'); // Base64-encoded audio data
548+
549+
$audioContentData = new AudioContentData(
550+
type: AudioContentData::ALLOWED_TYPE, // it can only take input_audio for audio content
551+
input_audio: new InputAudioData(
552+
data: $data,
553+
format: AudioFormatType::MP3, // Supported formats: mp3, wav
554+
),
555+
);
556+
557+
$textContentData = new TextContentData(
558+
type: TextContentData::ALLOWED_TYPE,
559+
text: 'Please transcribe this audio file.',
560+
);
561+
562+
$messageData = new MessageData(
563+
content: [
564+
$textContentData,
565+
$audioContentData,
566+
],
567+
role: RoleType::USER,
568+
);
569+
570+
$chatData = new ChatData(
571+
messages: [
572+
$messageData,
573+
],
574+
model: $model,
575+
);
576+
577+
$response = LaravelOpenRouter::chatRequest($chatData);
578+
```
579+
580+
> [!NOTE]
581+
> Only `mp3` and `wav` formats are supported for audio inputs.
582+
>
583+
> And make sure to provide valid `base64-encoded` audio data.
584+
539585
#### Cost Request
540586

541587
To retrieve the cost of a generation, first make a `chat request` and obtain the `generationId`. Then, pass the generationId to the `costRequest` method:

src/DTO/AudioContentData.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MoeMizrak\LaravelOpenrouter\DTO;
6+
7+
use MoeMizrak\LaravelOpenrouter\Rules\AllowedValues;
8+
9+
/**
10+
* DTO for the audio contents.
11+
*
12+
* Class AudioContentData
13+
* @package MoeMizrak\LaravelOpenrouter\DTO
14+
*/
15+
final class AudioContentData extends DataTransferObject
16+
{
17+
/**
18+
* The allowed type for image content.
19+
*/
20+
public const ALLOWED_TYPE = 'input_audio';
21+
22+
/**
23+
* @inheritDoc
24+
*/
25+
public function __construct(
26+
/**
27+
* Type of the content. (i.e. input_audio)
28+
*
29+
* @var string
30+
*/
31+
#[AllowedValues([self::ALLOWED_TYPE])]
32+
public string $type = self::ALLOWED_TYPE,
33+
34+
/**
35+
* DTO of input audio.
36+
*
37+
* @var InputAudioData
38+
*/
39+
public InputAudioData $input_audio
40+
) {
41+
parent::__construct(...func_get_args());
42+
}
43+
44+
/**
45+
* @return array
46+
*/
47+
public function convertToArray(): array
48+
{
49+
return array_filter(
50+
[
51+
'type' => $this->type,
52+
'input_audio' => $this->input_audio?->convertToArray(),
53+
],
54+
fn($value) => $value !== null
55+
);
56+
}
57+
}

src/DTO/InputAudioData.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MoeMizrak\LaravelOpenrouter\DTO;
6+
7+
/**
8+
* DTO for the input audio which are data and format for the audio.
9+
*
10+
* Class InputAudioData
11+
* @package MoeMizrak\LaravelOpenrouter\DTO
12+
*/
13+
final class InputAudioData extends DataTransferObject
14+
{
15+
/**
16+
* @inheritDoc
17+
*/
18+
public function __construct(
19+
/**
20+
* base64 encoded audio data
21+
*
22+
* @var string
23+
*/
24+
public string $data,
25+
26+
/**
27+
* Optional, detail about the audio format
28+
* Supported audio formats are: mp3, wav.
29+
*
30+
* @var string|null
31+
*/
32+
public ?string $format = null
33+
) {
34+
parent::__construct(...func_get_args());
35+
}
36+
37+
/**
38+
* @return array
39+
*/
40+
public function convertToArray(): array
41+
{
42+
return array_filter(
43+
[
44+
'data' => $this->data,
45+
'format' => $this->format,
46+
],
47+
fn($value) => $value !== null
48+
);
49+
}
50+
}

src/Types/AudioFormatType.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MoeMizrak\LaravelOpenrouter\Types;
6+
7+
/**
8+
* Audio can be provided in different formats for now: mp3, wav.
9+
* See: https://openrouter.ai/docs/features/multimodal/audio
10+
*
11+
* Class AudioFormatType
12+
* @package MoeMizrak\LaravelOpenrouter\Types
13+
*/
14+
final readonly class AudioFormatType
15+
{
16+
/**
17+
* MP3 audio format
18+
*
19+
* @var string
20+
*/
21+
const MP3 = 'mp3';
22+
23+
/**
24+
* WAV audio format
25+
*
26+
* @var string
27+
*/
28+
const WAV = 'wav';
29+
}

tests/OpenRouterAPITest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
use GuzzleHttp\Psr7\Response;
77
use Illuminate\Support\Arr;
88
use Mockery\MockInterface;
9+
use MoeMizrak\LaravelOpenrouter\DTO\AudioContentData;
910
use MoeMizrak\LaravelOpenrouter\DTO\ChatData;
1011
use MoeMizrak\LaravelOpenrouter\DTO\CostResponseData;
1112
use MoeMizrak\LaravelOpenrouter\DTO\ImageContentPartData;
1213
use MoeMizrak\LaravelOpenrouter\DTO\ImageUrlData;
14+
use MoeMizrak\LaravelOpenrouter\DTO\InputAudioData;
1315
use MoeMizrak\LaravelOpenrouter\DTO\LimitResponseData;
1416
use MoeMizrak\LaravelOpenrouter\DTO\MessageData;
1517
use MoeMizrak\LaravelOpenrouter\DTO\ProviderPreferencesData;
@@ -20,6 +22,7 @@
2022
use MoeMizrak\LaravelOpenrouter\Exceptions\OpenRouterValidationException;
2123
use MoeMizrak\LaravelOpenrouter\Facades\LaravelOpenRouter;
2224
use MoeMizrak\LaravelOpenrouter\OpenRouterRequest;
25+
use MoeMizrak\LaravelOpenrouter\Types\AudioFormatType;
2326
use MoeMizrak\LaravelOpenrouter\Types\DataCollectionType;
2427
use MoeMizrak\LaravelOpenrouter\Types\EffortType;
2528
use MoeMizrak\LaravelOpenrouter\Types\RoleType;
@@ -538,6 +541,43 @@ public function it_successfully_sends_image_and_text_content_in_messages_in_the_
538541
$this->assertNotNull(Arr::get($response->choices[0], 'message.content'));
539542
}
540543

544+
// test for the audio content
545+
#[Test]
546+
public function it_successfully_sends_audio_in_content_in_messages_in_the_open_route_api_request()
547+
{
548+
/* SETUP */
549+
$data = base64_encode('fake-audio-data'); // Simulated base64 audio data
550+
$audioContentData = new AudioContentData(
551+
type: AudioContentData::ALLOWED_TYPE, // it can only take input_audio for audio content
552+
input_audio: new InputAudioData(
553+
data: $data,
554+
format: AudioFormatType::MP3,
555+
),
556+
);
557+
$messageData = new MessageData(
558+
content: [
559+
$audioContentData,
560+
],
561+
role: RoleType::USER,
562+
);
563+
$chatData = new ChatData(
564+
messages: [
565+
$messageData,
566+
],
567+
model: $this->model,
568+
max_tokens: $this->maxTokens,
569+
);
570+
$this->mockOpenRouter($this->mockBasicBody());
571+
572+
/* EXECUTE */
573+
$response = $this->api->chatRequest($chatData);
574+
575+
/* ASSERT */
576+
$this->generalTestAssertions($response);
577+
$this->assertEquals(RoleType::ASSISTANT, Arr::get($response->choices[0], 'message.role'));
578+
$this->assertNotNull(Arr::get($response->choices[0], 'message.content'));
579+
}
580+
541581
#[Test]
542582
public function it_successfully_sends_multiple_text_content_in_messages_in_the_open_route_api_request()
543583
{

0 commit comments

Comments
 (0)