openai/openai-python

Public

mirrored from https://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

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