uipath-langchain 0.0.131__py3-none-any.whl → 0.0.133__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.
@@ -0,0 +1,191 @@
1
+ import json
2
+ import logging
3
+ from typing import Iterable, Optional, Type
4
+
5
+ import httpx
6
+ from jsonschema_pydantic import jsonschema_to_pydantic as create_model # type: ignore
7
+ from langchain_core.caches import BaseCache
8
+ from langchain_core.runnables.utils import Output
9
+ from langchain_core.tools import BaseTool, StructuredTool
10
+ from langgraph.types import interrupt
11
+ from pydantic import BaseModel
12
+ from uipath import UiPath
13
+ from uipath.agent.models.agent import (
14
+ AgentDefinition,
15
+ AgentEscalationChannel,
16
+ AgentEscalationResourceConfig,
17
+ AgentIntegrationToolResourceConfig,
18
+ AgentProcessToolResourceConfig,
19
+ AgentResourceConfig,
20
+ )
21
+ from uipath.models import CreateAction, InvokeProcess
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ def create_process_tool(resource: AgentProcessToolResourceConfig) -> Iterable[BaseTool]:
27
+ async def process(**kwargs) -> BaseModel:
28
+ return interrupt(
29
+ InvokeProcess(
30
+ name=resource.name,
31
+ input_arguments=kwargs,
32
+ process_folder_path=resource.properties.folder_path,
33
+ )
34
+ )
35
+
36
+ input_schema = create_model(resource.input_schema)
37
+
38
+ class ProcessTool(StructuredTool):
39
+ @property
40
+ def OutputType(self) -> type[Output]:
41
+ return create_model(resource.output_schema)
42
+
43
+ yield ProcessTool(
44
+ name=resource.name,
45
+ args_schema=input_schema,
46
+ description=resource.description,
47
+ coroutine=process,
48
+ )
49
+
50
+
51
+ def create_escalation_tool_from_channel(channel: AgentEscalationChannel) -> BaseTool:
52
+ async def escalate(**kwargs) -> BaseModel:
53
+ recipients = channel.recipients
54
+ if len(recipients) > 1:
55
+ logger.warning(
56
+ "Received more than one recipient. Defaulting to first recipient."
57
+ )
58
+ assignee = recipients[0].value if recipients else None
59
+ return interrupt(
60
+ CreateAction(
61
+ title=channel.description,
62
+ data=kwargs,
63
+ assignee=assignee,
64
+ app_name=channel.properties.app_name,
65
+ app_folder_path=None, # Channels specify folder name but not folder path.
66
+ app_folder_key=channel.properties.resource_key,
67
+ app_key=channel.properties.resource_key,
68
+ app_version=channel.properties.app_version,
69
+ )
70
+ )
71
+
72
+ input_schema = create_model(channel.input_schema)
73
+
74
+ class EscalationTool(StructuredTool):
75
+ @property
76
+ def OutputType(self) -> type[Output]:
77
+ return create_model(channel.output_schema)
78
+
79
+ return EscalationTool(
80
+ name=channel.name,
81
+ args_schema=input_schema,
82
+ description=channel.description,
83
+ coroutine=escalate,
84
+ )
85
+
86
+
87
+ def create_escalation_tool(
88
+ resource: AgentEscalationResourceConfig,
89
+ ) -> Iterable[BaseTool]:
90
+ for channel in resource.channels:
91
+ yield create_escalation_tool_from_channel(channel)
92
+
93
+
94
+ def create_integration_tool(
95
+ resource: AgentIntegrationToolResourceConfig,
96
+ ) -> Iterable[BaseTool]:
97
+ async def integration(**kwargs) -> BaseModel:
98
+ uipath = UiPath()
99
+ remote_connection = await uipath.connections.retrieve_async(
100
+ resource.properties.connection.id
101
+ )
102
+ token = await uipath.connections.retrieve_token_async(
103
+ resource.properties.connection.id
104
+ )
105
+ tool_url = f"{remote_connection.api_base_uri}/v3/element/instances/{remote_connection.element_instance_id}{resource.properties.tool_path}"
106
+
107
+ response = await httpx.AsyncClient().request(
108
+ resource.properties.method,
109
+ tool_url,
110
+ headers={"Authorization": f"Bearer {token.access_token}"},
111
+ content=json.dumps(kwargs),
112
+ )
113
+ return response.json()
114
+
115
+ input_schema = create_model(resource.input_schema)
116
+
117
+ class IntegrationTool(StructuredTool):
118
+ @property
119
+ def OutputType(self) -> type[Output]:
120
+ return create_model({})
121
+
122
+ yield IntegrationTool(
123
+ name=resource.name,
124
+ args_schema=input_schema,
125
+ description=resource.description,
126
+ coroutine=integration,
127
+ )
128
+
129
+
130
+ def create_cached_wrapper_from_tool(
131
+ wrapped: BaseTool, cache: Optional[BaseCache]
132
+ ) -> BaseTool:
133
+ if cache is None:
134
+ return wrapped
135
+ else:
136
+
137
+ async def cached_invocation(**kwargs) -> BaseModel:
138
+ namespace = f"{wrapped.name}.tool_invoke"
139
+ key = str(kwargs)
140
+ cached = cache.lookup(namespace, key)
141
+ if cached:
142
+ return cached[0]
143
+ response = await wrapped.ainvoke(input=kwargs)
144
+ cache.update(namespace, key, [response])
145
+ return response
146
+
147
+ input_schema = wrapped.args_schema
148
+
149
+ class CachedTool(StructuredTool):
150
+ OutputType: Type[BaseModel] = wrapped.OutputType
151
+
152
+ return CachedTool(
153
+ name=wrapped.name,
154
+ args_schema=input_schema,
155
+ description=wrapped.description,
156
+ coroutine=cached_invocation,
157
+ )
158
+
159
+
160
+ def create_cached_wrapper(
161
+ tools: Iterable[BaseTool], cache: Optional[BaseCache]
162
+ ) -> Iterable[BaseTool]:
163
+ for wrapped in tools:
164
+ yield create_cached_wrapper_from_tool(wrapped, cache)
165
+
166
+
167
+ def create_resource_tool(
168
+ resource: AgentResourceConfig, cache: Optional[BaseCache] = None
169
+ ) -> Iterable[BaseTool]:
170
+ match resource:
171
+ case AgentProcessToolResourceConfig():
172
+ return create_cached_wrapper(create_process_tool(resource), cache)
173
+ case AgentIntegrationToolResourceConfig():
174
+ return create_cached_wrapper(create_integration_tool(resource), cache)
175
+ case AgentEscalationResourceConfig():
176
+ return create_cached_wrapper(create_escalation_tool(resource), cache)
177
+ case _:
178
+ raise NotImplementedError()
179
+
180
+
181
+ def safe_extract_tools(
182
+ agent_definition: AgentDefinition, cache: Optional[BaseCache] = None
183
+ ) -> list[BaseTool]:
184
+ tools = []
185
+ for resource in agent_definition.resources:
186
+ try:
187
+ for structured_tool in create_resource_tool(resource, cache):
188
+ tools.append(structured_tool)
189
+ except NotImplementedError:
190
+ logger.warning(f"Unable to convert {resource.name} into a tool.")
191
+ return tools
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uipath-langchain
3
- Version: 0.0.131
3
+ Version: 0.0.133
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
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Topic :: Software Development :: Build Tools
16
16
  Requires-Python: >=3.10
