@@ -49,6 +49,9 @@ def __init__(self) -> None:
49
49
50
50
51
51
class BaseAzureClient (BaseClient [_HttpxClientT , _DefaultStreamT ]):
52
+ _azure_endpoint : httpx .URL | None
53
+ _azure_deployment : str | None
54
+
52
55
@override
53
56
def _build_request (
54
57
self ,
@@ -58,11 +61,29 @@ def _build_request(
58
61
) -> httpx .Request :
59
62
if options .url in _deployments_endpoints and is_mapping (options .json_data ):
60
63
model = options .json_data .get ("model" )
61
- if model is not None and not "/deployments" in str (self .base_url ):
64
+ if model is not None and "/deployments" not in str (self .base_url . path ):
62
65
options .url = f"/deployments/{ model } { options .url } "
63
66
64
67
return super ()._build_request (options , retries_taken = retries_taken )
65
68
69
+ @override
70
+ def _prepare_url (self , url : str ) -> httpx .URL :
71
+ """Adjust the URL if the client was configured with an Azure endpoint + deployment
72
+ and the API feature being called is **not** a deployments-based endpoint
73
+ (i.e. requires /deployments/deployment-name in the URL path).
74
+ """
75
+ if self ._azure_deployment and self ._azure_endpoint and url not in _deployments_endpoints :
76
+ merge_url = httpx .URL (url )
77
+ if merge_url .is_relative_url :
78
+ merge_raw_path = (
79
+ self ._azure_endpoint .raw_path .rstrip (b"/" ) + b"/openai/" + merge_url .raw_path .lstrip (b"/" )
80
+ )
81
+ return self ._azure_endpoint .copy_with (raw_path = merge_raw_path )
82
+
83
+ return merge_url
84
+
85
+ return super ()._prepare_url (url )
86
+
66
87
67
88
class AzureOpenAI (BaseAzureClient [httpx .Client , Stream [Any ]], OpenAI ):
68
89
@overload
@@ -160,8 +181,8 @@ def __init__(
160
181
161
182
azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request.
162
183
163
- azure_deployment: A model deployment, if given sets the base client URL to include `/deployments/{azure_deployment}`.
164
- Note: this means you won't be able to use non-deployment endpoints. Not supported with Assistants APIs.
184
+ azure_deployment: A model deployment, if given with `azure_endpoint`, sets the base client URL to include `/deployments/{azure_deployment}`.
185
+ Not supported with Assistants APIs.
165
186
"""
166
187
if api_key is None :
167
188
api_key = os .environ .get ("AZURE_OPENAI_API_KEY" )
@@ -224,6 +245,8 @@ def __init__(
224
245
self ._api_version = api_version
225
246
self ._azure_ad_token = azure_ad_token
226
247
self ._azure_ad_token_provider = azure_ad_token_provider
248
+ self ._azure_deployment = azure_deployment if azure_endpoint else None
249
+ self ._azure_endpoint = httpx .URL (azure_endpoint ) if azure_endpoint else None
227
250
228
251
@override
229
252
def copy (
@@ -307,20 +330,30 @@ def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions:
307
330
308
331
return options
309
332
310
- def _configure_realtime (self , model : str , extra_query : Query ) -> tuple [Query , dict [str , str ]]:
333
+ def _configure_realtime (self , model : str , extra_query : Query ) -> tuple [httpx . URL , dict [str , str ]]:
311
334
auth_headers = {}
312
335
query = {
313
336
** extra_query ,
314
337
"api-version" : self ._api_version ,
315
- "deployment" : model ,
338
+ "deployment" : self . _azure_deployment or model ,
316
339
}
317
340
if self .api_key != "<missing API key>" :
318
341
auth_headers = {"api-key" : self .api_key }
319
342
else :
320
343
token = self ._get_azure_ad_token ()
321
344
if token :
322
345
auth_headers = {"Authorization" : f"Bearer { token } " }
323
- return query , auth_headers
346
+
347
+ if self .websocket_base_url is not None :
348
+ base_url = httpx .URL (self .websocket_base_url )
349
+ merge_raw_path = base_url .raw_path .rstrip (b"/" ) + b"/realtime"
350
+ realtime_url = base_url .copy_with (raw_path = merge_raw_path )
351
+ else :
352
+ base_url = self ._prepare_url ("/realtime" )
353
+ realtime_url = base_url .copy_with (scheme = "wss" )
354
+
355
+ url = realtime_url .copy_with (params = {** query })
356
+ return url , auth_headers
324
357
325
358
326
359
class AsyncAzureOpenAI (BaseAzureClient [httpx .AsyncClient , AsyncStream [Any ]], AsyncOpenAI ):
@@ -422,8 +455,8 @@ def __init__(
422
455
423
456
azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request.
424
457
425
- azure_deployment: A model deployment, if given sets the base client URL to include `/deployments/{azure_deployment}`.
426
- Note: this means you won't be able to use non-deployment endpoints. Not supported with Assistants APIs.
458
+ azure_deployment: A model deployment, if given with `azure_endpoint`, sets the base client URL to include `/deployments/{azure_deployment}`.
459
+ Not supported with Assistants APIs.
427
460
"""
428
461
if api_key is None :
429
462
api_key = os .environ .get ("AZURE_OPENAI_API_KEY" )
@@ -486,6 +519,8 @@ def __init__(
486
519
self ._api_version = api_version
487
520
self ._azure_ad_token = azure_ad_token
488
521
self ._azure_ad_token_provider = azure_ad_token_provider
522
+ self ._azure_deployment = azure_deployment if azure_endpoint else None
523
+ self ._azure_endpoint = httpx .URL (azure_endpoint ) if azure_endpoint else None
489
524
490
525
@override
491
526
def copy (
@@ -571,17 +606,27 @@ async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOp
571
606
572
607
return options
573
608
574
- async def _configure_realtime (self , model : str , extra_query : Query ) -> tuple [Query , dict [str , str ]]:
609
+ async def _configure_realtime (self , model : str , extra_query : Query ) -> tuple [httpx . URL , dict [str , str ]]:
575
610
auth_headers = {}
576
611
query = {
577
612
** extra_query ,
578
613
"api-version" : self ._api_version ,
579
- "deployment" : model ,
614
+ "deployment" : self . _azure_deployment or model ,
580
615
}
581
616
if self .api_key != "<missing API key>" :
582
617
auth_headers = {"api-key" : self .api_key }
583
618
else :
584
619
token = await self ._get_azure_ad_token ()
585
620
if token :
586
621
auth_headers = {"Authorization" : f"Bearer { token } " }
587
- return query , auth_headers
622
+
623
+ if self .websocket_base_url is not None :
624
+ base_url = httpx .URL (self .websocket_base_url )
625
+ merge_raw_path = base_url .raw_path .rstrip (b"/" ) + b"/realtime"
626
+ realtime_url = base_url .copy_with (raw_path = merge_raw_path )
627
+ else :
628
+ base_url = self ._prepare_url ("/realtime" )
629
+ realtime_url = base_url .copy_with (scheme = "wss" )
630
+
631
+ url = realtime_url .copy_with (params = {** query })
632
+ return url , auth_headers
0 commit comments