From 979f3118248cdabb00076d59800bb169a54e2f1e Mon Sep 17 00:00:00 2001 From: ionmincu Date: Wed, 28 May 2025 18:46:24 +0300 Subject: [PATCH 01/17] feat(dependency): add dependency tracking --- pyproject.toml | 2 +- src/uipath/_services/assets_service.py | 62 +++++++- src/uipath/_services/buckets_service.py | 84 +++++++++- src/uipath/_services/jobs_service.py | 129 ++++++++++++++- src/uipath/_services/llm_gateway_service.py | 70 +++++++- src/uipath/_services/processes_service.py | 22 ++- src/uipath/_services/queues_service.py | 142 +++++++++++++++-- src/uipath/tracing/_traced.py | 90 ++++++++++- src/uipath/tracing/_utils.py | 52 +++--- tests/tracing/test_traced.py | 167 ++++++++++++++++++++ 10 files changed, 756 insertions(+), 64 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6eacebc0..975be820 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.0.62" +version = "2.0.63" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10" diff --git a/src/uipath/_services/assets_service.py b/src/uipath/_services/assets_service.py index 3112ed64..a5396d18 100644 --- a/src/uipath/_services/assets_service.py +++ b/src/uipath/_services/assets_service.py @@ -25,7 +25,14 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: self._base_url = "assets" @traced( - name="assets_retrieve", run_type="uipath", hide_input=True, hide_output=True + name="assets_retrieve", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda name, **_kwargs: f"Asset:{name}", + "operationName": "GET Asset", + }, ) @infer_bindings(resource_type="asset") def retrieve( @@ -80,7 +87,14 @@ def retrieve( return Asset.model_validate(response.json()["value"][0]) @traced( - name="assets_retrieve", run_type="uipath", hide_input=True, hide_output=True + name="assets_retrieve", # Assuming this should be assets_retrieve_async or similar + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, name, **_kwargs: f"Asset:{name}", + "operationName": "GET Asset", + }, ) @infer_bindings(resource_type="asset") async def retrieve_async( @@ -126,7 +140,14 @@ async def retrieve_async( return Asset.model_validate(response.json()["value"][0]) @traced( - name="assets_credential", run_type="uipath", hide_input=True, hide_output=True + name="assets_credential", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, name, **_kwargs: f"Asset:{name}", + "operationName": "GET Credential", + }, ) @infer_bindings(resource_type="asset") def retrieve_credential( @@ -179,7 +200,14 @@ def retrieve_credential( return user_asset.credential_password @traced( - name="assets_credential", run_type="uipath", hide_input=True, hide_output=True + name="assets_credential", # Assuming this should be assets_credential_async or similar + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, name, **_kwargs: f"Asset:{name}", + "operationName": "GET Credential", + }, ) @infer_bindings(resource_type="asset") async def retrieve_credential_async( @@ -231,7 +259,18 @@ async def retrieve_credential_async( return user_asset.credential_password - @traced(name="assets_update", run_type="uipath", hide_input=True, hide_output=True) + @traced( + name="assets_update", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + robot_asset, + **_kwargs: f"Asset:{robot_asset.name}", + "operationName": "UPDATE Asset", + }, + ) def update( self, robot_asset: UserAsset, @@ -273,7 +312,18 @@ def update( return response.json() - @traced(name="assets_update", run_type="uipath", hide_input=True, hide_output=True) + @traced( + name="assets_update", # Assuming this should be assets_update_async or similar + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + robot_asset, + **_kwargs: f"Asset:{robot_asset.name}", + "operationName": "UPDATE Asset", + }, + ) async def update_async( self, robot_asset: UserAsset, diff --git a/src/uipath/_services/buckets_service.py b/src/uipath/_services/buckets_service.py index a41bb59e..6a0fe6ed 100644 --- a/src/uipath/_services/buckets_service.py +++ b/src/uipath/_services/buckets_service.py @@ -28,7 +28,19 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: self.custom_client = httpx.Client() self.custom_client_async = httpx.AsyncClient() - @traced(name="buckets_download", run_type="uipath") + @traced( + name="buckets_download", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + name=None, + key=None, + **_kwargs: f"Bucket:{name or key}", + "operationName": "DOWNLOAD File", + }, + ) @infer_bindings(resource_type="bucket") def download( self, @@ -84,7 +96,19 @@ def download( file_content = self.custom_client.get(read_uri, headers=headers).content file.write(file_content) - @traced(name="buckets_download", run_type="uipath") + @traced( + name="buckets_download_async", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + name=None, + key=None, + **_kwargs: f"Bucket:{name or key}", + "operationName": "DOWNLOAD File", + }, + ) @infer_bindings(resource_type="bucket") async def download_async( self, @@ -146,7 +170,19 @@ async def download_async( ).content file.write(file_content) - @traced(name="buckets_upload", run_type="uipath") + @traced( + name="buckets_upload", + run_type="uipath", + input_processor=_upload_from_memory_input_processor, + hide_output=True, + dependency={ + "targetName": lambda _s, + name=None, + key=None, + **_kwargs: f"Bucket:{name or key}", + "operationName": "UPLOAD File", + }, + ) @infer_bindings(resource_type="bucket") def upload( self, @@ -229,7 +265,19 @@ def upload( write_uri, headers=headers, files={"file": file} ) - @traced(name="buckets_upload", run_type="uipath") + @traced( + name="buckets_upload_async", + run_type="uipath", + input_processor=_upload_from_memory_input_processor, + hide_output=True, + dependency={ + "targetName": lambda _s, + name=None, + key=None, + **_kwargs: f"Bucket:{name or key}", + "operationName": "UPLOAD File", + }, + ) @infer_bindings(resource_type="bucket") async def upload_async( self, @@ -317,7 +365,19 @@ async def upload_async( write_uri, headers=headers, files={"file": file} ) - @traced(name="buckets_retrieve", run_type="uipath") + @traced( + name="buckets_retrieve", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + name=None, + key=None, + **_kwargs: f"Bucket:{name or key}", + "operationName": "GET Bucket", + }, + ) @infer_bindings(resource_type="bucket") def retrieve( self, @@ -365,7 +425,19 @@ def retrieve( raise Exception(f"Bucket with name '{name}' not found") from e return Bucket.model_validate(response) - @traced(name="buckets_retrieve", run_type="uipath") + @traced( + name="buckets_retrieve_async", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + name=None, + key=None, + **_kwargs: f"Bucket:{name or key}", + "operationName": "GET Bucket", + }, + ) @infer_bindings(resource_type="bucket") async def retrieve_async( self, diff --git a/src/uipath/_services/jobs_service.py b/src/uipath/_services/jobs_service.py index 64ce5e24..d214b1c9 100644 --- a/src/uipath/_services/jobs_service.py +++ b/src/uipath/_services/jobs_service.py @@ -38,7 +38,19 @@ def resume(self, *, inbox_id: str, payload: Any) -> None: ... @overload def resume(self, *, job_id: str, payload: Any) -> None: ... - @traced(name="jobs_resume", run_type="uipath") + @traced( + name="jobs_resume", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + inbox_id=None, + job_id=None, + **_kwargs: f"Job:{job_id or inbox_id}", + "operationName": "RESUME Job", + }, + ) def resume( self, *, @@ -84,6 +96,19 @@ def resume( content=spec.content, ) + @traced( + name="jobs_resume", # Matches sync version + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + inbox_id=None, + job_id=None, + **_kwargs: f"Job:{job_id or inbox_id}", + "operationName": "RESUME Job", + }, + ) async def resume_async( self, *, @@ -149,6 +174,16 @@ async def main(): # noqa: D103 def custom_headers(self) -> Dict[str, str]: return self.folder_headers + @traced( + name="jobs_retrieve", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, job_key, **_kwargs: f"Job:{job_key}", + "operationName": "GET Job", + }, + ) def retrieve( self, job_key: str, @@ -185,6 +220,16 @@ def retrieve( return Job.model_validate(response.json()) + @traced( + name="jobs_retrieve", # Matches sync version + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, job_key, **_kwargs: f"Job:{job_key}", + "operationName": "GET Job", + }, + ) async def retrieve_async( self, job_key: str, @@ -334,7 +379,16 @@ def _retrieve_spec( }, ) - @traced(name="jobs_list_attachments", run_type="uipath") + @traced( + name="jobs_list_attachments", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, job_key, **_kwargs: f"Job:{job_key}:Attachments", + "operationName": "LIST JobAttachments", + }, + ) def list_attachments( self, *, @@ -372,7 +426,16 @@ def list_attachments( return [item.get("attachmentId") for item in response] - @traced(name="jobs_list_attachments", run_type="uipath") + @traced( + name="jobs_list_attachments", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, job_key, **_kwargs: f"Job:{job_key}:Attachments", + "operationName": "LIST JobAttachments", + }, + ) async def list_attachments_async( self, *, @@ -427,7 +490,19 @@ async def main(): return [item.get("attachmentId") for item in response] - @traced(name="jobs_link_attachment", run_type="uipath") + @traced( + name="jobs_link_attachment", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + attachment_key, + job_key, + **_kwargs: f"Job:{job_key}:Attachment:{attachment_key}", + "operationName": "LINK JobAttachment", + }, + ) def link_attachment( self, *, @@ -465,7 +540,19 @@ def link_attachment( json=spec.json, ) - @traced(name="jobs_link_attachment", run_type="uipath") + @traced( + name="jobs_link_attachment", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + attachment_key, + job_key, + **_kwargs: f"Job:{job_key}:Attachment:{attachment_key}", + "operationName": "LINK JobAttachment", + }, + ) async def link_attachment_async( self, *, @@ -541,7 +628,21 @@ def _link_job_attachment_spec( }, ) - @traced(name="jobs_create_attachment", run_type="uipath") + @traced( + name="jobs_create_attachment", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + name, + job_key=None, + **_kwargs: f"Job:{job_key}:Attachment:{name}" + if job_key + else f"Attachment:{name}", + "operationName": "CREATE Attachment", + }, + ) def create_attachment( self, *, @@ -680,7 +781,21 @@ def create_attachment( # Return only the UUID return attachment_id - @traced(name="jobs_create_attachment", run_type="uipath") + @traced( + name="jobs_create_attachment", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + name, + job_key=None, + **_kwargs: f"Job:{job_key}:Attachment:{name}" + if job_key + else f"Attachment:{name}", + "operationName": "CREATE Attachment", + }, + ) async def create_attachment_async( self, *, diff --git a/src/uipath/_services/llm_gateway_service.py b/src/uipath/_services/llm_gateway_service.py index 1e63c211..692a4b02 100644 --- a/src/uipath/_services/llm_gateway_service.py +++ b/src/uipath/_services/llm_gateway_service.py @@ -54,7 +54,19 @@ class UiPathOpenAIService(BaseService): def __init__(self, config: Config, execution_context: ExecutionContext) -> None: super().__init__(config=config, execution_context=execution_context) - @traced(name="llm_embeddings_usage", run_type="uipath") + @traced( + name="llm_embeddings_usage", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + input, + embedding_model, + **_kwargs: f"LLMGateway:OpenAI:EmbeddingModel:{embedding_model}", + "operationName": "GET Embedding Usage", + }, + ) async def embeddings_usage( self, input: str, embedding_model: str = EmbeddingModels.text_embedding_ada_002 ): @@ -81,7 +93,19 @@ async def embeddings_usage( return UsageInfo.model_validate(response.json()) - @traced(name="llm_embeddings", run_type="uipath") + @traced( + name="llm_embeddings", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + input, + embedding_model, + **_kwargs: f"LLMGateway:OpenAI:EmbeddingModel:{embedding_model}", + "operationName": "GET Embedding", + }, + ) async def embeddings( self, input: str, embedding_model: str = EmbeddingModels.text_embedding_ada_002 ): @@ -107,7 +131,19 @@ async def embeddings( return TextEmbedding.model_validate(response.json()) - @traced(name="llm_chat_completions", run_type="uipath") + @traced( + name="llm_chat_completions", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + messages, + model, + **_kwargs: f"LLMGateway:OpenAI:ChatModel:{model}", + "operationName": "GET Chat Completion", + }, + ) async def chat_completions( self, messages: List[Dict[str, str]], @@ -157,7 +193,19 @@ async def chat_completions( return ChatCompletion.model_validate(response.json()) - @traced(name="llm_chat_completions_usage", run_type="uipath") + @traced( + name="llm_chat_completions_usage", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + messages, + model, + **_kwargs: f"LLMGateway:OpenAI:ChatModel:{model}", + "operationName": "GET Chat Completion Usage", + }, + ) async def chat_completions_usage( self, messages: List[Dict[str, str]], @@ -216,7 +264,19 @@ class UiPathLlmChatService(BaseService): def __init__(self, config: Config, execution_context: ExecutionContext) -> None: super().__init__(config=config, execution_context=execution_context) - @traced(name="llm_chat_completions", run_type="uipath") + @traced( + name="llm_chat_completions", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + messages, + model, + **_kwargs: f"LLMGateway:Normalized:ChatModel:{model}", + "operationName": "GET Normalized Chat Completion", + }, + ) async def chat_completions( self, messages: List[Dict[str, str]], diff --git a/src/uipath/_services/processes_service.py b/src/uipath/_services/processes_service.py index 6ee9ca44..35c857a7 100644 --- a/src/uipath/_services/processes_service.py +++ b/src/uipath/_services/processes_service.py @@ -23,7 +23,16 @@ class ProcessesService(FolderContext, BaseService): def __init__(self, config: Config, execution_context: ExecutionContext) -> None: super().__init__(config=config, execution_context=execution_context) - @traced(name="processes_invoke", run_type="uipath") + @traced( + name="processes_invoke", + run_type="uipath", + hide_input=True, # Input arguments can be sensitive + hide_output=True, # Job details might contain sensitive info + dependency={ + "targetName": lambda _s, name, **_kwargs: f"Process:{name}", + "operationName": "INVOKE Process", + }, + ) @infer_bindings(resource_type="process") def invoke( self, @@ -81,7 +90,16 @@ def invoke( return Job.model_validate(response.json()["value"][0]) - @traced(name="processes_invoke", run_type="uipath") + @traced( + name="processes_invoke", + run_type="uipath", + hide_input=True, # Input arguments can be sensitive + hide_output=True, # Job details might contain sensitive info + dependency={ + "targetName": lambda _s, name, **_kwargs: f"Process:{name}", + "operationName": "INVOKE Process", + }, + ) @infer_bindings(resource_type="process") async def invoke_async( self, diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 2a6e8fb9..007d517e 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -21,7 +21,17 @@ class QueuesService(FolderContext, BaseService): def __init__(self, config: Config, execution_context: ExecutionContext) -> None: super().__init__(config=config, execution_context=execution_context) - @traced(name="queues_list_items", run_type="uipath") + @traced( + name="queues_list_items", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + **_kwargs: f"QueueItems:Context:{_s.folder_path or _s.folder_id or 'Global'}", + "operationName": "LIST QueueItems", + }, + ) def list_items(self) -> Response: """Retrieves a list of queue items from the Orchestrator. @@ -33,7 +43,17 @@ def list_items(self) -> Response: return response.json() - @traced(name="queues_list_items", run_type="uipath") + @traced( + name="queues_list_items", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + **_kwargs: f"QueueItems:Context:{_s.folder_path or _s.folder_id or 'Global'}", + "operationName": "LIST QueueItems", + }, + ) async def list_items_async(self) -> Response: """Asynchronously retrieves a list of queue items from the Orchestrator. @@ -44,7 +64,18 @@ async def list_items_async(self) -> Response: response = await self.request_async(spec.method, url=spec.endpoint) return response.json() - @traced(name="queues_create_item", run_type="uipath") + @traced( + name="queues_create_item", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + item, + **_kwargs: f"Queue:{(item.queue_name if hasattr(item, 'queue_name') else item.get('queue_name', 'UnknownQueue'))}", + "operationName": "CREATE QueueItem", + }, + ) def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: """Creates a new queue item in the Orchestrator. @@ -60,7 +91,18 @@ def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: response = self.request(spec.method, url=spec.endpoint, json=spec.json) return response.json() - @traced(name="queues_create_item", run_type="uipath") + @traced( + name="queues_create_item", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + item, + **_kwargs: f"Queue:{(item.queue_name if hasattr(item, 'queue_name') else item.get('queue_name', 'UnknownQueue'))}", + "operationName": "CREATE QueueItem", + }, + ) async def create_item_async( self, item: Union[Dict[str, Any], QueueItem] ) -> Response: @@ -80,7 +122,16 @@ async def create_item_async( ) return response.json() - @traced(name="queues_create_items", run_type="uipath") + @traced( + name="queues_create_items", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, queue_name, **_kwargs: f"Queue:{queue_name}", + "operationName": "CREATE QueueItems", + }, + ) def create_items( self, items: List[Union[Dict[str, Any], QueueItem]], @@ -101,7 +152,16 @@ def create_items( response = self.request(spec.method, url=spec.endpoint, json=spec.json) return response.json() - @traced(name="queues_create_items", run_type="uipath") + @traced( + name="queues_create_items", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, queue_name, **_kwargs: f"Queue:{queue_name}", + "operationName": "CREATE QueueItems", + }, + ) async def create_items_async( self, items: List[Union[Dict[str, Any], QueueItem]], @@ -124,7 +184,18 @@ async def create_items_async( ) return response.json() - @traced(name="queues_create_transaction_item", run_type="uipath") + @traced( + name="queues_create_transaction_item", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + item, + **_kwargs: f"Queue:{(item.queue_name if hasattr(item, 'queue_name') else item.get('queue_name', 'UnknownQueue'))}", + "operationName": "CREATE TransactionItem", + }, + ) def create_transaction_item( self, item: Union[Dict[str, Any], TransactionItem], no_robot: bool = False ) -> Response: @@ -141,7 +212,18 @@ def create_transaction_item( response = self.request(spec.method, url=spec.endpoint, json=spec.json) return response.json() - @traced(name="queues_create_transaction_item", run_type="uipath") + @traced( + name="queues_create_transaction_item", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": lambda _s, + item, + **_kwargs: f"Queue:{(item.queue_name if hasattr(item, 'queue_name') else item.get('queue_name', 'UnknownQueue'))}", + "operationName": "CREATE TransactionItem", + }, + ) async def create_transaction_item_async( self, item: Union[Dict[str, Any], TransactionItem], no_robot: bool = False ) -> Response: @@ -160,7 +242,16 @@ async def create_transaction_item_async( ) return response.json() - @traced(name="queues_update_progress_of_transaction_item", run_type="uipath") + @traced( + name="queues_update_progress_of_transaction_item", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": "QueueItem", + "operationName": "UPDATE TransactionProgress", + }, + ) def update_progress_of_transaction_item( self, transaction_key: str, progress: str ) -> Response: @@ -179,7 +270,16 @@ def update_progress_of_transaction_item( response = self.request(spec.method, url=spec.endpoint, json=spec.json) return response.json() - @traced(name="queues_update_progress_of_transaction_item", run_type="uipath") + @traced( + name="queues_update_progress_of_transaction_item", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": "QueueItem", + "operationName": "UPDATE TransactionProgress", + }, + ) async def update_progress_of_transaction_item_async( self, transaction_key: str, progress: str ) -> Response: @@ -200,7 +300,16 @@ async def update_progress_of_transaction_item_async( ) return response.json() - @traced(name="queues_complete_transaction_item", run_type="uipath") + @traced( + name="queues_complete_transaction_item", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": "QueueItem", + "operationName": "COMPLETE TransactionItem", + }, + ) def complete_transaction_item( self, transaction_key: str, result: Union[Dict[str, Any], TransactionItemResult] ) -> Response: @@ -219,7 +328,16 @@ def complete_transaction_item( response = self.request(spec.method, url=spec.endpoint, json=spec.json) return response.json() - @traced(name="queues_complete_transaction_item", run_type="uipath") + @traced( + name="queues_complete_transaction_item", + run_type="uipath", + hide_input=True, + hide_output=True, + dependency={ + "targetName": "QueueItem", + "operationName": "COMPLETE TransactionItem", + }, + ) async def complete_transaction_item_async( self, transaction_key: str, result: Union[Dict[str, Any], TransactionItemResult] ) -> Response: diff --git a/src/uipath/tracing/_traced.py b/src/uipath/tracing/_traced.py index 7fb74c80..1eaab4a5 100644 --- a/src/uipath/tracing/_traced.py +++ b/src/uipath/tracing/_traced.py @@ -2,8 +2,9 @@ import inspect import json import logging +import os from functools import wraps -from typing import Any, Callable, List, Optional, Tuple +from typing import Any, Callable, List, Optional, Tuple, TypedDict, Union from opentelemetry import trace @@ -14,6 +15,14 @@ tracer = trace.get_tracer(__name__) +class DependencyInfo(TypedDict, total=False): + """Type definition for dependency tracking information.""" + + sourceName: Union[str, Callable[..., str]] + targetName: Union[str, Callable[..., str]] + operationName: Union[str, Callable[..., str]] + + class TracingManager: """Static utility class to manage tracing implementations and decorated functions.""" @@ -129,6 +138,7 @@ def _opentelemetry_traced( span_type: Optional[str] = None, input_processor: Optional[Callable[..., Any]] = None, output_processor: Optional[Callable[..., Any]] = None, + dependency: Optional[DependencyInfo] = None, ): """Default tracer implementation using OpenTelemetry.""" @@ -155,6 +165,21 @@ def sync_wrapper(*args, **kwargs): processed_inputs = input_processor(json.loads(inputs)) inputs = json.dumps(processed_inputs, default=str) span.set_attribute("inputs", inputs) + + # Add dependency information as a JSON attribute if provided + if dependency is not None: + # Process any callable dependency values using function args/kwargs + processed_dependency = {} + for key, value in dependency.items(): + if callable(value): + # Call the function with the same arguments as the decorated function + processed_dependency[key] = value(*args, **kwargs) + else: + processed_dependency[key] = value + + dependency_json = json.dumps(processed_dependency, default=str) + span.set_attribute("dependency", dependency_json) + try: result = func(*args, **kwargs) # Process output if processor is provided @@ -190,6 +215,21 @@ async def async_wrapper(*args, **kwargs): processed_inputs = input_processor(json.loads(inputs)) inputs = json.dumps(processed_inputs, default=str) span.set_attribute("inputs", inputs) + + # Add dependency information as a JSON attribute if provided + if dependency is not None: + # Process any callable dependency values using function args/kwargs + processed_dependency = {} + for key, value in dependency.items(): + if callable(value): + # Call the function with the same arguments as the decorated function + processed_dependency[key] = value(*args, **kwargs) + else: + processed_dependency[key] = value + + dependency_json = json.dumps(processed_dependency, default=str) + span.set_attribute("dependency", dependency_json) + try: result = await func(*args, **kwargs) # Process output if processor is provided @@ -225,6 +265,21 @@ def generator_wrapper(*args, **kwargs): processed_inputs = input_processor(json.loads(inputs)) inputs = json.dumps(processed_inputs, default=str) span.set_attribute("inputs", inputs) + + # Add dependency information as a JSON attribute if provided + if dependency is not None: + # Process any callable dependency values using function args/kwargs + processed_dependency = {} + for key, value in dependency.items(): + if callable(value): + # Call the function with the same arguments as the decorated function + processed_dependency[key] = value(*args, **kwargs) + else: + processed_dependency[key] = value + + dependency_json = json.dumps(processed_dependency, default=str) + span.set_attribute("dependency", dependency_json) + outputs = [] try: for item in func(*args, **kwargs): @@ -266,6 +321,21 @@ async def async_generator_wrapper(*args, **kwargs): processed_inputs = input_processor(json.loads(inputs)) inputs = json.dumps(processed_inputs, default=str) span.set_attribute("inputs", inputs) + + # Add dependency information as a JSON attribute if provided + if dependency is not None: + # Process any callable dependency values using function args/kwargs + processed_dependency = {} + for key, value in dependency.items(): + if callable(value): + # Call the function with the same arguments as the decorated function + processed_dependency[key] = value(*args, **kwargs) + else: + processed_dependency[key] = value + + dependency_json = json.dumps(processed_dependency, default=str) + span.set_attribute("dependency", dependency_json) + outputs = [] try: async for item in func(*args, **kwargs): @@ -337,10 +407,12 @@ def traced( output_processor: Optional[Callable[..., Any]] = None, hide_input: bool = False, hide_output: bool = False, + dependency: Optional[DependencyInfo] = None, ): """Decorator that will trace function invocations. Args: + name: Optional name for the span (defaults to function name) run_type: Optional string to categorize the run type span_type: Optional string to categorize the span type input_processor: Optional function to process function inputs before recording @@ -349,6 +421,15 @@ def traced( Should accept the function output and return a processed value hide_input: If True, don't log any input data hide_output: If True, don't log any output data + dependency: Optional dictionary with dependency tracking information: + sourceName: The source system/component (str or callable returning str) + targetName: The target system/component (str or callable returning str) + operationName: The operation being performed (str or callable returning str) + + For sourceName, targetName, and operationName: + - If a string is provided, it's used directly + - If a callable is provided, it's called with the same args/kwargs as the + decorated function and should return a string """ # Apply default processors selectively based on hide flags if hide_input: @@ -356,6 +437,12 @@ def traced( if hide_output: output_processor = _default_output_processor + if dependency is not None: + if dependency.get("sourceName") is None: + dependency["sourceName"] = lambda *args: os.environ.get( + "UIPATH_PROCESS_KEY", "Unknown source" + ) + # Store the parameters for later reapplication params = { "name": name, @@ -363,6 +450,7 @@ def traced( "span_type": span_type, "input_processor": input_processor, "output_processor": output_processor, + "dependency": dependency, } # Check for custom implementation first diff --git a/src/uipath/tracing/_utils.py b/src/uipath/tracing/_utils.py index 654a4a3b..0566dfd5 100644 --- a/src/uipath/tracing/_utils.py +++ b/src/uipath/tracing/_utils.py @@ -150,30 +150,10 @@ def otel_span_to_uipath_span(otel_span: ReadableSpan) -> UiPathSpan: status = 2 # Error attributes_dict["error"] = otel_span.status.description - original_inputs = attributes_dict.get("inputs", None) - original_outputs = attributes_dict.get("outputs", None) - - if original_inputs: - try: - if isinstance(original_inputs, str): - json_inputs = json.loads(original_inputs) - attributes_dict["inputs"] = json_inputs - else: - attributes_dict["inputs"] = original_inputs - except Exception as e: - print(f"Error parsing inputs: {e}") - attributes_dict["inputs"] = str(original_inputs) - - if original_outputs: - try: - if isinstance(original_outputs, str): - json_outputs = json.loads(original_outputs) - attributes_dict["outputs"] = json_outputs - else: - attributes_dict["outputs"] = original_outputs - except Exception as e: - print(f"Error parsing outputs: {e}") - attributes_dict["outputs"] = str(original_outputs) + # Parse JSON attributes using the helper method + for attr_name in ["inputs", "outputs", "dependency"]: + if attr_name in attributes_dict: + _SpanUtils._process_json_attribute(attributes_dict, attr_name) # Add events as additional attributes if they exist if otel_span.events: @@ -208,13 +188,22 @@ def otel_span_to_uipath_span(otel_span: ReadableSpan) -> UiPathSpan: ).isoformat() end_time_str = None + duration = None # Initialize duration as None + if otel_span.end_time is not None: end_time_str = datetime.fromtimestamp( (otel_span.end_time or 0) / 1e9 ).isoformat() + # Calculate duration in seconds if both start and end times are available + if otel_span.start_time is not None: + duration = (otel_span.end_time - otel_span.start_time) / 1e9 else: end_time_str = datetime.now().isoformat() + # Add duration to attributes if available + if duration is not None: + attributes_dict["duration"] = duration + return UiPathSpan( id=span_id, trace_id=trace_id, @@ -271,3 +260,18 @@ def format_args_for_trace( f"Error formatting arguments for trace: {e}. Using args and kwargs directly." ) return {"args": args, "kwargs": kwargs} + + @staticmethod + def _process_json_attribute(attributes_dict, attr_name): + original_value = attributes_dict.get(attr_name) + + if not original_value: + return original_value + + try: + if isinstance(original_value, str): + json_data = json.loads(original_value) + attributes_dict[attr_name] = json_data + + except Exception as e: + logger.warning(f"Error parsing {attr_name}: {e}") diff --git a/tests/tracing/test_traced.py b/tests/tracing/test_traced.py index 252cf4af..b0525b58 100644 --- a/tests/tracing/test_traced.py +++ b/tests/tracing/test_traced.py @@ -572,3 +572,170 @@ def fully_private_function(sensitive_input): output_json = span.attributes["output"] output = json.loads(output_json) assert output == {"redacted": "Output data not logged for privacy/security"} + + +def test_traced_with_static_dependency_info(setup_tracer): + """Test traced decorator with static dependency information.""" + exporter, provider = setup_tracer + + dependency_info = { + "sourceName": "TestSource", + "targetName": "TestTarget", + "operationName": "TestOperation", + } + + @traced(dependency=dependency_info) + def function_with_dependency(x, y): + return x + y + + result = function_with_dependency(10, 20) + assert result == 30 + + provider.shutdown() + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "function_with_dependency" + assert "dependency" in span.attributes + + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["sourceName"] == "TestSource" + assert dependency_attr["targetName"] == "TestTarget" + assert dependency_attr["operationName"] == "TestOperation" + + +def test_traced_with_callable_dependency_info(setup_tracer): + """Test traced decorator with callable dependency information.""" + exporter, provider = setup_tracer + + def get_source_name(x, y): + return f"Source_{x}" + + def get_target_name(*args, **kwargs): + return f"Target_{kwargs.get('y')}" + + dependency_info = { + "sourceName": get_source_name, + "targetName": get_target_name, + "operationName": "CallableOperation", + } + + @traced(dependency=dependency_info) + def function_with_callable_dependency(x, y): + return x * y + + result = function_with_callable_dependency( + 5, y=4 + ) # Use keyword arg for get_target_name + assert result == 20 + + provider.shutdown() + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "function_with_callable_dependency" + assert "dependency" in span.attributes + + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["sourceName"] == "Source_5" + assert dependency_attr["targetName"] == "Target_4" + assert dependency_attr["operationName"] == "CallableOperation" + + +def test_traced_with_dependency_and_env_var(setup_tracer, monkeypatch): + """Test traced decorator with dependency using UIPATH_PROCESS_KEY env var.""" + exporter, provider = setup_tracer + monkeypatch.setenv("UIPATH_PROCESS_KEY", "EnvProcessKey") + + dependency_info = { + "targetName": "EnvTarget", + "operationName": "EnvOperation", + } # sourceName will be picked from env var + + @traced(dependency=dependency_info) + def function_with_env_dependency(a): + return f"Processed {a}" + + result = function_with_env_dependency("data") + assert result == "Processed data" + + provider.shutdown() + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "function_with_env_dependency" + assert "dependency" in span.attributes + + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["sourceName"] == "EnvProcessKey" + assert dependency_attr["targetName"] == "EnvTarget" + assert dependency_attr["operationName"] == "EnvOperation" + + +@pytest.mark.asyncio +async def test_traced_async_with_dependency_info(setup_tracer): + """Test traced decorator with dependency information for async functions.""" + exporter, provider = setup_tracer + + dependency_info = { + "sourceName": "AsyncSource", + "targetName": "AsyncTarget", + "operationName": lambda msg: f"AsyncOp_{msg}", + } + + @traced(dependency=dependency_info) + async def async_function_with_dependency(msg): + await sleep(0.01) + return f"Async says: {msg}" + + result = await async_function_with_dependency("hello") + assert result == "Async says: hello" + + provider.shutdown() + await sleep(0.1) # ensure spans are processed + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "async_function_with_dependency" + assert "dependency" in span.attributes + + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["sourceName"] == "AsyncSource" + assert dependency_attr["targetName"] == "AsyncTarget" + assert dependency_attr["operationName"] == "AsyncOp_hello" + + +def test_traced_generator_with_dependency_info(setup_tracer): + """Test traced decorator with dependency information for generator functions.""" + exporter, provider = setup_tracer + + dependency_info = { + "sourceName": "GeneratorSource", + "targetName": "GeneratorTarget", + "operationName": lambda n_items: f"GenOp_Count_{n_items}", + } + + @traced(dependency=dependency_info) + def generator_with_dependency(n_items): + for i in range(n_items): + yield f"Item {i}" + + results = list(generator_with_dependency(2)) + assert results == ["Item 0", "Item 1"] + + provider.shutdown() + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "generator_with_dependency" + assert "dependency" in span.attributes + + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["sourceName"] == "GeneratorSource" + assert dependency_attr["targetName"] == "GeneratorTarget" + assert dependency_attr["operationName"] == "GenOp_Count_2" From 36d83819fff76681781e725ae048186b7d51ff5b Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 29 May 2025 02:08:55 +0300 Subject: [PATCH 02/17] fix --- src/uipath/_services/queues_service.py | 4 ++-- src/uipath/tracing/_traced.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 007d517e..e5d3731f 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -72,7 +72,7 @@ async def list_items_async(self) -> Response: dependency={ "targetName": lambda _s, item, - **_kwargs: f"Queue:{(item.queue_name if hasattr(item, 'queue_name') else item.get('queue_name', 'UnknownQueue'))}", + **_kwargs: f"Queue:{(item.name if hasattr(item, 'name') else item.get('name', 'UnknownQueue'))}", "operationName": "CREATE QueueItem", }, ) @@ -99,7 +99,7 @@ def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: dependency={ "targetName": lambda _s, item, - **_kwargs: f"Queue:{(item.queue_name if hasattr(item, 'queue_name') else item.get('queue_name', 'UnknownQueue'))}", + **_kwargs: f"Queue:{(item.name if hasattr(item, 'name') else item.get('name', 'UnknownQueue'))}", "operationName": "CREATE QueueItem", }, ) diff --git a/src/uipath/tracing/_traced.py b/src/uipath/tracing/_traced.py index 1eaab4a5..2a6a7e19 100644 --- a/src/uipath/tracing/_traced.py +++ b/src/uipath/tracing/_traced.py @@ -439,7 +439,7 @@ def traced( if dependency is not None: if dependency.get("sourceName") is None: - dependency["sourceName"] = lambda *args: os.environ.get( + dependency["sourceName"] = lambda *args, **kwargs: os.environ.get( "UIPATH_PROCESS_KEY", "Unknown source" ) From e7cf251c8b9e3ca02440bf061d0636c41a7f8185 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 29 May 2025 10:59:00 +0300 Subject: [PATCH 03/17] fix --- src/uipath/_services/assets_service.py | 22 ++-- src/uipath/_services/buckets_service.py | 30 ++---- src/uipath/_services/jobs_service.py | 18 ++-- src/uipath/_services/llm_gateway_service.py | 20 +--- src/uipath/_services/processes_service.py | 4 +- src/uipath/_services/queues_service.py | 26 ++--- src/uipath/tracing/_traced.py | 105 ++++++++++++++------ tests/tracing/test_traced.py | 12 +-- 8 files changed, 113 insertions(+), 124 deletions(-) diff --git a/src/uipath/_services/assets_service.py b/src/uipath/_services/assets_service.py index a5396d18..4c1d3be1 100644 --- a/src/uipath/_services/assets_service.py +++ b/src/uipath/_services/assets_service.py @@ -30,7 +30,7 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=True, hide_output=True, dependency={ - "targetName": lambda name, **_kwargs: f"Asset:{name}", + "targetName": lambda inputs: f"Asset:{inputs['name']}", "operationName": "GET Asset", }, ) @@ -87,12 +87,12 @@ def retrieve( return Asset.model_validate(response.json()["value"][0]) @traced( - name="assets_retrieve", # Assuming this should be assets_retrieve_async or similar + name="assets_retrieve", run_type="uipath", hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, name, **_kwargs: f"Asset:{name}", + "targetName": lambda inputs: f"Asset:{inputs['name']}", "operationName": "GET Asset", }, ) @@ -145,7 +145,7 @@ async def retrieve_async( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, name, **_kwargs: f"Asset:{name}", + "targetName": lambda inputs: f"Asset:{inputs['name']}", "operationName": "GET Credential", }, ) @@ -200,12 +200,12 @@ def retrieve_credential( return user_asset.credential_password @traced( - name="assets_credential", # Assuming this should be assets_credential_async or similar + name="assets_credential", run_type="uipath", hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, name, **_kwargs: f"Asset:{name}", + "targetName": lambda inputs: f"Asset:{inputs['name']}", "operationName": "GET Credential", }, ) @@ -265,9 +265,7 @@ async def retrieve_credential_async( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - robot_asset, - **_kwargs: f"Asset:{robot_asset.name}", + "targetName": lambda inputs: f"Asset:{inputs['robot_asset'].name}", "operationName": "UPDATE Asset", }, ) @@ -313,14 +311,12 @@ def update( return response.json() @traced( - name="assets_update", # Assuming this should be assets_update_async or similar + name="assets_update", run_type="uipath", hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - robot_asset, - **_kwargs: f"Asset:{robot_asset.name}", + "targetName": lambda inputs: f"Asset:{inputs['robot_asset'].name}", "operationName": "UPDATE Asset", }, ) diff --git a/src/uipath/_services/buckets_service.py b/src/uipath/_services/buckets_service.py index 6a0fe6ed..be9b0067 100644 --- a/src/uipath/_services/buckets_service.py +++ b/src/uipath/_services/buckets_service.py @@ -34,10 +34,7 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - name=None, - key=None, - **_kwargs: f"Bucket:{name or key}", + "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "DOWNLOAD File", }, ) @@ -102,10 +99,7 @@ def download( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - name=None, - key=None, - **_kwargs: f"Bucket:{name or key}", + "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "DOWNLOAD File", }, ) @@ -176,10 +170,7 @@ async def download_async( input_processor=_upload_from_memory_input_processor, hide_output=True, dependency={ - "targetName": lambda _s, - name=None, - key=None, - **_kwargs: f"Bucket:{name or key}", + "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "UPLOAD File", }, ) @@ -271,10 +262,7 @@ def upload( input_processor=_upload_from_memory_input_processor, hide_output=True, dependency={ - "targetName": lambda _s, - name=None, - key=None, - **_kwargs: f"Bucket:{name or key}", + "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "UPLOAD File", }, ) @@ -371,10 +359,7 @@ async def upload_async( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - name=None, - key=None, - **_kwargs: f"Bucket:{name or key}", + "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "GET Bucket", }, ) @@ -431,10 +416,7 @@ def retrieve( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - name=None, - key=None, - **_kwargs: f"Bucket:{name or key}", + "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "GET Bucket", }, ) diff --git a/src/uipath/_services/jobs_service.py b/src/uipath/_services/jobs_service.py index d214b1c9..1bde326a 100644 --- a/src/uipath/_services/jobs_service.py +++ b/src/uipath/_services/jobs_service.py @@ -44,10 +44,7 @@ def resume(self, *, job_id: str, payload: Any) -> None: ... hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - inbox_id=None, - job_id=None, - **_kwargs: f"Job:{job_id or inbox_id}", + "targetName": lambda inputs: f"Job:{inputs.get('job_id') or inputs.get('inbox_id')}", "operationName": "RESUME Job", }, ) @@ -97,15 +94,12 @@ def resume( ) @traced( - name="jobs_resume", # Matches sync version + name="jobs_resume", run_type="uipath", hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - inbox_id=None, - job_id=None, - **_kwargs: f"Job:{job_id or inbox_id}", + "targetName": lambda inputs: f"Job:{inputs.get('job_id') or inputs.get('inbox_id')}", "operationName": "RESUME Job", }, ) @@ -180,7 +174,7 @@ def custom_headers(self) -> Dict[str, str]: hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, job_key, **_kwargs: f"Job:{job_key}", + "targetName": lambda inputs: f"Job:{inputs['job_key']}", "operationName": "GET Job", }, ) @@ -221,12 +215,12 @@ def retrieve( return Job.model_validate(response.json()) @traced( - name="jobs_retrieve", # Matches sync version + name="jobs_retrieve", run_type="uipath", hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, job_key, **_kwargs: f"Job:{job_key}", + "targetName": lambda inputs: f"Job:{inputs['job_key']}", "operationName": "GET Job", }, ) diff --git a/src/uipath/_services/llm_gateway_service.py b/src/uipath/_services/llm_gateway_service.py index 692a4b02..6e1ae416 100644 --- a/src/uipath/_services/llm_gateway_service.py +++ b/src/uipath/_services/llm_gateway_service.py @@ -60,10 +60,7 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - input, - embedding_model, - **_kwargs: f"LLMGateway:OpenAI:EmbeddingModel:{embedding_model}", + "targetName": lambda inputs: f"LLMGateway:OpenAI:EmbeddingModel:{inputs['embedding_model']}", "operationName": "GET Embedding Usage", }, ) @@ -99,10 +96,7 @@ async def embeddings_usage( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - input, - embedding_model, - **_kwargs: f"LLMGateway:OpenAI:EmbeddingModel:{embedding_model}", + "targetName": lambda inputs: f"LLMGateway:OpenAI:EmbeddingModel:{inputs['embedding_model']}", "operationName": "GET Embedding", }, ) @@ -137,10 +131,7 @@ async def embeddings( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - messages, - model, - **_kwargs: f"LLMGateway:OpenAI:ChatModel:{model}", + "targetName": lambda inputs: f"LLMGateway:OpenAI:ChatModel:{inputs['model']}", "operationName": "GET Chat Completion", }, ) @@ -199,10 +190,7 @@ async def chat_completions( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - messages, - model, - **_kwargs: f"LLMGateway:OpenAI:ChatModel:{model}", + "targetName": lambda inputs: f"LLMGateway:OpenAI:ChatModel:{inputs['model']}", "operationName": "GET Chat Completion Usage", }, ) diff --git a/src/uipath/_services/processes_service.py b/src/uipath/_services/processes_service.py index 35c857a7..8490f20f 100644 --- a/src/uipath/_services/processes_service.py +++ b/src/uipath/_services/processes_service.py @@ -29,7 +29,7 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=True, # Input arguments can be sensitive hide_output=True, # Job details might contain sensitive info dependency={ - "targetName": lambda _s, name, **_kwargs: f"Process:{name}", + "targetName": lambda inputs: f"Process:{inputs['name']}", "operationName": "INVOKE Process", }, ) @@ -96,7 +96,7 @@ def invoke( hide_input=True, # Input arguments can be sensitive hide_output=True, # Job details might contain sensitive info dependency={ - "targetName": lambda _s, name, **_kwargs: f"Process:{name}", + "targetName": lambda inputs: f"Process:{inputs['name']}", "operationName": "INVOKE Process", }, ) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index e5d3731f..20a63384 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -27,8 +27,7 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - **_kwargs: f"QueueItems:Context:{_s.folder_path or _s.folder_id or 'Global'}", + "targetName": lambda inputs: f"QueueItems:Context:{inputs['self']._folder_path or inputs['self']._folder_id or 'Global'}", "operationName": "LIST QueueItems", }, ) @@ -49,8 +48,7 @@ def list_items(self) -> Response: hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - **_kwargs: f"QueueItems:Context:{_s.folder_path or _s.folder_id or 'Global'}", + "targetName": lambda inputs: f"QueueItems:Context:{inputs['self']._folder_path or inputs['self']._folder_id or 'Global'}", "operationName": "LIST QueueItems", }, ) @@ -70,9 +68,7 @@ async def list_items_async(self) -> Response: hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - item, - **_kwargs: f"Queue:{(item.name if hasattr(item, 'name') else item.get('name', 'UnknownQueue'))}", + "targetName": lambda inputs: f"Queue:{(inputs['item'].name if hasattr(inputs['item'], 'name') else inputs['item'].get('name', 'UnknownQueue'))}", "operationName": "CREATE QueueItem", }, ) @@ -97,9 +93,7 @@ def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - item, - **_kwargs: f"Queue:{(item.name if hasattr(item, 'name') else item.get('name', 'UnknownQueue'))}", + "targetName": lambda inputs: f"Queue:{(inputs['item'].name if hasattr(inputs['item'], 'name') else inputs['item'].get('name', 'UnknownQueue'))}", "operationName": "CREATE QueueItem", }, ) @@ -128,7 +122,7 @@ async def create_item_async( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, queue_name, **_kwargs: f"Queue:{queue_name}", + "targetName": lambda inputs: f"Queue:{inputs['queue_name']}", "operationName": "CREATE QueueItems", }, ) @@ -158,7 +152,7 @@ def create_items( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, queue_name, **_kwargs: f"Queue:{queue_name}", + "targetName": lambda inputs: f"Queue:{inputs['queue_name']}", "operationName": "CREATE QueueItems", }, ) @@ -190,9 +184,7 @@ async def create_items_async( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - item, - **_kwargs: f"Queue:{(item.queue_name if hasattr(item, 'queue_name') else item.get('queue_name', 'UnknownQueue'))}", + "targetName": lambda inputs: f"Queue:{(inputs['item'].queue_name if hasattr(inputs['item'], 'queue_name') else inputs['item'].get('queue_name', 'UnknownQueue'))}", "operationName": "CREATE TransactionItem", }, ) @@ -218,9 +210,7 @@ def create_transaction_item( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - item, - **_kwargs: f"Queue:{(item.queue_name if hasattr(item, 'queue_name') else item.get('queue_name', 'UnknownQueue'))}", + "targetName": lambda inputs: f"Queue:{(inputs['item'].queue_name if hasattr(inputs['item'], 'queue_name') else inputs['item'].get('queue_name', 'UnknownQueue'))}", "operationName": "CREATE TransactionItem", }, ) diff --git a/src/uipath/tracing/_traced.py b/src/uipath/tracing/_traced.py index 2a6a7e19..7e52097a 100644 --- a/src/uipath/tracing/_traced.py +++ b/src/uipath/tracing/_traced.py @@ -157,23 +157,34 @@ def sync_wrapper(*args, **kwargs): span.set_attribute("run_type", run_type) # Format arguments for tracing - inputs = _SpanUtils.format_args_for_trace_json( + inputs_json_str = _SpanUtils.format_args_for_trace_json( # Store the raw JSON string of args inspect.signature(func), *args, **kwargs ) - # Apply input processor if provided + + # Determine the value for the 'inputs' attribute if input_processor is not None: - processed_inputs = input_processor(json.loads(inputs)) - inputs = json.dumps(processed_inputs, default=str) - span.set_attribute("inputs", inputs) + processed_inputs_val = input_processor(json.loads(inputs_json_str)) + inputs_attribute_value = json.dumps( + processed_inputs_val, default=str + ) + else: + inputs_attribute_value = ( + inputs_json_str # Use raw JSON string if no processor + ) + span.set_attribute("inputs", inputs_attribute_value) # Add dependency information as a JSON attribute if provided if dependency is not None: - # Process any callable dependency values using function args/kwargs processed_dependency = {} + parsed_args_dict = None + # Check if any dependency value is callable to decide if we need to parse args + if any(callable(v) for v in dependency.values()): + parsed_args_dict = json.loads(inputs_json_str) + for key, value in dependency.items(): if callable(value): - # Call the function with the same arguments as the decorated function - processed_dependency[key] = value(*args, **kwargs) + # We've already ensured parsed_args_dict is populated if any value is callable + processed_dependency[key] = value(parsed_args_dict) else: processed_dependency[key] = value @@ -207,23 +218,32 @@ async def async_wrapper(*args, **kwargs): span.set_attribute("run_type", run_type) # Format arguments for tracing - inputs = _SpanUtils.format_args_for_trace_json( + inputs_json_str = _SpanUtils.format_args_for_trace_json( # Store the raw JSON string of args inspect.signature(func), *args, **kwargs ) - # Apply input processor if provided + + # Determine the value for the 'inputs' attribute if input_processor is not None: - processed_inputs = input_processor(json.loads(inputs)) - inputs = json.dumps(processed_inputs, default=str) - span.set_attribute("inputs", inputs) + processed_inputs_val = input_processor(json.loads(inputs_json_str)) + inputs_attribute_value = json.dumps( + processed_inputs_val, default=str + ) + else: + inputs_attribute_value = ( + inputs_json_str # Use raw JSON string if no processor + ) + span.set_attribute("inputs", inputs_attribute_value) # Add dependency information as a JSON attribute if provided if dependency is not None: - # Process any callable dependency values using function args/kwargs processed_dependency = {} + parsed_args_dict = None + if any(callable(v) for v in dependency.values()): + parsed_args_dict = json.loads(inputs_json_str) + for key, value in dependency.items(): if callable(value): - # Call the function with the same arguments as the decorated function - processed_dependency[key] = value(*args, **kwargs) + processed_dependency[key] = value(parsed_args_dict) else: processed_dependency[key] = value @@ -257,23 +277,32 @@ def generator_wrapper(*args, **kwargs): span.set_attribute("run_type", run_type) # Format arguments for tracing - inputs = _SpanUtils.format_args_for_trace_json( + inputs_json_str = _SpanUtils.format_args_for_trace_json( # Store the raw JSON string of args inspect.signature(func), *args, **kwargs ) - # Apply input processor if provided + + # Determine the value for the 'inputs' attribute if input_processor is not None: - processed_inputs = input_processor(json.loads(inputs)) - inputs = json.dumps(processed_inputs, default=str) - span.set_attribute("inputs", inputs) + processed_inputs_val = input_processor(json.loads(inputs_json_str)) + inputs_attribute_value = json.dumps( + processed_inputs_val, default=str + ) + else: + inputs_attribute_value = ( + inputs_json_str # Use raw JSON string if no processor + ) + span.set_attribute("inputs", inputs_attribute_value) # Add dependency information as a JSON attribute if provided if dependency is not None: - # Process any callable dependency values using function args/kwargs processed_dependency = {} + parsed_args_dict = None + if any(callable(v) for v in dependency.values()): + parsed_args_dict = json.loads(inputs_json_str) + for key, value in dependency.items(): if callable(value): - # Call the function with the same arguments as the decorated function - processed_dependency[key] = value(*args, **kwargs) + processed_dependency[key] = value(parsed_args_dict) else: processed_dependency[key] = value @@ -313,23 +342,32 @@ async def async_generator_wrapper(*args, **kwargs): span.set_attribute("run_type", run_type) # Format arguments for tracing - inputs = _SpanUtils.format_args_for_trace_json( + inputs_json_str = _SpanUtils.format_args_for_trace_json( # Store the raw JSON string of args inspect.signature(func), *args, **kwargs ) - # Apply input processor if provided + + # Determine the value for the 'inputs' attribute if input_processor is not None: - processed_inputs = input_processor(json.loads(inputs)) - inputs = json.dumps(processed_inputs, default=str) - span.set_attribute("inputs", inputs) + processed_inputs_val = input_processor(json.loads(inputs_json_str)) + inputs_attribute_value = json.dumps( + processed_inputs_val, default=str + ) + else: + inputs_attribute_value = ( + inputs_json_str # Use raw JSON string if no processor + ) + span.set_attribute("inputs", inputs_attribute_value) # Add dependency information as a JSON attribute if provided if dependency is not None: - # Process any callable dependency values using function args/kwargs processed_dependency = {} + parsed_args_dict = None + if any(callable(v) for v in dependency.values()): + parsed_args_dict = json.loads(inputs_json_str) + for key, value in dependency.items(): if callable(value): - # Call the function with the same arguments as the decorated function - processed_dependency[key] = value(*args, **kwargs) + processed_dependency[key] = value(parsed_args_dict) else: processed_dependency[key] = value @@ -439,7 +477,8 @@ def traced( if dependency is not None: if dependency.get("sourceName") is None: - dependency["sourceName"] = lambda *args, **kwargs: os.environ.get( + # Updated to accept a single dictionary argument, though it's not used by default + dependency["sourceName"] = lambda _dep_args_dict: os.environ.get( "UIPATH_PROCESS_KEY", "Unknown source" ) diff --git a/tests/tracing/test_traced.py b/tests/tracing/test_traced.py index b0525b58..0da0b7fa 100644 --- a/tests/tracing/test_traced.py +++ b/tests/tracing/test_traced.py @@ -609,11 +609,11 @@ def test_traced_with_callable_dependency_info(setup_tracer): """Test traced decorator with callable dependency information.""" exporter, provider = setup_tracer - def get_source_name(x, y): - return f"Source_{x}" + def get_source_name(inputs: Dict[str, Any]): + return f"Source_{inputs['x']}" - def get_target_name(*args, **kwargs): - return f"Target_{kwargs.get('y')}" + def get_target_name(inputs: Dict[str, Any]): + return f"Target_{inputs['y']}" dependency_info = { "sourceName": get_source_name, @@ -683,7 +683,7 @@ async def test_traced_async_with_dependency_info(setup_tracer): dependency_info = { "sourceName": "AsyncSource", "targetName": "AsyncTarget", - "operationName": lambda msg: f"AsyncOp_{msg}", + "operationName": lambda inputs: f"AsyncOp_{inputs['msg']}", } @traced(dependency=dependency_info) @@ -716,7 +716,7 @@ def test_traced_generator_with_dependency_info(setup_tracer): dependency_info = { "sourceName": "GeneratorSource", "targetName": "GeneratorTarget", - "operationName": lambda n_items: f"GenOp_Count_{n_items}", + "operationName": lambda inputs: f"GenOp_Count_{inputs['n_items']}", } @traced(dependency=dependency_info) From 54222ed1d36b20d78333cfa47cc17ae2f1246e38 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 29 May 2025 11:06:46 +0300 Subject: [PATCH 04/17] fix --- src/uipath/_services/assets_service.py | 16 +++++------ src/uipath/_services/buckets_service.py | 22 +++++++------- src/uipath/_services/jobs_service.py | 16 +++++------ src/uipath/_services/llm_gateway_service.py | 16 +++++------ src/uipath/_services/processes_service.py | 8 +++--- src/uipath/_services/queues_service.py | 32 ++++++++++----------- 6 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/uipath/_services/assets_service.py b/src/uipath/_services/assets_service.py index 4c1d3be1..f340421f 100644 --- a/src/uipath/_services/assets_service.py +++ b/src/uipath/_services/assets_service.py @@ -27,8 +27,8 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: @traced( name="assets_retrieve", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Asset:{inputs['name']}", "operationName": "GET Asset", @@ -89,8 +89,8 @@ def retrieve( @traced( name="assets_retrieve", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Asset:{inputs['name']}", "operationName": "GET Asset", @@ -142,7 +142,7 @@ async def retrieve_async( @traced( name="assets_credential", run_type="uipath", - hide_input=True, + hide_input=False, hide_output=True, dependency={ "targetName": lambda inputs: f"Asset:{inputs['name']}", @@ -202,7 +202,7 @@ def retrieve_credential( @traced( name="assets_credential", run_type="uipath", - hide_input=True, + hide_input=False, hide_output=True, dependency={ "targetName": lambda inputs: f"Asset:{inputs['name']}", @@ -262,7 +262,7 @@ async def retrieve_credential_async( @traced( name="assets_update", run_type="uipath", - hide_input=True, + hide_input=False, hide_output=True, dependency={ "targetName": lambda inputs: f"Asset:{inputs['robot_asset'].name}", @@ -313,7 +313,7 @@ def update( @traced( name="assets_update", run_type="uipath", - hide_input=True, + hide_input=False, hide_output=True, dependency={ "targetName": lambda inputs: f"Asset:{inputs['robot_asset'].name}", diff --git a/src/uipath/_services/buckets_service.py b/src/uipath/_services/buckets_service.py index be9b0067..714fd219 100644 --- a/src/uipath/_services/buckets_service.py +++ b/src/uipath/_services/buckets_service.py @@ -31,8 +31,8 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: @traced( name="buckets_download", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "DOWNLOAD File", @@ -96,8 +96,8 @@ def download( @traced( name="buckets_download_async", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "DOWNLOAD File", @@ -168,7 +168,8 @@ async def download_async( name="buckets_upload", run_type="uipath", input_processor=_upload_from_memory_input_processor, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "UPLOAD File", @@ -260,7 +261,8 @@ def upload( name="buckets_upload_async", run_type="uipath", input_processor=_upload_from_memory_input_processor, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "UPLOAD File", @@ -356,8 +358,8 @@ async def upload_async( @traced( name="buckets_retrieve", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "GET Bucket", @@ -413,8 +415,8 @@ def retrieve( @traced( name="buckets_retrieve_async", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", "operationName": "GET Bucket", diff --git a/src/uipath/_services/jobs_service.py b/src/uipath/_services/jobs_service.py index 1bde326a..b4613079 100644 --- a/src/uipath/_services/jobs_service.py +++ b/src/uipath/_services/jobs_service.py @@ -41,8 +41,8 @@ def resume(self, *, job_id: str, payload: Any) -> None: ... @traced( name="jobs_resume", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Job:{inputs.get('job_id') or inputs.get('inbox_id')}", "operationName": "RESUME Job", @@ -96,8 +96,8 @@ def resume( @traced( name="jobs_resume", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Job:{inputs.get('job_id') or inputs.get('inbox_id')}", "operationName": "RESUME Job", @@ -171,8 +171,8 @@ def custom_headers(self) -> Dict[str, str]: @traced( name="jobs_retrieve", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Job:{inputs['job_key']}", "operationName": "GET Job", @@ -217,8 +217,8 @@ def retrieve( @traced( name="jobs_retrieve", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Job:{inputs['job_key']}", "operationName": "GET Job", diff --git a/src/uipath/_services/llm_gateway_service.py b/src/uipath/_services/llm_gateway_service.py index 6e1ae416..847ca6e6 100644 --- a/src/uipath/_services/llm_gateway_service.py +++ b/src/uipath/_services/llm_gateway_service.py @@ -57,8 +57,8 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: @traced( name="llm_embeddings_usage", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"LLMGateway:OpenAI:EmbeddingModel:{inputs['embedding_model']}", "operationName": "GET Embedding Usage", @@ -93,8 +93,8 @@ async def embeddings_usage( @traced( name="llm_embeddings", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"LLMGateway:OpenAI:EmbeddingModel:{inputs['embedding_model']}", "operationName": "GET Embedding", @@ -128,8 +128,8 @@ async def embeddings( @traced( name="llm_chat_completions", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"LLMGateway:OpenAI:ChatModel:{inputs['model']}", "operationName": "GET Chat Completion", @@ -187,8 +187,8 @@ async def chat_completions( @traced( name="llm_chat_completions_usage", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"LLMGateway:OpenAI:ChatModel:{inputs['model']}", "operationName": "GET Chat Completion Usage", diff --git a/src/uipath/_services/processes_service.py b/src/uipath/_services/processes_service.py index 8490f20f..45975216 100644 --- a/src/uipath/_services/processes_service.py +++ b/src/uipath/_services/processes_service.py @@ -26,8 +26,8 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: @traced( name="processes_invoke", run_type="uipath", - hide_input=True, # Input arguments can be sensitive - hide_output=True, # Job details might contain sensitive info + hide_input=False, # Input arguments can be sensitive + hide_output=False, # Job details might contain sensitive info dependency={ "targetName": lambda inputs: f"Process:{inputs['name']}", "operationName": "INVOKE Process", @@ -93,8 +93,8 @@ def invoke( @traced( name="processes_invoke", run_type="uipath", - hide_input=True, # Input arguments can be sensitive - hide_output=True, # Job details might contain sensitive info + hide_input=False, # Input arguments can be sensitive + hide_output=False, # Job details might contain sensitive info dependency={ "targetName": lambda inputs: f"Process:{inputs['name']}", "operationName": "INVOKE Process", diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 20a63384..4eaf8af1 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -24,8 +24,8 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: @traced( name="queues_list_items", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"QueueItems:Context:{inputs['self']._folder_path or inputs['self']._folder_id or 'Global'}", "operationName": "LIST QueueItems", @@ -45,8 +45,8 @@ def list_items(self) -> Response: @traced( name="queues_list_items", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"QueueItems:Context:{inputs['self']._folder_path or inputs['self']._folder_id or 'Global'}", "operationName": "LIST QueueItems", @@ -65,8 +65,8 @@ async def list_items_async(self) -> Response: @traced( name="queues_create_item", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Queue:{(inputs['item'].name if hasattr(inputs['item'], 'name') else inputs['item'].get('name', 'UnknownQueue'))}", "operationName": "CREATE QueueItem", @@ -90,8 +90,8 @@ def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: @traced( name="queues_create_item", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Queue:{(inputs['item'].name if hasattr(inputs['item'], 'name') else inputs['item'].get('name', 'UnknownQueue'))}", "operationName": "CREATE QueueItem", @@ -119,8 +119,8 @@ async def create_item_async( @traced( name="queues_create_items", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Queue:{inputs['queue_name']}", "operationName": "CREATE QueueItems", @@ -149,8 +149,8 @@ def create_items( @traced( name="queues_create_items", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Queue:{inputs['queue_name']}", "operationName": "CREATE QueueItems", @@ -181,8 +181,8 @@ async def create_items_async( @traced( name="queues_create_transaction_item", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Queue:{(inputs['item'].queue_name if hasattr(inputs['item'], 'queue_name') else inputs['item'].get('queue_name', 'UnknownQueue'))}", "operationName": "CREATE TransactionItem", @@ -207,8 +207,8 @@ def create_transaction_item( @traced( name="queues_create_transaction_item", run_type="uipath", - hide_input=True, - hide_output=True, + hide_input=False, + hide_output=False, dependency={ "targetName": lambda inputs: f"Queue:{(inputs['item'].queue_name if hasattr(inputs['item'], 'queue_name') else inputs['item'].get('queue_name', 'UnknownQueue'))}", "operationName": "CREATE TransactionItem", From 6f8eb0916df117638d2b1acbca1a46f138f2ca0f Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 29 May 2025 11:47:05 +0300 Subject: [PATCH 05/17] fix2 --- src/uipath/_services/queues_service.py | 30 ++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 4eaf8af1..1713228a 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -11,6 +11,20 @@ from ._base_service import BaseService +def _get_queue_item_name_for_tracing( + inputs: Dict[str, Any], primary: str, secondary: str +) -> str: + item = inputs.get("item") + if not item: + return "Queue:UnknownItem" + name_val = ( + item.get(primary, item.get(secondary, "UnknownQueue")) + if isinstance(item, dict) + else getattr(item, primary, getattr(item, secondary, "UnknownQueue")) + ) + return f"Queue:{name_val}" + + class QueuesService(FolderContext, BaseService): """Service for managing UiPath queues and queue items. @@ -68,7 +82,9 @@ async def list_items_async(self) -> Response: hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Queue:{(inputs['item'].name if hasattr(inputs['item'], 'name') else inputs['item'].get('name', 'UnknownQueue'))}", + "targetName": lambda inputs: _get_queue_item_name_for_tracing( + inputs, "name", "Name" + ), "operationName": "CREATE QueueItem", }, ) @@ -93,7 +109,9 @@ def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Queue:{(inputs['item'].name if hasattr(inputs['item'], 'name') else inputs['item'].get('name', 'UnknownQueue'))}", + "targetName": lambda inputs: _get_queue_item_name_for_tracing( + inputs, "name", "Name" + ), "operationName": "CREATE QueueItem", }, ) @@ -184,7 +202,9 @@ async def create_items_async( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Queue:{(inputs['item'].queue_name if hasattr(inputs['item'], 'queue_name') else inputs['item'].get('queue_name', 'UnknownQueue'))}", + "targetName": lambda inputs: _get_queue_item_name_for_tracing( + inputs, "queue_name", "QueueName" + ), "operationName": "CREATE TransactionItem", }, ) @@ -210,7 +230,9 @@ def create_transaction_item( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Queue:{(inputs['item'].queue_name if hasattr(inputs['item'], 'queue_name') else inputs['item'].get('queue_name', 'UnknownQueue'))}", + "targetName": lambda inputs: _get_queue_item_name_for_tracing( + inputs, "queue_name", "QueueName" + ), "operationName": "CREATE TransactionItem", }, ) From 878357bfa1e67bcb75b7d60ad0ae83e2437be6c6 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 29 May 2025 13:56:29 +0300 Subject: [PATCH 06/17] fix4 --- src/uipath/_services/assets_service.py | 18 +- src/uipath/_services/buckets_service.py | 24 ++- src/uipath/_services/jobs_service.py | 68 ++++--- src/uipath/_services/llm_gateway_service.py | 15 +- src/uipath/_services/processes_service.py | 10 +- src/uipath/_services/queues_service.py | 74 ++++++-- src/uipath/tracing/_traced.py | 8 + tests/tracing/test_traced.py | 189 ++++++++++++++++++++ 8 files changed, 328 insertions(+), 78 deletions(-) diff --git a/src/uipath/_services/assets_service.py b/src/uipath/_services/assets_service.py index f340421f..52fdfec3 100644 --- a/src/uipath/_services/assets_service.py +++ b/src/uipath/_services/assets_service.py @@ -30,7 +30,8 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Asset:{inputs['name']}", + "targetName": lambda inputs: inputs["name"], + "targetType": "Asset", "operationName": "GET Asset", }, ) @@ -92,7 +93,8 @@ def retrieve( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Asset:{inputs['name']}", + "targetName": lambda inputs: inputs["name"], + "targetType": "Asset", "operationName": "GET Asset", }, ) @@ -145,7 +147,8 @@ async def retrieve_async( hide_input=False, hide_output=True, dependency={ - "targetName": lambda inputs: f"Asset:{inputs['name']}", + "targetName": lambda inputs: inputs["name"], + "targetType": "Asset", "operationName": "GET Credential", }, ) @@ -205,7 +208,8 @@ def retrieve_credential( hide_input=False, hide_output=True, dependency={ - "targetName": lambda inputs: f"Asset:{inputs['name']}", + "targetName": lambda inputs: inputs["name"], + "targetType": "Asset", "operationName": "GET Credential", }, ) @@ -265,7 +269,8 @@ async def retrieve_credential_async( hide_input=False, hide_output=True, dependency={ - "targetName": lambda inputs: f"Asset:{inputs['robot_asset'].name}", + "targetName": lambda inputs: inputs["robot_asset"].name, + "targetType": "Asset", "operationName": "UPDATE Asset", }, ) @@ -316,7 +321,8 @@ def update( hide_input=False, hide_output=True, dependency={ - "targetName": lambda inputs: f"Asset:{inputs['robot_asset'].name}", + "targetName": lambda inputs: inputs["robot_asset"].name, + "targetType": "Asset", "operationName": "UPDATE Asset", }, ) diff --git a/src/uipath/_services/buckets_service.py b/src/uipath/_services/buckets_service.py index 714fd219..9c5efd26 100644 --- a/src/uipath/_services/buckets_service.py +++ b/src/uipath/_services/buckets_service.py @@ -34,7 +34,9 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", + "targetName": lambda inputs: inputs.get("name"), + "targetType": "Bucket", + "targetId": lambda inputs: inputs.get("key"), "operationName": "DOWNLOAD File", }, ) @@ -99,7 +101,9 @@ def download( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", + "targetName": lambda inputs: inputs.get("name"), + "targetType": "Bucket", + "targetId": lambda inputs: inputs.get("key"), "operationName": "DOWNLOAD File", }, ) @@ -171,7 +175,9 @@ async def download_async( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", + "targetName": lambda inputs: inputs.get("name"), + "targetType": "Bucket", + "targetId": lambda inputs: inputs.get("key"), "operationName": "UPLOAD File", }, ) @@ -264,7 +270,9 @@ def upload( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", + "targetName": lambda inputs: inputs.get("name"), + "targetType": "Bucket", + "targetId": lambda inputs: inputs.get("key"), "operationName": "UPLOAD File", }, ) @@ -361,7 +369,9 @@ async def upload_async( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", + "targetName": lambda inputs: inputs.get("name"), + "targetType": "Bucket", + "targetId": lambda inputs: inputs.get("key"), "operationName": "GET Bucket", }, ) @@ -418,7 +428,9 @@ def retrieve( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Bucket:{inputs.get('name') or inputs.get('key')}", + "targetName": lambda inputs: inputs.get("name"), + "targetType": "Bucket", + "targetId": lambda inputs: inputs.get("key"), "operationName": "GET Bucket", }, ) diff --git a/src/uipath/_services/jobs_service.py b/src/uipath/_services/jobs_service.py index b4613079..671d699d 100644 --- a/src/uipath/_services/jobs_service.py +++ b/src/uipath/_services/jobs_service.py @@ -41,10 +41,10 @@ def resume(self, *, job_id: str, payload: Any) -> None: ... @traced( name="jobs_resume", run_type="uipath", - hide_input=False, - hide_output=False, dependency={ - "targetName": lambda inputs: f"Job:{inputs.get('job_id') or inputs.get('inbox_id')}", + "targetName": lambda inputs: inputs.get("job_id"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_id"), "operationName": "RESUME Job", }, ) @@ -96,10 +96,10 @@ def resume( @traced( name="jobs_resume", run_type="uipath", - hide_input=False, - hide_output=False, dependency={ - "targetName": lambda inputs: f"Job:{inputs.get('job_id') or inputs.get('inbox_id')}", + "targetName": lambda inputs: inputs.get("job_id"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_id"), "operationName": "RESUME Job", }, ) @@ -171,10 +171,10 @@ def custom_headers(self) -> Dict[str, str]: @traced( name="jobs_retrieve", run_type="uipath", - hide_input=False, - hide_output=False, dependency={ - "targetName": lambda inputs: f"Job:{inputs['job_key']}", + "targetName": lambda inputs: inputs.get("job_key"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_key"), "operationName": "GET Job", }, ) @@ -217,10 +217,10 @@ def retrieve( @traced( name="jobs_retrieve", run_type="uipath", - hide_input=False, - hide_output=False, dependency={ - "targetName": lambda inputs: f"Job:{inputs['job_key']}", + "targetName": lambda inputs: inputs.get("job_key"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_key"), "operationName": "GET Job", }, ) @@ -376,10 +376,10 @@ def _retrieve_spec( @traced( name="jobs_list_attachments", run_type="uipath", - hide_input=True, - hide_output=True, dependency={ - "targetName": lambda _s, job_key, **_kwargs: f"Job:{job_key}:Attachments", + "targetName": lambda inputs: inputs.get("job_key"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_key"), "operationName": "LIST JobAttachments", }, ) @@ -423,10 +423,10 @@ def list_attachments( @traced( name="jobs_list_attachments", run_type="uipath", - hide_input=True, - hide_output=True, dependency={ - "targetName": lambda _s, job_key, **_kwargs: f"Job:{job_key}:Attachments", + "targetName": lambda inputs: inputs.get("job_key"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_key"), "operationName": "LIST JobAttachments", }, ) @@ -490,10 +490,9 @@ async def main(): hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - attachment_key, - job_key, - **_kwargs: f"Job:{job_key}:Attachment:{attachment_key}", + "targetName": lambda inputs: inputs.get("job_key"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_key"), "operationName": "LINK JobAttachment", }, ) @@ -540,10 +539,9 @@ def link_attachment( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - attachment_key, - job_key, - **_kwargs: f"Job:{job_key}:Attachment:{attachment_key}", + "targetName": lambda inputs: inputs.get("job_key"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_key"), "operationName": "LINK JobAttachment", }, ) @@ -628,12 +626,9 @@ def _link_job_attachment_spec( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - name, - job_key=None, - **_kwargs: f"Job:{job_key}:Attachment:{name}" - if job_key - else f"Attachment:{name}", + "targetName": lambda inputs: inputs.get("job_key"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_key"), "operationName": "CREATE Attachment", }, ) @@ -781,12 +776,9 @@ def create_attachment( hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - name, - job_key=None, - **_kwargs: f"Job:{job_key}:Attachment:{name}" - if job_key - else f"Attachment:{name}", + "targetName": lambda inputs: inputs.get("job_key"), + "targetType": "Job", + "targetId": lambda inputs: inputs.get("job_key"), "operationName": "CREATE Attachment", }, ) diff --git a/src/uipath/_services/llm_gateway_service.py b/src/uipath/_services/llm_gateway_service.py index 847ca6e6..8fa414e8 100644 --- a/src/uipath/_services/llm_gateway_service.py +++ b/src/uipath/_services/llm_gateway_service.py @@ -61,6 +61,8 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_output=False, dependency={ "targetName": lambda inputs: f"LLMGateway:OpenAI:EmbeddingModel:{inputs['embedding_model']}", + "targetType": "LLMGateway", + "targetId": lambda inputs: inputs["embedding_model"], "operationName": "GET Embedding Usage", }, ) @@ -97,6 +99,8 @@ async def embeddings_usage( hide_output=False, dependency={ "targetName": lambda inputs: f"LLMGateway:OpenAI:EmbeddingModel:{inputs['embedding_model']}", + "targetType": "LLMGateway", + "targetId": lambda inputs: inputs["embedding_model"], "operationName": "GET Embedding", }, ) @@ -132,6 +136,8 @@ async def embeddings( hide_output=False, dependency={ "targetName": lambda inputs: f"LLMGateway:OpenAI:ChatModel:{inputs['model']}", + "targetType": "LLMGateway", + "targetId": lambda inputs: inputs["model"], "operationName": "GET Chat Completion", }, ) @@ -191,6 +197,8 @@ async def chat_completions( hide_output=False, dependency={ "targetName": lambda inputs: f"LLMGateway:OpenAI:ChatModel:{inputs['model']}", + "targetType": "LLMGateway", + "targetId": lambda inputs: inputs["model"], "operationName": "GET Chat Completion Usage", }, ) @@ -258,10 +266,9 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=True, hide_output=True, dependency={ - "targetName": lambda _s, - messages, - model, - **_kwargs: f"LLMGateway:Normalized:ChatModel:{model}", + "targetName": lambda inputs: f"LLMGateway:Normalized:ChatModel:{inputs['model']}", + "targetType": "LLMGateway", + "targetId": lambda inputs: inputs["model"], "operationName": "GET Normalized Chat Completion", }, ) diff --git a/src/uipath/_services/processes_service.py b/src/uipath/_services/processes_service.py index 45975216..44b0132c 100644 --- a/src/uipath/_services/processes_service.py +++ b/src/uipath/_services/processes_service.py @@ -26,10 +26,9 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: @traced( name="processes_invoke", run_type="uipath", - hide_input=False, # Input arguments can be sensitive - hide_output=False, # Job details might contain sensitive info dependency={ - "targetName": lambda inputs: f"Process:{inputs['name']}", + "targetName": lambda inputs: inputs.get("name"), + "targetType": "Process", "operationName": "INVOKE Process", }, ) @@ -93,10 +92,9 @@ def invoke( @traced( name="processes_invoke", run_type="uipath", - hide_input=False, # Input arguments can be sensitive - hide_output=False, # Job details might contain sensitive info dependency={ - "targetName": lambda inputs: f"Process:{inputs['name']}", + "targetName": lambda inputs: inputs.get("name"), + "targetType": "Process", "operationName": "INVOKE Process", }, ) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 1713228a..95f91ef4 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -16,13 +16,13 @@ def _get_queue_item_name_for_tracing( ) -> str: item = inputs.get("item") if not item: - return "Queue:UnknownItem" + return "UnknownItem" name_val = ( item.get(primary, item.get(secondary, "UnknownQueue")) if isinstance(item, dict) else getattr(item, primary, getattr(item, secondary, "UnknownQueue")) ) - return f"Queue:{name_val}" + return name_val class QueuesService(FolderContext, BaseService): @@ -41,8 +41,10 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"QueueItems:Context:{inputs['self']._folder_path or inputs['self']._folder_id or 'Global'}", - "operationName": "LIST QueueItems", + "targetName": "All", # Changed + "targetType": "QueueCollection", + "targetId": "All", + "operationName": "UiPath.Queues.GetItems", }, ) def list_items(self) -> Response: @@ -62,8 +64,10 @@ def list_items(self) -> Response: hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"QueueItems:Context:{inputs['self']._folder_path or inputs['self']._folder_id or 'Global'}", - "operationName": "LIST QueueItems", + "targetName": "All", # Changed + "targetType": "QueueCollection", + "targetId": "All", + "operationName": "UiPath.Queues.GetItems", }, ) async def list_items_async(self) -> Response: @@ -85,7 +89,14 @@ async def list_items_async(self) -> Response: "targetName": lambda inputs: _get_queue_item_name_for_tracing( inputs, "name", "Name" ), - "operationName": "CREATE QueueItem", + "targetType": "Queue", + "targetId": lambda inputs: ( + _get_queue_item_name_for_tracing(inputs, "key", "Key") + if _get_queue_item_name_for_tracing(inputs, "key", "Key") + != "UnknownQueue" + else _get_queue_item_name_for_tracing(inputs, "name", "Name") + ), + "operationName": "UiPath.Queues.AddItem", }, ) def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: @@ -112,7 +123,14 @@ def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: "targetName": lambda inputs: _get_queue_item_name_for_tracing( inputs, "name", "Name" ), - "operationName": "CREATE QueueItem", + "targetType": "Queue", + "targetId": lambda inputs: ( + _get_queue_item_name_for_tracing(inputs, "key", "Key") + if _get_queue_item_name_for_tracing(inputs, "key", "Key") + != "UnknownQueue" + else _get_queue_item_name_for_tracing(inputs, "name", "Name") + ), + "operationName": "UiPath.Queues.AddItem", }, ) async def create_item_async( @@ -140,8 +158,10 @@ async def create_item_async( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Queue:{inputs['queue_name']}", - "operationName": "CREATE QueueItems", + "targetName": lambda inputs: inputs["queue_name"], # Changed + "targetType": "Queue", + "targetId": lambda inputs: inputs["queue_name"], + "operationName": "UiPath.Queues.AddItem", }, ) def create_items( @@ -170,8 +190,10 @@ def create_items( hide_input=False, hide_output=False, dependency={ - "targetName": lambda inputs: f"Queue:{inputs['queue_name']}", - "operationName": "CREATE QueueItems", + "targetName": lambda inputs: inputs["queue_name"], # Changed + "targetType": "Queue", + "targetId": lambda inputs: inputs["queue_name"], + "operationName": "UiPath.Queues.AddItem", }, ) async def create_items_async( @@ -205,7 +227,11 @@ async def create_items_async( "targetName": lambda inputs: _get_queue_item_name_for_tracing( inputs, "queue_name", "QueueName" ), - "operationName": "CREATE TransactionItem", + "targetType": "Queue", # Added + "targetId": lambda inputs: _get_queue_item_name_for_tracing( # Added + inputs, "queue_name", "QueueName" + ), + "operationName": "UiPath.Queues.CreateTransaction", }, ) def create_transaction_item( @@ -233,7 +259,11 @@ def create_transaction_item( "targetName": lambda inputs: _get_queue_item_name_for_tracing( inputs, "queue_name", "QueueName" ), - "operationName": "CREATE TransactionItem", + "targetType": "Queue", # Added + "targetId": lambda inputs: _get_queue_item_name_for_tracing( # Added + inputs, "queue_name", "QueueName" + ), + "operationName": "UiPath.Queues.CreateTransaction", }, ) async def create_transaction_item_async( @@ -261,7 +291,9 @@ async def create_transaction_item_async( hide_output=True, dependency={ "targetName": "QueueItem", - "operationName": "UPDATE TransactionProgress", + "targetType": "QueueItem", # Added + "targetId": lambda inputs: inputs.get("transaction_key"), # Added + "operationName": "UiPath.Queues.UpdateTransactionProgress", }, ) def update_progress_of_transaction_item( @@ -289,7 +321,9 @@ def update_progress_of_transaction_item( hide_output=True, dependency={ "targetName": "QueueItem", - "operationName": "UPDATE TransactionProgress", + "targetType": "QueueItem", # Added + "targetId": lambda inputs: inputs.get("transaction_key"), # Added + "operationName": "UiPath.Queues.UpdateTransactionProgress", }, ) async def update_progress_of_transaction_item_async( @@ -319,7 +353,9 @@ async def update_progress_of_transaction_item_async( hide_output=True, dependency={ "targetName": "QueueItem", - "operationName": "COMPLETE TransactionItem", + "targetType": "QueueItem", # Added + "targetId": lambda inputs: inputs.get("transaction_key"), # Added + "operationName": "UiPath.Queues.CompleteTransaction", }, ) def complete_transaction_item( @@ -347,7 +383,9 @@ def complete_transaction_item( hide_output=True, dependency={ "targetName": "QueueItem", - "operationName": "COMPLETE TransactionItem", + "targetType": "QueueItem", # Added + "targetId": lambda inputs: inputs.get("transaction_key"), # Added + "operationName": "UiPath.Queues.CompleteTransaction", }, ) async def complete_transaction_item_async( diff --git a/src/uipath/tracing/_traced.py b/src/uipath/tracing/_traced.py index 7e52097a..27ef8e75 100644 --- a/src/uipath/tracing/_traced.py +++ b/src/uipath/tracing/_traced.py @@ -19,7 +19,10 @@ class DependencyInfo(TypedDict, total=False): """Type definition for dependency tracking information.""" sourceName: Union[str, Callable[..., str]] + sourceType: Union[str, Callable[..., str]] # Added targetName: Union[str, Callable[..., str]] + targetType: Union[str, Callable[..., str]] # Added + targetId: Union[str, Callable[..., str]] # Added operationName: Union[str, Callable[..., str]] @@ -482,6 +485,11 @@ def traced( "UIPATH_PROCESS_KEY", "Unknown source" ) + if dependency.get("sourceType") is None: + dependency["sourceType"] = ( + "Agent" # Corrected from sourceName to sourceType + ) + # Store the parameters for later reapplication params = { "name": name, diff --git a/tests/tracing/test_traced.py b/tests/tracing/test_traced.py index 0da0b7fa..62a00185 100644 --- a/tests/tracing/test_traced.py +++ b/tests/tracing/test_traced.py @@ -72,9 +72,83 @@ def sample_function(x, y): assert span.attributes["span_type"] == "function_call_sync" assert "inputs" in span.attributes assert "output" in span.attributes + # No dependency attribute by default + assert "dependency" not in span.attributes assert span.attributes["output"] == "5" +@traced( + dependency={ + "targetName": "TestTarget", + "targetType": "TestType", + "targetId": "TestId", + "operationName": "TestOperation", + } +) +def sample_function_with_dependency(x, y): + return x + y + + +def test_traced_sync_function_with_dependency(setup_tracer): + exporter, provider = setup_tracer + + result = sample_function_with_dependency(2, 3) + assert result == 5 + + provider.shutdown() + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "sample_function_with_dependency" + assert span.attributes["span_type"] == "function_call_sync" + assert "inputs" in span.attributes + assert "output" in span.attributes + assert span.attributes["output"] == "5" + assert "dependency" in span.attributes + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["targetName"] == "TestTarget" + assert dependency_attr["targetType"] == "TestType" + assert dependency_attr["targetId"] == "TestId" + assert dependency_attr["operationName"] == "TestOperation" + assert dependency_attr["sourceName"] == "Unknown source" # Default + assert dependency_attr["sourceType"] == "Agent" # Default + + +@traced( + dependency={ + "targetName": lambda inputs: inputs["target"], + "targetType": lambda inputs: inputs["type"], + "targetId": lambda inputs: inputs["id"], + "operationName": "DynamicOperation", + } +) +def sample_function_with_callable_dependency(target, type, id): + return f"{target}-{type}-{id}" + + +def test_traced_sync_function_with_callable_dependency(setup_tracer): + exporter, provider = setup_tracer + + result = sample_function_with_callable_dependency("MyTarget", "MyType", "MyId") + assert result == "MyTarget-MyType-MyId" + + provider.shutdown() + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "sample_function_with_callable_dependency" + assert "dependency" in span.attributes + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["targetName"] == "MyTarget" + assert dependency_attr["targetType"] == "MyType" + assert dependency_attr["targetId"] == "MyId" + assert dependency_attr["operationName"] == "DynamicOperation" + assert dependency_attr["sourceName"] == "Unknown source" + assert dependency_attr["sourceType"] == "Agent" + + @pytest.mark.asyncio async def test_traced_async_function(setup_tracer): exporter, provider = setup_tracer @@ -97,9 +171,47 @@ async def sample_async_function(x, y): assert span.attributes["span_type"] == "function_call_async" assert "inputs" in span.attributes assert "output" in span.attributes + # No dependency attribute by default + assert "dependency" not in span.attributes assert span.attributes["output"] == "6" +@traced( + dependency={ + "targetName": "AsyncTarget", + "targetType": "AsyncType", + "targetId": "AsyncId", + "operationName": "AsyncOperation", + } +) +async def sample_async_function_with_dependency(x, y): + return x * y + + +@pytest.mark.asyncio +async def test_traced_async_function_with_dependency(setup_tracer): + exporter, provider = setup_tracer + + result = await sample_async_function_with_dependency(2, 3) + assert result == 6 + + provider.shutdown() + await sleep(0.1) # allow time for export + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "sample_async_function_with_dependency" + assert "dependency" in span.attributes + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["targetName"] == "AsyncTarget" + assert dependency_attr["targetType"] == "AsyncType" + assert dependency_attr["targetId"] == "AsyncId" + assert dependency_attr["operationName"] == "AsyncOperation" + assert dependency_attr["sourceName"] == "Unknown source" + assert dependency_attr["sourceType"] == "Agent" + + def test_traced_generator_function(setup_tracer): exporter, provider = setup_tracer @@ -120,9 +232,46 @@ def sample_generator_function(n): assert span.attributes["span_type"] == "function_call_generator_sync" assert "inputs" in span.attributes assert "output" in span.attributes + # No dependency attribute by default + assert "dependency" not in span.attributes assert span.attributes["output"] == "[0, 1, 2]" +@traced( + dependency={ + "targetName": "GenTarget", + "targetType": "GenType", + "targetId": "GenId", + "operationName": "GenOperation", + } +) +def sample_generator_function_with_dependency(n): + for i in range(n): + yield i + + +def test_traced_generator_function_with_dependency(setup_tracer): + exporter, provider = setup_tracer + + results = list(sample_generator_function_with_dependency(3)) + assert results == [0, 1, 2] + + provider.shutdown() + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "sample_generator_function_with_dependency" + assert "dependency" in span.attributes + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["targetName"] == "GenTarget" + assert dependency_attr["targetType"] == "GenType" + assert dependency_attr["targetId"] == "GenId" + assert dependency_attr["operationName"] == "GenOperation" + assert dependency_attr["sourceName"] == "Unknown source" + assert dependency_attr["sourceType"] == "Agent" + + @pytest.mark.asyncio async def test_traced_async_generator_function(setup_tracer): exporter, provider = setup_tracer @@ -144,9 +293,49 @@ async def sample_async_generator_function(n): assert span.attributes["span_type"] == "function_call_generator_async" assert "inputs" in span.attributes assert "output" in span.attributes + # No dependency attribute by default + assert "dependency" not in span.attributes assert span.attributes["output"] == "[0, 1, 2]" +@traced( + dependency={ + "targetName": "AsyncGenTarget", + "targetType": "AsyncGenType", + "targetId": "AsyncGenId", + "operationName": "AsyncGenOperation", + } +) +async def sample_async_generator_function_with_dependency(n): + for i in range(n): + yield i + + +@pytest.mark.asyncio +async def test_traced_async_generator_function_with_dependency(setup_tracer): + exporter, provider = setup_tracer + + results = [ + item async for item in sample_async_generator_function_with_dependency(3) + ] + assert results == [0, 1, 2] + + provider.shutdown() + spans = exporter.get_exported_spans() + + assert len(spans) == 1 + span = spans[0] + assert span.name == "sample_async_generator_function_with_dependency" + assert "dependency" in span.attributes + dependency_attr = json.loads(span.attributes["dependency"]) + assert dependency_attr["targetName"] == "AsyncGenTarget" + assert dependency_attr["targetType"] == "AsyncGenType" + assert dependency_attr["targetId"] == "AsyncGenId" + assert dependency_attr["operationName"] == "AsyncGenOperation" + assert dependency_attr["sourceName"] == "Unknown source" + assert dependency_attr["sourceType"] == "Agent" + + def test_traced_with_basic_processors(setup_tracer): """Test traced decorator with basic input and output processors.""" exporter, provider = setup_tracer From fab5f969dc1d499c9f7f5dc5121c4f49681413d3 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Sat, 31 May 2025 13:48:30 +0300 Subject: [PATCH 07/17] fix5 --- src/uipath/_services/queues_service.py | 36 ++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 95f91ef4..88bff91f 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -41,10 +41,9 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: hide_input=False, hide_output=False, dependency={ - "targetName": "All", # Changed - "targetType": "QueueCollection", - "targetId": "All", - "operationName": "UiPath.Queues.GetItems", + "targetName": "All", + "targetType": "Queue", + "operationName": "LIST QueueItems", }, ) def list_items(self) -> Response: @@ -64,10 +63,9 @@ def list_items(self) -> Response: hide_input=False, hide_output=False, dependency={ - "targetName": "All", # Changed - "targetType": "QueueCollection", - "targetId": "All", - "operationName": "UiPath.Queues.GetItems", + "targetName": "All", + "targetType": "Queue", + "operationName": "LIST QueueItems", }, ) async def list_items_async(self) -> Response: @@ -96,7 +94,7 @@ async def list_items_async(self) -> Response: != "UnknownQueue" else _get_queue_item_name_for_tracing(inputs, "name", "Name") ), - "operationName": "UiPath.Queues.AddItem", + "operationName": "ADD QueueItem", }, ) def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: @@ -130,7 +128,7 @@ def create_item(self, item: Union[Dict[str, Any], QueueItem]) -> Response: != "UnknownQueue" else _get_queue_item_name_for_tracing(inputs, "name", "Name") ), - "operationName": "UiPath.Queues.AddItem", + "operationName": "ADD QueueItem", }, ) async def create_item_async( @@ -161,7 +159,7 @@ async def create_item_async( "targetName": lambda inputs: inputs["queue_name"], # Changed "targetType": "Queue", "targetId": lambda inputs: inputs["queue_name"], - "operationName": "UiPath.Queues.AddItem", + "operationName": "ADD QueueItems", }, ) def create_items( @@ -193,7 +191,7 @@ def create_items( "targetName": lambda inputs: inputs["queue_name"], # Changed "targetType": "Queue", "targetId": lambda inputs: inputs["queue_name"], - "operationName": "UiPath.Queues.AddItem", + "operationName": "ADD QueueItems", }, ) async def create_items_async( @@ -227,11 +225,11 @@ async def create_items_async( "targetName": lambda inputs: _get_queue_item_name_for_tracing( inputs, "queue_name", "QueueName" ), - "targetType": "Queue", # Added + "targetType": "Queue", "targetId": lambda inputs: _get_queue_item_name_for_tracing( # Added inputs, "queue_name", "QueueName" ), - "operationName": "UiPath.Queues.CreateTransaction", + "operationName": "ADD QueueItem", }, ) def create_transaction_item( @@ -263,7 +261,7 @@ def create_transaction_item( "targetId": lambda inputs: _get_queue_item_name_for_tracing( # Added inputs, "queue_name", "QueueName" ), - "operationName": "UiPath.Queues.CreateTransaction", + "operationName": "ADD QueueItem", }, ) async def create_transaction_item_async( @@ -293,7 +291,7 @@ async def create_transaction_item_async( "targetName": "QueueItem", "targetType": "QueueItem", # Added "targetId": lambda inputs: inputs.get("transaction_key"), # Added - "operationName": "UiPath.Queues.UpdateTransactionProgress", + "operationName": "UPDATE QueueItem", }, ) def update_progress_of_transaction_item( @@ -323,7 +321,7 @@ def update_progress_of_transaction_item( "targetName": "QueueItem", "targetType": "QueueItem", # Added "targetId": lambda inputs: inputs.get("transaction_key"), # Added - "operationName": "UiPath.Queues.UpdateTransactionProgress", + "operationName": "UPDATE QueueItem", }, ) async def update_progress_of_transaction_item_async( @@ -355,7 +353,7 @@ async def update_progress_of_transaction_item_async( "targetName": "QueueItem", "targetType": "QueueItem", # Added "targetId": lambda inputs: inputs.get("transaction_key"), # Added - "operationName": "UiPath.Queues.CompleteTransaction", + "operationName": "UPDATE QueueItem", }, ) def complete_transaction_item( @@ -385,7 +383,7 @@ def complete_transaction_item( "targetName": "QueueItem", "targetType": "QueueItem", # Added "targetId": lambda inputs: inputs.get("transaction_key"), # Added - "operationName": "UiPath.Queues.CompleteTransaction", + "operationName": "UPDATE QueueItem", }, ) async def complete_transaction_item_async( From f191c90556cf8ee50831b90c72c92a51e8493553 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Wed, 4 Jun 2025 16:43:43 +0300 Subject: [PATCH 08/17] fix queue complete_transaction_item --- src/uipath/_services/queues_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 88bff91f..704819ef 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -372,7 +372,7 @@ def complete_transaction_item( """ spec = self._complete_transaction_item_spec(transaction_key, result) response = self.request(spec.method, url=spec.endpoint, json=spec.json) - return response.json() + return response @traced( name="queues_complete_transaction_item", From 5dd83f51a641a3fe4ecbafa5d04488feb6e37e31 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Wed, 4 Jun 2025 16:46:26 +0300 Subject: [PATCH 09/17] remove hide input output --- src/uipath/_services/queues_service.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 704819ef..dfc29b0c 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -285,8 +285,6 @@ async def create_transaction_item_async( @traced( name="queues_update_progress_of_transaction_item", run_type="uipath", - hide_input=True, - hide_output=True, dependency={ "targetName": "QueueItem", "targetType": "QueueItem", # Added @@ -315,8 +313,6 @@ def update_progress_of_transaction_item( @traced( name="queues_update_progress_of_transaction_item", run_type="uipath", - hide_input=True, - hide_output=True, dependency={ "targetName": "QueueItem", "targetType": "QueueItem", # Added @@ -347,8 +343,6 @@ async def update_progress_of_transaction_item_async( @traced( name="queues_complete_transaction_item", run_type="uipath", - hide_input=True, - hide_output=True, dependency={ "targetName": "QueueItem", "targetType": "QueueItem", # Added @@ -377,8 +371,6 @@ def complete_transaction_item( @traced( name="queues_complete_transaction_item", run_type="uipath", - hide_input=True, - hide_output=True, dependency={ "targetName": "QueueItem", "targetType": "QueueItem", # Added From 426373ad928b38cfd03247feb1d264cc21f7490e Mon Sep 17 00:00:00 2001 From: ionmincu Date: Wed, 4 Jun 2025 18:36:27 +0300 Subject: [PATCH 10/17] add jobs list --- src/uipath/_services/jobs_service.py | 54 ++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/uipath/_services/jobs_service.py b/src/uipath/_services/jobs_service.py index 671d699d..3ab86989 100644 --- a/src/uipath/_services/jobs_service.py +++ b/src/uipath/_services/jobs_service.py @@ -921,3 +921,57 @@ async def main(): # Return only the UUID return attachment_id + + @traced( + name="jobs_list", + run_type="uipath", + dependency={ + "targetType": "Job", + "operationName": "LIST Jobs", + }, + ) + def list_jobs( + self, + *, + folder_key: Optional[str] = None, + folder_path: Optional[str] = None, + top: Optional[int] = None, + skip: Optional[int] = None, + filters: Optional[dict] = None, + ) -> List[Job]: + """List all jobs in a folder. + + Args: + folder_key (Optional[str]): The key of the folder to list jobs from. + folder_path (Optional[str]): The path of the folder to list jobs from. + top (Optional[int]): Max number of jobs to return. + skip (Optional[int]): Number of jobs to skip (for paging). + filters (Optional[dict]): Additional OData filters as a dict. + + Returns: + List[Job]: List of Job objects in the folder. + """ + params = {} + if top is not None: + params["$top"] = top + if skip is not None: + params["$skip"] = skip + if filters: + # Merge filters into OData $filter string + filter_str = " and ".join(f"{k} eq '{v}'" for k, v in filters.items()) + params["$filter"] = filter_str + headers = {**header_folder(folder_key, folder_path)} + spec = RequestSpec( + method="GET", + endpoint=Endpoint("/orchestrator_/odata/Jobs"), + params=params, + headers=headers, + ) + response = self.request( + spec.method, + url=spec.endpoint, + params=spec.params, + headers=spec.headers, + ) + jobs_data = response.json().get("value", []) + return [Job.model_validate(job) for job in jobs_data] From 56e219114b7971ecd88c594138808f1d56e930c5 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 5 Jun 2025 11:24:14 +0300 Subject: [PATCH 11/17] fix json args encoder --- src/uipath/_services/queues_service.py | 4 ++-- src/uipath/tracing/_utils.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index dfc29b0c..b210f66b 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -255,11 +255,11 @@ def create_transaction_item( hide_output=False, dependency={ "targetName": lambda inputs: _get_queue_item_name_for_tracing( - inputs, "queue_name", "QueueName" + inputs, "name", "Name" ), "targetType": "Queue", # Added "targetId": lambda inputs: _get_queue_item_name_for_tracing( # Added - inputs, "queue_name", "QueueName" + inputs, "name", "Name" ), "operationName": "ADD QueueItem", }, diff --git a/src/uipath/tracing/_utils.py b/src/uipath/tracing/_utils.py index 0566dfd5..245c714c 100644 --- a/src/uipath/tracing/_utils.py +++ b/src/uipath/tracing/_utils.py @@ -11,10 +11,18 @@ from opentelemetry.sdk.trace import ReadableSpan from opentelemetry.trace import StatusCode +from pydantic import BaseModel logger = logging.getLogger(__name__) +def _custom_encoder(obj): + if isinstance(obj, BaseModel): + return obj.model_dump() # Convert Pydantic models to dictionaries + + return str(obj) # Fallback for other types + + @dataclass class UiPathSpan: """Represents a span in the UiPath tracing system.""" From be2552870ae566daa0f0ac3109910d6be673ca6b Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 5 Jun 2025 11:26:43 +0300 Subject: [PATCH 12/17] fix encoder --- src/uipath/tracing/_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uipath/tracing/_utils.py b/src/uipath/tracing/_utils.py index 245c714c..cf7e69b7 100644 --- a/src/uipath/tracing/_utils.py +++ b/src/uipath/tracing/_utils.py @@ -230,7 +230,7 @@ def format_args_for_trace_json( ) -> str: """Return a JSON string of inputs from the function signature.""" result = _SpanUtils.format_args_for_trace(signature, *args, **kwargs) - return json.dumps(result, default=str) + return json.dumps(result, default=_custom_encoder) @staticmethod def format_args_for_trace( From 885c21af3ac11851f4fc0325573e54b1899edc3b Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 5 Jun 2025 11:58:04 +0300 Subject: [PATCH 13/17] add get queue item --- src/uipath/_services/queues_service.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index b210f66b..75dbaa75 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -398,6 +398,32 @@ async def complete_transaction_item_async( ) return response.json() + @traced( + name="queues_get_queue_item", + run_type="uipath", + dependency={ + "targetName": lambda inputs: inputs.get("queue_name", "UnknownQueueItem"), + "targetType": "Queue", + "targetId": lambda inputs: inputs.get("key"), + "operationName": "GET QueueItem", + }, + ) + def get_queue_item(self, key: int, queue_name: str) -> Response: + """Retrieves a queue item by its key from the Orchestrator. + + Args: + key: The unique integer key of the queue item. + + Returns: + Response: HTTP response containing the queue item details. + """ + spec = RequestSpec( + method="GET", + endpoint=Endpoint(f"/orchestrator_/odata/QueueItems({key})"), + ) + response = self.request(spec.method, url=spec.endpoint) + return response.json() + @property def custom_headers(self) -> Dict[str, str]: return self.folder_headers From 8ca973566d8e626e255d7c9cb2fcdc6afed521ee Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 5 Jun 2025 15:20:46 +0300 Subject: [PATCH 14/17] add name to complete transaction --- src/uipath/_services/queues_service.py | 10 +++++----- src/uipath/models/queues.py | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 75dbaa75..88898758 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -344,9 +344,9 @@ async def update_progress_of_transaction_item_async( name="queues_complete_transaction_item", run_type="uipath", dependency={ - "targetName": "QueueItem", - "targetType": "QueueItem", # Added - "targetId": lambda inputs: inputs.get("transaction_key"), # Added + "targetName": lambda inputs: inputs.get("result"), # Added + "targetType": "Queue", + "targetId": lambda inputs: inputs.get("transaction_key"), "operationName": "UPDATE QueueItem", }, ) @@ -373,8 +373,8 @@ def complete_transaction_item( run_type="uipath", dependency={ "targetName": "QueueItem", - "targetType": "QueueItem", # Added - "targetId": lambda inputs: inputs.get("transaction_key"), # Added + "targetType": "QueueItem", + "targetId": lambda inputs: inputs.get("transaction_key").name, "operationName": "UPDATE QueueItem", }, ) diff --git a/src/uipath/models/queues.py b/src/uipath/models/queues.py index 15e4e925..49421d83 100644 --- a/src/uipath/models/queues.py +++ b/src/uipath/models/queues.py @@ -127,6 +127,11 @@ class TransactionItemResult(BaseModel): json_encoders={datetime: lambda v: v.isoformat() if v else None}, ) + name: str = Field( + description="The name of the queue in which to search for the next item or in which to insert the item before marking it as InProgress and sending it to the robot.", + alias="Name", + ) + is_successful: Optional[bool] = Field( default=None, description="States if the processing was successful or not.", From 3bfa431ca10ad910a719f09a38fa13e2785f8b5a Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 5 Jun 2025 15:22:01 +0300 Subject: [PATCH 15/17] fix name --- src/uipath/_services/queues_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index 88898758..abc8e996 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -344,7 +344,7 @@ async def update_progress_of_transaction_item_async( name="queues_complete_transaction_item", run_type="uipath", dependency={ - "targetName": lambda inputs: inputs.get("result"), # Added + "targetName": lambda inputs: inputs.get("result").get("name"), "targetType": "Queue", "targetId": lambda inputs: inputs.get("transaction_key"), "operationName": "UPDATE QueueItem", @@ -374,7 +374,7 @@ def complete_transaction_item( dependency={ "targetName": "QueueItem", "targetType": "QueueItem", - "targetId": lambda inputs: inputs.get("transaction_key").name, + "targetId": lambda inputs: inputs.get("transaction_key"), "operationName": "UPDATE QueueItem", }, ) From 3756c997f29785661522b8c264ecd002e5d764f5 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 5 Jun 2025 15:31:35 +0300 Subject: [PATCH 16/17] fix queues_create_transaction_item --- src/uipath/_services/queues_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uipath/_services/queues_service.py b/src/uipath/_services/queues_service.py index abc8e996..5788215d 100644 --- a/src/uipath/_services/queues_service.py +++ b/src/uipath/_services/queues_service.py @@ -223,11 +223,11 @@ async def create_items_async( hide_output=False, dependency={ "targetName": lambda inputs: _get_queue_item_name_for_tracing( - inputs, "queue_name", "QueueName" + inputs, "name", "Name" ), "targetType": "Queue", "targetId": lambda inputs: _get_queue_item_name_for_tracing( # Added - inputs, "queue_name", "QueueName" + inputs, "name", "Name" ), "operationName": "ADD QueueItem", }, From eebb74d347871056aa165fc66f1307f453b70fe4 Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 12 Jun 2025 22:33:35 +0300 Subject: [PATCH 17/17] add buckets retrieve --- src/uipath/_services/assets_service.py | 2 -- src/uipath/_services/buckets_service.py | 30 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/uipath/_services/assets_service.py b/src/uipath/_services/assets_service.py index 52fdfec3..452e3adc 100644 --- a/src/uipath/_services/assets_service.py +++ b/src/uipath/_services/assets_service.py @@ -27,8 +27,6 @@ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: @traced( name="assets_retrieve", run_type="uipath", - hide_input=False, - hide_output=False, dependency={ "targetName": lambda inputs: inputs["name"], "targetType": "Asset", diff --git a/src/uipath/_services/buckets_service.py b/src/uipath/_services/buckets_service.py index 9c5efd26..720bafda 100644 --- a/src/uipath/_services/buckets_service.py +++ b/src/uipath/_services/buckets_service.py @@ -555,3 +555,33 @@ def _retrieve_by_key_spec( **header_folder(folder_key, folder_path), }, ) + + def retrieve_read_uri( + self, + *, + bucket_id: int, + blob_file_path: str, + folder_key: Optional[str] = None, + folder_path: Optional[str] = None, + ) -> dict: + """Get the read URI and headers for a file in a bucket. + + Args: + bucket_id (int): The ID of the bucket. + blob_file_path (str): The path to the file in the bucket. + folder_key (Optional[str]): The key of the folder where the bucket resides. + folder_path (Optional[str]): The path of the folder where the bucket resides. + + Returns: + dict: Contains 'Uri', 'Headers', and 'RequiresAuth'. + """ + spec = self._retrieve_readUri_spec( + bucket_id, blob_file_path, folder_key=folder_key, folder_path=folder_path + ) + result = self.request( + spec.method, + url=spec.endpoint, + params=spec.params, + headers=spec.headers, + ).json() + return result