17
17
  Requires-Dist: httpx>=0.27.0
18
+ Requires-Dist: jsonschema-pydantic>=0.6
18
19
  Requires-Dist: langchain-community>=0.3.21
19
20
  Requires-Dist: langchain-core>=0.3.34
20
21
  Requires-Dist: langchain-openai>=0.3.3
@@ -25,7 +26,7 @@ Requires-Dist: openai>=1.65.5
25
26
  Requires-Dist: openinference-instrumentation-langchain>=0.1.50
26
27
  Requires-Dist: pydantic-settings>=2.6.0
27
28
  Requires-Dist: python-dotenv>=1.0.1
28
- Requires-Dist: uipath<2.2.0,>=2.1.54
29
+ Requires-Dist: uipath<2.2.0,>=2.1.60
29
30
  Provides-Extra: langchain
30
31
  Description-Content-Type: text/markdown
31
32
 
@@ -1,21 +1,25 @@
1
1
  uipath_langchain/__init__.py,sha256=VBrvQn7d3nuOdN7zEnV2_S-uhmkjgEIlXiFVeZxZakQ,80
2
2
  uipath_langchain/middlewares.py,sha256=6ljfbtWekrYc5G9KWDLSaViJ1DVIaNM-4qeB1BfHywE,731
3
3
  uipath_langchain/_cli/__init__.py,sha256=juqd9PbXs4yg45zMJ7BHAOPQjb7sgEbWE9InBtGZhfo,24
