uipath-langchain 0.0.83__tar.gz → 0.0.85__tar.gz
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.
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/PKG-INFO +1 -1
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/pyproject.toml +1 -1
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_runtime.py +3 -1
- uipath_langchain-0.0.85/uipath_langchain/_utils/__init__.py +4 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/tracers/AsyncUiPathTracer.py +109 -57
- uipath_langchain-0.0.85/uipath_langchain/tracers/__init__.py +5 -0
- uipath_langchain-0.0.85/uipath_langchain/tracers/_events.py +33 -0
- uipath_langchain-0.0.85/uipath_langchain/tracers/_instrument_traceable.py +285 -0
- uipath_langchain-0.0.83/uipath_langchain/_utils/__init__.py +0 -3
- uipath_langchain-0.0.83/uipath_langchain/tracers/__init__.py +0 -4
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/.gitignore +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/README.md +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/__init__.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/__init__.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_context.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_escalation.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_exception.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_input.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_output.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_utils/_graph.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/cli_init.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/cli_run.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_utils/_request_mixin.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_utils/_settings.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_utils/_sleep_policy.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_utils/tests/cached_embeddings/text-embedding-3-large5034ec3c-85c9-54b8-ac89-5e0cbcf99e3b +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_utils/tests/cached_embeddings/text-embedding-3-largec48857ed-1302-5954-9e24-69fa9b45e457 +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_utils/tests/tests_uipath_cache.db +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/chat/__init__.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/chat/models.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/chat/utils/__init__.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/chat/utils/_chat_types.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/embeddings/__init__.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/embeddings/embeddings.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/middlewares.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/retrievers/__init__.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/retrievers/context_grounding_retriever.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/tracers/UiPathTracer.py +0 -0
- {uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/tracers/_utils.py +0 -0
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_runtime.py
RENAMED
|
@@ -14,6 +14,7 @@ from uipath_sdk._cli._runtime._contracts import (
|
|
|
14
14
|
UiPathRuntimeResult,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
+
from ..._utils import _instrument_traceable
|
|
17
18
|
from ...tracers import AsyncUiPathTracer
|
|
18
19
|
from ._context import LangGraphRuntimeContext
|
|
19
20
|
from ._exception import LangGraphRuntimeError
|
|
@@ -43,6 +44,7 @@ class LangGraphRuntime(UiPathBaseRuntime):
|
|
|
43
44
|
Raises:
|
|
44
45
|
LangGraphRuntimeError: If execution fails
|
|
45
46
|
"""
|
|
47
|
+
_instrument_traceable()
|
|
46
48
|
|
|
47
49
|
await self.validate()
|
|
48
50
|
|
|
@@ -71,7 +73,7 @@ class LangGraphRuntime(UiPathBaseRuntime):
|
|
|
71
73
|
callbacks: List[BaseCallbackHandler] = []
|
|
72
74
|
|
|
73
75
|
if self.context.job_id and self.context.tracing_enabled:
|
|
74
|
-
tracer = AsyncUiPathTracer()
|
|
76
|
+
tracer = AsyncUiPathTracer(context=self.context.trace_context)
|
|
75
77
|
await tracer.init_trace(
|
|
76
78
|
self.context.entrypoint, self.context.job_id
|
|
77
79
|
)
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/tracers/AsyncUiPathTracer.py
RENAMED
|
@@ -6,13 +6,15 @@ import re
|
|
|
6
6
|
import uuid
|
|
7
7
|
import warnings
|
|
8
8
|
from os import environ as env
|
|
9
|
-
from typing import Any, Optional
|
|
9
|
+
from typing import Any, Dict, Optional
|
|
10
10
|
|
|
11
11
|
import httpx
|
|
12
12
|
from langchain_core.tracers.base import AsyncBaseTracer
|
|
13
13
|
from langchain_core.tracers.schemas import Run
|
|
14
14
|
from pydantic import PydanticDeprecationWarning
|
|
15
|
+
from uipath_sdk._cli._runtime._contracts import UiPathTraceContext
|
|
15
16
|
|
|
17
|
+
from ._events import CustomTraceEvents, FunctionCallEventData
|
|
16
18
|
from ._utils import _setup_tracer_httpx_logging, _simple_serialize_defaults
|
|
17
19
|
|
|
18
20
|
logger = logging.getLogger(__name__)
|
|
@@ -27,78 +29,101 @@ class Status:
|
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class AsyncUiPathTracer(AsyncBaseTracer):
|
|
30
|
-
def __init__(
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
context: Optional[UiPathTraceContext] = None,
|
|
35
|
+
client: Optional[httpx.AsyncClient] = None,
|
|
36
|
+
**kwargs,
|
|
37
|
+
):
|
|
31
38
|
super().__init__(**kwargs)
|
|
32
39
|
|
|
33
40
|
self.client = client or httpx.AsyncClient()
|
|
34
41
|
self.retries = 3
|
|
35
42
|
self.log_queue: queue.Queue[dict[str, Any]] = queue.Queue()
|
|
36
43
|
|
|
44
|
+
self.context = context or UiPathTraceContext()
|
|
45
|
+
|
|
37
46
|
llm_ops_pattern = self._get_base_url() + "{orgId}/llmops_"
|
|
38
|
-
self.orgId = env.get(
|
|
39
|
-
"UIPATH_ORGANIZATION_ID", "00000000-0000-0000-0000-000000000000"
|
|
40
|
-
)
|
|
41
|
-
self.tenantId = env.get(
|
|
42
|
-
"UIPATH_TENANT_ID", "00000000-0000-0000-0000-000000000000"
|
|
43
|
-
)
|
|
44
|
-
self.url = llm_ops_pattern.format(orgId=self.orgId).rstrip("/")
|
|
45
47
|
|
|
46
|
-
self.
|
|
48
|
+
self.url = llm_ops_pattern.format(orgId=self.context.org_id).rstrip("/")
|
|
49
|
+
|
|
50
|
+
auth_token = env.get("UNATTENDED_USER_ACCESS_TOKEN") or env.get(
|
|
47
51
|
"UIPATH_ACCESS_TOKEN"
|
|
48
52
|
)
|
|
49
53
|
|
|
50
|
-
self.
|
|
51
|
-
self.folderKey = env.get("UIPATH_FOLDER_KEY")
|
|
52
|
-
self.processKey = env.get("UIPATH_PROCESS_UUID")
|
|
53
|
-
self.parent_span_id = env.get("UIPATH_PARENT_SPAN_ID")
|
|
54
|
-
|
|
55
|
-
self.referenceId = self.jobKey or str(uuid.uuid4())
|
|
56
|
-
|
|
57
|
-
self.headers = {
|
|
58
|
-
"Authorization": f"Bearer {self.auth_token}",
|
|
59
|
-
}
|
|
54
|
+
self.headers = {"Authorization": f"Bearer {auth_token}"}
|
|
60
55
|
|
|
61
56
|
self.running = True
|
|
62
57
|
self.worker_task = asyncio.create_task(self._worker())
|
|
58
|
+
self.function_call_run_map: Dict[str, Run] = {}
|
|
59
|
+
|
|
60
|
+
async def on_custom_event(
|
|
61
|
+
self,
|
|
62
|
+
name: str,
|
|
63
|
+
data: Any,
|
|
64
|
+
*,
|
|
65
|
+
run_id: uuid.UUID,
|
|
66
|
+
tags=None,
|
|
67
|
+
metadata=None,
|
|
68
|
+
**kwargs: Any,
|
|
69
|
+
) -> None:
|
|
70
|
+
if name == CustomTraceEvents.UIPATH_TRACE_FUNCTION_CALL:
|
|
71
|
+
# only handle the function call event
|
|
72
|
+
|
|
73
|
+
if not isinstance(data, FunctionCallEventData):
|
|
74
|
+
logger.warning(
|
|
75
|
+
f"Received unexpected data type for function call event: {type(data)}"
|
|
76
|
+
)
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
if data.event_type == "call":
|
|
80
|
+
run = self.run_map[str(run_id)]
|
|
81
|
+
child_run = run.create_child(
|
|
82
|
+
name=data.function_name,
|
|
83
|
+
run_type=data.run_type,
|
|
84
|
+
tags=data.tags,
|
|
85
|
+
inputs=data.inputs,
|
|
86
|
+
)
|
|
63
87
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
env.get("UIPATH_URL") or "https://cloud.uipath.com/dummyOrg/dummyTennant/"
|
|
67
|
-
)
|
|
68
|
-
uipath_url = uipath_url.rstrip("/")
|
|
88
|
+
if data.metadata is not None:
|
|
89
|
+
run.add_metadata(data.metadata)
|
|
69
90
|
|
|
70
|
-
|
|
71
|
-
|
|
91
|
+
call_uuid = data.call_uuid
|
|
92
|
+
self.function_call_run_map[call_uuid] = child_run
|
|
72
93
|
|
|
73
|
-
|
|
74
|
-
# hence split it again using "/" to get ['https:', 'alpha.uipath.com', 'ada', 'byoa']
|
|
75
|
-
base_url_parts = parts[1].split("/")
|
|
94
|
+
self._send_span(run)
|
|
76
95
|
|
|
77
|
-
|
|
78
|
-
|
|
96
|
+
if data.event_type == "completion":
|
|
97
|
+
call_uuid = data.call_uuid
|
|
98
|
+
previous_run = self.function_call_run_map.pop(call_uuid, None)
|
|
79
99
|
|
|
80
|
-
|
|
100
|
+
if previous_run:
|
|
101
|
+
previous_run.end(
|
|
102
|
+
outputs=self._safe_dict_dump(data.output), error=data.error
|
|
103
|
+
)
|
|
104
|
+
self._send_span(previous_run)
|
|
81
105
|
|
|
82
106
|
async def init_trace(self, run_name, trace_id=None) -> None:
|
|
83
|
-
|
|
107
|
+
if self.context.trace_id:
|
|
108
|
+
# trace id already set no need to do anything
|
|
109
|
+
return
|
|
84
110
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
else:
|
|
88
|
-
await self.start_trace(run_name, trace_id)
|
|
111
|
+
# no trace id, start a new trace
|
|
112
|
+
await self.start_trace(run_name, trace_id)
|
|
89
113
|
|
|
90
114
|
async def start_trace(self, run_name, trace_id=None) -> None:
|
|
91
|
-
self.
|
|
92
|
-
|
|
115
|
+
self.context.trace_id = str(uuid.uuid4())
|
|
116
|
+
|
|
117
|
+
run_name = run_name or f"Job Run: {self.context.trace_id}"
|
|
93
118
|
trace_data = {
|
|
94
|
-
"id": self.
|
|
119
|
+
"id": self.context.trace_id,
|
|
95
120
|
"name": re.sub(
|
|
96
121
|
"[!@#$<>\.]", "", run_name
|
|
97
122
|
), # if we use these characters the Agents UI throws some error (but llmops backend seems fine)
|
|
98
|
-
"referenceId": self.
|
|
123
|
+
"referenceId": self.context.reference_id,
|
|
99
124
|
"attributes": "{}",
|
|
100
|
-
"organizationId": self.
|
|
101
|
-
"tenantId": self.
|
|
125
|
+
"organizationId": self.context.org_id,
|
|
126
|
+
"tenantId": self.context.tenant_id,
|
|
102
127
|
}
|
|
103
128
|
|
|
104
129
|
for attempt in range(self.retries):
|
|
@@ -176,9 +201,9 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
176
201
|
|
|
177
202
|
async def _persist_run(self, run: Run) -> None:
|
|
178
203
|
# Determine if this is a start or end trace based on whether end_time is set
|
|
179
|
-
|
|
204
|
+
self._send_span(run)
|
|
180
205
|
|
|
181
|
-
|
|
206
|
+
def _send_span(self, run: Run) -> None:
|
|
182
207
|
"""Send span data for a run to the API"""
|
|
183
208
|
run_id = str(run.id)
|
|
184
209
|
|
|
@@ -193,27 +218,27 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
193
218
|
parent_id = (
|
|
194
219
|
str(run.parent_run_id)
|
|
195
220
|
if run.parent_run_id is not None
|
|
196
|
-
else self.parent_span_id
|
|
221
|
+
else self.context.parent_span_id
|
|
197
222
|
)
|
|
198
|
-
attributes = self.
|
|
223
|
+
attributes = self._safe_jsons_dump(self._run_to_dict(run))
|
|
199
224
|
status = self._determine_status(run.error)
|
|
200
225
|
|
|
201
226
|
span_data = {
|
|
202
227
|
"id": run_id,
|
|
203
228
|
"parentId": parent_id,
|
|
204
|
-
"traceId": self.
|
|
229
|
+
"traceId": self.context.trace_id,
|
|
205
230
|
"name": run.name,
|
|
206
231
|
"startTime": start_time,
|
|
207
232
|
"endTime": end_time,
|
|
208
|
-
"referenceId": self.
|
|
233
|
+
"referenceId": self.context.reference_id,
|
|
209
234
|
"attributes": attributes,
|
|
210
|
-
"organizationId": self.
|
|
211
|
-
"tenantId": self.
|
|
235
|
+
"organizationId": self.context.org_id,
|
|
236
|
+
"tenantId": self.context.tenant_id,
|
|
212
237
|
"spanType": "LangGraphRun",
|
|
213
238
|
"status": status,
|
|
214
|
-
"jobKey": self.
|
|
215
|
-
"folderKey": self.
|
|
216
|
-
"processKey": self.
|
|
239
|
+
"jobKey": self.context.job_id,
|
|
240
|
+
"folderKey": self.context.folder_key,
|
|
241
|
+
"processKey": self.context.folder_key,
|
|
217
242
|
}
|
|
218
243
|
|
|
219
244
|
self.log_queue.put(span_data)
|
|
@@ -237,14 +262,23 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
237
262
|
|
|
238
263
|
return Status.SUCCESS
|
|
239
264
|
|
|
240
|
-
def
|
|
265
|
+
def _safe_jsons_dump(self, obj) -> str:
|
|
241
266
|
try:
|
|
242
267
|
json_str = json.dumps(obj, default=_simple_serialize_defaults)
|
|
243
268
|
return json_str
|
|
244
269
|
except Exception as e:
|
|
245
|
-
logger.warning(e)
|
|
270
|
+
logger.warning(f"Error serializing object to JSON: {e}")
|
|
246
271
|
return "{ }"
|
|
247
272
|
|
|
273
|
+
def _safe_dict_dump(self, obj) -> Dict[str, Any]:
|
|
274
|
+
try:
|
|
275
|
+
serialized = json.loads(json.dumps(obj, default=_simple_serialize_defaults))
|
|
276
|
+
return serialized
|
|
277
|
+
except Exception as e:
|
|
278
|
+
# Last resort - string representation
|
|
279
|
+
logger.warning(f"Error serializing object to JSON: {e}")
|
|
280
|
+
return {"raw": str(obj)}
|
|
281
|
+
|
|
248
282
|
def _run_to_dict(self, run: Run):
|
|
249
283
|
with warnings.catch_warnings():
|
|
250
284
|
warnings.simplefilter("ignore", category=PydanticDeprecationWarning)
|
|
@@ -254,3 +288,21 @@ class AsyncUiPathTracer(AsyncBaseTracer):
|
|
|
254
288
|
"inputs": run.inputs.copy() if run.inputs is not None else None,
|
|
255
289
|
"outputs": run.outputs.copy() if run.outputs is not None else None,
|
|
256
290
|
}
|
|
291
|
+
|
|
292
|
+
def _get_base_url(self) -> str:
|
|
293
|
+
uipath_url = (
|
|
294
|
+
env.get("UIPATH_URL") or "https://cloud.uipath.com/dummyOrg/dummyTennant/"
|
|
295
|
+
)
|
|
296
|
+
uipath_url = uipath_url.rstrip("/")
|
|
297
|
+
|
|
298
|
+
# split by "//" to get ['', 'https:', 'alpha.uipath.com/ada/byoa']
|
|
299
|
+
parts = uipath_url.split("//")
|
|
300
|
+
|
|
301
|
+
# after splitting by //, the base URL will be at index 1 along with the rest,
|
|
302
|
+
# hence split it again using "/" to get ['https:', 'alpha.uipath.com', 'ada', 'byoa']
|
|
303
|
+
base_url_parts = parts[1].split("/")
|
|
304
|
+
|
|
305
|
+
# combine scheme and netloc to get the base URL
|
|
306
|
+
base_url = parts[0] + "//" + base_url_parts[0] + "/"
|
|
307
|
+
|
|
308
|
+
return base_url
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
2
|
+
|
|
3
|
+
RUN_TYPE_T = Literal[
|
|
4
|
+
"tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"
|
|
5
|
+
]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CustomTraceEvents:
|
|
9
|
+
UIPATH_TRACE_FUNCTION_CALL = "__uipath_trace_function_call"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FunctionCallEventData:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
function_name: str,
|
|
16
|
+
event_type: str,
|
|
17
|
+
inputs: Dict[str, Any],
|
|
18
|
+
call_uuid: str,
|
|
19
|
+
output: Any,
|
|
20
|
+
error: Optional[str] = None,
|
|
21
|
+
run_type: Optional[RUN_TYPE_T] = None,
|
|
22
|
+
tags: Optional[List[str]] = None,
|
|
23
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
24
|
+
):
|
|
25
|
+
self.function_name = function_name
|
|
26
|
+
self.event_type = event_type
|
|
27
|
+
self.inputs = inputs
|
|
28
|
+
self.call_uuid = call_uuid
|
|
29
|
+
self.output = output
|
|
30
|
+
self.error = error
|
|
31
|
+
self.run_type = run_type or "chain"
|
|
32
|
+
self.tags = tags
|
|
33
|
+
self.metadata = metadata
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import importlib
|
|
3
|
+
import inspect
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
import uuid
|
|
7
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
8
|
+
|
|
9
|
+
from langchain_core.callbacks import dispatch_custom_event
|
|
10
|
+
|
|
11
|
+
from ._events import CustomTraceEvents, FunctionCallEventData
|
|
12
|
+
|
|
13
|
+
# Original module and traceable function references
|
|
14
|
+
original_langsmith: Any = None
|
|
15
|
+
original_traceable: Any = None
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def dispatch_trace_event(
|
|
21
|
+
func_name,
|
|
22
|
+
inputs: Dict[str, Any],
|
|
23
|
+
event_type="call",
|
|
24
|
+
call_uuid=None,
|
|
25
|
+
result=None,
|
|
26
|
+
exception=None,
|
|
27
|
+
run_type: Optional[
|
|
28
|
+
Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"]
|
|
29
|
+
] = None,
|
|
30
|
+
tags: Optional[List[str]] = None,
|
|
31
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
32
|
+
):
|
|
33
|
+
"""Dispatch trace event to our server."""
|
|
34
|
+
|
|
35
|
+
event_data = FunctionCallEventData(
|
|
36
|
+
function_name=func_name,
|
|
37
|
+
event_type=event_type,
|
|
38
|
+
inputs=inputs,
|
|
39
|
+
call_uuid=call_uuid,
|
|
40
|
+
output=result,
|
|
41
|
+
error=str(exception) if exception else None,
|
|
42
|
+
run_type=run_type,
|
|
43
|
+
tags=tags,
|
|
44
|
+
metadata=metadata,
|
|
45
|
+
)
|
|
46
|
+
dispatch_custom_event(CustomTraceEvents.UIPATH_TRACE_FUNCTION_CALL, event_data)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def format_args_for_trace(
|
|
50
|
+
signature: inspect.Signature, *args: Any, **kwargs: Any
|
|
51
|
+
) -> Dict[str, Any]:
|
|
52
|
+
try:
|
|
53
|
+
"""Return a dictionary of inputs from the function signature."""
|
|
54
|
+
# Create a parameter mapping by partially binding the arguments
|
|
55
|
+
parameter_binding = signature.bind_partial(*args, **kwargs)
|
|
56
|
+
|
|
57
|
+
# Fill in default values for any unspecified parameters
|
|
58
|
+
parameter_binding.apply_defaults()
|
|
59
|
+
|
|
60
|
+
# Extract the input parameters, skipping special Python parameters
|
|
61
|
+
result = {}
|
|
62
|
+
for name, value in parameter_binding.arguments.items():
|
|
63
|
+
# Skip class and instance references
|
|
64
|
+
if name in ("self", "cls"):
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
# Handle **kwargs parameters specially
|
|
68
|
+
param_info = signature.parameters.get(name)
|
|
69
|
+
if param_info and param_info.kind == inspect.Parameter.VAR_KEYWORD:
|
|
70
|
+
# Flatten nested kwargs directly into the result
|
|
71
|
+
if isinstance(value, dict):
|
|
72
|
+
result.update(value)
|
|
73
|
+
else:
|
|
74
|
+
# Regular parameter
|
|
75
|
+
result[name] = value
|
|
76
|
+
|
|
77
|
+
return result
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.warning(
|
|
80
|
+
f"Error formatting arguments for trace: {e}. Using args and kwargs directly."
|
|
81
|
+
)
|
|
82
|
+
return {"args": args, "kwargs": kwargs}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Create patched version of traceable
|
|
86
|
+
def patched_traceable(*decorator_args, **decorator_kwargs):
|
|
87
|
+
# Handle the case when @traceable is used directly as decorator without arguments
|
|
88
|
+
if (
|
|
89
|
+
len(decorator_args) == 1
|
|
90
|
+
and callable(decorator_args[0])
|
|
91
|
+
and not decorator_kwargs
|
|
92
|
+
):
|
|
93
|
+
func = decorator_args[0]
|
|
94
|
+
return _create_appropriate_wrapper(func, original_traceable(func), {})
|
|
95
|
+
|
|
96
|
+
# Handle the case when @traceable(args) is used with parameters
|
|
97
|
+
original_decorated = original_traceable(*decorator_args, **decorator_kwargs)
|
|
98
|
+
|
|
99
|
+
def uipath_trace_decorator(func):
|
|
100
|
+
# Apply the original decorator with its arguments
|
|
101
|
+
wrapped_func = original_decorated(func)
|
|
102
|
+
return _create_appropriate_wrapper(func, wrapped_func, decorator_kwargs)
|
|
103
|
+
|
|
104
|
+
return uipath_trace_decorator
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _create_appropriate_wrapper(
|
|
108
|
+
original_func: Any, wrapped_func: Any, decorator_kwargs: Dict[str, Any]
|
|
109
|
+
):
|
|
110
|
+
"""Create the appropriate wrapper based on function type."""
|
|
111
|
+
|
|
112
|
+
# Get the function name and tags from decorator arguments
|
|
113
|
+
func_name = decorator_kwargs.get("name", original_func.__name__)
|
|
114
|
+
tags = decorator_kwargs.get("tags", None)
|
|
115
|
+
metadata = decorator_kwargs.get("metadata", None)
|
|
116
|
+
run_type = decorator_kwargs.get("run_type", None)
|
|
117
|
+
|
|
118
|
+
# Async generator function
|
|
119
|
+
if inspect.isasyncgenfunction(wrapped_func):
|
|
120
|
+
|
|
121
|
+
@functools.wraps(wrapped_func)
|
|
122
|
+
async def async_gen_wrapper(*args, **kwargs):
|
|
123
|
+
try:
|
|
124
|
+
call_uuid = str(uuid.uuid4())
|
|
125
|
+
|
|
126
|
+
inputs = format_args_for_trace(
|
|
127
|
+
inspect.signature(original_func), *args, **kwargs
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
dispatch_trace_event(
|
|
131
|
+
func_name,
|
|
132
|
+
inputs,
|
|
133
|
+
"call",
|
|
134
|
+
call_uuid,
|
|
135
|
+
run_type=run_type,
|
|
136
|
+
tags=tags,
|
|
137
|
+
metadata=metadata,
|
|
138
|
+
)
|
|
139
|
+
async_gen = wrapped_func(*args, **kwargs)
|
|
140
|
+
|
|
141
|
+
results = []
|
|
142
|
+
|
|
143
|
+
async for item in async_gen:
|
|
144
|
+
results.append(item)
|
|
145
|
+
yield item
|
|
146
|
+
|
|
147
|
+
dispatch_trace_event(
|
|
148
|
+
func_name, inputs, "completion", call_uuid, results
|
|
149
|
+
)
|
|
150
|
+
except Exception as e:
|
|
151
|
+
dispatch_trace_event(
|
|
152
|
+
func_name, inputs, "completion", call_uuid, exception=e
|
|
153
|
+
)
|
|
154
|
+
raise
|
|
155
|
+
|
|
156
|
+
return async_gen_wrapper
|
|
157
|
+
|
|
158
|
+
# Sync generator function
|
|
159
|
+
elif inspect.isgeneratorfunction(wrapped_func):
|
|
160
|
+
|
|
161
|
+
@functools.wraps(wrapped_func)
|
|
162
|
+
def gen_wrapper(*args, **kwargs):
|
|
163
|
+
try:
|
|
164
|
+
call_uuid = str(uuid.uuid4())
|
|
165
|
+
|
|
166
|
+
inputs = format_args_for_trace(
|
|
167
|
+
inspect.signature(original_func), *args, **kwargs
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
results = []
|
|
171
|
+
|
|
172
|
+
dispatch_trace_event(
|
|
173
|
+
func_name,
|
|
174
|
+
inputs,
|
|
175
|
+
"call",
|
|
176
|
+
call_uuid,
|
|
177
|
+
run_type=run_type,
|
|
178
|
+
tags=tags,
|
|
179
|
+
metadata=metadata,
|
|
180
|
+
)
|
|
181
|
+
gen = wrapped_func(*args, **kwargs)
|
|
182
|
+
for item in gen:
|
|
183
|
+
results.append(item)
|
|
184
|
+
yield item
|
|
185
|
+
dispatch_trace_event(
|
|
186
|
+
func_name, inputs, "completion", call_uuid, results
|
|
187
|
+
)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
dispatch_trace_event(
|
|
190
|
+
func_name, inputs, "completion", call_uuid, exception=e
|
|
191
|
+
)
|
|
192
|
+
raise
|
|
193
|
+
|
|
194
|
+
return gen_wrapper
|
|
195
|
+
|
|
196
|
+
# Async function
|
|
197
|
+
elif inspect.iscoroutinefunction(wrapped_func):
|
|
198
|
+
|
|
199
|
+
@functools.wraps(wrapped_func)
|
|
200
|
+
async def async_wrapper(*args, **kwargs):
|
|
201
|
+
try:
|
|
202
|
+
call_uuid = str(uuid.uuid4())
|
|
203
|
+
|
|
204
|
+
inputs = format_args_for_trace(
|
|
205
|
+
inspect.signature(original_func), *args, **kwargs
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
dispatch_trace_event(
|
|
209
|
+
func_name,
|
|
210
|
+
inputs,
|
|
211
|
+
"call",
|
|
212
|
+
call_uuid,
|
|
213
|
+
run_type=run_type,
|
|
214
|
+
tags=tags,
|
|
215
|
+
metadata=metadata,
|
|
216
|
+
)
|
|
217
|
+
result = await wrapped_func(*args, **kwargs)
|
|
218
|
+
dispatch_trace_event(func_name, inputs, "completion", call_uuid, result)
|
|
219
|
+
return result
|
|
220
|
+
except Exception as e:
|
|
221
|
+
dispatch_trace_event(
|
|
222
|
+
func_name, inputs, "completion", call_uuid, exception=e
|
|
223
|
+
)
|
|
224
|
+
raise
|
|
225
|
+
|
|
226
|
+
return async_wrapper
|
|
227
|
+
|
|
228
|
+
# Regular sync function (default case)
|
|
229
|
+
else:
|
|
230
|
+
|
|
231
|
+
@functools.wraps(wrapped_func)
|
|
232
|
+
def sync_wrapper(*args, **kwargs):
|
|
233
|
+
try:
|
|
234
|
+
call_uuid = str(uuid.uuid4())
|
|
235
|
+
|
|
236
|
+
inputs = format_args_for_trace(
|
|
237
|
+
inspect.signature(original_func), *args, **kwargs
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
dispatch_trace_event(
|
|
241
|
+
func_name,
|
|
242
|
+
inputs,
|
|
243
|
+
"call",
|
|
244
|
+
call_uuid,
|
|
245
|
+
run_type=run_type,
|
|
246
|
+
tags=tags,
|
|
247
|
+
metadata=metadata,
|
|
248
|
+
)
|
|
249
|
+
result = wrapped_func(*args, **kwargs)
|
|
250
|
+
dispatch_trace_event(func_name, inputs, "completion", call_uuid, result)
|
|
251
|
+
return result
|
|
252
|
+
except Exception as e:
|
|
253
|
+
dispatch_trace_event(
|
|
254
|
+
func_name, inputs, "completion", call_uuid, exception=e
|
|
255
|
+
)
|
|
256
|
+
raise
|
|
257
|
+
|
|
258
|
+
return sync_wrapper
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# Apply the patch
|
|
262
|
+
def _instrument_traceable():
|
|
263
|
+
"""Apply the patch to langsmith module at import time."""
|
|
264
|
+
global original_langsmith, original_traceable
|
|
265
|
+
|
|
266
|
+
# Import the original module if not already done
|
|
267
|
+
if original_langsmith is None:
|
|
268
|
+
# Temporarily remove our custom module from sys.modules
|
|
269
|
+
if "langsmith" in sys.modules:
|
|
270
|
+
original_langsmith = sys.modules["langsmith"]
|
|
271
|
+
del sys.modules["langsmith"]
|
|
272
|
+
|
|
273
|
+
# Import the original module
|
|
274
|
+
original_langsmith = importlib.import_module("langsmith")
|
|
275
|
+
|
|
276
|
+
# Store the original traceable
|
|
277
|
+
original_traceable = original_langsmith.traceable
|
|
278
|
+
|
|
279
|
+
# Replace the traceable function with our patched version
|
|
280
|
+
original_langsmith.traceable = patched_traceable
|
|
281
|
+
|
|
282
|
+
# Put our modified module back
|
|
283
|
+
sys.modules["langsmith"] = original_langsmith
|
|
284
|
+
|
|
285
|
+
return original_langsmith
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_context.py
RENAMED
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_escalation.py
RENAMED
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_exception.py
RENAMED
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_input.py
RENAMED
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_cli/_runtime/_output.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_utils/_request_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/_utils/_sleep_policy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/chat/utils/_chat_types.py
RENAMED
|
File without changes
|
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/embeddings/embeddings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{uipath_langchain-0.0.83 → uipath_langchain-0.0.85}/uipath_langchain/tracers/UiPathTracer.py
RENAMED
|
File without changes
|
|
File without changes
|