uipath-langchain 0.0.103__py3-none-any.whl → 0.0.105__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of uipath-langchain might be problematic. Click here for more details.

@@ -11,6 +11,8 @@ from uipath import UiPath
11
11
 
12
12
  class ContextGroundingRetriever(BaseRetriever):
13
13
  index_name: str
14
+ folder_path: Optional[str] = None
15
+ folder_key: Optional[str] = None
14
16
  uipath_sdk: Optional[UiPath] = None
15
17
  number_of_results: Optional[int] = 10
16
18
 
@@ -24,6 +26,8 @@ class ContextGroundingRetriever(BaseRetriever):
24
26
  self.index_name,
25
27
  query,
26
28
  self.number_of_results if self.number_of_results is not None else 10,
29
+ folder_path=self.folder_path,
30
+ folder_key=self.folder_key,
27
31
  )
28
32
 
29
33
  return [
@@ -47,6 +51,8 @@ class ContextGroundingRetriever(BaseRetriever):
47
51
  self.index_name,
48
52
  query,
49
53
  self.number_of_results if self.number_of_results is not None else 10,
54
+ folder_path=self.folder_path,
55
+ folder_key=self.folder_key,
50
56
  )
51
57
 
52
58
  return [
@@ -0,0 +1,3 @@
1
+ from .context_grounding_vectorstore import ContextGroundingVectorStore
2
+
3
+ __all__ = ["ContextGroundingVectorStore"]
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath-langchain
3
- Version: 0.0.103
3
+ Version: 0.0.105
4
4
  Summary: UiPath Langchain
5
5
  Project-URL: Homepage, https://uipath.com
6
6
  Project-URL: Repository, https://github.com/UiPath/uipath-langchain-python
7
+ Project-URL: Documentation, https://uipath.github.io/uipath-python/
7
8
  Maintainer-email: Marius Cosareanu <marius.cosareanu@uipath.com>, Cristian Pufu <cristian.pufu@uipath.com>
8
9
  License-File: LICENSE
9
10
  Classifier: Development Status :: 3 - Alpha
@@ -23,8 +24,6 @@ Requires-Dist: langgraph>=0.2.70
23
24
  Requires-Dist: openai>=1.65.5
24
25
  Requires-Dist: pydantic-settings>=2.6.0
25
26
  Requires-Dist: python-dotenv>=1.0.1
26
- Requires-Dist: requests>=2.23.3
27
- Requires-Dist: types-requests>=2.32.0.20241016
28
27
  Requires-Dist: uipath<2.1.0,>=2.0.33
29
28
  Provides-Extra: langchain
30
29
  Description-Content-Type: text/markdown
@@ -19,25 +19,20 @@ uipath_langchain/_utils/_settings.py,sha256=MhwEVj4gVRSar0RBf2w2hTjO-5Qm-HpCuufq
19
19
  uipath_langchain/_utils/_sleep_policy.py,sha256=e9pHdjmcCj4CVoFM1jMyZFelH11YatsgWfpyrfXzKBQ,1251
20
20
  uipath_langchain/chat/__init__.py,sha256=WDcvy91ixvZ3Mq7Ae94g5CjyQwXovDBnEv1NlD5SXBE,116
21
21
  uipath_langchain/chat/models.py,sha256=sLz8yzEMUMSNsCFyywRxFwe2JisR3TP-n1vbeRKl9H8,10225
22
- uipath_langchain/chat/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- uipath_langchain/chat/utils/_chat_types.py,sha256=iTUh4gY8ML2yLzTQ7H4ZFXChA8RbPz7NgkJK4x7fSbs,1226
24
22
  uipath_langchain/embeddings/__init__.py,sha256=QICtYB58ZyqFfDQrEaO8lTEgAU5NuEKlR7iIrS0OBtc,156
25
23
  uipath_langchain/embeddings/embeddings.py,sha256=gntzTfwO1pHbgnXiPdfETJaaurvQWqxVUCH75VMah54,4274
26
24
  uipath_langchain/retrievers/__init__.py,sha256=rOn7PyyHgZ4pMnXWPkGqmuBmx8eGuo-Oyndo7Wm9IUU,108
27
- uipath_langchain/retrievers/context_grounding_retriever.py,sha256=CeVSMEz5xTQIladkzDLeQXGC1_ycW72gb0RB41JZeYA,2000
25
+ uipath_langchain/retrievers/context_grounding_retriever.py,sha256=YLCIwy89LhLnNqcM0YJ5mZoeNyCs5UiKD3Wly8gnW1E,2239
28
26
  uipath_langchain/tracers/AsyncUiPathTracer.py,sha256=VLqoegxa8J3VwMCkGicO626nSzyCQv-Zj14lZrWXA2Y,8900
29
27
  uipath_langchain/tracers/UiPathTracer.py,sha256=V5g1nlB0TI1wYvUIkfCEcAdhy4thqeMBmjflWtxc-_M,5601
30
28
  uipath_langchain/tracers/__init__.py,sha256=MwrQh6iuPXMS72S5EX0JdCAX0TKe-l7fIPGV3EG0Ssk,256
31
29
  uipath_langchain/tracers/_events.py,sha256=CJri76SSdu7rGJIkXurJ2C5sQahfSK4E5UWwWYdEAtE,922
32
30
  uipath_langchain/tracers/_instrument_traceable.py,sha256=0e841zVzcPWjOGtmBx0GeHbq3JoqsmWv6gVPzDOKNTM,13496
33
31
  uipath_langchain/tracers/_utils.py,sha256=JOT1tKMdvqjMDtj2WbmbOWMeMlTXBWavxWpogX7KlRA,1543
34
- uipath_langchain/utils/__init__.py,sha256=-w-4TD9ZnJDCpj4VIPXhJciukrmDJJbmnOFnhAkAaEU,81
35
- uipath_langchain/utils/_request_mixin.py,sha256=WFyTDyAthSci1DRwUwS21I3hLntD7HdVzYc0ZPoi3ys,18296
36
- uipath_langchain/utils/_settings.py,sha256=MhwEVj4gVRSar0RBf2w2hTjO-5Qm-HpCuufqN3gSWjA,3390
37
- uipath_langchain/utils/_sleep_policy.py,sha256=e9pHdjmcCj4CVoFM1jMyZFelH11YatsgWfpyrfXzKBQ,1251
32
+ uipath_langchain/vectorstores/__init__.py,sha256=w8qs1P548ud1aIcVA_QhBgf_jZDrRMK5Lono78yA8cs,114
38
33
  uipath_langchain/vectorstores/context_grounding_vectorstore.py,sha256=eTa5sX43-ydB1pj9VNHUPbB-hC36fZK_CGrNe5U2Nrw,9393
39
- uipath_langchain-0.0.103.dist-info/METADATA,sha256=APsivdYkuoM0ek-as3rboX81hQmseePmqz0Tp-OrD-s,4177
40
- uipath_langchain-0.0.103.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
- uipath_langchain-0.0.103.dist-info/entry_points.txt,sha256=FUtzqGOEntlJKMJIXhQUfT7ZTbQmGhke1iCmDWZaQZI,81
42
- uipath_langchain-0.0.103.dist-info/licenses/LICENSE,sha256=JDpt-uotAkHFmxpwxi6gwx6HQ25e-lG4U_Gzcvgp7JY,1063
43
- uipath_langchain-0.0.103.dist-info/RECORD,,
34
+ uipath_langchain-0.0.105.dist-info/METADATA,sha256=1mIsfmBry1pZP1KS3VTTqKka1pK-0gcwr31rE5TEAMI,4166
35
+ uipath_langchain-0.0.105.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
36
+ uipath_langchain-0.0.105.dist-info/entry_points.txt,sha256=FUtzqGOEntlJKMJIXhQUfT7ZTbQmGhke1iCmDWZaQZI,81
37
+ uipath_langchain-0.0.105.dist-info/licenses/LICENSE,sha256=JDpt-uotAkHFmxpwxi6gwx6HQ25e-lG4U_Gzcvgp7JY,1063
38
+ uipath_langchain-0.0.105.dist-info/RECORD,,
File without changes
@@ -1,54 +0,0 @@
1
- from typing import Any, Dict, List, Optional, Union
2
-
3
- from pydantic import BaseModel, Field
4
-
5
-
6
- class ToolCall(BaseModel):
7
- id: str
8
- name: str
9
- arguments: Dict[Any, Any]
10
-
11
-
12
- class Message(BaseModel):
13
- role: str
14
- content: Optional[Union[str, Dict[Any, Any]]] = None
15
- tool_calls: Optional[List[ToolCall]] = None
16
-
17
-
18
- class Tool(BaseModel):
19
- name: str
20
- description: str
21
- parameters: Optional[Dict[Any, Any]] = None
22
-
23
-
24
- class ToolChoice(BaseModel):
25
- type: str
26
- name: Optional[str] = None
27
-
28
-
29
- class ChatCompletionRequest(BaseModel):
30
- max_tokens: int
31
- # n: int
32
- # top_p: float
33
- temperature: float
34
- frequency_penalty: float
35
- presence_penalty: float
36
- messages: List[Message]
37
- tools: Optional[List[Tool]] = None
38
- tool_choice: Optional[ToolChoice] = None
39
- response_format: Optional[Dict[str, Any]] = None
40
-
41
-
42
- class ModelSettings(BaseModel):
43
- top_p: float = 1.0
44
- n: int = 1
45
- temperature: float = 0.0
46
- frequency_penalty: float = 0.0
47
- presence_penalty: float = 0.0
48
- max_tokens: int = 4096
49
- tool_choice: Optional[str] = None
50
- enforced_tool_name: Optional[str] = None
51
-
52
-
53
- class UiPathOutput(BaseModel):
54
- output_field: str = Field(..., description="The output field")
@@ -1,3 +0,0 @@
1
- from ._request_mixin import UiPathRequestMixin
2
-
3
- __all__ = ["UiPathRequestMixin"]
@@ -1,488 +0,0 @@
1
- # mypy: disable-error-code="no-redef,arg-type"
2
- import json
3
- import logging
4
- import os
5
- import time
6
- from typing import Any, Dict, List, Mapping, Optional
7
-
8
- import httpx
9
- import openai
10
- from langchain_core.embeddings import Embeddings
11
- from langchain_core.language_models.chat_models import _cleanup_llm_representation
12
- from pydantic import BaseModel, Field, SecretStr
13
- from tenacity import (
14
- AsyncRetrying,
15
- Retrying,
16
- retry_if_exception_type,
17
- stop_after_attempt,
18
- wait_exponential_jitter,
19
- )
20
-
21
- from uipath_langchain.utils._settings import (
22
- UiPathClientFactorySettings,
23
- UiPathClientSettings,
24
- get_uipath_token_header,
25
- )
26
- from uipath_langchain.utils._sleep_policy import before_sleep_log
27
-
28
-
29
- def get_from_uipath_url():
30
- url = os.getenv("UIPATH_URL")
31
- if url:
32
- return "/".join(url.split("/", 3)[:3])
33
- return None
34
-
35
-
36
- class UiPathRequestMixin(BaseModel):
37
- class Config:
38
- arbitrary_types_allowed = True
39
-
40
- default_headers: Optional[Mapping[str, str]] = {
41
- "X-UiPath-Streaming-Enabled": "false",
42
- }
43
- model_name: Optional[str] = Field(
44
- default_factory=lambda: os.getenv("UIPATH_MODEL_NAME", "gpt-4o-2024-08-06"),
45
- alias="model",
46
- )
47
- settings: Optional[UiPathClientSettings] = None
48
- client_id: Optional[str] = Field(
49
- default_factory=lambda: os.getenv("UIPATH_CLIENT_ID")
50
- )
51
- client_secret: Optional[str] = Field(
52
- default_factory=lambda: os.getenv("UIPATH_CLIENT_SECRET")
53
- )
54
- base_url: Optional[str] = Field(
55
- default_factory=lambda data: getattr(data["settings"], "base_url", None)
56
- or os.getenv("UIPATH_BASE_URL")
57
- or get_from_uipath_url(),
58
- alias="azure_endpoint",
59
- )
60
- access_token: Optional[str] = Field(
61
- default_factory=lambda data: (
62
- getattr(data["settings"], "access_token", None)
63
- or os.getenv("UIPATH_ACCESS_TOKEN") # Environment variable
64
- or os.getenv("UIPATH_SERVICE_TOKEN") # Environment variable
65
- or get_uipath_token_header(
66
- UiPathClientFactorySettings(
67
- UIPATH_BASE_URL=data["base_url"],
68
- UIPATH_CLIENT_ID=data["client_id"],
69
- UIPATH_CLIENT_SECRET=data["client_secret"],
70
- )
71
- ) # Get service token from UiPath
72
- )
73
- )
74
- org_id: Any = Field(
75
- default_factory=lambda data: getattr(data["settings"], "org_id", None)
76
- or os.getenv("UIPATH_ORGANIZATION_ID", "")
77
- )
78
- tenant_id: Any = Field(
79
- default_factory=lambda data: getattr(data["settings"], "tenant_id", None)
80
- or os.getenv("UIPATH_TENANT_ID", "")
81
- )
82
- requesting_product: Any = Field(
83
- default_factory=lambda data: getattr(
84
- data["settings"], "requesting_product", None
85
- )
86
- or os.getenv("UIPATH_REQUESTING_PRODUCT", "")
87
- )
88
- requesting_feature: Any = Field(
89
- default_factory=lambda data: getattr(
90
- data["settings"], "requesting_feature", None
91
- )
92
- or os.getenv("UIPATH_REQUESTING_FEATURE", "")
93
- )
94
- default_request_timeout: Any = Field(
95
- default_factory=lambda data: float(
96
- getattr(data["settings"], "timeout_seconds", None)
97
- or os.getenv("UIPATH_TIMEOUT_SECONDS", "120")
98
- ),
99
- alias="timeout",
100
- )
101
-
102
- openai_api_version: Optional[str] = Field(
103
- default_factory=lambda: os.getenv("OPENAI_API_VERSION", "2024-08-01-preview"),
104
- alias="api_version",
105
- )
106
- include_account_id: bool = False
107
- temperature: Optional[float] = 0.0
108
- max_tokens: Optional[int] = 1000
109
- frequency_penalty: Optional[float] = None
110
- presence_penalty: Optional[float] = None
111
-
112
- logger: Optional[logging.Logger] = None
113
- max_retries: Optional[int] = 5
114
- base_delay: float = 5.0
115
- max_delay: float = 60.0
116
-
117
- _url: Optional[str] = None
118
- _auth_headers: Optional[Dict[str, str]] = None
119
-
120
- # required to instantiate AzureChatOpenAI subclasses
121
- azure_endpoint: Optional[str] = Field(
122
- default="placeholder", description="Bypassed Azure endpoint"
123
- )
124
- openai_api_key: Optional[SecretStr] = Field(
125
- default=SecretStr("placeholder"), description="Bypassed API key"
126
- )
127
- # required to instatiate ChatAnthropic subclasses (will be needed when passthrough is implemented for Anthropic models)
128
- stop_sequences: Optional[List[str]] = Field(
129
- default=None, description="Bypassed stop sequence"
130
- )
131
-
132
- def _request(
133
- self, url: str, request_body: Dict[str, Any], headers: Dict[str, str]
134
- ) -> Dict[str, Any]:
135
- """Run an asynchronous call to the LLM."""
136
- # if self.logger:
137
- # self.logger.info(f"Completion request: {request_body['messages'][:2]}")
138
- with httpx.Client(
139
- event_hooks={
140
- "request": [self._log_request_duration],
141
- "response": [self._log_response_duration],
142
- }
143
- ) as client:
144
- response = client.post(
145
- url,
146
- headers=headers,
147
- json=request_body,
148
- timeout=self.default_request_timeout,
149
- )
150
-
151
- # Handle HTTP errors and map them to OpenAI exceptions
152
- try:
153
- response.raise_for_status()
154
- except httpx.HTTPStatusError as err:
155
- if self.logger:
156
- self.logger.error(
157
- "Error querying UiPath: %s (%s)",
158
- err.response.reason_phrase,
159
- err.response.status_code,
160
- extra={
161
- "ActionName": self.settings.action_name,
162
- "ActionId": self.settings.action_id,
163
- }
164
- if self.settings
165
- else None,
166
- )
167
- raise self._make_status_error_from_response(err.response) from err
168
-
169
- return response.json()
170
-
171
- def _call(
172
- self, url: str, request_body: Dict[str, Any], headers: Dict[str, str]
173
- ) -> Dict[str, Any]:
174
- """Run a synchronous call with retries to LLM"""
175
- if self.max_retries is None:
176
- return self._request(url, request_body, headers)
177
-
178
- retryer = Retrying(
179
- stop=stop_after_attempt(self.max_retries),
180
- wait=wait_exponential_jitter(
181
- initial=self.base_delay,
182
- max=self.max_delay,
183
- jitter=1.0,
184
- ),
185
- retry=retry_if_exception_type(
186
- (openai.RateLimitError, httpx.TimeoutException)
187
- ),
188
- reraise=True,
189
- before_sleep=before_sleep_log(self.logger, logging.WARNING)
190
- if self.logger is not None
191
- else None,
192
- )
193
-
194
- try:
195
- return retryer(self._request, url, request_body, headers)
196
- except openai.APIStatusError as err:
197
- if self.logger:
198
- self.logger.error(
199
- "Failed querying LLM after retries: %s",
200
- err,
201
- extra={
202
- "ActionName": self.settings.action_name,
203
- "ActionId": self.settings.action_id,
204
- }
205
- if self.settings
206
- else None,
207
- )
208
- raise err
209
-
210
- async def _arequest(
211
- self, url: str, request_body: Dict[str, Any], headers: Dict[str, str]
212
- ) -> Dict[str, Any]:
213
- # if self.logger:
214
- # self.logger.info(f"Completion request: {request_body['messages'][:2]}")
215
- async with httpx.AsyncClient(
216
- event_hooks={
217
- "request": [self._alog_request_duration],
218
- "response": [self._alog_response_duration],
219
- }
220
- ) as client:
221
- response = await client.post(
222
- url,
223
- headers=headers,
224
- json=request_body,
225
- timeout=self.default_request_timeout,
226
- )
227
- # Handle HTTP errors and map them to OpenAI exceptions
228
- try:
229
- response.raise_for_status()
230
- except httpx.HTTPStatusError as err:
231
- if self.logger:
232
- self.logger.error(
233
- "Error querying LLM: %s (%s)",
234
- err.response.reason_phrase,
235
- err.response.status_code,
236
- extra={
237
- "ActionName": self.settings.action_name,
238
- "ActionId": self.settings.action_id,
239
- }
240
- if self.settings
241
- else None,
242
- )
243
- raise self._make_status_error_from_response(err.response) from err
244
-
245
- return response.json()
246
-
247
- async def _acall(
248
- self, url: str, request_body: Dict[str, Any], headers: Dict[str, str]
249
- ) -> Dict[str, Any]:
250
- """Run an asynchronous call with retries to the LLM."""
251
- if self.max_retries is None:
252
- return await self._arequest(url, request_body, headers)
253
-
254
- retryer = AsyncRetrying(
255
- stop=stop_after_attempt(self.max_retries),
256
- wait=wait_exponential_jitter(
257
- initial=self.base_delay,
258
- max=self.max_delay,
259
- jitter=1.0,
260
- ),
261
- retry=retry_if_exception_type(
262
- (openai.RateLimitError, httpx.TimeoutException)
263
- ),
264
- reraise=True,
265
- before_sleep=before_sleep_log(self.logger, logging.WARNING)
266
- if self.logger is not None
267
- else None,
268
- )
269
-
270
- try:
271
- response: Any = await retryer(self._arequest, url, request_body, headers)
272
- if self.logger:
273
- self.logger.info(
274
- f"[uipath_langchain_client] Finished retryer after {retryer.statistics['attempt_number'] - 1} retries",
275
- extra={
276
- "retry": f"{retryer.statistics['attempt_number'] - 1}",
277
- "ActionName": self.settings.action_name,
278
- "ActionId": self.settings.action_id,
279
- }
280
- if self.settings
281
- else {
282
- "retry": f"{retryer.statistics['attempt_number'] - 1}",
283
- },
284
- )
285
- return response
286
- except openai.APIStatusError as err:
287
- if self.logger:
288
- self.logger.error(
289
- "[uipath_langchain_client] Failed querying LLM after retries: %s",
290
- err,
291
- extra={
292
- "reason": err.message,
293
- "statusCode": err.status_code,
294
- "ActionName": self.settings.action_name,
295
- "ActionId": self.settings.action_id,
296
- }
297
- if self.settings
298
- else {
299
- "reason": err.message,
300
- "statusCode": err.status_code,
301
- },
302
- )
303
- raise err
304
-
305
- def _make_status_error_from_response(
306
- self,
307
- response: httpx.Response,
308
- ) -> openai.APIStatusError:
309
- """Function reproduced from openai._client to handle UiPath errors."""
310
- if response.is_closed and not response.is_stream_consumed:
311
- # We can't read the response body as it has been closed
312
- # before it was read. This can happen if an event hook
313
- # raises a status error.
314
- body = None
315
- err_msg = f"Error code: {response.status_code}"
316
- else:
317
- err_text = response.text.strip()
318
- body = err_text
319
-
320
- try:
321
- body = json.loads(err_text)
322
- err_msg = f"Error code: {response.status_code} - {body}"
323
- except Exception:
324
- err_msg = err_text or f"Error code: {response.status_code}"
325
-
326
- return self._make_status_error(err_msg, body=body, response=response)
327
-
328
- def _make_status_error(
329
- self,
330
- err_msg: str,
331
- *,
332
- body: object,
333
- response: httpx.Response,
334
- ) -> openai.APIStatusError:
335
- """Function reproduced from openai._client to handle UiPath errors."""
336
- data = body.get("error", body) if isinstance(body, Mapping) else body
337
- if response.status_code == 400:
338
- return openai.BadRequestError(err_msg, response=response, body=data)
339
-
340
- if response.status_code == 401:
341
- return openai.AuthenticationError(err_msg, response=response, body=data)
342
-
343
- if response.status_code == 403:
344
- return openai.PermissionDeniedError(err_msg, response=response, body=data)
345
-
346
- if response.status_code == 404:
347
- return openai.NotFoundError(err_msg, response=response, body=data)
348
-
349
- if response.status_code == 409:
350
- return openai.ConflictError(err_msg, response=response, body=data)
351
-
352
- if response.status_code == 422:
353
- return openai.UnprocessableEntityError(
354
- err_msg, response=response, body=data
355
- )
356
-
357
- if response.status_code == 429:
358
- return openai.RateLimitError(err_msg, response=response, body=data)
359
-
360
- if response.status_code >= 500:
361
- return openai.InternalServerError(err_msg, response=response, body=data)
362
- return openai.APIStatusError(err_msg, response=response, body=data)
363
-
364
- def _log_request_duration(self, request: httpx.Request):
365
- """Log the start time of the request."""
366
- if self.logger:
367
- request.extensions["start_time"] = time.monotonic()
368
-
369
- def _log_response_duration(self, response: httpx.Response):
370
- """Log the duration of the request."""
371
- if self.logger:
372
- start_time = response.request.extensions.get("start_time")
373
- if start_time:
374
- duration = time.monotonic() - start_time
375
- type = "embedding"
376
- if not isinstance(self, Embeddings):
377
- type = "normalized" if self.is_normalized else "completion"
378
- self.logger.info(
379
- f"[uipath_langchain_client] Request to {response.request.url} took {duration:.2f} seconds.",
380
- extra={
381
- "requestUrl": f"{response.request.url}",
382
- "duration": f"{duration:.2f}",
383
- "type": type,
384
- "ActionName": self.settings.action_name,
385
- "ActionId": self.settings.action_id,
386
- }
387
- if self.settings
388
- else {
389
- "requestUrl": f"{response.request.url}",
390
- "duration": f"{duration:.2f}",
391
- "type": type,
392
- },
393
- )
394
-
395
- async def _alog_request_duration(self, request: httpx.Request):
396
- """Log the start time of the request."""
397
- self._log_request_duration(request)
398
-
399
- async def _alog_response_duration(self, response: httpx.Response):
400
- """Log the duration of the request."""
401
- self._log_response_duration(response)
402
-
403
- @property
404
- def _llm_type(self) -> str:
405
- """Get the type of language model used by this chat model. Used for logging purposes only."""
406
- return "uipath"
407
-
408
- @property
409
- def _identifying_params(self) -> Dict[str, Any]:
410
- return {
411
- "url": self.url,
412
- "model": self.model_name,
413
- "temperature": self.temperature,
414
- "max_tokens": self.max_tokens,
415
- "frequency_penalty": self.frequency_penalty,
416
- "presence_penalty": self.presence_penalty,
417
- }
418
-
419
- def _prepare_url(self, url: str) -> httpx.URL:
420
- return httpx.URL(self.url)
421
-
422
- def _build_headers(self, options, retries_taken: int = 0) -> httpx.Headers:
423
- return httpx.Headers(self.auth_headers)
424
-
425
- @property
426
- def url(self) -> str:
427
- if not self._url:
428
- self._url = (
429
- f"{self.base_url}/{self.org_id}/{self.tenant_id}/{self.endpoint}"
430
- )
431
- return self._url
432
-
433
- @property
434
- def endpoint(self) -> str:
435
- raise NotImplementedError(
436
- "The endpoint property is not implemented for this class."
437
- )
438
-
439
- @property
440
- def auth_headers(self) -> Dict[str, str]:
441
- if not self._auth_headers:
442
- self._auth_headers = {
443
- **self.default_headers, # type: ignore
444
- "Authorization": f"Bearer {self.access_token}",
445
- "X-UiPath-LlmGateway-RequestingProduct": self.requesting_product,
446
- "X-UiPath-LlmGateway-RequestingFeature": self.requesting_feature,
447
- "X-UiPath-LlmGateway-TimeoutSeconds": str(self.default_request_timeout),
448
- }
449
- if self.is_normalized and self.model_name:
450
- self._auth_headers["X-UiPath-LlmGateway-NormalizedApi-ModelName"] = (
451
- self.model_name
452
- )
453
- if self.include_account_id:
454
- self._auth_headers["x-uipath-internal-accountid"] = self.org_id
455
- self._auth_headers["x-uipath-internal-tenantid"] = self.tenant_id
456
- return self._auth_headers
457
-
458
- def _get_llm_string(self, stop: Optional[list[str]] = None, **kwargs: Any) -> str:
459
- serialized_repr = getattr(self, "_serialized", self.model_dump())
460
- _cleanup_llm_representation(serialized_repr, 1)
461
- kwargs = serialized_repr.get("kwargs", serialized_repr)
462
- for key in [
463
- "base_url",
464
- "access_token",
465
- "client_id",
466
- "client_secret",
467
- "org_id",
468
- "tenant_id",
469
- "requesting_product",
470
- "requesting_feature",
471
- "azure_endpoint",
472
- "openai_api_version",
473
- "openai_api_key",
474
- "default_request_timeout",
475
- "max_retries",
476
- "base_delay",
477
- "max_delay",
478
- "logger",
479
- "settings",
480
- ]:
481
- if key in kwargs:
482
- kwargs.pop(key, None)
483
- llm_string = json.dumps(serialized_repr, sort_keys=True)
484
- return llm_string
485
-
486
- @property
487
- def is_normalized(self) -> bool:
488
- return False
@@ -1,91 +0,0 @@
1
- # mypy: disable-error-code="syntax"
2
- import os
3
- from enum import Enum
4
- from typing import Any, Optional
5
-
6
- import httpx
7
- from pydantic import Field
8
- from pydantic_settings import BaseSettings
9
-
10
-
11
- class UiPathCachedPathsSettings(BaseSettings):
12
- cached_completion_db: str = Field(
13
- default=os.path.join(
14
- os.path.dirname(__file__), "tests", "tests_uipath_cache.db"
15
- ),
16
- alias="CACHED_COMPLETION_DB",
17
- )
18
- cached_embeddings_dir: str = Field(
19
- default=os.path.join(os.path.dirname(__file__), "tests", "cached_embeddings"),
20
- alias="CACHED_EMBEDDINGS_DIR",
21
- )
22
-
23
-
24
- uipath_cached_paths_settings = UiPathCachedPathsSettings()
25
- uipath_token_header: Optional[str] = None
26
-
27
-
28
- class UiPathClientFactorySettings(BaseSettings):
29
- base_url: str = Field(default="", alias="UIPATH_BASE_URL")
30
- client_id: str = Field(default="", alias="UIPATH_CLIENT_ID")
31
- client_secret: str = Field(default="", alias="UIPATH_CLIENT_SECRET")
32
-
33
-
34
- class UiPathClientSettings(BaseSettings):
35
- access_token: str = Field(default_factory=lambda: get_uipath_token_header())
36
- base_url: str = Field(default="", alias="UIPATH_BASE_URL")
37
- org_id: str = Field(default="", alias="UIPATH_ORGANIZATION_ID")
38
- tenant_id: str = Field(default="", alias="UIPATH_TENANT_ID")
39
- requesting_product: str = Field(default="", alias="UIPATH_REQUESTING_PRODUCT")
40
- requesting_feature: str = Field(default="", alias="UIPATH_REQUESTING_FEATURE")
41
- timeout_seconds: str = Field(default="120", alias="UIPATH_TIMEOUT_SECONDS")
42
- action_name: str = Field(default="DefaultActionName", alias="UIPATH_ACTION_NAME")
43
- action_id: str = Field(default="DefaultActionId", alias="UIPATH_ACTION_ID")
44
-
45
-
46
- class UiPathEndpoints(Enum):
47
- NORMALIZED_COMPLETION_ENDPOINT = "llmgateway_/api/chat/completions"
48
- PASSTHROUGH_COMPLETION_ENDPOINT = "llmgateway_/openai/deployments/{model}/chat/completions?api-version={api_version}"
49
- EMBEDDING_ENDPOINT = (
50
- "llmgateway_/openai/deployments/{model}/embeddings?api-version={api_version}"
51
- )
52
-
53
-
54
- def get_uipath_token_header(
55
- settings: Any = None,
56
- ) -> str:
57
- global uipath_token_header
58
- if not uipath_token_header:
59
- settings = settings or UiPathClientFactorySettings()
60
- url_get_token = f"{settings.base_url}/identity_/connect/token"
61
- token_credentials = dict(
62
- client_id=settings.client_id,
63
- client_secret=settings.client_secret,
64
- grant_type="client_credentials",
65
- )
66
- with httpx.Client() as client:
67
- res = client.post(url_get_token, data=token_credentials)
68
- res_json = res.json()
69
- uipath_token_header = res_json.get("access_token")
70
-
71
- return uipath_token_header or ""
72
-
73
-
74
- async def get_token_header_async(
75
- settings: Any = None,
76
- ) -> str:
77
- global uipath_token_header
78
- if not uipath_token_header:
79
- settings = settings or UiPathClientFactorySettings()
80
- url_get_token = f"{settings.base_url}/identity_/connect/token"
81
- token_credentials = dict(
82
- client_id=settings.client_id,
83
- client_secret=settings.client_secret,
84
- grant_type="client_credentials",
85
- )
86
-
87
- with httpx.Client() as client:
88
- res_json = client.post(url_get_token, data=token_credentials).json()
89
- uipath_token_header = res_json.get("access_token")
90
-
91
- return uipath_token_header or ""
@@ -1,41 +0,0 @@
1
- import logging
2
- from typing import Callable
3
-
4
- from tenacity import (
5
- RetryCallState,
6
- _utils,
7
- )
8
-
9
-
10
- def before_sleep_log(
11
- logger: "logging.Logger",
12
- log_level: int,
13
- exc_info: bool = False,
14
- ) -> Callable[["RetryCallState"], None]:
15
- """Before call strategy that logs to some logger the attempt."""
16
-
17
- def log_it(retry_state: "RetryCallState") -> None:
18
- if retry_state.outcome is None:
19
- raise RuntimeError("log_it() called before outcome was set")
20
-
21
- if retry_state.next_action is None:
22
- raise RuntimeError("log_it() called before next_action was set")
23
-
24
- if retry_state.outcome.failed:
25
- ex = retry_state.outcome.exception()
26
- verb, value = "raised", f"{ex.__class__.__name__}: {ex}"
27
- else:
28
- verb, value = "returned", retry_state.outcome.result()
29
-
30
- if retry_state.fn is None:
31
- fn_name = "<unknown>"
32
- else:
33
- fn_name = _utils.get_callback_name(retry_state.fn)
34
-
35
- logger.log(
36
- log_level,
37
- f"Retrying #{retry_state.attempt_number} {fn_name} in {retry_state.next_action.sleep} seconds as it {verb} {value}.",
38
- {"retries": f"{retry_state.attempt_number}"},
39
- )
40
-
41
- return log_it