Skip to content

Commit b42fe2f

Browse files
authored
fix(OpenAI): prevent registration of headers on all requests (#688)
* fix(OpenAI): prevent registration of headers on all requests * chore: add test for addHeader
1 parent 4ee51f1 commit b42fe2f

File tree

6 files changed

+58
-7
lines changed

6 files changed

+58
-7
lines changed

src/Contracts/TransporterContract.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
*/
1818
interface TransporterContract
1919
{
20+
/**
21+
* Adds a custom header that will be included in all subsequent requests.
22+
*/
23+
public function addHeader(string $name, string $value): self;
24+
2025
/**
2126
* Sends a request to a server expecting an object back.
2227
*

src/OpenAI.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ public static function client(string $apiKey, ?string $organization = null, ?str
1616
->withApiKey($apiKey)
1717
->withOrganization($organization)
1818
->withProject($project)
19-
->withHttpHeader('OpenAI-Beta', 'assistants=v2')
2019
->make();
2120
}
2221

src/Resources/Assistants.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ public function create(array $parameters): AssistantResponse
2727
$payload = Payload::create('assistants', $parameters);
2828

2929
/** @var Response<array{id: string, object: string, created_at: int, name: ?string, reasoning_effort?: ?string, description: ?string, model: string, instructions: ?string, tools: array<int, array{type: 'code_interpreter'}|array{type: 'file_search'}|array{type: 'function', function: array{description: string, name: string, parameters: array<string, mixed>}}>, tool_resources: array{code_interpreter?: array{file_ids: array<int,string>}, file_search?: array{vector_store_ids: array<int,string>}}, metadata: array<string, string>, temperature: ?float, top_p: ?float, response_format: string|array{type: 'text'|'json_object'}}> $response */
30-
$response = $this->transporter->requestObject($payload);
30+
$response = $this->transporter
31+
->addHeader('OpenAI-Beta', 'assistants=v2')
32+
->requestObject($payload);
3133

3234
return AssistantResponse::from($response->data(), $response->meta());
3335
}
@@ -42,7 +44,9 @@ public function retrieve(string $id): AssistantResponse
4244
$payload = Payload::retrieve('assistants', $id);
4345

4446
/** @var Response<array{id: string, object: string, created_at: int, name: ?string, reasoning_effort?: ?string, description: ?string, model: string, instructions: ?string, tools: array<int, array{type: 'code_interpreter'}|array{type: 'file_search'}|array{type: 'function', function: array{description: string, name: string, parameters: array<string, mixed>}}>, tool_resources: array{code_interpreter?: array{file_ids: array<int,string>}, file_search?: array{vector_store_ids: array<int,string>}}, metadata: array<string, string>, temperature: ?float, top_p: ?float, response_format: string|array{type: 'text'|'json_object'}}> $response */
45-
$response = $this->transporter->requestObject($payload);
47+
$response = $this->transporter
48+
->addHeader('OpenAI-Beta', 'assistants=v2')
49+
->requestObject($payload);
4650

4751
return AssistantResponse::from($response->data(), $response->meta());
4852
}
@@ -59,7 +63,9 @@ public function modify(string $id, array $parameters): AssistantResponse
5963
$payload = Payload::modify('assistants', $id, $parameters);
6064

6165
/** @var Response<array{id: string, object: string, created_at: int, name: ?string, reasoning_effort?: ?string, description: ?string, model: string, instructions: ?string, tools: array<int, array{type: 'code_interpreter'}|array{type: 'file_search'}|array{type: 'function', function: array{description: string, name: string, parameters: array<string, mixed>}}>, tool_resources: array{code_interpreter?: array{file_ids: array<int,string>}, file_search?: array{vector_store_ids: array<int,string>}}, metadata: array<string, string>, temperature: ?float, top_p: ?float, response_format: string|array{type: 'text'|'json_object'}}> $response */
62-
$response = $this->transporter->requestObject($payload);
66+
$response = $this->transporter
67+
->addHeader('OpenAI-Beta', 'assistants=v2')
68+
->requestObject($payload);
6369

