openai/openai-python

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
codex/gate-pypi-publish

Branches

Tags

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

Clone

HTTPS

Download ZIP

tests/lib/test_azure.py

955lines · modecode

1from __future__ import annotations
2
3import logging
4from typing import Union, cast
5from typing_extensions import Literal, Protocol
6
7import httpx
8import pytest
9from respx import MockRouter
10
11from openai import OpenAIError
12from tests.utils import update_env
13from openai._types import Omit
14from openai._utils import SensitiveHeadersFilter, is_dict
15from openai._models import FinalRequestOptions
16from openai.lib.azure import AzureOpenAI, AsyncAzureOpenAI
17
18Client = Union[AzureOpenAI, AsyncAzureOpenAI]
19
20
21sync_client = AzureOpenAI(
22 api_version="2023-07-01",
23 api_key="example API key",
24 azure_endpoint="https://example-resource.azure.openai.com",
25)
26
27async_client = AsyncAzureOpenAI(
28 api_version="2023-07-01",
29 api_key="example API key",
30 azure_endpoint="https://example-resource.azure.openai.com",
31)
32
33
34class MockRequestCall(Protocol):
35 request: httpx.Request
36
37
38@pytest.mark.parametrize("client", [sync_client, async_client])
39def test_implicit_deployment_path(client: Client) -> None:
40 req = client._build_request(
41 FinalRequestOptions.construct(
42 method="post",
43 url="/chat/completions",
44 json_data={"model": "my-deployment-model"},
45 )
46 )
47 assert (
48 req.url
49 == "https://example-resource.azure.openai.com/openai/deployments/my-deployment-model/chat/completions?api-version=2023-07-01"
50 )
51
52
53@pytest.mark.parametrize(
54 "client,method",
55 [
56 (sync_client, "copy"),
57 (sync_client, "with_options"),
58 (async_client, "copy"),
59 (async_client, "with_options"),
60 ],
61)
62def test_client_copying(client: Client, method: Literal["copy", "with_options"]) -> None:
63 if method == "copy":
64 copied = client.copy()
65 else:
66 copied = client.with_options()
67
68 assert copied._custom_query == {"api-version": "2023-07-01"}
69
70
71@pytest.mark.parametrize(
72 "client",
73 [sync_client, async_client],
74)
75def test_client_copying_override_options(client: Client) -> None:
76 copied = client.copy(
77 api_version="2022-05-01",
78 )
79 assert copied._custom_query == {"api-version": "2022-05-01"}
80
81
82def test_enforce_credentials_false_sync() -> None:
83 with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
84 AzureOpenAI(
85 api_version="2024-02-01",
86 api_key=None,
87 azure_ad_token=None,
88 azure_ad_token_provider=None,
89 azure_endpoint="https://example-resource.azure.openai.com",
90 _enforce_credentials=False,
91 )
92
93
94@pytest.mark.respx()
95def test_enforce_credentials_false_sync_uses_default_api_key_header(respx_mock: MockRouter) -> None:
96 respx_mock.post(
97 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
98 ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
99
100 with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
101 client = AzureOpenAI(
102 api_version="2024-02-01",
103 api_key=None,
104 azure_ad_token=None,
105 azure_ad_token_provider=None,
106 azure_endpoint="https://example-resource.azure.openai.com",
107 default_headers={"api-key": "manual-api-key"},
108 _enforce_credentials=False,
109 )
110 client.chat.completions.create(messages=[], model="gpt-4")
111
112 calls = cast("list[MockRequestCall]", respx_mock.calls)
113 assert calls[0].request.headers.get("api-key") == "manual-api-key"
114 assert calls[0].request.headers.get("Authorization") is None
115
116
117@pytest.mark.respx()
118def test_enforce_credentials_false_sync_uses_request_authorization_header(respx_mock: MockRouter) -> None:
119 respx_mock.post(
120 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
121 ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
122
123 with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
124 client = AzureOpenAI(
125 api_version="2024-02-01",
126 api_key=None,
127 azure_ad_token=None,
128 azure_ad_token_provider=None,
129 azure_endpoint="https://example-resource.azure.openai.com",
130 _enforce_credentials=False,
131 )
132 client.chat.completions.create(
133 messages=[],
134 model="gpt-4",
135 extra_headers={"authorization": "Bearer manual-token"},
136 )
137
138 calls = cast("list[MockRequestCall]", respx_mock.calls)
139 assert calls[0].request.headers.get("Authorization") == "Bearer manual-token"
140 assert calls[0].request.headers.get("api-key") is None
141
142
143def test_enforce_credentials_true_sync() -> None:
144 with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
145 with pytest.raises(OpenAIError, match="Missing credentials"):
146 AzureOpenAI(
147 api_version="2024-02-01",
148 api_key=None,
149 azure_ad_token=None,
150 azure_ad_token_provider=None,
151 azure_endpoint="https://example-resource.azure.openai.com",
152 )
153
154
155def test_enforce_credentials_false_async() -> None:
156 with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
157 AsyncAzureOpenAI(
158 api_version="2024-02-01",
159 api_key=None,
160 azure_ad_token=None,
161 azure_ad_token_provider=None,
162 azure_endpoint="https://example-resource.azure.openai.com",
163 _enforce_credentials=False,
164 )
165
166
167@pytest.mark.asyncio
168@pytest.mark.respx()
169async def test_enforce_credentials_false_async_uses_default_api_key_header(respx_mock: MockRouter) -> None:
170 respx_mock.post(
171 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
172 ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
173
174 with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
175 client = AsyncAzureOpenAI(
176 api_version="2024-02-01",
177 api_key=None,
178 azure_ad_token=None,
179 azure_ad_token_provider=None,
180 azure_endpoint="https://example-resource.azure.openai.com",
181 default_headers={"api-key": "manual-api-key"},
182 _enforce_credentials=False,
183 )
184 await client.chat.completions.create(messages=[], model="gpt-4")
185
186 calls = cast("list[MockRequestCall]", respx_mock.calls)
187 assert calls[0].request.headers.get("api-key") == "manual-api-key"
188 assert calls[0].request.headers.get("Authorization") is None
189
190
191@pytest.mark.asyncio
192@pytest.mark.respx()
193async def test_enforce_credentials_false_async_uses_request_authorization_header(respx_mock: MockRouter) -> None:
194 respx_mock.post(
195 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
196 ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
197
198 with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
199 client = AsyncAzureOpenAI(
200 api_version="2024-02-01",
201 api_key=None,
202 azure_ad_token=None,
203 azure_ad_token_provider=None,
204 azure_endpoint="https://example-resource.azure.openai.com",
205 _enforce_credentials=False,
206 )
207 await client.chat.completions.create(
208 messages=[],
209 model="gpt-4",
210 extra_headers={"authorization": "Bearer manual-token"},
211 )
212
213 calls = cast("list[MockRequestCall]", respx_mock.calls)
214 assert calls[0].request.headers.get("Authorization") == "Bearer manual-token"
215 assert calls[0].request.headers.get("api-key") is None
216
217
218def test_enforce_credentials_true_async() -> None:
219 with update_env(AZURE_OPENAI_API_KEY=Omit(), AZURE_OPENAI_AD_TOKEN=Omit()):
220 with pytest.raises(OpenAIError, match="Missing credentials"):
221 AsyncAzureOpenAI(
222 api_version="2024-02-01",
223 api_key=None,
224 azure_ad_token=None,
225 azure_ad_token_provider=None,
226 azure_endpoint="https://example-resource.azure.openai.com",
227 )
228
229
230@pytest.mark.respx()
231def test_client_token_provider_refresh_sync(respx_mock: MockRouter) -> None:
232 respx_mock.post(
233 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
234 ).mock(
235 side_effect=[
236 httpx.Response(500, json={"error": "server error"}),
237 httpx.Response(200, json={"foo": "bar"}),
238 ]
239 )
240
241 counter = 0
242
243 def token_provider() -> str:
244 nonlocal counter
245
246 counter += 1
247
248 if counter == 1:
249 return "first"
250
251 return "second"
252
253 client = AzureOpenAI(
254 api_version="2024-02-01",
255 azure_ad_token_provider=token_provider,
256 azure_endpoint="https://example-resource.azure.openai.com",
257 )
258 client.chat.completions.create(messages=[], model="gpt-4")
259
260 calls = cast("list[MockRequestCall]", respx_mock.calls)
261
262 assert len(calls) == 2
263
264 assert calls[0].request.headers.get("Authorization") == "Bearer first"
265 assert calls[1].request.headers.get("Authorization") == "Bearer second"
266
267
268@pytest.mark.asyncio
269@pytest.mark.respx()
270async def test_client_token_provider_refresh_async(respx_mock: MockRouter) -> None:
271 respx_mock.post(
272 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01"
273 ).mock(
274 side_effect=[
275 httpx.Response(500, json={"error": "server error"}),
276 httpx.Response(200, json={"foo": "bar"}),
277 ]
278 )
279
280 counter = 0
281
282 def token_provider() -> str:
283 nonlocal counter
284
285 counter += 1
286
287 if counter == 1:
288 return "first"
289
290 return "second"
291
292 client = AsyncAzureOpenAI(
293 api_version="2024-02-01",
294 azure_ad_token_provider=token_provider,
295 azure_endpoint="https://example-resource.azure.openai.com",
296 )
297
298 await client.chat.completions.create(messages=[], model="gpt-4")
299
300 calls = cast("list[MockRequestCall]", respx_mock.calls)
301
302 assert len(calls) == 2
303
304 assert calls[0].request.headers.get("Authorization") == "Bearer first"
305 assert calls[1].request.headers.get("Authorization") == "Bearer second"
306
307
308class TestAzureLogging:
309 @pytest.fixture(autouse=True)
310 def logger_with_filter(self) -> logging.Logger:
311 logger = logging.getLogger("openai")
312 logger.setLevel(logging.DEBUG)
313 logger.addFilter(SensitiveHeadersFilter())
314 return logger
315
316 @pytest.mark.respx()
317 def test_azure_api_key_redacted(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None:
318 respx_mock.post(
319 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01"
320 ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
321
322 client = AzureOpenAI(
323 api_version="2024-06-01",
324 api_key="example_api_key",
325 azure_endpoint="https://example-resource.azure.openai.com",
326 )
327
328 with caplog.at_level(logging.DEBUG):
329 client.chat.completions.create(messages=[], model="gpt-4")
330
331 for record in caplog.records:
332 if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]):
333 assert record.args["headers"]["api-key"] == "<redacted>"
334
335 @pytest.mark.respx()
336 def test_azure_bearer_token_redacted(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None:
337 respx_mock.post(
338 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01"
339 ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
340
341 client = AzureOpenAI(
342 api_version="2024-06-01",
343 azure_ad_token="example_token",
344 azure_endpoint="https://example-resource.azure.openai.com",
345 )
346
347 with caplog.at_level(logging.DEBUG):
348 client.chat.completions.create(messages=[], model="gpt-4")
349
350 for record in caplog.records:
351 if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]):
352 assert record.args["headers"]["Authorization"] == "<redacted>"
353
354 @pytest.mark.asyncio
355 @pytest.mark.respx()
356 async def test_azure_api_key_redacted_async(self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None:
357 respx_mock.post(
358 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01"
359 ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
360
361 client = AsyncAzureOpenAI(
362 api_version="2024-06-01",
363 api_key="example_api_key",
364 azure_endpoint="https://example-resource.azure.openai.com",
365 )
366
367 with caplog.at_level(logging.DEBUG):
368 await client.chat.completions.create(messages=[], model="gpt-4")
369
370 for record in caplog.records:
371 if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]):
372 assert record.args["headers"]["api-key"] == "<redacted>"
373
374 @pytest.mark.asyncio
375 @pytest.mark.respx()
376 async def test_azure_bearer_token_redacted_async(
377 self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture
378 ) -> None:
379 respx_mock.post(
380 "https://example-resource.azure.openai.com/openai/deployments/gpt-4/chat/completions?api-version=2024-06-01"
381 ).mock(return_value=httpx.Response(200, json={"model": "gpt-4"}))
382
383 client = AsyncAzureOpenAI(
384 api_version="2024-06-01",
385 azure_ad_token="example_token",
386 azure_endpoint="https://example-resource.azure.openai.com",
387 )
388
389 with caplog.at_level(logging.DEBUG):
390 await client.chat.completions.create(messages=[], model="gpt-4")
391
392 for record in caplog.records:
393 if is_dict(record.args) and record.args.get("headers") and is_dict(record.args["headers"]):
394 assert record.args["headers"]["Authorization"] == "<redacted>"
395
396
397@pytest.mark.parametrize(
398 "client,base_url,api,json_data,expected",
399 [
400 # Deployment-based endpoints
401 # AzureOpenAI: No deployment specified
402 (
403 AzureOpenAI(
404 api_version="2024-02-01",
405 api_key="example API key",
406 azure_endpoint="https://example-resource.azure.openai.com",
407 ),
408 "https://example-resource.azure.openai.com/openai/",
409 "/chat/completions",
410 {"model": "deployment-body"},
411 "https://example-resource.azure.openai.com/openai/deployments/deployment-body/chat/completions?api-version=2024-02-01",
412 ),
413 # AzureOpenAI: Deployment specified
414 (
415 AzureOpenAI(
416 api_version="2024-02-01",
417 api_key="example API key",
418 azure_endpoint="https://example-resource.azure.openai.com",
419 azure_deployment="deployment-client",
420 ),
421 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/",
422 "/chat/completions",
423 {"model": "deployment-body"},
424 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/chat/completions?api-version=2024-02-01",
425 ),
426 # AzureOpenAI: "deployments" in the DNS name
427 (
428 AzureOpenAI(
429 api_version="2024-02-01",
430 api_key="example API key",
431 azure_endpoint="https://deployments.example-resource.azure.openai.com",
432 ),
433 "https://deployments.example-resource.azure.openai.com/openai/",
434 "/chat/completions",
435 {"model": "deployment-body"},
436 "https://deployments.example-resource.azure.openai.com/openai/deployments/deployment-body/chat/completions?api-version=2024-02-01",
437 ),
438 # AzureOpenAI: Deployment called deployments
439 (
440 AzureOpenAI(
441 api_version="2024-02-01",
442 api_key="example API key",
443 azure_endpoint="https://example-resource.azure.openai.com",
444 azure_deployment="deployments",
445 ),
446 "https://example-resource.azure.openai.com/openai/deployments/deployments/",
447 "/chat/completions",
448 {"model": "deployment-body"},
449 "https://example-resource.azure.openai.com/openai/deployments/deployments/chat/completions?api-version=2024-02-01",
450 ),
451 # AzureOpenAI: base_url and azure_deployment specified; ignored b/c not supported
452 (
453 AzureOpenAI( # type: ignore
454 api_version="2024-02-01",
455 api_key="example API key",
456 base_url="https://example.azure-api.net/PTU/",
457 azure_deployment="deployment-client",
458 ),
459 "https://example.azure-api.net/PTU/",
460 "/chat/completions",
461 {"model": "deployment-body"},
462 "https://example.azure-api.net/PTU/deployments/deployment-body/chat/completions?api-version=2024-02-01",
463 ),
464 # AsyncAzureOpenAI: No deployment specified
465 (
466 AsyncAzureOpenAI(
467 api_version="2024-02-01",
468 api_key="example API key",
469 azure_endpoint="https://example-resource.azure.openai.com",
470 ),
471 "https://example-resource.azure.openai.com/openai/",
472 "/chat/completions",
473 {"model": "deployment-body"},
474 "https://example-resource.azure.openai.com/openai/deployments/deployment-body/chat/completions?api-version=2024-02-01",
475 ),
476 # AsyncAzureOpenAI: Deployment specified
477 (
478 AsyncAzureOpenAI(
479 api_version="2024-02-01",
480 api_key="example API key",
481 azure_endpoint="https://example-resource.azure.openai.com",
482 azure_deployment="deployment-client",
483 ),
484 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/",
485 "/chat/completions",
486 {"model": "deployment-body"},
487 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/chat/completions?api-version=2024-02-01",
488 ),
489 # AsyncAzureOpenAI: "deployments" in the DNS name
490 (
491 AsyncAzureOpenAI(
492 api_version="2024-02-01",
493 api_key="example API key",
494 azure_endpoint="https://deployments.example-resource.azure.openai.com",
495 ),
496 "https://deployments.example-resource.azure.openai.com/openai/",
497 "/chat/completions",
498 {"model": "deployment-body"},
499 "https://deployments.example-resource.azure.openai.com/openai/deployments/deployment-body/chat/completions?api-version=2024-02-01",
500 ),
501 # AsyncAzureOpenAI: Deployment called deployments
502 (
503 AsyncAzureOpenAI(
504 api_version="2024-02-01",
505 api_key="example API key",
506 azure_endpoint="https://example-resource.azure.openai.com",
507 azure_deployment="deployments",
508 ),
509 "https://example-resource.azure.openai.com/openai/deployments/deployments/",
510 "/chat/completions",
511 {"model": "deployment-body"},
512 "https://example-resource.azure.openai.com/openai/deployments/deployments/chat/completions?api-version=2024-02-01",
513 ),
514 # AsyncAzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported
515 (
516 AsyncAzureOpenAI( # type: ignore
517 api_version="2024-02-01",
518 api_key="example API key",
519 base_url="https://example.azure-api.net/PTU/",
520 azure_deployment="deployment-client",
521 ),
522 "https://example.azure-api.net/PTU/",
523 "/chat/completions",
524 {"model": "deployment-body"},
525 "https://example.azure-api.net/PTU/deployments/deployment-body/chat/completions?api-version=2024-02-01",
526 ),
527 ],
528)
529def test_prepare_url_deployment_endpoint(
530 client: Client, base_url: str, api: str, json_data: dict[str, str], expected: str
531) -> None:
532 req = client._build_request(
533 FinalRequestOptions.construct(
534 method="post",
535 url=api,
536 json_data=json_data,
537 )
538 )
539 assert req.url == expected
540 assert client.base_url == base_url
541
542
543@pytest.mark.parametrize(
544 "client,base_url,api,json_data,expected",
545 [
546 # Non-deployment endpoints
547 # AzureOpenAI: No deployment specified
548 (
549 AzureOpenAI(
550 api_version="2024-02-01",
551 api_key="example API key",
552 azure_endpoint="https://example-resource.azure.openai.com",
553 ),
554 "https://example-resource.azure.openai.com/openai/",
555 "/models",
556 {},
557 "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01",
558 ),
559 # AzureOpenAI: No deployment specified
560 (
561 AzureOpenAI(
562 api_version="2024-02-01",
563 api_key="example API key",
564 azure_endpoint="https://example-resource.azure.openai.com",
565 ),
566 "https://example-resource.azure.openai.com/openai/",
567 "/assistants",
568 {"model": "deployment-body"},
569 "https://example-resource.azure.openai.com/openai/assistants?api-version=2024-02-01",
570 ),
571 # AzureOpenAI: Deployment specified
572 (
573 AzureOpenAI(
574 api_version="2024-02-01",
575 api_key="example API key",
576 azure_endpoint="https://example-resource.azure.openai.com",
577 azure_deployment="deployment-client",
578 ),
579 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/",
580 "/models",
581 {},
582 "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01",
583 ),
584 # AzureOpenAI: Deployment specified
585 (
586 AzureOpenAI(
587 api_version="2024-02-01",
588 api_key="example API key",
589 azure_endpoint="https://example-resource.azure.openai.com",
590 azure_deployment="deployment-client",
591 ),
592 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/",
593 "/assistants",
594 {"model": "deployment-body"},
595 "https://example-resource.azure.openai.com/openai/assistants?api-version=2024-02-01",
596 ),
597 # AzureOpenAI: "deployments" in the DNS name
598 (
599 AzureOpenAI(
600 api_version="2024-02-01",
601 api_key="example API key",
602 azure_endpoint="https://deployments.example-resource.azure.openai.com",
603 ),
604 "https://deployments.example-resource.azure.openai.com/openai/",
605 "/models",
606 {},
607 "https://deployments.example-resource.azure.openai.com/openai/models?api-version=2024-02-01",
608 ),
609 # AzureOpenAI: Deployment called "deployments"
610 (
611 AzureOpenAI(
612 api_version="2024-02-01",
613 api_key="example API key",
614 azure_endpoint="https://example-resource.azure.openai.com",
615 azure_deployment="deployments",
616 ),
617 "https://example-resource.azure.openai.com/openai/deployments/deployments/",
618 "/models",
619 {},
620 "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01",
621 ),
622 # AzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported
623 (
624 AzureOpenAI( # type: ignore
625 api_version="2024-02-01",
626 api_key="example API key",
627 base_url="https://example.azure-api.net/PTU/",
628 azure_deployment="deployment-client",
629 ),
630 "https://example.azure-api.net/PTU/",
631 "/models",
632 {},
633 "https://example.azure-api.net/PTU/models?api-version=2024-02-01",
634 ),
635 # AsyncAzureOpenAI: No deployment specified
636 (
637 AsyncAzureOpenAI(
638 api_version="2024-02-01",
639 api_key="example API key",
640 azure_endpoint="https://example-resource.azure.openai.com",
641 ),
642 "https://example-resource.azure.openai.com/openai/",
643 "/models",
644 {},
645 "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01",
646 ),
647 # AsyncAzureOpenAI: No deployment specified
648 (
649 AsyncAzureOpenAI(
650 api_version="2024-02-01",
651 api_key="example API key",
652 azure_endpoint="https://example-resource.azure.openai.com",
653 ),
654 "https://example-resource.azure.openai.com/openai/",
655 "/assistants",
656 {"model": "deployment-body"},
657 "https://example-resource.azure.openai.com/openai/assistants?api-version=2024-02-01",
658 ),
659 # AsyncAzureOpenAI: Deployment specified
660 (
661 AsyncAzureOpenAI(
662 api_version="2024-02-01",
663 api_key="example API key",
664 azure_endpoint="https://example-resource.azure.openai.com",
665 azure_deployment="deployment-client",
666 ),
667 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/",
668 "/models",
669 {},
670 "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01",
671 ),
672 # AsyncAzureOpenAI: Deployment specified
673 (
674 AsyncAzureOpenAI(
675 api_version="2024-02-01",
676 api_key="example API key",
677 azure_endpoint="https://example-resource.azure.openai.com",
678 azure_deployment="deployment-client",
679 ),
680 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/",
681 "/assistants",
682 {"model": "deployment-body"},
683 "https://example-resource.azure.openai.com/openai/assistants?api-version=2024-02-01",
684 ),
685 # AsyncAzureOpenAI: "deployments" in the DNS name
686 (
687 AsyncAzureOpenAI(
688 api_version="2024-02-01",
689 api_key="example API key",
690 azure_endpoint="https://deployments.example-resource.azure.openai.com",
691 ),
692 "https://deployments.example-resource.azure.openai.com/openai/",
693 "/models",
694 {},
695 "https://deployments.example-resource.azure.openai.com/openai/models?api-version=2024-02-01",
696 ),
697 # AsyncAzureOpenAI: Deployment called "deployments"
698 (
699 AsyncAzureOpenAI(
700 api_version="2024-02-01",
701 api_key="example API key",
702 azure_endpoint="https://example-resource.azure.openai.com",
703 azure_deployment="deployments",
704 ),
705 "https://example-resource.azure.openai.com/openai/deployments/deployments/",
706 "/models",
707 {},
708 "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01",
709 ),
710 # AsyncAzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported
711 (
712 AsyncAzureOpenAI( # type: ignore
713 api_version="2024-02-01",
714 api_key="example API key",
715 base_url="https://example.azure-api.net/PTU/",
716 azure_deployment="deployment-client",
717 ),
718 "https://example.azure-api.net/PTU/",
719 "/models",
720 {},
721 "https://example.azure-api.net/PTU/models?api-version=2024-02-01",
722 ),
723 ],
724)
725def test_prepare_url_nondeployment_endpoint(
726 client: Client, base_url: str, api: str, json_data: dict[str, str], expected: str
727) -> None:
728 req = client._build_request(
729 FinalRequestOptions.construct(
730 method="post",
731 url=api,
732 json_data=json_data,
733 )
734 )
735 assert req.url == expected
736 assert client.base_url == base_url
737
738
739@pytest.mark.parametrize(
740 "client,base_url,json_data,expected",
741 [
742 # Realtime endpoint
743 # AzureOpenAI: No deployment specified
744 (
745 AzureOpenAI(
746 api_version="2024-02-01",
747 api_key="example API key",
748 azure_endpoint="https://example-resource.azure.openai.com",
749 ),
750 "https://example-resource.azure.openai.com/openai/",
751 {"model": "deployment-body"},
752 "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-body",
753 ),
754 # AzureOpenAI: Deployment specified
755 (
756 AzureOpenAI(
757 api_version="2024-02-01",
758 api_key="example API key",
759 azure_endpoint="https://example-resource.azure.openai.com",
760 azure_deployment="deployment-client",
761 ),
762 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/",
763 {"model": "deployment-body"},
764 "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-client",
765 ),
766 # AzureOpenAI: "deployments" in the DNS name
767 (
768 AzureOpenAI(
769 api_version="2024-02-01",
770 api_key="example API key",
771 azure_endpoint="https://deployments.azure.openai.com",
772 ),
773 "https://deployments.azure.openai.com/openai/",
774 {"model": "deployment-body"},
775 "wss://deployments.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-body",
776 ),
777 # AzureOpenAI: Deployment called "deployments"
778 (
779 AzureOpenAI(
780 api_version="2024-02-01",
781 api_key="example API key",
782 azure_endpoint="https://example-resource.azure.openai.com",
783 azure_deployment="deployments",
784 ),
785 "https://example-resource.azure.openai.com/openai/deployments/deployments/",
786 {"model": "deployment-body"},
787 "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployments",
788 ),
789 # AzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported
790 (
791 AzureOpenAI( # type: ignore
792 api_version="2024-02-01",
793 api_key="example API key",
794 base_url="https://example.azure-api.net/PTU/",
795 azure_deployment="my-deployment",
796 ),
797 "https://example.azure-api.net/PTU/",
798 {"model": "deployment-body"},
799 "wss://example.azure-api.net/PTU/realtime?api-version=2024-02-01&deployment=deployment-body",
800 ),
801 # AzureOpenAI: websocket_base_url specified
802 (
803 AzureOpenAI(
804 api_version="2024-02-01",
805 api_key="example API key",
806 azure_endpoint="https://example-resource.azure.openai.com",
807 websocket_base_url="wss://example-resource.azure.openai.com/base",
808 ),
809 "https://example-resource.azure.openai.com/openai/",
810 {"model": "deployment-body"},
811 "wss://example-resource.azure.openai.com/base/realtime?api-version=2024-02-01&deployment=deployment-body",
812 ),
813 ],
814)
815def test_prepare_url_realtime(client: AzureOpenAI, base_url: str, json_data: dict[str, str], expected: str) -> None:
816 url, _ = client._configure_realtime(json_data["model"], {})
817 assert str(url) == expected
818 assert client.base_url == base_url
819
820
821@pytest.mark.parametrize(
822 "client,base_url,json_data,expected",
823 [
824 # AsyncAzureOpenAI: No deployment specified
825 (
826 AsyncAzureOpenAI(
827 api_version="2024-02-01",
828 api_key="example API key",
829 azure_endpoint="https://example-resource.azure.openai.com",
830 ),
831 "https://example-resource.azure.openai.com/openai/",
832 {"model": "deployment-body"},
833 "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-body",
834 ),
835 # AsyncAzureOpenAI: Deployment specified
836 (
837 AsyncAzureOpenAI(
838 api_version="2024-02-01",
839 api_key="example API key",
840 azure_endpoint="https://example-resource.azure.openai.com",
841 azure_deployment="deployment-client",
842 ),
843 "https://example-resource.azure.openai.com/openai/deployments/deployment-client/",
844 {"model": "deployment-body"},
845 "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-client",
846 ),
847 # AsyncAzureOpenAI: "deployments" in the DNS name
848 (
849 AsyncAzureOpenAI(
850 api_version="2024-02-01",
851 api_key="example API key",
852 azure_endpoint="https://deployments.azure.openai.com",
853 ),
854 "https://deployments.azure.openai.com/openai/",
855 {"model": "deployment-body"},
856 "wss://deployments.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployment-body",
857 ),
858 # AsyncAzureOpenAI: Deployment called "deployments"
859 (
860 AsyncAzureOpenAI(
861 api_version="2024-02-01",
862 api_key="example API key",
863 azure_endpoint="https://example-resource.azure.openai.com",
864 azure_deployment="deployments",
865 ),
866 "https://example-resource.azure.openai.com/openai/deployments/deployments/",
867 {"model": "deployment-body"},
868 "wss://example-resource.azure.openai.com/openai/realtime?api-version=2024-02-01&deployment=deployments",
869 ),
870 # AsyncAzureOpenAI: base_url and azure_deployment specified; azure_deployment ignored b/c not supported
871 (
872 AsyncAzureOpenAI( # type: ignore
873 api_version="2024-02-01",
874 api_key="example API key",
875 base_url="https://example.azure-api.net/PTU/",
876 azure_deployment="deployment-client",
877 ),
878 "https://example.azure-api.net/PTU/",
879 {"model": "deployment-body"},
880 "wss://example.azure-api.net/PTU/realtime?api-version=2024-02-01&deployment=deployment-body",
881 ),
882 # AsyncAzureOpenAI: websocket_base_url specified
883 (
884 AsyncAzureOpenAI(
885 api_version="2024-02-01",
886 api_key="example API key",
887 azure_endpoint="https://example-resource.azure.openai.com",
888 websocket_base_url="wss://example-resource.azure.openai.com/base",
889 ),
890 "https://example-resource.azure.openai.com/openai/",
891 {"model": "deployment-body"},
892 "wss://example-resource.azure.openai.com/base/realtime?api-version=2024-02-01&deployment=deployment-body",
893 ),
894 ],
895)
896async def test_prepare_url_realtime_async(
897 client: AsyncAzureOpenAI, base_url: str, json_data: dict[str, str], expected: str
898) -> None:
899 url, _ = await client._configure_realtime(json_data["model"], {})
900 assert str(url) == expected
901 assert client.base_url == base_url
902
903
904def test_client_sets_base_url(client: Client) -> None:
905 client = AzureOpenAI(
906 api_version="2024-02-01",
907 api_key="example API key",
908 azure_endpoint="https://example-resource.azure.openai.com",
909 azure_deployment="my-deployment",
910 )
911 assert client.base_url == "https://example-resource.azure.openai.com/openai/deployments/my-deployment/"
912
913 # (not recommended) user sets base_url to target different deployment
914 client.base_url = "https://example-resource.azure.openai.com/openai/deployments/different-deployment/"
915 req = client._build_request(
916 FinalRequestOptions.construct(
917 method="post",
918 url="/chat/completions",
919 json_data={"model": "placeholder"},
920 )
921 )
922 assert (
923 req.url
924 == "https://example-resource.azure.openai.com/openai/deployments/different-deployment/chat/completions?api-version=2024-02-01"
925 )
926 req = client._build_request(
927 FinalRequestOptions.construct(
928 method="post",
929 url="/models",
930 json_data={},
931 )
932 )
933 assert req.url == "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01"
934
935 # (not recommended) user sets base_url to remove deployment
936 client.base_url = "https://example-resource.azure.openai.com/openai/"
937 req = client._build_request(
938 FinalRequestOptions.construct(
939 method="post",
940 url="/chat/completions",
941 json_data={"model": "deployment"},
942 )
943 )
944 assert (
945 req.url
946 == "https://example-resource.azure.openai.com/openai/deployments/deployment/chat/completions?api-version=2024-02-01"
947 )
948 req = client._build_request(
949 FinalRequestOptions.construct(
950 method="post",
951 url="/models",
952 json_data={},
953 )
954 )
955 assert req.url == "https://example-resource.azure.openai.com/openai/models?api-version=2024-02-01"