diff --git a/packages/uipath-platform/src/uipath/platform/common/_span_utils.py b/packages/uipath-platform/src/uipath/platform/common/_span_utils.py index 448648067..41384615f 100644 --- a/packages/uipath-platform/src/uipath/platform/common/_span_utils.py +++ b/packages/uipath-platform/src/uipath/platform/common/_span_utils.py @@ -18,6 +18,17 @@ # SourceEnum.Robots = 4 (default for Python SDK / coded agents) DEFAULT_SOURCE = 4 +SOURCE_NAMES: dict[int, str] = { + 0: "Testing", + 1: "Agents", + 2: "ProcessOrchestration", + 3: "ApiWorkflows", + 4: "Robots", + 5: "ConversationalAgentsService", + 6: "IntegrationServiceTrigger", + 7: "Playground", +} + class AttachmentProvider(IntEnum): ORCHESTRATOR = 0 diff --git a/packages/uipath/pyproject.toml b/packages/uipath/pyproject.toml index a1e30f8e6..62d710fd2 100644 --- a/packages/uipath/pyproject.toml +++ b/packages/uipath/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.10.11" +version = "2.10.12" 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.11" diff --git a/packages/uipath/src/uipath/tracing/_otel_exporters.py b/packages/uipath/src/uipath/tracing/_otel_exporters.py index e3b4bbb5f..86791af5d 100644 --- a/packages/uipath/src/uipath/tracing/_otel_exporters.py +++ b/packages/uipath/src/uipath/tracing/_otel_exporters.py @@ -13,6 +13,7 @@ from uipath._utils._ssl_context import get_httpx_client_kwargs from uipath.platform.common import _SpanUtils +from uipath.platform.common._span_utils import DEFAULT_SOURCE, SOURCE_NAMES from uipath.platform.common.retry import NON_RETRYABLE_STATUS_CODES logger = logging.getLogger(__name__) @@ -389,7 +390,11 @@ def _process_span_attributes(self, span_data: Dict[str, Any]) -> None: def _build_url(self, span_list: list[Dict[str, Any]]) -> str: """Construct the URL for the API request.""" trace_id = str(span_list[0]["TraceId"]) - return f"{self.base_url}/api/Traces/spans?traceId={trace_id}&source=Robots" + source_int = span_list[0].get("Source", DEFAULT_SOURCE) + source_name = SOURCE_NAMES.get(source_int, "Robots") + return ( + f"{self.base_url}/api/Traces/spans?traceId={trace_id}&source={source_name}" + ) def _send_with_retries( self, url: str, payload: list[Dict[str, Any]], max_retries: int = 4 diff --git a/packages/uipath/tests/tracing/test_otel_exporters.py b/packages/uipath/tests/tracing/test_otel_exporters.py index a55fa5d60..0397a0c3c 100644 --- a/packages/uipath/tests/tracing/test_otel_exporters.py +++ b/packages/uipath/tests/tracing/test_otel_exporters.py @@ -216,6 +216,51 @@ def test_get_base_url(): assert exporter.base_url == "https://custom-trace.example.com/prefix" +def test_build_url_uses_span_source(): + """Test _build_url derives source query param from span's Source field.""" + with ( + patch("uipath.tracing._otel_exporters.httpx.Client"), + patch.dict(os.environ, {"UIPATH_ACCESS_TOKEN": "t"}, clear=True), + ): + exporter = LlmOpsHttpExporter() + exporter.base_url = "https://example.com/llmops" + + # Source=1 (Agents) + url = exporter._build_url([{"TraceId": "abc", "Source": 1}]) + assert ( + url + == "https://example.com/llmops/api/Traces/spans?traceId=abc&source=Agents" + ) + + # Source=4 (Robots) - default + url = exporter._build_url([{"TraceId": "abc", "Source": 4}]) + assert ( + url + == "https://example.com/llmops/api/Traces/spans?traceId=abc&source=Robots" + ) + + # Missing Source defaults to Robots + url = exporter._build_url([{"TraceId": "abc"}]) + assert ( + url + == "https://example.com/llmops/api/Traces/spans?traceId=abc&source=Robots" + ) + + # Source=7 (Playground) + url = exporter._build_url([{"TraceId": "abc", "Source": 7}]) + assert ( + url + == "https://example.com/llmops/api/Traces/spans?traceId=abc&source=Playground" + ) + + # Unknown source int falls back to Robots + url = exporter._build_url([{"TraceId": "abc", "Source": 99}]) + assert ( + url + == "https://example.com/llmops/api/Traces/spans?traceId=abc&source=Robots" + ) + + def test_internal_headers_set_when_trace_base_url_present(): """Test that internal tenant/account headers are set when UIPATH_TRACE_BASE_URL is configured.""" with patch.dict( diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index f7e3594d4..0d7ade036 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -2531,7 +2531,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.10.11" +version = "2.10.12" source = { editable = "." } dependencies = [ { name = "applicationinsights" }, @@ -2652,7 +2652,7 @@ wheels = [ [[package]] name = "uipath-platform" -version = "0.0.17" +version = "0.0.18" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -2661,9 +2661,9 @@ dependencies = [ { name = "truststore" }, { name = "uipath-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/c9/e7568133f3a657af16b67444c4e090259941078acc62acb1e2c072903da4/uipath_platform-0.0.17.tar.gz", hash = "sha256:a2c228462d7e2642dcfc249547d9b8e94ba1c72b68f16ba673ee3e58204e9365", size = 264143, upload-time = "2026-03-06T20:34:22.23Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/eb/848292baafcb7273b189004161b754e46431bb28a711b7f359a2b7642b27/uipath_platform-0.0.18.tar.gz", hash = "sha256:0d0cf196ffc06de90c3ec12a7b52d88b81f38a34e361eb81690ff88cd2a9a0bd", size = 264141, upload-time = "2026-03-09T16:24:11.158Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/36/d1/a1c357dbea16a8b5d5b8ae5311ff2353cc03a8b5dd15ff83b0b693687930/uipath_platform-0.0.17-py3-none-any.whl", hash = "sha256:7b88f2b4eb189877fb2f99d704fc0cbc6e4244f01dac59cf812fa8a03db95e36", size = 159073, upload-time = "2026-03-06T20:34:20.993Z" }, + { url = "https://files.pythonhosted.org/packages/36/69/a96b8c66d4a0ed5d5ac1a3e44a5836d1b9e4151519a884b67d8db18fb3d8/uipath_platform-0.0.18-py3-none-any.whl", hash = "sha256:9f46b0b01254b95b18cc753bd91f5a5802d0a15e7284224d6f9a1309bb71b6bf", size = 159073, upload-time = "2026-03-09T16:24:09.717Z" }, ] [[package]]