openai/openai-python

Public

mirrored fromhttps://github.com/openai/openai-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
dev/hayden/bedrock-aws-auth

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

tests/lib/test_bedrock.py

1208lines · modecode

1from __future__ import annotations
2
3import json
4from typing import Any, Union, Protocol, cast
5from pathlib import Path
6
7import httpx
8import pytest
9from httpx import URL
10from respx import MockRouter
11
12import openai.lib._bedrock_auth as bedrock_auth_module
13from openai import OpenAIError, NotFoundError
14from tests.utils import update_env
15from openai._types import Omit
16from openai.lib.bedrock import BedrockOpenAI, AsyncBedrockOpenAI
17
18Client = Union[BedrockOpenAI, AsyncBedrockOpenAI]
19
20RESPONSE_BODY: dict[str, Any] = {
21 "id": "resp_123",
22 "object": "response",
23 "created_at": 0,
24 "status": "completed",
25 "background": False,
26 "error": None,
27 "incomplete_details": None,
28 "instructions": None,
29 "max_output_tokens": None,
30 "max_tool_calls": None,
31 "model": "gpt-4o",
32 "output": [],
33 "parallel_tool_calls": True,
34 "previous_response_id": None,
35 "prompt_cache_key": None,
36 "reasoning": {"effort": None, "summary": None},
37 "safety_identifier": None,
38 "service_tier": "default",
39 "store": True,
40 "temperature": 1.0,
41 "text": {"format": {"type": "text"}, "verbosity": "medium"},
42 "tool_choice": "auto",
43 "tools": [],
44 "top_logprobs": 0,
45 "top_p": 1.0,
46 "truncation": "disabled",
47 "usage": {
48 "input_tokens": 0,
49 "input_tokens_details": {"cached_tokens": 0},
50 "output_tokens": 0,
51 "output_tokens_details": {"reasoning_tokens": 0},
52 "total_tokens": 0,
53 },
54 "user": None,
55 "metadata": {},
56}
57COMPACTED_RESPONSE_BODY: dict[str, Any] = {
58 "id": "resp_123",
59 "created_at": 0,
60 "object": "response.compaction",
61 "output": [],
62 "usage": RESPONSE_BODY["usage"],
63}
64INPUT_ITEMS_BODY: dict[str, Any] = {
65 "object": "list",
66 "data": [],
67 "first_id": "item_123",
68 "last_id": "item_123",
69 "has_more": False,
70}
71INPUT_TOKENS_BODY: dict[str, Any] = {
72 "object": "response.input_tokens",
73 "input_tokens": 1,
74}
75
76
77class MockRequestCall(Protocol):
78 request: httpx.Request
79
80
81class MockAwsCredentials:
82 def __init__(self, access_key: str, secret_key: str, token: str | None = None) -> None:
83 self.access_key = access_key
84 self.secret_key = secret_key
85 self.token = token
86
87
88def make_sync_client(**kwargs: Any) -> BedrockOpenAI:
89 return BedrockOpenAI(http_client=httpx.Client(trust_env=False), **kwargs)
90
91
92def make_async_client(**kwargs: Any) -> AsyncBedrockOpenAI:
93 return AsyncBedrockOpenAI(http_client=httpx.AsyncClient(trust_env=False), **kwargs)
94
95
96def response_created_sse() -> str:
97 event: dict[str, Any] = {"type": "response.created", "sequence_number": 0, "response": RESPONSE_BODY}
98 return f"event: response.created\ndata: {json.dumps(event)}\n\n"
99
100
101@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
102def test_region_derived_base_url(client_cls: type[Client]) -> None:
103 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION="us-east-1", AWS_DEFAULT_REGION=Omit()):
104 client = (
105 make_sync_client(api_key="token") if client_cls is BedrockOpenAI else make_async_client(api_key="token")
106 )
107
108 assert client.base_url == URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/")
109
110
111@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
112def test_bedrock_config_precedence(client_cls: type[Client]) -> None:
113 with update_env(
114 AWS_BEDROCK_BASE_URL="https://env.example.com/openai/v1",
115 AWS_BEARER_TOKEN_BEDROCK="env token",
116 AWS_REGION="us-east-1",
117 AWS_DEFAULT_REGION="us-west-2",
118 ):
119 client = (
120 make_sync_client(
121 base_url="https://explicit.example.com/openai/v1/responses",
122 api_key="explicit token",
123 )
124 if client_cls is BedrockOpenAI
125 else make_async_client(
126 base_url="https://explicit.example.com/openai/v1/responses",
127 api_key="explicit token",
128 )
129 )
130
131 assert client.base_url == URL("https://explicit.example.com/openai/v1/")
132 assert client.api_key == "explicit token"
133
134
135@pytest.mark.respx()
136def test_env_bearer_does_not_require_botocore(monkeypatch: pytest.MonkeyPatch, respx_mock: MockRouter) -> None:
137 real_import_module = bedrock_auth_module.importlib.import_module
138
139 def import_module(name: str) -> Any:
140 if name.startswith("botocore"):
141 raise ImportError(name)
142 return real_import_module(name)
143
144 monkeypatch.setattr(bedrock_auth_module.importlib, "import_module", import_module)
145 respx_mock.post("https://example.com/openai/v1/responses").mock(
146 return_value=httpx.Response(200, json=RESPONSE_BODY)
147 )
148 with update_env(
149 AWS_BEDROCK_BASE_URL="https://example.com/openai/v1",
150 AWS_BEARER_TOKEN_BEDROCK="env token",
151 ):
152 client = make_sync_client()
153
154 client.responses.create(model="gpt-4o", input="hello")
155
156 request = cast("list[MockRequestCall]", respx_mock.calls)[0].request
157 assert request.headers["Authorization"] == "Bearer env token"
158
159
160def test_empty_env_bearer_without_botocore_uses_aws_credentials(monkeypatch: pytest.MonkeyPatch) -> None:
161 real_import_module = bedrock_auth_module.importlib.import_module
162
163 def import_module(name: str) -> Any:
164 if name.startswith("botocore"):
165 raise ImportError(name)
166 return real_import_module(name)
167
168 monkeypatch.setattr(bedrock_auth_module.importlib, "import_module", import_module)
169 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_BEARER_TOKEN_BEDROCK="", AWS_REGION="us-east-1"):
170 client = make_sync_client()
171 with pytest.raises(OpenAIError, match="requires optional AWS dependencies"):
172 client.get("/models", cast_to=httpx.Response)
173
174
175@pytest.mark.respx()
176def test_env_bearer_does_not_use_botocore_bearer_auth(monkeypatch: pytest.MonkeyPatch, respx_mock: MockRouter) -> None:
177 auth_module = bedrock_auth_module.importlib.import_module("botocore.auth")
178 calls = 0
179 real_add_auth = auth_module.BearerAuth.add_auth
180
181 def add_auth(auth: object, request: object) -> None:
182 nonlocal calls
183 calls += 1
184 real_add_auth(auth, request)
185
186 monkeypatch.setattr(auth_module.BearerAuth, "add_auth", add_auth)
187 respx_mock.post("https://example.com/openai/v1/responses").mock(
188 return_value=httpx.Response(200, json=RESPONSE_BODY)
189 )
190 with update_env(AWS_BEARER_TOKEN_BEDROCK="env token"):
191 client = make_sync_client(base_url="https://example.com/openai/v1")
192
193 client.responses.create(model="gpt-4o", input="hello")
194
195 request = cast("list[MockRequestCall]", respx_mock.calls)[0].request
196 assert request.headers["Authorization"] == "Bearer env token"
197 assert calls == 0
198
199
200@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
201def test_empty_env_bearer_falls_back_to_aws_credentials(client_cls: type[Client]) -> None:
202 with update_env(AWS_BEARER_TOKEN_BEDROCK="", AWS_REGION="us-east-1"):
203 client = make_sync_client() if client_cls is BedrockOpenAI else make_async_client()
204
205 assert client.api_key == ""
206 assert client._uses_aws_auth()
207
208
209@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
210def test_bedrock_region_precedence(client_cls: type[Client]) -> None:
211 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION="us-east-1", AWS_DEFAULT_REGION="us-west-2"):
212 explicit_region_client = (
213 make_sync_client(aws_region="eu-west-1", api_key="token")
214 if client_cls is BedrockOpenAI
215 else make_async_client(aws_region="eu-west-1", api_key="token")
216 )
217 aws_region_client = (
218 make_sync_client(api_key="token") if client_cls is BedrockOpenAI else make_async_client(api_key="token")
219 )
220
221 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION="us-west-2"):
222 default_region_client = (
223 make_sync_client(api_key="token") if client_cls is BedrockOpenAI else make_async_client(api_key="token")
224 )
225
226 assert explicit_region_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/")
227 assert aws_region_client.base_url == URL("https://bedrock-mantle.us-east-1.api.aws/openai/v1/")
228 assert default_region_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
229
230
231@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
232def test_aws_profile_supplies_region(client_cls: type[Client], tmp_path: Path) -> None:
233 config_path = tmp_path / "config"
234 config_path.write_text("[profile production]\nregion = eu-central-1\n")
235 with update_env(
236 AWS_CONFIG_FILE=str(config_path),
237 AWS_BEDROCK_BASE_URL=Omit(),
238 AWS_REGION=Omit(),
239 AWS_DEFAULT_REGION=Omit(),
240 ):
241 client = (
242 make_sync_client(aws_profile="production")
243 if client_cls is BedrockOpenAI
244 else make_async_client(aws_profile="production")
245 )
246
247 assert client.aws_region == "eu-central-1"
248 assert client.base_url == URL("https://bedrock-mantle.eu-central-1.api.aws/openai/v1/")
249
250
251@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
252def test_normalizes_responses_url(client_cls: type[Client]) -> None:
253 client = (
254 make_sync_client(base_url="https://example.com/openai/v1/responses", api_key="token")
255 if client_cls is BedrockOpenAI
256 else make_async_client(base_url="https://example.com/openai/v1/responses", api_key="token")
257 )
258
259 assert client.base_url == URL("https://example.com/openai/v1/")
260
261
262@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
263def test_requires_endpoint_configuration(client_cls: type[Client]) -> None:
264 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()):
265 with pytest.raises(OpenAIError, match="Bedrock requires an AWS region"):
266 client_cls(api_key="token")
267
268
269@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
270def test_does_not_use_openai_api_key(client_cls: type[Client]) -> None:
271 with update_env(
272 OPENAI_API_KEY="openai token",
273 AWS_BEARER_TOKEN_BEDROCK=Omit(),
274 AWS_BEDROCK_BASE_URL="https://example.com/openai/v1",
275 AWS_REGION="us-east-1",
276 ):
277 client = make_sync_client() if client_cls is BedrockOpenAI else make_async_client()
278
279 assert client.api_key == ""
280 assert client._uses_aws_auth()
281
282
283@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
284def test_rejects_static_token_and_provider(client_cls: type[Client]) -> None:
285 with pytest.raises(OpenAIError, match="authentication is ambiguous"):
286 client_cls(
287 base_url="https://example.com/openai/v1",
288 api_key="token",
289 bedrock_token_provider=lambda: "provider token",
290 )
291
292
293@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
294def test_rejects_empty_explicit_bearer_token(client_cls: type[Client]) -> None:
295 with pytest.raises(OpenAIError, match="must not be empty"):
296 client_cls(base_url="https://example.com/openai/v1", api_key="")
297
298
299@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
300def test_rejects_bearer_and_aws_credentials(client_cls: type[Client]) -> None:
301 with pytest.raises(OpenAIError, match="authentication is ambiguous"):
302 client_cls(
303 base_url="https://example.com/openai/v1",
304 api_key="token",
305 aws_access_key_id="access key",
306 aws_secret_access_key="secret key",
307 )
308
309
310@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
311def test_rejects_partial_explicit_aws_credentials(client_cls: type[Client]) -> None:
312 with pytest.raises(OpenAIError, match="require both"):
313 client_cls(
314 base_url="https://example.com/openai/v1",
315 aws_region="us-east-1",
316 aws_access_key_id="access key",
317 )
318
319
320@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
321def test_requires_refreshable_tokens_to_use_provider_option(client_cls: type[Client]) -> None:
322 with pytest.raises(OpenAIError, match="bedrock_token_provider"):
323 client_cls(
324 base_url="https://example.com/openai/v1",
325 api_key=lambda: "provider token", # type: ignore[arg-type]
326 )
327
328
329@pytest.mark.respx()
330def test_token_provider_refresh_sync(respx_mock: MockRouter) -> None:
331 respx_mock.post("https://example.com/openai/v1/responses").mock(
332 side_effect=[
333 httpx.Response(500, json={"error": "server error"}),
334 httpx.Response(200, json=RESPONSE_BODY),
335 ]
336 )
337 tokens = iter(["first", "second"])
338 client = BedrockOpenAI(
339 base_url="https://example.com/openai/v1",
340 bedrock_token_provider=lambda: next(tokens),
341 http_client=httpx.Client(trust_env=False),
342 max_retries=1,
343 )
344
345 client.responses.create(model="gpt-4o", input="hello")
346
347 calls = cast("list[MockRequestCall]", respx_mock.calls)
348 assert calls[0].request.headers["Authorization"] == "Bearer first"
349 assert calls[1].request.headers["Authorization"] == "Bearer second"
350
351
352@pytest.mark.asyncio
353@pytest.mark.respx()
354async def test_token_provider_refresh_async(respx_mock: MockRouter) -> None:
355 respx_mock.post("https://example.com/openai/v1/responses").mock(
356 side_effect=[
357 httpx.Response(500, json={"error": "server error"}),
358 httpx.Response(200, json=RESPONSE_BODY),
359 ]
360 )
361 tokens = iter(["first", "second"])
362 client = AsyncBedrockOpenAI(
363 base_url="https://example.com/openai/v1",
364 bedrock_token_provider=lambda: next(tokens),
365 http_client=httpx.AsyncClient(trust_env=False),
366 max_retries=1,
367 )
368
369 await client.responses.create(model="gpt-4o", input="hello")
370
371 calls = cast("list[MockRequestCall]", respx_mock.calls)
372 assert calls[0].request.headers["Authorization"] == "Bearer first"
373 assert calls[1].request.headers["Authorization"] == "Bearer second"
374
375
376@pytest.mark.respx()
377def test_explicit_aws_credentials_override_ambient_bearer(respx_mock: MockRouter) -> None:
378 respx_mock.post("https://example.com/openai/v1/responses").mock(
379 return_value=httpx.Response(200, json=RESPONSE_BODY)
380 )
381 with update_env(AWS_BEARER_TOKEN_BEDROCK="ambient token"):
382 client = BedrockOpenAI(
383 base_url="https://example.com/openai/v1",
384 aws_region="us-east-1",
385 aws_access_key_id="access key",
386 aws_secret_access_key="secret key",
387 aws_session_token="session token",
388 http_client=httpx.Client(trust_env=False),
389 )
390
391 client.responses.create(model="gpt-4o", input="hello")
392
393 request = cast("list[MockRequestCall]", respx_mock.calls)[0].request
394 assert request.headers["Authorization"].startswith("AWS4-HMAC-SHA256 Credential=access key/")
395 assert request.headers["X-Amz-Security-Token"] == "session token"
396
397
398@pytest.mark.respx()
399def test_aws_credentials_provider_refreshes_before_retries(respx_mock: MockRouter) -> None:
400 respx_mock.post("https://example.com/openai/v1/responses").mock(
401 side_effect=[
402 httpx.Response(500, json={"error": "server error"}),
403 httpx.Response(200, json=RESPONSE_BODY),
404 ]
405 )
406 credentials = iter(
407 [
408 MockAwsCredentials("first access key", "first secret", "first session token"),
409 MockAwsCredentials("second access key", "second secret", "second session token"),
410 ]
411 )
412 client = BedrockOpenAI(
413 base_url="https://example.com/openai/v1",
414 aws_region="us-east-1",
415 aws_credentials_provider=lambda: next(credentials),
416 http_client=httpx.Client(trust_env=False),
417 max_retries=1,
418 )
419
420 client.responses.create(model="gpt-4o", input="hello")
421
422 calls = cast("list[MockRequestCall]", respx_mock.calls)
423 assert "Credential=first access key/" in calls[0].request.headers["Authorization"]
424 assert calls[0].request.headers["X-Amz-Security-Token"] == "first session token"
425 assert "Credential=second access key/" in calls[1].request.headers["Authorization"]
426 assert calls[1].request.headers["X-Amz-Security-Token"] == "second session token"
427
428
429def test_preserves_token_provider_across_with_options() -> None:
430 client = BedrockOpenAI(
431 base_url="https://example.com/openai/v1",
432 bedrock_token_provider=lambda: "provider token",
433 http_client=httpx.Client(trust_env=False),
434 )
435
436 copied_client = client.with_options(timeout=1)
437
438 assert copied_client._refresh_api_key() == "provider token"
439
440
441def test_preserves_environment_bearer_across_with_options() -> None:
442 requests: list[httpx.Request] = []
443
444 def handler(request: httpx.Request) -> httpx.Response:
445 requests.append(request)
446 return httpx.Response(200, request=request, json={})
447
448 with update_env(AWS_BEARER_TOKEN_BEDROCK="first token"):
449 client = BedrockOpenAI(
450 base_url="https://example.com/openai/v1",
451 http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
452 )
453
454 with update_env(AWS_BEARER_TOKEN_BEDROCK="second token"):
455 copied_client = client.with_options(timeout=1)
456 copied_client.get("/models", cast_to=httpx.Response)
457
458 assert copied_client.api_key == "first token"
459 assert requests[0].headers["Authorization"] == "Bearer first token"
460
461
462def test_environment_bearer_routing_copy_remains_mutable() -> None:
463 requests: list[httpx.Request] = []
464
465 def handler(request: httpx.Request) -> httpx.Response:
466 requests.append(request)
467 return httpx.Response(200, request=request, json={})
468
469 with update_env(AWS_BEARER_TOKEN_BEDROCK="first token"):
470 client = BedrockOpenAI(
471 aws_region="us-east-1",
472 http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
473 )
474 copied_client = client.with_options(aws_region="us-west-2")
475 copied_client.api_key = "second token"
476 copied_client.get("/models", cast_to=httpx.Response)
477
478 assert copied_client.api_key == "second token"
479 assert requests[0].headers["Authorization"] == "Bearer second token"
480
481
482def test_legacy_api_key_mutation_updates_requests_and_copies() -> None:
483 requests: list[httpx.Request] = []
484
485 def handler(request: httpx.Request) -> httpx.Response:
486 requests.append(request)
487 return httpx.Response(200, request=request, json={})
488
489 client = BedrockOpenAI(
490 base_url="https://example.com/openai/v1",
491 api_key="first token",
492 http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
493 )
494 client.api_key = "second token"
495 client.get("/models", cast_to=httpx.Response)
496 copied_client = client.with_options(timeout=1)
497 copied_client.get("/models", cast_to=httpx.Response)
498 client.api_key = "first token"
499 reverted_client = client.with_options(timeout=2)
500 reverted_client.get("/models", cast_to=httpx.Response)
501
502 assert copied_client.api_key == "second token"
503 assert reverted_client.api_key == "first token"
504 assert [request.headers["Authorization"] for request in requests] == [
505 "Bearer second token",
506 "Bearer second token",
507 "Bearer first token",
508 ]
509
510
511def test_legacy_api_key_mutation_switches_aws_client_to_bearer() -> None:
512 requests: list[httpx.Request] = []
513
514 def handler(request: httpx.Request) -> httpx.Response:
515 requests.append(request)
516 return httpx.Response(200, request=request, json={})
517
518 client = BedrockOpenAI(
519 base_url="https://example.com/openai/v1",
520 aws_region="us-east-1",
521 aws_access_key_id="access key",
522 aws_secret_access_key="secret key",
523 http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
524 )
525 client.api_key = "bearer token"
526 client.get("/models", cast_to=httpx.Response, options={"follow_redirects": True})
527
528 assert requests[0].headers["Authorization"] == "Bearer bearer token"
529
530
531def test_explicit_aws_copy_override_wins_over_mutated_api_key() -> None:
532 client = BedrockOpenAI(
533 base_url="https://example.com/openai/v1",
534 api_key="first token",
535 http_client=httpx.Client(trust_env=False),
536 )
537 client.api_key = "second token"
538
539 copied_client = client.with_options(
540 aws_region="us-east-1",
541 aws_access_key_id="access key",
542 aws_secret_access_key="secret key",
543 )
544
545 assert copied_client._uses_aws_auth()
546 assert copied_client.api_key == ""
547
548
549def test_clearing_legacy_bearer_does_not_switch_to_aws_authentication() -> None:
550 network_calls = 0
551
552 def handler(request: httpx.Request) -> httpx.Response:
553 nonlocal network_calls
554 network_calls += 1
555 return httpx.Response(200, request=request)
556
557 with update_env(AWS_ACCESS_KEY_ID="access key", AWS_SECRET_ACCESS_KEY="secret key"):
558 client = BedrockOpenAI(
559 base_url="https://example.com/openai/v1",
560 api_key="bearer token",
561 http_client=httpx.Client(transport=httpx.MockTransport(handler), trust_env=False),
562 )
563 client.api_key = None # type: ignore[assignment]
564 with pytest.raises(OpenAIError, match="bearer credential must not be empty"):
565 client.get("/models", cast_to=httpx.Response)
566
567 assert network_calls == 0
568
569
570def test_legacy_state_repr_does_not_expose_credentials() -> None:
571 client = BedrockOpenAI(
572 base_url="https://example.com/openai/v1",
573 aws_region="us-east-1",
574 aws_access_key_id="secret access key id",
575 aws_secret_access_key="secret access key",
576 aws_session_token="secret session token",
577 http_client=httpx.Client(trust_env=False),
578 )
579
580 assert "secret" not in repr(client._bedrock_state)
581
582 bearer_client = BedrockOpenAI(
583 base_url="https://example.com/openai/v1",
584 api_key="secret bearer token",
585 http_client=httpx.Client(trust_env=False),
586 )
587 assert "secret bearer token" not in repr(bearer_client._bedrock_runtime_signature)
588
589
590@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
591def test_direct_routing_mutations_survive_clone(client_cls: type[Client]) -> None:
592 client = (
593 make_sync_client(base_url="https://first.example/openai/v1", aws_region="us-east-1", api_key="token")
594 if client_cls is BedrockOpenAI
595 else make_async_client(
596 base_url="https://first.example/openai/v1",
597 aws_region="us-east-1",
598 api_key="token",
599 )
600 )
601 client.base_url = "https://second.example/openai/v1"
602 client.aws_region = "us-west-2"
603
604 copied_client = client.with_options(timeout=1)
605
606 assert copied_client.base_url == URL("https://second.example/openai/v1/")
607 assert copied_client.aws_region == "us-west-2"
608 assert not copied_client._bedrock_state.uses_region_derived_base_url
609
610
611@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
612def test_direct_region_mutation_survives_clone(client_cls: type[Client]) -> None:
613 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()):
614 client = (
615 make_sync_client(aws_region="us-east-1", api_key="token")
616 if client_cls is BedrockOpenAI
617 else make_async_client(aws_region="us-east-1", api_key="token")
618 )
619 client.aws_region = "us-west-2"
620 copied_client = client.with_options(timeout=1)
621
622 assert copied_client.aws_region == "us-west-2"
623 assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
624
625
626@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
627def test_direct_base_url_mutation_survives_auth_override(client_cls: type[Client]) -> None:
628 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()):
629 client = (
630 make_sync_client(
631 aws_region="us-east-1",
632 aws_access_key_id="access key",
633 aws_secret_access_key="secret key",
634 )
635 if client_cls is BedrockOpenAI
636 else make_async_client(
637 aws_region="us-east-1",
638 aws_access_key_id="access key",
639 aws_secret_access_key="secret key",
640 )
641 )
642 client.base_url = "https://custom.example/openai/v1"
643 copied_client = client.with_options(api_key="bearer token")
644
645 assert copied_client.base_url == URL("https://custom.example/openai/v1/")
646 assert not copied_client._uses_aws_auth()
647
648
649def test_preserves_aws_credentials_across_with_options() -> None:
650 client = BedrockOpenAI(
651 base_url="https://example.com/openai/v1",
652 aws_region="us-east-1",
653 aws_access_key_id="access key",
654 aws_secret_access_key="secret key",
655 http_client=httpx.Client(trust_env=False),
656 )
657
658 copied_client = client.with_options(timeout=1)
659
660 assert copied_client._uses_aws_auth()
661 assert copied_client._bedrock_state.aws_access_key_id == "access key"
662
663
664@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
665def test_preserves_default_chain_mode_across_with_options(client_cls: type[Client]) -> None:
666 with update_env(AWS_BEARER_TOKEN_BEDROCK=Omit(), AWS_REGION="us-east-1"):
667 client = make_sync_client() if client_cls is BedrockOpenAI else make_async_client()
668
669 with update_env(AWS_BEARER_TOKEN_BEDROCK="late bearer", AWS_REGION="us-east-1"):
670 copied_client = client.with_options(timeout=1)
671
672 assert copied_client._uses_aws_auth()
673 assert copied_client._bedrock_state.aws_profile is None
674 assert copied_client._bedrock_state.aws_access_key_id is None
675 assert copied_client._bedrock_state.aws_credentials_provider is None
676 assert copied_client.api_key == ""
677
678
679@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
680def test_preserves_region_derived_url_provenance_across_multiple_copies(client_cls: type[Client]) -> None:
681 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()):
682 client = (
683 make_sync_client(aws_region="us-east-1", api_key="token")
684 if client_cls is BedrockOpenAI
685 else make_async_client(aws_region="us-east-1", api_key="token")
686 )
687 copied_client = client.with_options(timeout=1).with_options(aws_region="eu-west-1")
688
689 assert copied_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/")
690
691
692@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
693def test_preserves_region_derived_url_after_auth_override(client_cls: type[Client]) -> None:
694 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()):
695 client = (
696 make_sync_client(aws_region="us-east-1", api_key="token")
697 if client_cls is BedrockOpenAI
698 else make_async_client(aws_region="us-east-1", api_key="token")
699 )
700 copied_client = client.with_options(
701 aws_access_key_id="access key",
702 aws_secret_access_key="secret key",
703 ).with_options(aws_region="us-west-2")
704
705 assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
706
707
708@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
709def test_blank_base_url_restores_region_derived_url_provenance(client_cls: type[Client]) -> None:
710 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()):
711 client = (
712 make_sync_client(aws_region="us-east-1", api_key="token")
713 if client_cls is BedrockOpenAI
714 else make_async_client(aws_region="us-east-1", api_key="token")
715 )
716 copied_client = client.with_options(base_url="").with_options(aws_region="eu-west-1")
717
718 assert copied_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/")
719
720
721@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
722def test_with_options_replaces_the_aws_credential_source(client_cls: type[Client], tmp_path: Path) -> None:
723 config_path = tmp_path / "config"
724 config_path.write_text("[profile other-profile]\nregion = us-east-1\n")
725 explicit_credentials_client = (
726 make_sync_client(
727 base_url="https://example.com/openai/v1",
728 aws_region="us-east-1",
729 aws_access_key_id="access key",
730 aws_secret_access_key="secret key",
731 )
732 if client_cls is BedrockOpenAI
733 else make_async_client(
734 base_url="https://example.com/openai/v1",
735 aws_region="us-east-1",
736 aws_access_key_id="access key",
737 aws_secret_access_key="secret key",
738 )
739 )
740 with update_env(AWS_CONFIG_FILE=str(config_path)):
741 profile_client = explicit_credentials_client.with_options(aws_profile="other-profile")
742
743 assert profile_client._bedrock_state.aws_profile == "other-profile"
744 assert profile_client._bedrock_state.aws_access_key_id is None
745 assert profile_client._bedrock_state.aws_secret_access_key is None
746
747 explicit_credentials_client = profile_client.with_options(
748 aws_access_key_id="replacement access key",
749 aws_secret_access_key="replacement secret key",
750 )
751
752 assert explicit_credentials_client._bedrock_state.aws_profile is None
753 assert explicit_credentials_client._bedrock_state.aws_access_key_id == "replacement access key"
754 assert explicit_credentials_client._bedrock_state.aws_secret_access_key == "replacement secret key"
755
756
757@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
758def test_with_options_replacing_profile_re_resolves_profile_region(client_cls: type[Client], tmp_path: Path) -> None:
759 config_path = tmp_path / "config"
760 config_path.write_text("[profile east]\nregion = us-east-1\n[profile west]\nregion = us-west-2\n")
761
762 with update_env(
763 AWS_CONFIG_FILE=str(config_path),
764 AWS_BEARER_TOKEN_BEDROCK=Omit(),
765 AWS_BEDROCK_BASE_URL=Omit(),
766 AWS_REGION=Omit(),
767 AWS_DEFAULT_REGION=Omit(),
768 ):
769 client = (
770 make_sync_client(aws_profile="east")
771 if client_cls is BedrockOpenAI
772 else make_async_client(aws_profile="east")
773 )
774
775 with update_env(
776 AWS_CONFIG_FILE=str(config_path),
777 AWS_BEARER_TOKEN_BEDROCK=Omit(),
778 AWS_BEDROCK_BASE_URL="https://late-environment.example.com/v1",
779 AWS_REGION=Omit(),
780 AWS_DEFAULT_REGION=Omit(),
781 ):
782 copied_client = client.with_options(aws_profile="west")
783
784 assert copied_client.aws_region == "us-west-2"
785 assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
786 assert copied_client._bedrock_state.aws_profile == "west"
787
788
789@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
790@pytest.mark.parametrize(
791 ("copy_kwargs", "uses_aws_auth"),
792 [
793 ({"api_key": "bearer token"}, False),
794 (
795 {
796 "aws_access_key_id": "access key",
797 "aws_secret_access_key": "secret key",
798 },
799 True,
800 ),
801 ],
802)
803def test_profile_derived_region_survives_auth_override(
804 client_cls: type[Client],
805 copy_kwargs: dict[str, Any],
806 uses_aws_auth: bool,
807 tmp_path: Path,
808) -> None:
809 config_path = tmp_path / "config"
810 config_path.write_text("[profile west]\nregion = us-west-2\n")
811
812 with update_env(
813 AWS_CONFIG_FILE=str(config_path),
814 AWS_BEARER_TOKEN_BEDROCK=Omit(),
815 AWS_BEDROCK_BASE_URL=Omit(),
816 AWS_REGION=Omit(),
817 AWS_DEFAULT_REGION=Omit(),
818 ):
819 client = (
820 make_sync_client(aws_profile="west")
821 if client_cls is BedrockOpenAI
822 else make_async_client(aws_profile="west")
823 )
824 copied_client = client.with_options(**copy_kwargs)
825
826 assert copied_client.aws_region == "us-west-2"
827 assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
828 assert copied_client._uses_aws_auth() is uses_aws_auth
829
830
831@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
832@pytest.mark.parametrize(
833 ("aws_profile", "config_contents"),
834 [
835 ("west", "[profile west]\nregion = us-west-2\n[profile east]\nregion = us-east-1\n"),
836 (None, "[default]\nregion = us-west-2\n[profile east]\nregion = us-east-1\n"),
837 ],
838 ids=["named-profile", "default-profile"],
839)
840def test_profile_derived_region_survives_credential_override_with_custom_base_url(
841 client_cls: type[Client], aws_profile: str | None, config_contents: str, tmp_path: Path
842) -> None:
843 config_path = tmp_path / "config"
844 config_path.write_text(config_contents)
845
846 with update_env(
847 AWS_CONFIG_FILE=str(config_path),
848 AWS_PROFILE=Omit(),
849 AWS_BEARER_TOKEN_BEDROCK=Omit(),
850 AWS_REGION=Omit(),
851 AWS_DEFAULT_REGION=Omit(),
852 ):
853 client = (
854 make_sync_client(base_url="https://custom.example/openai/v1", aws_profile=aws_profile)
855 if client_cls is BedrockOpenAI
856 else make_async_client(base_url="https://custom.example/openai/v1", aws_profile=aws_profile)
857 )
858 assert client.aws_region == "us-west-2"
859
860 client.aws_region = None
861 client.with_options(timeout=1)
862
863 copied_client = client.with_options(
864 aws_access_key_id="replacement access key",
865 aws_secret_access_key="replacement secret key",
866 )
867 profile_client = copied_client.with_options(aws_profile="east")
868
869 assert client.aws_region == "us-west-2"
870 assert copied_client.aws_region == "us-west-2"
871 assert copied_client._bedrock_state.aws_region == "us-west-2"
872 assert copied_client.base_url == URL("https://custom.example/openai/v1/")
873 assert copied_client._uses_aws_auth()
874 assert profile_client.aws_region == "us-east-1"
875 assert profile_client.base_url == URL("https://custom.example/openai/v1/")
876
877
878@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
879def test_with_options_switching_from_bearer_to_profile_re_resolves_environment_region(
880 client_cls: type[Client], tmp_path: Path
881) -> None:
882 config_path = tmp_path / "config"
883 config_path.write_text("[profile west]\nregion = us-west-2\n")
884
885 with update_env(
886 AWS_CONFIG_FILE=str(config_path),
887 AWS_BEDROCK_BASE_URL=Omit(),
888 AWS_REGION="us-east-1",
889 AWS_DEFAULT_REGION=Omit(),
890 ):
891 client = (
892 make_sync_client(api_key="token") if client_cls is BedrockOpenAI else make_async_client(api_key="token")
893 )
894
895 with update_env(
896 AWS_CONFIG_FILE=str(config_path),
897 AWS_BEARER_TOKEN_BEDROCK=Omit(),
898 AWS_BEDROCK_BASE_URL=Omit(),
899 AWS_REGION=Omit(),
900 AWS_DEFAULT_REGION=Omit(),
901 ):
902 copied_client = client.with_options(aws_profile="west")
903
904 assert copied_client.aws_region == "us-west-2"
905 assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
906
907
908def test_with_options_supports_subclasses_with_the_previous_constructor_signature() -> None:
909 class LegacyBedrockOpenAI(BedrockOpenAI):
910 def __init__(
911 self,
912 *,
913 api_key: str | None = None,
914 bedrock_token_provider: Any = None,
915 aws_region: str | None = None,
916 organization: str | None = None,
917 project: str | None = None,
918 webhook_secret: str | None = None,
919 base_url: str | httpx.URL | None = None,
920 websocket_base_url: str | httpx.URL | None = None,
921 timeout: Any = None,
922 max_retries: int = 2,
923 default_headers: Any = None,
924 default_query: Any = None,
925 http_client: httpx.Client | None = None,
926 _enforce_credentials: bool = True,
927 ) -> None:
928 super().__init__(
929 api_key=api_key,
930 bedrock_token_provider=bedrock_token_provider,
931 aws_region=aws_region,
932 organization=organization,
933 project=project,
934 webhook_secret=webhook_secret,
935 base_url=base_url,
936 websocket_base_url=websocket_base_url,
937 timeout=timeout,
938 max_retries=max_retries,
939 default_headers=default_headers,
940 default_query=default_query,
941 http_client=http_client,
942 _enforce_credentials=_enforce_credentials,
943 )
944
945 with update_env(AWS_BEDROCK_BASE_URL=Omit()):
946 client = LegacyBedrockOpenAI(
947 api_key="token",
948 aws_region="us-east-1",
949 http_client=httpx.Client(trust_env=False),
950 )
951
952 copied_client = client.with_options(timeout=1).with_options(aws_region="us-west-2")
953
954 assert isinstance(copied_client, LegacyBedrockOpenAI)
955 assert copied_client.api_key == "token"
956 assert copied_client.base_url == URL("https://bedrock-mantle.us-west-2.api.aws/openai/v1/")
957
958
959@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
960def test_with_options_api_key_replaces_token_provider(client_cls: type[Client]) -> None:
961 client = (
962 make_sync_client(
963 base_url="https://example.com/openai/v1",
964 bedrock_token_provider=lambda: "provider token",
965 )
966 if client_cls is BedrockOpenAI
967 else make_async_client(
968 base_url="https://example.com/openai/v1",
969 bedrock_token_provider=lambda: "provider token",
970 )
971 )
972
973 copied_client = client.with_options(api_key="static token")
974
975 assert copied_client.api_key == "static token"
976 assert copied_client._bedrock_token_provider is None
977
978
979@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
980def test_with_options_rejects_explicit_bearer_provider_and_aws_credentials(client_cls: type[Client]) -> None:
981 client = (
982 make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
983 if client_cls is BedrockOpenAI
984 else make_async_client(base_url="https://example.com/openai/v1", api_key="token")
985 )
986
987 with pytest.raises(OpenAIError, match="authentication is ambiguous"):
988 client.with_options(
989 bedrock_token_provider=lambda: "provider token",
990 aws_access_key_id="access key",
991 aws_secret_access_key="secret key",
992 )
993
994
995@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
996def test_with_options_aws_region_recomputes_region_derived_base_url(client_cls: type[Client]) -> None:
997 with update_env(AWS_BEDROCK_BASE_URL=Omit(), AWS_REGION=Omit(), AWS_DEFAULT_REGION=Omit()):
998 client = (
999 make_sync_client(aws_region="us-east-1", api_key="token")
1000 if client_cls is BedrockOpenAI
1001 else make_async_client(aws_region="us-east-1", api_key="token")
1002 )
1003
1004 copied_client = client.with_options(aws_region="eu-west-1")
1005
1006 assert copied_client.aws_region == "eu-west-1"
1007 assert copied_client.base_url == URL("https://bedrock-mantle.eu-west-1.api.aws/openai/v1/")
1008
1009
1010@pytest.mark.parametrize("client_cls", [BedrockOpenAI, AsyncBedrockOpenAI])
1011def test_with_options_aws_region_keeps_explicit_base_url(client_cls: type[Client]) -> None:
1012 client = (
1013 make_sync_client(base_url="https://example.com/openai/v1", aws_region="us-east-1", api_key="token")
1014 if client_cls is BedrockOpenAI
1015 else make_async_client(base_url="https://example.com/openai/v1", aws_region="us-east-1", api_key="token")
1016 )
1017
1018 copied_client = client.with_options(aws_region="eu-west-1")
1019
1020 assert copied_client.aws_region == "eu-west-1"
1021 assert copied_client.base_url == URL("https://example.com/openai/v1/")
1022
1023
1024@pytest.mark.parametrize(
1025 "copy_kwargs",
1026 [
1027 {"admin_api_key": "admin token"},
1028 {"workload_identity": cast(Any, {})},
1029 ],
1030)
1031def test_rejects_non_bedrock_copy_auth(copy_kwargs: dict[str, Any]) -> None:
1032 client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
1033
1034 with pytest.raises(OpenAIError, match="only supports Bedrock bearer token or AWS credential authentication"):
1035 client.with_options(**copy_kwargs)
1036
1037
1038@pytest.mark.respx()
1039def test_passes_non_responses_resources_through(respx_mock: MockRouter) -> None:
1040 respx_mock.post("https://example.com/openai/v1/chat/completions").mock(
1041 return_value=httpx.Response(
1042 404,
1043 json={"error": {"message": "AWS does not support chat completions here"}},
1044 headers={"x-request-id": "req_chat"},
1045 )
1046 )
1047 client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
1048
1049 with pytest.raises(NotFoundError, match="AWS does not support chat completions here") as exc:
1050 client.chat.completions.create(model="gpt-4o", messages=[])
1051
1052 assert exc.value.request_id == "req_chat"
1053 calls = cast("list[MockRequestCall]", respx_mock.calls)
1054 assert calls[0].request.url == URL("https://example.com/openai/v1/chat/completions")
1055
1056
1057@pytest.mark.asyncio
1058@pytest.mark.respx()
1059async def test_passes_non_responses_resources_through_async(respx_mock: MockRouter) -> None:
1060 respx_mock.post("https://example.com/openai/v1/chat/completions").mock(
1061 return_value=httpx.Response(
1062 404,
1063 json={"error": {"message": "AWS does not support chat completions here"}},
1064 headers={"x-request-id": "req_chat"},
1065 )
1066 )
1067 client = make_async_client(base_url="https://example.com/openai/v1", api_key="token")
1068
1069 with pytest.raises(NotFoundError, match="AWS does not support chat completions here") as exc:
1070 await client.chat.completions.create(model="gpt-4o", messages=[])
1071
1072 assert exc.value.request_id == "req_chat"
1073 calls = cast("list[MockRequestCall]", respx_mock.calls)
1074 assert calls[0].request.url == URL("https://example.com/openai/v1/chat/completions")
1075
1076
1077@pytest.mark.respx()
1078def test_passes_responses_features_through(respx_mock: MockRouter) -> None:
1079 respx_mock.post("https://example.com/openai/v1/responses").mock(
1080 return_value=httpx.Response(200, json=RESPONSE_BODY)
1081 )
1082 client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
1083
1084 response = client.responses.create(
1085 model="gpt-4o",
1086 input="hello",
1087 tools=[{"type": "web_search_preview"}], # type: ignore[list-item]
1088 )
1089
1090 assert response.id == "resp_123"
1091 calls = cast("list[MockRequestCall]", respx_mock.calls)
1092 assert json.loads(calls[0].request.content)["tools"] == [{"type": "web_search_preview"}]
1093
1094
1095@pytest.mark.respx()
1096def test_passes_admin_security_routes_through(respx_mock: MockRouter) -> None:
1097 respx_mock.get("https://example.com/openai/v1/organization/invites").mock(
1098 return_value=httpx.Response(
1099 404,
1100 json={"error": {"message": "AWS does not support organization invites here"}},
1101 headers={"x-request-id": "req_admin"},
1102 )
1103 )
1104 client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
1105
1106 with pytest.raises(NotFoundError, match="AWS does not support organization invites here"):
1107 list(client.admin.organization.invites.list())
1108
1109 calls = cast("list[MockRequestCall]", respx_mock.calls)
1110 assert calls[0].request.headers["Authorization"] == "Bearer token"
1111
1112
1113@pytest.mark.respx()
1114def test_refreshes_token_provider_for_admin_security_routes(respx_mock: MockRouter) -> None:
1115 respx_mock.get("https://example.com/openai/v1/organization/invites").mock(
1116 side_effect=[
1117 httpx.Response(500, json={"error": "server error"}),
1118 httpx.Response(
1119 404,
1120 json={"error": {"message": "AWS does not support organization invites here"}},
1121 headers={"x-request-id": "req_admin"},
1122 ),
1123 ]
1124 )
1125 tokens = iter(["first", "second"])
1126 client = BedrockOpenAI(
1127 base_url="https://example.com/openai/v1",
1128 bedrock_token_provider=lambda: next(tokens),
1129 http_client=httpx.Client(trust_env=False),
1130 max_retries=1,
1131 )
1132
1133 with pytest.raises(NotFoundError, match="AWS does not support organization invites here"):
1134 list(client.admin.organization.invites.list())
1135
1136 calls = cast("list[MockRequestCall]", respx_mock.calls)
1137 assert calls[0].request.headers["Authorization"] == "Bearer first"
1138 assert calls[1].request.headers["Authorization"] == "Bearer second"
1139
1140
1141@pytest.mark.respx()
1142def test_allows_responses_http_methods(respx_mock: MockRouter) -> None:
1143 respx_mock.post("https://example.com/openai/v1/responses").mock(
1144 return_value=httpx.Response(200, json=RESPONSE_BODY)
1145 )
1146 respx_mock.get("https://example.com/openai/v1/responses/resp_123?starting_after=1&stream=true").mock(
1147 return_value=httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"})
1148 )
1149 respx_mock.get("https://example.com/openai/v1/responses/resp_123?stream=true").mock(
1150 return_value=httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"})
1151 )
1152 respx_mock.get("https://example.com/openai/v1/responses/resp_123").mock(
1153 return_value=httpx.Response(200, json=RESPONSE_BODY)
1154 )
1155 respx_mock.post("https://example.com/openai/v1/responses/resp_123/cancel").mock(
1156 return_value=httpx.Response(200, json=RESPONSE_BODY)
1157 )
1158 respx_mock.post("https://example.com/openai/v1/responses/compact").mock(
1159 return_value=httpx.Response(200, json=COMPACTED_RESPONSE_BODY)
1160 )
1161 respx_mock.get("https://example.com/openai/v1/responses/resp_123/input_items").mock(
1162 return_value=httpx.Response(200, json=INPUT_ITEMS_BODY)
1163 )
1164 respx_mock.post("https://example.com/openai/v1/responses/input_tokens").mock(
1165 return_value=httpx.Response(200, json=INPUT_TOKENS_BODY)
1166 )
1167 client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
1168
1169 assert client.responses.create(model="gpt-4o", input="hello", background=True).id == "resp_123"
1170 assert client.responses.retrieve("resp_123").id == "resp_123"
1171 assert [event.type for event in client.responses.retrieve("resp_123", starting_after=1, stream=True)] == [
1172 "response.created"
1173 ]
1174 assert [event.type for event in client.responses.retrieve("resp_123", stream=True)] == ["response.created"]
1175 assert client.responses.cancel("resp_123").id == "resp_123"
1176 assert client.responses.compact(model="gpt-4o").object == "response.compaction"
1177 assert list(client.responses.input_items.list("resp_123")) == []
1178 assert client.responses.input_tokens.count(model="gpt-4o", input="hello").input_tokens == 1
1179
1180 calls = cast("list[MockRequestCall]", respx_mock.calls)
1181 assert {call.request.headers["Authorization"] for call in calls} == {"Bearer token"}
1182
1183
1184@pytest.mark.respx()
1185def test_allows_sse_and_response_wrappers(respx_mock: MockRouter) -> None:
1186 respx_mock.post("https://example.com/openai/v1/responses").mock(
1187 side_effect=[
1188 httpx.Response(200, text=response_created_sse(), headers={"Content-Type": "text/event-stream"}),
1189 httpx.Response(200, json=RESPONSE_BODY),
1190 httpx.Response(200, json=RESPONSE_BODY),
1191 ]
1192 )
1193 client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
1194
1195 events = list(client.responses.create(model="gpt-4o", input="hello", stream=True))
1196 assert [event.type for event in events] == ["response.created"]
1197
1198 raw_response = client.responses.with_raw_response.create(model="gpt-4o", input="hello")
1199 assert raw_response.parse().id == "resp_123"
1200
1201 with client.responses.with_streaming_response.create(model="gpt-4o", input="hello") as response:
1202 assert response.parse().id == "resp_123"
1203
1204
1205def test_does_not_guard_responses_connect() -> None:
1206 client = make_sync_client(base_url="https://example.com/openai/v1", api_key="token")
1207
1208 assert client.responses.connect() is not None
1209