1212logger = logging .getLogger (__name__ )
1313
1414
15+ def _rewrite_openai_url (
16+ original_url : str , params : httpx .QueryParams
17+ ) -> httpx .URL | None :
18+ """Rewrite OpenAI URLs to UiPath gateway completions endpoint.
19+
20+ Handles three URL patterns:
21+ - responses: false -> .../openai/deployments/.../chat/completions?api-version=...
22+ - responses: true -> .../openai/responses?api-version=...
23+ - responses API base -> .../{model}?api-version=... (no /openai/ path)
24+
25+ All are rewritten to .../completions
26+ """
27+ if "/openai/deployments/" in original_url :
28+ base_url = original_url .split ("/openai/deployments/" )[0 ]
29+ elif "/openai/responses" in original_url :
30+ base_url = original_url .split ("/openai/responses" )[0 ]
31+ else :
32+ # Handle base URL case (no /openai/ path appended yet)
33+ # Strip query string to get base URL
34+ base_url = original_url .split ("?" )[0 ]
35+
36+ new_url_str = f"{ base_url } /completions"
37+ if params :
38+ return httpx .URL (new_url_str , params = params )
39+ return httpx .URL (new_url_str )
40+
41+
1542class UiPathURLRewriteTransport (httpx .AsyncHTTPTransport ):
1643 def __init__ (self , verify : bool = True , ** kwargs ):
1744 super ().__init__ (verify = verify , ** kwargs )
1845
1946 async def handle_async_request (self , request : httpx .Request ) -> httpx .Response :
20- original_url = str (request .url )
21-
22- if "/openai/deployments/" in original_url :
23- base_url = original_url .split ("/openai/deployments/" )[0 ]
24- query_string = request .url .params
25- new_url_str = f"{ base_url } /completions"
26- if query_string :
27- request .url = httpx .URL (new_url_str , params = query_string )
28- else :
29- request .url = httpx .URL (new_url_str )
47+ new_url = _rewrite_openai_url (str (request .url ), request .url .params )
48+ if new_url :
49+ request .url = new_url
3050
3151 return await super ().handle_async_request (request )
3252
@@ -36,16 +56,9 @@ def __init__(self, verify: bool = True, **kwargs):
3656 super ().__init__ (verify = verify , ** kwargs )
3757
3858 def handle_request (self , request : httpx .Request ) -> httpx .Response :
39- original_url = str (request .url )
40-
41- if "/openai/deployments/" in original_url :
42- base_url = original_url .split ("/openai/deployments/" )[0 ]
43- query_string = request .url .params
44- new_url_str = f"{ base_url } /completions"
45- if query_string :
46- request .url = httpx .URL (new_url_str , params = query_string )
47- else :
48- request .url = httpx .URL (new_url_str )
59+ new_url = _rewrite_openai_url (str (request .url ), request .url .params )
60+ if new_url :
61+ request .url = new_url
4962
5063 return super ().handle_request (request )
5164
@@ -58,6 +71,9 @@ def __init__(
5871 api_version : str = "2024-12-01-preview" ,
5972 org_id : Optional [str ] = None ,
6073 tenant_id : Optional [str ] = None ,
74+ agenthub_config : Optional [str ] = None ,
75+ extra_headers : Optional [dict [str , str ]] = None ,
76+ byo_connection_id : Optional [str ] = None ,
6177 ** kwargs ,
6278 ):
6379 org_id = org_id or os .getenv ("UIPATH_ORGANIZATION_ID" )
@@ -81,18 +97,24 @@ def __init__(
8197 self ._vendor = "openai"
8298 self ._model_name = model_name
8399 self ._url : Optional [str ] = None
100+ self ._agenthub_config = agenthub_config
101+ self ._byo_connection_id = byo_connection_id
102+ self ._extra_headers = extra_headers or {}
103+
104+ client_kwargs = get_httpx_client_kwargs ()
105+ verify = client_kwargs .get ("verify" , True )
84106
85107 super ().__init__ (
86108 azure_endpoint = self ._build_base_url (),
87109 model_name = model_name ,
88110 default_headers = self ._build_headers (token ),
89111 http_async_client = httpx .AsyncClient (
90- transport = UiPathURLRewriteTransport (verify = True ),
91- ** get_httpx_client_kwargs () ,
112+ transport = UiPathURLRewriteTransport (verify = verify ),
113+ ** client_kwargs ,
92114 ),
93115 http_client = httpx .Client (
94- transport = UiPathSyncURLRewriteTransport (verify = True ),
95- ** get_httpx_client_kwargs () ,
116+ transport = UiPathSyncURLRewriteTransport (verify = verify ),
117+ ** client_kwargs ,
96118 ),
97119 api_key = token ,
98120 api_version = api_version ,
@@ -105,10 +127,18 @@ def _build_headers(self, token: str) -> dict[str, str]:
105127 "X-UiPath-LlmGateway-ApiFlavor" : "auto" ,
106128 "Authorization" : f"Bearer { token } " ,
107129 }
130+
131+ if self ._agenthub_config :
132+ headers ["X-UiPath-AgentHub-Config" ] = self ._agenthub_config
133+ if self ._byo_connection_id :
134+ headers ["X-UiPath-LlmGateway-ByoIsConnectionId" ] = self ._byo_connection_id
108135 if job_key := os .getenv ("UIPATH_JOB_KEY" ):
109136 headers ["X-UiPath-JobKey" ] = job_key
110137 if process_key := os .getenv ("UIPATH_PROCESS_KEY" ):
111138 headers ["X-UiPath-ProcessKey" ] = process_key
139+
140+ # Allow extra_headers to override defaults
141+ headers .update (self ._extra_headers )
112142 return headers
113143
114144 @property
@@ -117,9 +147,9 @@ def endpoint(self) -> str:
117147 formatted_endpoint = vendor_endpoint .format (
118148 vendor = self ._vendor ,
119149 model = self ._model_name ,
120- api_version = self ._openai_api_version ,
121150 )
122- return formatted_endpoint .replace ("/completions" , "" )
151+ base_endpoint = formatted_endpoint .replace ("/completions" , "" )
152+ return f"{ base_endpoint } ?api-version={ self ._openai_api_version } "
123153
124154 def _build_base_url (self ) -> str :
125155 if not self ._url :
0 commit comments