Skip to content

feat: add ReferenceContext for span reference hierarchy propagation#1708

Open
jepadil23 wants to merge 10 commits into
mainfrom
feat/reference-hierarchy-context
Open

feat: add ReferenceContext for span reference hierarchy propagation#1708
jepadil23 wants to merge 10 commits into
mainfrom
feat/reference-hierarchy-context

Conversation

@jepadil23

Copy link
Copy Markdown

Summary

  • Adds ReferenceContext — an immutable, copy-on-write ordered list of ReferenceEntry objects (serviceType, referenceId, version) that flows via contextvars.ContextVar across await boundaries
  • _span_utils.py reads from ReferenceContextAccessor to populate UiPathSpan.context.referenceHierarchy — wire-format compatible with service-common x-uipath-tracebaggage header
  • Fixes BatchSpanProcessor thread-boundary issue by stamping the hierarchy as a span attribute at span start
  • Normalizes empty-string env vars (UIPATH_ORGANIZATION_ID, UIPATH_TENANT_ID, UIPATH_FOLDER_KEY) to None for Guid fields in UiPathSpan

Test plan

  • tests/services/test_reference_context.py — unit tests for ReferenceContext, ReferenceEntry, and ReferenceContextAccessor (push/pop/reset, async propagation, wire serialization)
  • Verify UiPathSpan.context.referenceHierarchy is populated when ReferenceContextAccessor has entries set upstream
  • Verify empty-string env vars normalize to None in span fields

🤖 Generated with Claude Code

jepadil23 and others added 3 commits June 8, 2026 21:12
Introduces an immutable, copy-on-write ReferenceContext (modelled after
service-common BaggageContext) that propagates the reference hierarchy
through all spans emitted to LLMOps/Traceview.

- `_reference_context.py`: ReferenceEntry, ReferenceContext (add /
  to_wire_list / from_baggage_header / to_baggage_header_value),
  ReferenceContextAccessor (ContextVar-backed, async-safe)
- `_span_utils.py`: UiPathSpan gains `context` field; to_dict() emits
  `Context.referenceHierarchy` when set; otel_span_to_uipath_span()
  reads ReferenceContextAccessor and builds the wire payload
- `uipath.tracing`: re-exports ReferenceEntry, ReferenceContext,
  ReferenceContextAccessor for downstream consumers
- Tests: 30 new tests covering immutability, wire format, baggage
  header round-trip, accessor lifecycle, and span wiring

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…hSpan

