Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement audio transcription endpoint - #59 #62

Merged
merged 3 commits into from
Mar 3, 2023
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
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,74 @@ $response->usage->totalTokens; // 21
$response->toArray(); // ['id' => 'chatcmpl-6pMyfj1HF4QXnfvjtfzvufZSQq6Eq', ...]
```

### `Audio` Resource

#### `transcribe`

Transcribes audio into the input language.

```php
$response = $client->audio()->transcribe([
'model' => 'whisper-1',
'file' => fopen('audio.mp3', 'r'),
'response_format' => 'verbose_json',
]);

$response->task; // 'transcribe'
$response->language; // 'english'
$response->duration; // 2.95
$reponse->text; // 'Hello, how are you?'

foreach ($response->segments as $segment) {
$segment->index; // 0
$segment->seek; // 0
$segment->start; // 0.0
$segment->end; // 4.0
$segment->text; // 'Hello, how are you?'
$segment->tokens; // [50364, 2425, 11, 577, 366, 291, 30, 50564]
$segment->temperature; // 0.0
$segment->avgLogprob; // -0.45045216878255206
$segment->compressionRatio; // 0.7037037037037037
$segment->noSpeechProb; // 0.1076972484588623
$segment->transient; // false
}

$response->toArray(); // ['task' => 'transcribe', ...]
```

#### `translate`

Translates audio into English.

```php
$response = $client->audio()->translate([
'model' => 'whisper-1',
'file' => fopen('german.mp3' 'r'),
'response_format' => 'verbose_json',
]);

$response->task; // 'translate'
$response->language; // 'english'
$response->duration; // 2.95
$reponse->text; // 'Hello, how are you?'

foreach ($response->segments as $segment) {
$segment->index; // 0
$segment->seek; // 0
$segment->start; // 0.0
$segment->end; // 4.0
$segment->text; // 'Hello, how are you?'
$segment->tokens; // [50364, 2425, 11, 577, 366, 291, 30, 50564]
$segment->temperature; // 0.0
$segment->avgLogprob; // -0.45045216878255206
$segment->compressionRatio; // 0.7037037037037037
$segment->noSpeechProb; // 0.1076972484588623
$segment->transient; // false
}