4
- uipath_langchain/_cli/cli_dev.py,sha256=VlI8qgCw-63I97tp_9lbXs-CVcNSjpd2sC13YNZAIuU,1401
5
- uipath_langchain/_cli/cli_eval.py,sha256=eGhvenAKJDdt9zIYWO7k01zJ7T1tGte7a6zz06iO6cw,2539
4
+ uipath_langchain/_cli/cli_dev.py,sha256=3e9RldNGirIk9184NdLK6kDuGeeqZjekTxbSZRtXjBE,1505
5
+ uipath_langchain/_cli/cli_eval.py,sha256=mvaKRF-pvxdNa2mk_ude9SYSd_kmMBo-hChlXokXOcw,2795
6
6
  uipath_langchain/_cli/cli_init.py,sha256=xhxJ8tuMSrVUNHvltgyPpOrvgMA-wq9shHeYYwvHILs,8199
7
7
  uipath_langchain/_cli/cli_new.py,sha256=KKLxCzz7cDQ__rRr_a496IHWlSQXhmrBNgmKHnXAnTY,2336
8
- uipath_langchain/_cli/cli_run.py,sha256=FT4ilVCBNSYllB-wS6__0E0fWKlrQEhO9N74wNHPffs,2056
8
+ uipath_langchain/_cli/cli_run.py,sha256=hRcoXJgOIFceCswzTfZKyzqVee3j-oSh-13EfuQmmE8,2614
9
9
  uipath_langchain/_cli/_runtime/_context.py,sha256=yyzYJDmk2fkH4T5gm4cLGRyXtjLESrpzHBT9euqluTA,817
10
10
  uipath_langchain/_cli/_runtime/_conversation.py,sha256=S1KTx_q-La7ikPRT3nBcIp8t-J9CF0QB0DCduQIIB28,11149
11
11
  uipath_langchain/_cli/_runtime/_exception.py,sha256=USKkLYkG-dzjX3fEiMMOHnVUpiXJs_xF0OQXCCOvbYM,546
12
- uipath_langchain/_cli/_runtime/_input.py,sha256=3IKrZR9xmxYg6l_1TbFpaLOWPDvK8d3nSKIHPGEEGzE,5715
12
+ uipath_langchain/_cli/_runtime/_input.py,sha256=Zx-8ZEr5Z796gdd3NnrlNObMIuXJobAV9mZwOql67Lo,5658
13
13
  uipath_langchain/_cli/_runtime/_output.py,sha256=yJOZPWv2FRUJWv1NRs9JmpB4QMTDXu8jrxoaKrfJvzw,9078
