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..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: @@ -44,6 +50,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..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() @@ -122,6 +125,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 +134,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"} 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" },