$response->toArray(); // ['task' => 'translate', ...]
```

### `Edits` Resource

#### `create`
Expand Down
11 changes: 11 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace OpenAI;

use OpenAI\Contracts\Transporter;
use OpenAI\Resources\Audio;
use OpenAI\Resources\Chat;
use OpenAI\Resources\Completions;
use OpenAI\Resources\Edits;
Expand Down Expand Up @@ -56,6 +57,16 @@ public function embeddings(): Embeddings
return new Embeddings($this->transporter);
}

/**
* Learn how to turn audio into text.
*
* @see https://platform.openai.com/docs/api-reference/audio
*/
public function audio(): Audio
{
return new Audio($this->transporter);
}

/**
* Given a prompt and an instruction, the model will return an edited version of the prompt.
*
Expand Down
2 changes: 1 addition & 1 deletion src/Contracts/Transporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface Transporter
*
* @throws ErrorException|UnserializableResponse|TransporterException
*/
public function requestObject(Payload $payload): array;
public function requestObject(Payload $payload): array|string;

/**
* Sends a content request to a server.
Expand Down
48 changes: 48 additions & 0 deletions src/Resources/Audio.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace OpenAI\Resources;

use OpenAI\Responses\Audio\TranscriptionResponse;
use OpenAI\Responses\Audio\TranslationResponse;
use OpenAI\ValueObjects\Transporter\Payload;

final class Audio
{
use Concerns\Transportable;

/**
* Transcribes audio into the input language.
*
* @see https://platform.openai.com/docs/api-reference/audio/create
*
* @param array<string, mixed> $parameters
*/
public function transcribe(array $parameters): TranscriptionResponse
{
$payload = Payload::upload('audio/transcriptions', $parameters);

/** @var array{task: ?string, language: ?string, duration: ?float, segments: array<int, array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>, text: string}|string $result */
$result = $this->transporter->requestObject($payload);

return TranscriptionResponse::from($result);
}

/**
* Translates audio into English.
*
* @see https://platform.openai.com/docs/api-reference/audio/create
*
* @param array<string, mixed> $parameters
*/
public function translate(array $parameters): TranslationResponse
{
$payload = Payload::upload('audio/translations', $parameters);

/** @var array{task: ?string, language: ?string, duration: ?float, segments: array<int, array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>, text: string}|string $result */
$result = $this->transporter->requestObject($payload);

return TranslationResponse::from($result);
}
}
72 changes: 72 additions & 0 deletions src/Responses/Audio/TranscriptionResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace OpenAI\Responses\Audio;

use OpenAI\Contracts\Response;
use OpenAI\Responses\Concerns\ArrayAccessible;

/**
* @implements Response<array{task: ?string, language: ?string, duration: ?float, segments: array<int, array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>, text: string}>
*/
final class TranscriptionResponse implements Response
{
/**
* @use ArrayAccessible<array{task: ?string, language: ?string, duration: ?float, segments: array<int, array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>, text: string}>
*/
use ArrayAccessible;

/**
* @param array<int, TranscriptionResponseSegment> $segments
*/
private function __construct(
public readonly ?string $task,
public readonly ?string $language,
public readonly ?float $duration,
public readonly array $segments,
public readonly string $text,
) {
}

/**
* Acts as static factory, and returns a new Response instance.
*
* @param array{task: ?string, language: ?string, duration: ?float, segments: array<int, array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>, text: string} $attributes
*/
public static function from(array|string $attributes): self
{
if (is_string($attributes)) {
$attributes = ['text' => $attributes];
}

$segments = isset($attributes['segments']) ? array_map(fn (array $result): TranscriptionResponseSegment => TranscriptionResponseSegment::from(
$result
), $attributes['segments']) : [];

return new self(
$attributes['task'] ?? null,
$attributes['language'] ?? null,
$attributes['duration'] ?? null,
$segments,
$attributes['text'],
);
}

/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [
'task' => $this->task,
'language' => $this->language,
'duration' => $this->duration,
'segments' => array_map(
static fn (TranscriptionResponseSegment $result): array => $result->toArray(),
$this->segments,
),
'text' => $this->text,
];
}
}
79 changes: 79 additions & 0 deletions src/Responses/Audio/TranscriptionResponseSegment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace OpenAI\Responses\Audio;

use OpenAI\Contracts\Response;
use OpenAI\Responses\Concerns\ArrayAccessible;

/**
* @implements Response<array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>
*/
final class TranscriptionResponseSegment implements Response
{
/**
* @use ArrayAccessible<array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>
*/
use ArrayAccessible;

/**
* @param array<int, int> $tokens
*/
private function __construct(
public readonly int $id,
public readonly int $seek,
public readonly float $start,
public readonly float $end,
public readonly string $text,
public readonly array $tokens,
public readonly float $temperature,
public readonly float $avgLogprob,
public readonly float $compressionRatio,
public readonly float $noSpeechProb,
public readonly bool $transient,
) {
}

/**
* Acts as static factory, and returns a new Response instance.
*
* @param array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool} $attributes
*/
public static function from(array $attributes): self
{
return new self(
$attributes['id'],
$attributes['seek'],
$attributes['start'],
$attributes['end'],
$attributes['text'],
$attributes['tokens'],
$attributes['temperature'],
$attributes['avg_logprob'],
$attributes['compression_ratio'],
$attributes['no_speech_prob'],
$attributes['transient'],
);
}

/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [
'id' => $this->id,
'seek' => $this->seek,
'start' => $this->start,
'end' => $this->end,
'text' => $this->text,
'tokens' => $this->tokens,
'temperature' => $this->temperature,
'avg_logprob' => $this->avgLogprob,
'compression_ratio' => $this->compressionRatio,
'no_speech_prob' => $this->noSpeechProb,
'transient' => $this->transient,
];
}
}
72 changes: 72 additions & 0 deletions src/Responses/Audio/TranslationResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace OpenAI\Responses\Audio;

use OpenAI\Contracts\Response;
use OpenAI\Responses\Concerns\ArrayAccessible;

/**
* @implements Response<array{task: ?string, language: ?string, duration: ?float, segments: array<int, array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>, text: string}>
*/
final class TranslationResponse implements Response
{
/**
* @use ArrayAccessible<array{task: ?string, language: ?string, duration: ?float, segments: array<int, array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>, text: string}>
*/
use ArrayAccessible;

/**
* @param array<int, TranslationResponseSegment> $segments
*/
private function __construct(
public readonly ?string $task,
public readonly ?string $language,
public readonly ?float $duration,
public readonly array $segments,
public readonly string $text,
) {
}

/**
* Acts as static factory, and returns a new Response instance.
*
* @param array{task: ?string, language: ?string, duration: ?float, segments: array<int, array{id: int, seek: int, start: float, end: float, text: string, tokens: array<int, int>, temperature: float, avg_logprob: float, compression_ratio: float, no_speech_prob: float, transient: bool}>, text: string} $attributes
*/
public static function from(array|string $attributes): self
{
if (is_string($attributes)) {
$attributes = ['text' => $attributes];
}

$segments = isset($attributes['segments']) ? array_map(fn (array $result): TranslationResponseSegment => TranslationResponseSegment::from(
$result
), $attributes['segments']) : [];

return new self(
$attributes['task'] ?? null,
$attributes['language'] ?? null,
$attributes['duration'] ?? null,
$segments,
$attributes['text'],
);
}

/**
* {@inheritDoc}
*/
public function toArray(): array
{
return [
'task' => $this->task,
'language' => $this->language,
'duration' => $this->duration,
'segments' => array_map(
static fn (TranslationResponseSegment $result): array => $result->toArray(),
$this->segments,
),
'text' => $this->text,
];
}
}
Loading