Skip to content

Commit d7e3238

Browse files
abikaliAyman Abi Aoun
and
Ayman Abi Aoun
authored
feat(OpenAI): Add Image Response usage (openai-php#571)
Co-authored-by: Ayman Abi Aoun <[email protected]>
1 parent c22f107 commit d7e3238

11 files changed

+400
-24
lines changed

src/Resources/Images.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function create(array $parameters): CreateResponse
2626
{
2727
$payload = Payload::create('images/generations', $parameters);
2828

29-
/** @var Response<array{created: int, data: array<int, array{url?: string, b64_json?: string, revised_prompt?: string}>}> $response */
29+
/** @var Response<array{created: int, data: array<int, array{url?: string, b64_json?: string, revised_prompt?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}> $response */
3030
$response = $this->transporter->requestObject($payload);
3131

3232
return CreateResponse::from($response->data(), $response->meta());
@@ -43,7 +43,7 @@ public function edit(array $parameters): EditResponse
4343
{
4444
$payload = Payload::upload('images/edits', $parameters);
4545

46-
/** @var Response<array{created: int, data: array<int, array{url?: string, b64_json?: string}>}> $response */
46+
/** @var Response<array{created: int, data: array<int, array{url?: string, b64_json?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}> $response */
4747
$response = $this->transporter->requestObject($payload);
4848

4949
return EditResponse::from($response->data(), $response->meta());
@@ -60,7 +60,7 @@ public function variation(array $parameters): VariationResponse
6060
{
6161
$payload = Payload::upload('images/variations', $parameters);
6262

63-
/** @var Response<array{created: int, data: array<int, array{url?: string, b64_json?: string}>}> $response */
63+
/** @var Response<array{created: int, data: array<int, array{url?: string, b64_json?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}> $response */
6464
$response = $this->transporter->requestObject($payload);
6565

6666
return VariationResponse::from($response->data(), $response->meta());

src/Responses/Images/CreateResponse.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
use OpenAI\Testing\Responses\Concerns\Fakeable;
1313

1414
/**
15-
* @implements ResponseContract<array{created: int, data: array<int, array{url?: string, b64_json?: string, revised_prompt?: string}>}>
15+
* @implements ResponseContract<array{created: int, data: array<int, array{url?: string, b64_json?: string, revised_prompt?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}>
1616
*/
1717
final class CreateResponse implements ResponseContract, ResponseHasMetaInformationContract
1818
{
1919
/**
20-
* @use ArrayAccessible<array{created: int, data: array<int, array{url?: string, b64_json?: string, revised_prompt?: string}>}>
20+
* @use ArrayAccessible<array{created: int, data: array<int, array{url?: string, b64_json?: string, revised_prompt?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}>
2121
*/
2222
use ArrayAccessible;
2323

@@ -31,12 +31,13 @@ private function __construct(
3131
public readonly int $created,
3232
public readonly array $data,
3333
private readonly MetaInformation $meta,
34+
public readonly ?ImageResponseUsage $usage = null,
3435
) {}
3536

3637
/**
3738
* Acts as static factory, and returns a new Response instance.
3839
*
39-
* @param array{created: int, data: array<int, array{url?: string, b64_json?: string, revised_prompt?: string}>} $attributes
40+
* @param array{created: int, data: array<int, array{url?: string, b64_json?: string, revised_prompt?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}} $attributes
4041
*/
4142
public static function from(array $attributes, MetaInformation $meta): self
4243
{
@@ -48,6 +49,7 @@ public static function from(array $attributes, MetaInformation $meta): self
4849
$attributes['created'],
4950
$results,
5051
$meta,
52+
isset($attributes['usage']) ? ImageResponseUsage::from($attributes['usage']) : null,
5153
);
5254
}
5355

@@ -56,12 +58,18 @@ public static function from(array $attributes, MetaInformation $meta): self
5658
*/
5759
public function toArray(): array
5860
{
59-
return [
61+
$result = [
6062
'created' => $this->created,
6163
'data' => array_map(
6264
static fn (CreateResponseData $result): array => $result->toArray(),
6365
$this->data,
6466
),
6567
];
68+
69+
if ($this->usage !== null) {
70+
$result['usage'] = $this->usage->toArray();
71+
}
72+
73+
return $result;
6674
}
6775
}

src/Responses/Images/EditResponse.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
use OpenAI\Testing\Responses\Concerns\Fakeable;
1313

1414
/**
15-
* @implements ResponseContract<array{created: int, data: array<int, array{url?: string, b64_json?: string}>}>
15+
* @implements ResponseContract<array{created: int, data: array<int, array{url?: string, b64_json?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}>
1616
*/
1717
final class EditResponse implements ResponseContract, ResponseHasMetaInformationContract
1818
{
1919
/**
20-
* @use ArrayAccessible<array{created: int, data: array<int, array{url?: string, b64_json?: string}>}>
20+
* @use ArrayAccessible<array{created: int, data: array<int, array{url?: string, b64_json?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}>
2121
*/
2222
use ArrayAccessible;
2323

@@ -31,12 +31,13 @@ private function __construct(
3131
public readonly int $created,
3232
public readonly array $data,
3333
private readonly MetaInformation $meta,
34+
public readonly ?ImageResponseUsage $usage = null,
3435
) {}
3536

3637
/**
3738
* Acts as static factory, and returns a new Response instance.
3839
*
39-
* @param array{created: int, data: array<int, array{url?: string, b64_json?: string}>} $attributes
40+
* @param array{created: int, data: array<int, array{url?: string, b64_json?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}} $attributes
4041
*/
4142
public static function from(array $attributes, MetaInformation $meta): self
4243
{
@@ -48,6 +49,7 @@ public static function from(array $attributes, MetaInformation $meta): self
4849
$attributes['created'],
4950
$results,
5051
$meta,
52+
isset($attributes['usage']) ? ImageResponseUsage::from($attributes['usage']) : null,
5153
);
5254
}
5355

@@ -56,12 +58,18 @@ public static function from(array $attributes, MetaInformation $meta): self
5658
*/
5759
public function toArray(): array
5860
{
59-
return [
61+
$result = [
6062
'created' => $this->created,
6163
'data' => array_map(
6264
static fn (EditResponseData $result): array => $result->toArray(),
6365
$this->data,
6466
),
6567
];
68+
69+
if ($this->usage !== null) {
70+
$result['usage'] = $this->usage->toArray();
71+
}
72+
73+
return $result;
6674
}
6775
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Images;
6+
7+
final class ImageResponseUsage
8+
{
9+
private function __construct(
10+
public readonly int $totalTokens,
11+
public readonly int $inputTokens,
12+
public readonly int $outputTokens,
13+
public readonly ImageResponseUsageInputTokensDetails $inputTokensDetails,
14+
) {}
15+
16+
/**
17+
* @param array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}} $attributes
18+
*/
19+
public static function from(array $attributes): self
20+
{
21+
return new self(
22+
$attributes['total_tokens'],
23+
$attributes['input_tokens'],
24+
$attributes['output_tokens'],
25+
ImageResponseUsageInputTokensDetails::from($attributes['input_tokens_details']),
26+
);
27+
}
28+
29+
/**
30+
* @return array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}
31+
*/
32+
public function toArray(): array
33+
{
34+
return [
35+
'total_tokens' => $this->totalTokens,
36+
'input_tokens' => $this->inputTokens,
37+
'output_tokens' => $this->outputTokens,
38+
'input_tokens_details' => $this->inputTokensDetails->toArray(),
39+
];
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Images;
6+
7+
final class ImageResponseUsageInputTokensDetails
8+
{
9+
private function __construct(
10+
public readonly int $textTokens,
11+
public readonly int $imageTokens,
12+
) {}
13+
14+
/**
15+
* @param array{text_tokens: int, image_tokens: int} $attributes
16+
*/
17+
public static function from(array $attributes): self
18+
{
19+
return new self(
20+
$attributes['text_tokens'],
21+
$attributes['image_tokens'],
22+
);
23+
}
24+
25+
/**
26+
* @return array{text_tokens: int, image_tokens: int}
27+
*/
28+
public function toArray(): array
29+
{
30+
return [
31+
'text_tokens' => $this->textTokens,
32+
'image_tokens' => $this->imageTokens,
33+
];
34+
}
35+
}

src/Responses/Images/VariationResponse.php

+12-4
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
use OpenAI\Testing\Responses\Concerns\Fakeable;
1313

1414
/**
15-
* @implements ResponseContract<array{created: int, data: array<int, array{url?: string, b64_json?: string}>}>
15+
* @implements ResponseContract<array{created: int, data: array<int, array{url?: string, b64_json?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}>
1616
*/
1717
final class VariationResponse implements ResponseContract, ResponseHasMetaInformationContract
1818
{
1919
/**
20-
* @use ArrayAccessible<array{created: int, data: array<int, array{url?: string, b64_json?: string}>}>
20+
* @use ArrayAccessible<array{created: int, data: array<int, array{url?: string, b64_json?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}}>
2121
*/
2222
use ArrayAccessible;
2323

@@ -31,12 +31,13 @@ private function __construct(
3131
public readonly int $created,
3232
public readonly array $data,
3333
private readonly MetaInformation $meta,
34+
public readonly ?ImageResponseUsage $usage = null,
3435
) {}
3536

3637
/**
3738
* Acts as static factory, and returns a new Response instance.
3839
*
39-
* @param array{created: int, data: array<int, array{url?: string, b64_json?: string}>} $attributes
40+
* @param array{created: int, data: array<int, array{url?: string, b64_json?: string}>, usage?: array{total_tokens: int, input_tokens: int, output_tokens: int, input_tokens_details: array{text_tokens: int, image_tokens: int}}} $attributes
4041
*/
4142
public static function from(array $attributes, MetaInformation $meta): self
4243
{
@@ -48,6 +49,7 @@ public static function from(array $attributes, MetaInformation $meta): self
4849
$attributes['created'],
4950
$results,
5051
$meta,
52+
isset($attributes['usage']) ? ImageResponseUsage::from($attributes['usage']) : null,
5153
);
5254
}
5355

@@ -56,12 +58,18 @@ public static function from(array $attributes, MetaInformation $meta): self
5658
*/
5759
public function toArray(): array
5860
{
59-
return [
61+
$result = [
6062
'created' => $this->created,
6163
'data' => array_map(
6264
static fn (VariationResponseData $result): array => $result->toArray(),
6365
$this->data,
6466
),
6567
];
68+
69+
if ($this->usage !== null) {
70+
$result['usage'] = $this->usage->toArray();
71+
}
72+
73+
return $result;
6674
}
6775
}

tests/Fixtures/Image.php

+72
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,30 @@ function imageCreateWithB46Json(): array
4646
];
4747
}
4848

49+
/**
50+
* @return array<string, mixed>
51+
*/
52+
function imageCreateWithUsage(): array
53+
{
54+
return [
55+
'created' => 1664136088,
56+
'data' => [
57+
[
58+
'url' => 'https://openai.com/image.png',
59+
],
60+
],
61+
'usage' => [
62+
'total_tokens' => 100,
63+
'input_tokens' => 50,
64+
'output_tokens' => 50,
65+
'input_tokens_details' => [
66+
'text_tokens' => 10,
67+
'image_tokens' => 40,
68+
],
69+
],
70+
];
71+
}
72+
4973
/**
5074
* @return array<string, mixed>
5175
*/
@@ -76,6 +100,30 @@ function imageEditWithB46Json(): array
76100
];
77101
}
78102

103+
/**
104+
* @return array<string, mixed>
105+
*/
106+
function imageEditWithUsage(): array
107+
{
108+
return [
109+
'created' => 1664136088,
110+
'data' => [
111+
[
112+
'url' => 'https://openai.com/image.png',
113+
],
114+
],
115+
'usage' => [
116+
'total_tokens' => 100,
117+
'input_tokens' => 50,
118+
'output_tokens' => 50,
119+
'input_tokens_details' => [
120+
'text_tokens' => 10,
121+
'image_tokens' => 40,
122+
],
123+
],
124+
];
125+
}
126+
79127
/**
80128
* @return array<string, mixed>
81129
*/
@@ -105,3 +153,27 @@ function imageVariationWithB46Json(): array
105153
],
106154
];
107155
}
156+
157+
/**
158+
* @return array<string, mixed>
159+
*/
160+
function imageVariationWithUsage(): array
161+
{
162+
return [
163+
'created' => 1664136088,
164+
'data' => [
165+
[
166+
'url' => 'https://openai.com/image.png',
167+
],
168+
],
169+
'usage' => [
170+
'total_tokens' => 100,
171+
'input_tokens' => 50,
172+
'output_tokens' => 50,
173+
'input_tokens_details' => [
174+
'text_tokens' => 10,
175+
'image_tokens' => 40,
176+
],
177+
],
178+
];
179+
}

0 commit comments

Comments
 (0)