6470
return AssistantResponse::from($response->data(), $response->meta());
6571
}
@@ -74,7 +80,9 @@ public function delete(string $id): AssistantDeleteResponse
7480
$payload = Payload::delete('assistants', $id);
7581

7682
/** @var Response<array{id: string, object: string, deleted: bool}> $response */
77-
$response = $this->transporter->requestObject($payload);
83+
$response = $this->transporter
84+
->addHeader('OpenAI-Beta', 'assistants=v2')
85+
->requestObject($payload);
7886

7987
return AssistantDeleteResponse::from($response->data(), $response->meta());
8088
}
@@ -91,7 +99,9 @@ public function list(array $parameters = []): AssistantListResponse
9199
$payload = Payload::list('assistants', $parameters);
92100

93101
/** @var Response<array{object: string, data: array<int, array{id: string, object: string, created_at: int, name: ?string, reasoning_effort?: ?string, description: ?string, model: string, instructions: ?string, tools: array<int, array{type: 'code_interpreter'}|array{type: 'file_search'}|array{type: 'function', function: array{description: string, name: string, parameters: array<string, mixed>}}>, tool_resources: array{code_interpreter?: array{file_ids: array<int,string>}, file_search?: array{vector_store_ids: array<int,string>}}, metadata: array<string, string>, temperature: ?float, top_p: ?float, response_format: string|array{type: 'text'|'json_object'}}>, first_id: ?string, last_id: ?string, has_more: bool}> $response */
94-
$response = $this->transporter->requestObject($payload);
102+
$response = $this->transporter
103+
->addHeader('OpenAI-Beta', 'assistants=v2')
104+
->requestObject($payload);
95105

96106
return AssistantListResponse::from($response->data(), $response->meta());
97107
}

src/Transporters/HttpTransporter.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,23 @@ final class HttpTransporter implements TransporterContract
3434
public function __construct(
3535
private readonly ClientInterface $client,
3636
private readonly BaseUri $baseUri,
37-
private readonly Headers $headers,
37+
private Headers $headers,
3838
private readonly QueryParams $queryParams,
3939
private readonly Closure $streamHandler,
4040
) {
4141
// ..
4242
}
4343

44+
/**
45+
* {@inheritDoc}
46+
*/
47+
public function addHeader(string $name, string $value): self
48+
{
49+
$this->headers = $this->headers->withCustomHeader($name, $value);
50+
51+
return $this;
52+
}
53+
4454
/**
4555
* {@inheritDoc}
4656
*/

tests/Pest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ function mockClient(string $method, string $resource, array $params, Response|Ad
1515
{
1616
$transporter = Mockery::mock(TransporterContract::class);
1717

18+
$transporter
19+
->shouldReceive('addHeader')
20+
->zeroOrMoreTimes()
21+
->andReturnSelf();
22+
1823
$transporter
1924
->shouldReceive($methodName)
2025
->once()

tests/Transporters/HttpTransporter.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,3 +602,25 @@
602602
->and($e->getErrorType())->toBe('invalid_request_error');
603603
});
604604
});
605+
606+
test('addHeader does not blow out headers when adding one', function () {
607+
$payload = Payload::list('models');
608+
609+
$this->http->addHeader('X-Test', '1');
610+
611+
$response = new Response(200, ['Content-Type' => 'application/json; charset=utf-8', ...metaHeaders()], json_encode(['ok' => true]));
612+
613+
$this->client
614+
->shouldReceive('sendRequest')
615+
->once()
616+
->withArgs(function (Psr7Request $request) {
617+
expect($request->getHeaderLine('Authorization'))->toBe('Bearer foo')
618+
->and($request->getHeaderLine('Content-Type'))->toBe('application/json')
619+
->and($request->getHeaderLine('X-Test'))->toBe('1');
620+
621+
return true;
622+
})
623+
->andReturn($response);
624+
625+
$this->http->requestObject($payload);
626+
});

0 commit comments

Comments
 (0)