14
- uipath_langchain/_cli/_runtime/_runtime.py,sha256=ai-r6cYp4YbJqGhxWGvL68BHtJGhYhAZF-MEhKT7ceY,14955
14
+ uipath_langchain/_cli/_runtime/_runtime.py,sha256=cVt0TlsEBmKv6EcFTtCOT9ITsguzLDqFGMdhPOiBPRc,14318
15
15
  uipath_langchain/_cli/_templates/langgraph.json.template,sha256=eeh391Gta_hoRgaNaZ58nW1LNvCVXA7hlAH6l7Veous,107
16
16
  uipath_langchain/_cli/_templates/main.py.template,sha256=GpSblGH2hwS9ibqQmX2iB2nsmOA5zDfEEF4ChLiMxbQ,875
17
17
  uipath_langchain/_cli/_utils/_graph.py,sha256=nMJWy8FmaD9rqPUY2lHc5uVpUzbXD1RO12uJnhe0kdo,6803
18
- uipath_langchain/_utils/__init__.py,sha256=WoY66enCygRXTh6v5B1UrRcFCnQYuPJ8oqDkwomXzLc,194
18
+ uipath_langchain/_tracing/__init__.py,sha256=UqrLc_WimpzKY82M0LJsgJ-HFQUQFjOmOlD1XQ8V-R4,181
19
+ uipath_langchain/_tracing/_instrument_traceable.py,sha256=8f9FyAKWE6kH1N8ErbpwqZHAzNjGwbLjQn7jdX5yAgA,4343
20
+ uipath_langchain/_tracing/_oteladapter.py,sha256=bhbLLTBwZ1rLDL-BgbvB8oR11y1Tp8jEw2mrAuVJ5go,8433
21
+ uipath_langchain/_tracing/_utils.py,sha256=r_fiSk3HDDAcePY_UbbEYiSbNqzn5gFeMPYBDvGrFx0,902
22
+ uipath_langchain/_utils/__init__.py,sha256=-w-4TD9ZnJDCpj4VIPXhJciukrmDJJbmnOFnhAkAaEU,81
19
23
  uipath_langchain/_utils/_request_mixin.py,sha256=ddKFs_0mjoFCmvPTiOTPJh1IIqYUo5CUka-B7zAZphE,19695
20
24
  uipath_langchain/_utils/_settings.py,sha256=2fExMQJ88YptfldmzMfZIpsx-m1gfMkeYGf5t6KIe0A,3084
21
25
  uipath_langchain/_utils/_sleep_policy.py,sha256=e9pHdjmcCj4CVoFM1jMyZFelH11YatsgWfpyrfXzKBQ,1251
@@ -26,15 +30,12 @@ uipath_langchain/embeddings/__init__.py,sha256=QICtYB58ZyqFfDQrEaO8lTEgAU5NuEKlR
26
30
  uipath_langchain/embeddings/embeddings.py,sha256=45gKyb6HVKigwE-0CXeZcAk33c0mteaEdPGa8hviqcw,4339
27
31
  uipath_langchain/retrievers/__init__.py,sha256=rOn7PyyHgZ4pMnXWPkGqmuBmx8eGuo-Oyndo7Wm9IUU,108
28
32
  uipath_langchain/retrievers/context_grounding_retriever.py,sha256=YLCIwy89LhLnNqcM0YJ5mZoeNyCs5UiKD3Wly8gnW1E,2239
29
- uipath_langchain/tracers/AsyncUiPathTracer.py,sha256=SgFLVU8ZtRaUjIiiM5jjYqlKc-4ec3tAXwZVceDM2ng,9147
30
- uipath_langchain/tracers/__init__.py,sha256=bMmqGCC4DsuLUdDVM4zvmVbRLPeULZ8K9ity0GAfy_4,197
31
- uipath_langchain/tracers/_events.py,sha256=CJri76SSdu7rGJIkXurJ2C5sQahfSK4E5UWwWYdEAtE,922
32
- uipath_langchain/tracers/_instrument_traceable.py,sha256=0e841zVzcPWjOGtmBx0GeHbq3JoqsmWv6gVPzDOKNTM,13496
33
- uipath_langchain/tracers/_utils.py,sha256=JOT1tKMdvqjMDtj2WbmbOWMeMlTXBWavxWpogX7KlRA,1543
33
+ uipath_langchain/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ uipath_langchain/tools/preconfigured.py,sha256=Ucl9mta_su66j-5uOC-vZzyc3V2-Em6q9WO1qsmQv48,6291
34
35
  uipath_langchain/vectorstores/__init__.py,sha256=w8qs1P548ud1aIcVA_QhBgf_jZDrRMK5Lono78yA8cs,114
