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"