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