UIPATH_FOLDER_KEY, UIPATH_ORGANIZATION_ID, and UIPATH_TENANT_ID default
to "" when unset. SpanReq.FolderKey is Guid? on the server — sending ""
causes a JSON deserialization exception ("could not convert to
System.Nullable[Guid]") which cascades into "The spans field is required"
from ASP.NET's model binding. Normalize all three to None so unset vars
serialize as null instead of an unparseable empty string.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…Processor thread boundary

ContextVar values are not propagated to background threads. The
BatchSpanProcessor exports spans in a worker thread where
ReferenceContextAccessor.get() always returns None, so Context.referenceHierarchy
was never populated in the wire payload.

Fix: register a span-start hook (_inject_reference_hierarchy) that runs
synchronously in on_start — same thread/context as the span creator — reads
the ambient ReferenceContext and stamps it as uipath.reference_hierarchy on the
span. The attribute travels with the span to the export thread.
otel_span_to_uipath_span now reads from the attribute instead of the ContextVar,
and pops it from attributes_dict so it does not appear in the wire Attributes field.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 10, 2026 19:10
@github-actions github-actions Bot added test:uipath-langchain Triggers tests in the uipath-langchain-python repository test:uipath-runtime test:uipath-integrations labels Jun 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new reference-hierarchy propagation mechanism for spans by introducing an immutable ReferenceContext carried via contextvars, stamping the hierarchy onto spans at start-time to survive BatchSpanProcessor thread boundaries, and wiring that stamped hierarchy into the UiPath span wire payload (Context.referenceHierarchy).

Changes:

  • Introduces ReferenceContext / ReferenceEntry plus a ReferenceContextAccessor backed by ContextVar.
  • Adds a span-start hook mechanism in core tracing processors and uses it in _span_utils.py to stamp uipath.reference_hierarchy at span creation time.
  • Extends UiPathSpan serialization to emit a top-level Context field and normalizes empty-string org/tenant/folder env vars to None.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/uipath/src/uipath/tracing/init.py Re-exports ReferenceContext* types from platform common tracing surface.
packages/uipath-platform/src/uipath/platform/common/_reference_context.py Adds the new immutable reference hierarchy model + ContextVar accessor + header (de)serialization.
packages/uipath-platform/src/uipath/platform/common/_span_utils.py Registers a span-start hook to stamp hierarchy and wires stamped hierarchy into UiPathSpan.Context.
packages/uipath-platform/src/uipath/platform/common/init.py Exposes ReferenceContext* from uipath.platform.common.
packages/uipath-core/src/uipath/core/tracing/processors.py Adds global span-start hook registration and invokes hooks in on_start.
packages/uipath-platform/tests/services/test_reference_context.py Adds unit tests for the new reference context model + span wiring behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/uipath-core/src/uipath/core/tracing/processors.py Outdated
Comment thread packages/uipath-platform/src/uipath/platform/common/_reference_context.py Outdated
Comment thread packages/uipath-platform/src/uipath/platform/common/_reference_context.py Outdated
Comment thread packages/uipath-platform/src/uipath/platform/common/_reference_context.py Outdated
Comment thread packages/uipath-platform/tests/services/test_reference_context.py Outdated
Comment thread packages/uipath-platform/tests/services/test_reference_context.py Outdated
Comment thread packages/uipath-core/src/uipath/core/tracing/processors.py Outdated
@saksharthakkar

Copy link
Copy Markdown
Contributor

You'll need to bump up the version here and then use it in other repos.

@saksharthakkar

Copy link
Copy Markdown
Contributor

Please make sure to address the review comments by Copilot and address these CI requirements
CI / check-dependency-bumps / check-dependency-bumps (pull_request)
CI / check-dependency-bumps / check-dependency-bumps (pull_request)Failing after 8s
CI / check-versions / check-version-availability (pull_request)
CI / check-versions / check-version-availability (pull_request)Failing after 6s
CI / lint / Lint (pull_request)
CI / lint / Lint (pull_request)Failing after 4s
Required
CI / lint / Lint uipath-platform (pull_request)
CI / lint / Lint uipath-platform (pull_request)Failing after 19s

jepadil23 and others added 4 commits July 6, 2026 00:42
Introduces an immutable, copy-on-write ReferenceContext (modelled after
service-common BaggageContext) that propagates the reference hierarchy
through all spans emitted to LLMOps/Traceview.

- `_reference_context.py`: ReferenceEntry, ReferenceContext (add /
  to_wire_list / from_baggage_header / to_baggage_header_value),
  ReferenceContextAccessor (ContextVar-backed, async-safe)
- `_span_utils.py`: UiPathSpan gains `context` field; to_dict() emits
  `Context.referenceHierarchy` when set; otel_span_to_uipath_span()
  reads ReferenceContextAccessor and builds the wire payload
- `uipath.tracing`: re-exports ReferenceEntry, ReferenceContext,
  ReferenceContextAccessor for downstream consumers
- Tests: 30 new tests covering immutability, wire format, baggage
  header round-trip, accessor lifecycle, and span wiring

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…Processor thread boundary

ContextVar values are not propagated to background threads. The
BatchSpanProcessor exports spans in a worker thread where
ReferenceContextAccessor.get() always returns None, so Context.referenceHierarchy
was never populated in the wire payload.

Fix: register a span-start hook (_inject_reference_hierarchy) that runs
synchronously in on_start — same thread/context as the span creator — reads
the ambient ReferenceContext and stamps it as uipath.reference_hierarchy on the
span. The attribute travels with the span to the export thread.
otel_span_to_uipath_span now reads from the attribute instead of the ContextVar,
and pops it from attributes_dict so it does not appear in the wire Attributes field.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Jul 6, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🚨 Heads up: uipath-integrations cross-tests are FAILING 🚨

Your changes may break one or more integrations in uipath-integrations-python:

  • uipath-openai-agents
  • uipath-google-adk
  • uipath-agent-framework
  • uipath-llamaindex
  • uipath-pydantic-ai

⚠️ These checks are NOT enforced by branch protection rules. Please review the failures before merging.

🔍 Inspect the failed run →

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

🚨 Heads up: uipath-langchain cross-tests are FAILING 🚨

Your changes may break the uipath-langchain-python integration.

⚠️ These checks are NOT enforced by branch protection rules. Please review the failures before merging.

🔍 Inspect the failed run →

factory = None
try:
trace_manager = UiPathTraceManager()
trace_manager.add_span_processor(ReferenceHierarchySpanProcessor())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if we can avoid duplication across multiple files
by putting something like this in common place
def create_trace_manager() -> UiPathTraceManager:
tm = UiPathTraceManager()
tm.add_span_processor(ReferenceHierarchySpanProcessor()) # one ordering choke point
return tm

return result

@staticmethod
def from_baggage_header(header_value: Optional[str]) -> "ReferenceContext":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this used any where?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:uipath-integrations test:uipath-langchain Triggers tests in the uipath-langchain-python repository

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants