From bfe2c1a17c59fa1f6caae22d8198ffdc2ebb9a18 Mon Sep 17 00:00:00 2001 From: Runnan Jia Date: Wed, 17 Jun 2026 10:00:34 -0700 Subject: [PATCH 1/3] feat(platform): include jobKey in LLM Gateway trace baggage build_trace_context_headers emits folderKey/agentId/processKey but omits jobKey, even though UiPathConfig.job_key is available. LLM Gateway now builds the model-call span from this baggage, so without jobKey the span cannot be attributed to the originating job. Callers currently work around this by hand-injecting jobKey via extra_baggage; emitting it natively fixes it once for every caller. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/uipath-platform/pyproject.toml | 2 +- .../src/uipath/platform/chat/llm_trace_context.py | 2 ++ .../uipath-platform/tests/services/test_llm_trace_context.py | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/uipath-platform/pyproject.toml b/packages/uipath-platform/pyproject.toml index 7fcf797e7..40955931c 100644 --- a/packages/uipath-platform/pyproject.toml +++ b/packages/uipath-platform/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath-platform" -version = "0.2.6" +version = "0.2.7" description = "HTTP client library for programmatic access to UiPath Platform" readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.11" diff --git a/packages/uipath-platform/src/uipath/platform/chat/llm_trace_context.py b/packages/uipath-platform/src/uipath/platform/chat/llm_trace_context.py index 916ced322..81047817d 100644 --- a/packages/uipath-platform/src/uipath/platform/chat/llm_trace_context.py +++ b/packages/uipath-platform/src/uipath/platform/chat/llm_trace_context.py @@ -44,6 +44,8 @@ def build_trace_context_headers( baggage_parts.append(f"agentId={agent_id}") if process_uuid := UiPathConfig.process_uuid: baggage_parts.append(f"processKey={process_uuid}") + if job_key := UiPathConfig.job_key: + baggage_parts.append(f"jobKey={job_key}") if baggage_parts: headers["x-uipath-tracebaggage"] = ",".join(baggage_parts) diff --git a/packages/uipath-platform/tests/services/test_llm_trace_context.py b/packages/uipath-platform/tests/services/test_llm_trace_context.py index b8aa164dc..abf06b0cb 100644 --- a/packages/uipath-platform/tests/services/test_llm_trace_context.py +++ b/packages/uipath-platform/tests/services/test_llm_trace_context.py @@ -122,6 +122,7 @@ def test_all_env_vars_present(self) -> None: "UIPATH_FOLDER_KEY": "folder-abc", ENV_PROJECT_KEY: "agent-123", "UIPATH_PROCESS_UUID": "process-789", + "UIPATH_JOB_KEY": "job-456", } with patch.dict(os.environ, env, clear=True): headers = build_trace_context_headers() @@ -130,6 +131,7 @@ def test_all_env_vars_present(self) -> None: assert "folderKey=folder-abc" in baggage assert "agentId=agent-123" in baggage assert "processKey=process-789" in baggage + assert "jobKey=job-456" in baggage def test_partial_env_vars(self) -> None: env = {"UIPATH_FOLDER_KEY": "folder-only"} From 31ca3701cba5ecb69701e7fc1ca27e33887b1c7f Mon Sep 17 00:00:00 2001 From: Runnan Jia Date: Fri, 10 Jul 2026 10:42:00 -0700 Subject: [PATCH 2/3] fix(platform): emit W3C-compliant traceparent so the gateway accepts it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build_trace_context_headers emitted x-uipath-traceparent-id as "00-{trace}-{span}" with a 32-hex span id and no trace-flags segment. The LLM Gateway parses this header strictly (UiPath.Tracing.TraceParent.TryParse): it requires exactly four segments {version}-{32-hex trace}-{16-hex span}-{flags} and a 16-hex span id. The malformed header was rejected, so the gateway synthesized a fresh root trace for every LLM call and dropped the inbound baggage — the gateway's completion (audit) span ended up under a different traceId with a null parent, orphaned from the agent run, and the jobKey/source baggage this PR adds never reached the gateway. Fix: format the span id as 16 hex (`016x`, a span id is 64-bit) and append the `-01` trace-flags segment. Verified safe for the other consumer (the Agents backend parser is lenient: >=3 parts, no span-length check). Updated the test to assert the correct 4-segment / 16-hex shape. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/uipath/platform/chat/llm_trace_context.py | 10 ++++++++-- .../tests/services/test_llm_trace_context.py | 11 +++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/uipath-platform/src/uipath/platform/chat/llm_trace_context.py b/packages/uipath-platform/src/uipath/platform/chat/llm_trace_context.py index 81047817d..c10047669 100644 --- a/packages/uipath-platform/src/uipath/platform/chat/llm_trace_context.py +++ b/packages/uipath-platform/src/uipath/platform/chat/llm_trace_context.py @@ -34,8 +34,14 @@ def build_trace_context_headers( ctx = span.get_span_context() if config_trace_id and ctx and ctx.span_id: trace_id = _SpanUtils.normalize_trace_id(config_trace_id) - span_id = format(ctx.span_id, "032x") - headers["x-uipath-traceparent-id"] = f"00-{trace_id}-{span_id}" + # An OTEL span id is 64-bit => 16 lowercase hex chars, and the W3C traceparent + # requires the trailing trace-flags segment. The LLM Gateway's strict parser + # (UiPath.Tracing.TraceParent.TryParse) rejects the header unless it is exactly + # {version}-{32-hex trace}-{16-hex span}-{2-hex flags}; on rejection the gateway + # synthesizes a fresh root trace and drops inbound baggage, orphaning the audit + # span from the caller's trace. Emitting 32-hex span / no flags was the bug. + span_id = format(ctx.span_id, "016x") + headers["x-uipath-traceparent-id"] = f"00-{trace_id}-{span_id}-01" baggage_parts: list[str] = list(extra_baggage) if extra_baggage else [] if folder_key := UiPathConfig.folder_key: diff --git a/packages/uipath-platform/tests/services/test_llm_trace_context.py b/packages/uipath-platform/tests/services/test_llm_trace_context.py index abf06b0cb..a05032113 100644 --- a/packages/uipath-platform/tests/services/test_llm_trace_context.py +++ b/packages/uipath-platform/tests/services/test_llm_trace_context.py @@ -44,7 +44,9 @@ def setup_method(self) -> None: def test_traceparent_from_config_and_span(self) -> None: span = _make_span() ctx = span.get_span_context() - expected_span_id = format(ctx.span_id, "032x") + # OTEL span id is 64-bit => 16 hex chars; traceparent carries the trailing flags + # segment. The gateway's strict W3C parser requires exactly this shape. + expected_span_id = format(ctx.span_id, "016x") config_trace = "abcdef1234567890abcdef1234567890" env = {"UIPATH_TRACE_ID": config_trace} with ( @@ -58,12 +60,13 @@ def test_traceparent_from_config_and_span(self) -> None: assert "x-uipath-traceparent-id" in headers value = headers["x-uipath-traceparent-id"] - assert value == f"00-{config_trace}-{expected_span_id}" + assert value == f"00-{config_trace}-{expected_span_id}-01" parts = value.split("-") - assert len(parts) == 3 + assert len(parts) == 4 assert parts[0] == "00" assert len(parts[1]) == 32 - assert len(parts[2]) == 32 + assert len(parts[2]) == 16 + assert parts[3] == "01" def test_no_traceparent_without_config_trace_id(self) -> None: headers = build_trace_context_headers() From a2769f991a0e76627146aaafe5bd86cf34777609 Mon Sep 17 00:00:00 2001 From: Runnan Jia Date: Fri, 10 Jul 2026 10:52:23 -0700 Subject: [PATCH 3/3] chore(platform): refresh lockfiles for uipath-platform 0.2.7 Rebased onto main (which had advanced to 0.2.6) and bumped to 0.2.7 so this change publishes as a clean new release carrying the traceparent-format fix and jobKey baggage. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/uipath-platform/uv.lock | 4 ++-- packages/uipath/uv.lock | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/uipath-platform/uv.lock b/packages/uipath-platform/uv.lock index cee6cf645..29a1abba1 100644 --- a/packages/uipath-platform/uv.lock +++ b/packages/uipath-platform/uv.lock @@ -3,7 +3,7 @@ revision = 3 requires-python = ">=3.11" [options] -exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer = "2026-07-08T17:52:02.959384Z" exclude-newer-span = "P2D" [options.exclude-newer-package] @@ -1095,7 +1095,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.2.6" +version = "0.2.7" source = { editable = "." } dependencies = [ { name = "httpx" }, diff --git a/packages/uipath/uv.lock b/packages/uipath/uv.lock index c02e3cbd3..730276088 100644 --- a/packages/uipath/uv.lock +++ b/packages/uipath/uv.lock @@ -3,7 +3,7 @@ revision = 3 requires-python = ">=3.11" [options] -exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer = "2026-07-08T17:52:11.22969Z" exclude-newer-span = "P2D" [options.exclude-newer-package] @@ -2741,7 +2741,7 @@ dev = [ [[package]] name = "uipath-platform" -version = "0.2.6" +version = "0.2.7" source = { editable = "../uipath-platform" } dependencies = [ { name = "httpx" },