35
36
  uipath_langchain/vectorstores/context_grounding_vectorstore.py,sha256=TncIXG-YsUlO0R5ZYzWsM-Dj1SVCZbzmo2LraVxXelc,9559
36
- uipath_langchain-0.0.131.dist-info/METADATA,sha256=1T4chCHDVrMR97PZYal-X_kCCLeLlcb6hawfHgkpebM,4235
37
- uipath_langchain-0.0.131.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
- uipath_langchain-0.0.131.dist-info/entry_points.txt,sha256=FUtzqGOEntlJKMJIXhQUfT7ZTbQmGhke1iCmDWZaQZI,81
39
- uipath_langchain-0.0.131.dist-info/licenses/LICENSE,sha256=JDpt-uotAkHFmxpwxi6gwx6HQ25e-lG4U_Gzcvgp7JY,1063
40
- uipath_langchain-0.0.131.dist-info/RECORD,,
37
+ uipath_langchain-0.0.133.dist-info/METADATA,sha256=zcSdl4I5iJsBi0jOf7IUUprBv39KfQZyrM1EzVH1OCQ,4275
38
+ uipath_langchain-0.0.133.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
39
+ uipath_langchain-0.0.133.dist-info/entry_points.txt,sha256=FUtzqGOEntlJKMJIXhQUfT7ZTbQmGhke1iCmDWZaQZI,81
40
+ uipath_langchain-0.0.133.dist-info/licenses/LICENSE,sha256=JDpt-uotAkHFmxpwxi6gwx6HQ25e-lG4U_Gzcvgp7JY,1063
41
+ uipath_langchain-0.0.133.dist-info/RECORD,,
@@ -1,274 +0,0 @@
1
- import asyncio
2
- import json
3
- import logging
4
- import queue
5
- import uuid
6
- import warnings
7
- from os import environ as env
8
- from typing import Any, Dict, Optional
9
-
10
- import httpx
11
- from langchain_core.tracers.base import AsyncBaseTracer
12
- from langchain_core.tracers.schemas import Run
13
- from pydantic import PydanticDeprecationWarning
14
- from uipath._cli._runtime._contracts import UiPathTraceContext
15
-
16
- from ._events import CustomTraceEvents, FunctionCallEventData
17
- from ._utils import _setup_tracer_httpx_logging, _simple_serialize_defaults
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
- _setup_tracer_httpx_logging("/llmops_/api/Agent/trace/")
22
-
23
-
24
- class Status:
25
- SUCCESS = 1
26
- ERROR = 2
27
- INTERRUPTED = 3
28
-
29
-
30
- class AsyncUiPathTracer(AsyncBaseTracer):
31
- def __init__(
32
- self,
33
- context: Optional[UiPathTraceContext] = None,
34
- client: Optional[httpx.AsyncClient] = None,
35
- **kwargs,
36
- ):
37
- super().__init__(**kwargs)
38
-
39
- self.client = client or httpx.AsyncClient()
40
- self.retries = 3
41
- self.log_queue: queue.Queue[dict[str, Any]] = queue.Queue()
42
-
43
- self.context = context or UiPathTraceContext()
44
-
45
- self.base_url = self._get_base_url()
46
-
47
- auth_token = env.get("UNATTENDED_USER_ACCESS_TOKEN") or env.get(
48
- "UIPATH_ACCESS_TOKEN"
49
- )
50
-
51
- self.headers = {"Authorization": f"Bearer {auth_token}"}
52
-
53
- self.running = True
54
- self.worker_task = asyncio.create_task(self._worker())
55
- self.function_call_run_map: Dict[str, Run] = {}
56
-
57
- async def on_custom_event(
58
- self,
59
- name: str,
60
- data: Any,
61
- *,
62
- run_id: uuid.UUID,
63
- tags=None,
64
- metadata=None,
65
- **kwargs: Any,
66
- ) -> None:
67
- if name == CustomTraceEvents.UIPATH_TRACE_FUNCTION_CALL:
68
- # only handle the function call event
69
-
70
- if not isinstance(data, FunctionCallEventData):
71
- logger.warning(
72
- f"Received unexpected data type for function call event: {type(data)}"
73
- )
74
- return
75
-
76
- if data.event_type == "call":
77
- run = self.run_map[str(run_id)]
78
- child_run = run.create_child(
79
- name=data.function_name,
80
- run_type=data.run_type,
81
- tags=data.tags,
82
- inputs=data.inputs,
83
- )
84
-
85
- if data.metadata is not None:
86
- run.add_metadata(data.metadata)
87
-
88
- call_uuid = data.call_uuid
89
- self.function_call_run_map[call_uuid] = child_run
90
-
91
- self._send_span(run)
92
-
93
- if data.event_type == "completion":
94
- call_uuid = data.call_uuid
95
- previous_run = self.function_call_run_map.pop(call_uuid, None)
96
-
97
- if previous_run:
98
- previous_run.end(
99
- outputs=self._safe_dict_dump(data.output), error=data.error
100
- )
101
- self._send_span(previous_run)
102
-
103
- async def wait_for_all_tracers(self) -> None:
104
- """
105
- Wait for all pending log requests to complete
106
- """
107
- self.running = False
108
- if self.worker_task:
109
- await self.worker_task
110
-
111
- async def _worker(self):
112
- """Worker loop that processes logs from the queue."""
113
- while self.running:
114
- try:
115
- if self.log_queue.empty():
116
- await asyncio.sleep(1)
117
- continue
118
-
119
- span_data = self.log_queue.get_nowait()
120
-
121
- url = self._build_url(self.context.trace_id)
122
-
123
- for attempt in range(self.retries):
124
- response = await self.client.post(
125
- url,
126
- headers=self.headers,
127
- json=[span_data], # api expects a list of spans
128
- timeout=10,
129
- )
130
-
131
- if response.is_success:
132
- break
133
-
134
- await asyncio.sleep(0.5 * (2**attempt)) # Exponential backoff
135
-
136
- if 400 <= response.status_code < 600:
137
- logger.warning(
138
- f"Error when sending trace: {response}. Body is: {response.text}"
139
- )
140
- except Exception as e:
141
- logger.warning(f"Exception when sending trace: {e}", exc_info=e)
142
-
143
- # wait for a bit to ensure all logs are sent
144
- await asyncio.sleep(1)
145
-
146
- # try to send any remaining logs in the queue
147
- while True:
148
- try:
149
- if self.log_queue.empty():
150
- break
151
-
152
- span_data = self.log_queue.get_nowait()
153
- url = self._build_url(self.context.trace_id)
154
-
155
- response = await self.client.post(
156
- url,
157
- headers=self.headers,
158
- json=[span_data], # api expects a list of spans
159
- timeout=10,
160
- )
161
- except Exception as e:
162
- logger.warning(f"Exception when sending trace: {e}", exc_info=e)
163
-
164
- async def _persist_run(self, run: Run) -> None:
165
- # Determine if this is a start or end trace based on whether end_time is set
166
- self._send_span(run)
167
-
168
- def _send_span(self, run: Run) -> None:
169
- """Send span data for a run to the API"""
170
- run_id = str(run.id)
171
-
172
- try:
173
- start_time = (
174
- run.start_time.isoformat() if run.start_time is not None else None
175
- )
176
- end_time = (
177
- run.end_time.isoformat() if run.end_time is not None else start_time
178
- )
179
-
180
- parent_id = (
181
- str(run.parent_run_id)
182
- if run.parent_run_id is not None
183
- else self.context.parent_span_id
184
- )
185
- attributes = self._safe_jsons_dump(self._run_to_dict(run))
186
- status = self._determine_status(run.error)
187
-
188
- span_data = {
189
- "id": run_id,
190
- "parentId": parent_id,
191
- "traceId": self.context.trace_id,
192
- "name": run.name,
193
- "startTime": start_time,
194
- "endTime": end_time,
195
- "referenceId": self.context.reference_id,
196
- "attributes": attributes,
197
- "organizationId": self.context.org_id,
198
- "tenantId": self.context.tenant_id,
199
- "spanType": "LangGraphRun",
200
- "status": status,
201
- "jobKey": self.context.job_id,
202
- "folderKey": self.context.folder_key,
203
- "processKey": self.context.folder_key,
204
- "expiryTimeUtc": None,
205
- }
206
-
207
- self.log_queue.put(span_data)
208
- except Exception as e:
209
- logger.warning(f"Exception when adding trace to queue: {e}.")
210
-
211
- async def _start_trace(self, run: Run) -> None:
212
- await super()._start_trace(run)
213
- await self._persist_run(run)
214
-
215
- async def _end_trace(self, run: Run) -> None:
216
- await super()._end_trace(run)
217
- await self._persist_run(run)
218
-
219
- def _determine_status(self, error: Optional[str]):
220
- if error:
221
- if error.startswith("GraphInterrupt("):
222
- return Status.INTERRUPTED
223
-
224
- return Status.ERROR
225
-
226
- return Status.SUCCESS
227
-
228
- def _safe_jsons_dump(self, obj) -> str:
229
- try:
230
- json_str = json.dumps(obj, default=_simple_serialize_defaults)
231
- return json_str
232
- except Exception as e:
233
- logger.warning(f"Error serializing object to JSON: {e}")
234
- return "{ }"
235
-
236
- def _safe_dict_dump(self, obj) -> Dict[str, Any]:
237
- try:
238
- serialized = json.loads(json.dumps(obj, default=_simple_serialize_defaults))
239
- return serialized
240
- except Exception as e:
241
- # Last resort - string representation
242
- logger.warning(f"Error serializing object to JSON: {e}")
243
- return {"raw": str(obj)}
244
-
245
- def _run_to_dict(self, run: Run):
246
- with warnings.catch_warnings():
247
- warnings.simplefilter("ignore", category=PydanticDeprecationWarning)
248
-
249
- # Helper function to safely copy values
250
- def safe_copy(value):
251
- if value is None:
252
- return None
253
- if hasattr(value, "copy") and callable(value.copy):
254
- return value.copy()
255
- return value
256
-
257
- return {
258
- **run.dict(exclude={"child_runs", "inputs", "outputs", "serialized"}),
259
- "inputs": safe_copy(run.inputs),
260
- "outputs": safe_copy(run.outputs),
261
- }
262
-
263
- def _get_base_url(self) -> str:
264
- uipath_url = (
265
- env.get("UIPATH_URL") or "https://cloud.uipath.com/dummyOrg/dummyTennant/"
266
- )
267
-
268
- uipath_url = uipath_url.rstrip("/")
269
-
270
- return uipath_url
271
-
272
- def _build_url(self, trace_id: Optional[str]) -> str:
273
- """Construct the URL for the API request."""
274
- return f"{self.base_url}/llmopstenant_/api/Traces/spans?traceId={trace_id}&source=Robots"
@@ -1,7 +0,0 @@
1
- from ._instrument_traceable import _instrument_traceable_attributes
2
- from .AsyncUiPathTracer import AsyncUiPathTracer
3
-
4
- __all__ = [
5
- "AsyncUiPathTracer",
6
- "_instrument_traceable_attributes",
7
- ]
@@ -1,33 +0,0 @@
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