feat(tracing): migrate span ingestion to v3 API with string enums#1684
feat(tracing): migrate span ingestion to v3 API with string enums#1684saksharthakkar wants to merge 19 commits into
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…vel StrEnums Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rEnum types Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…yLevel from platform.common
…string enums - Import SpanStatus from uipath.platform.common._span_utils (StrEnum) - Remove local int-based SpanStatus class and inner Status class - Update _build_url to /api/Traces/v3/spans - Update _determine_status() return type to SpanStatus (string values) - Update upsert_span status_override param type to Optional[SpanStatus] - Update debug log message to reference v3 path - Add 4 new tests verifying v3 URL and string enum status values - Fix VerbosityLevel.OFF assertion from int 6 to string "Off" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…m platform.common Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gration Fix ruff import-sort and formatting in span_utils tests and otel_exporters; add TestV3EndToEnd integration test asserting v3/spans URL and string enum values (Status="Ok", Source="CodedAgents") reach the HTTP layer end-to-end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…wboat doc Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onflicts - Keep both StrEnum imports and lru_cache import - Combine StrEnum types (v3 migration) with resolve_project_id() (stable span id from main) - Merge TestStrEnums with the _clear_id_cache fixture and constants imports Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016228TgPXqjJTxmZDY4Jtmd
…ions - Emit ReferenceVersion (was AgentVersion) — SpanV3Req has no AgentVersion field; agent version is carried by ReferenceVersion (pairs with ReferenceId) - Default org/tenant/folder Guid fields to None instead of "" so unset env vars omit cleanly rather than 400 the v3 ingest serializer - Bump uipath 2.11.12->2.11.13, uipath-platform 0.1.76->0.1.77 (versions already on PyPI) - ruff format test_otel_exporters.py Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016228TgPXqjJTxmZDY4Jtmd
uipath's tracing code now imports SpanStatus from uipath-platform, so a standalone uipath install must require the new 0.1.77. Satisfies the check-dependency-bumps CI gate for co-changed internal packages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016228TgPXqjJTxmZDY4Jtmd
🚨 Heads up:
|
Bump versions above main (uipath 2.11.14, uipath-platform 0.1.79): main published 2.11.13 and bumped platform to 0.1.78. Keep main's uipath-runtime>=0.11.4. Pin uipath -> uipath-platform>=0.1.79 for the dependency-bump gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016228TgPXqjJTxmZDY4Jtmd
5dd81eb to
e2e3d73
Compare
- _determine_status: GraphInterrupt is a HITL pause, map to SpanStatus.RUNNING
(restores v2 int-3==Running semantics; StatusEnum has no Interrupted member)
- SpanSource / _SOURCE_BY_INT: mirror the server's full 17-member SourceEnum
(0-16) so every server-known source round-trips; previously only {1,2,3,4,10}
were mapped and other ints were silently relabeled CodedAgents (data-fidelity
regression, since v3 rejects raw int forwarding)
- Unknown uipath.source int now logs a warning instead of silent relabel
- Tests: graph_interrupt expects Running; parametrized full int->source map;
unknown-int warning test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016228TgPXqjJTxmZDY4Jtmd
276c821 to
8639147
Compare
There was a problem hiding this comment.
Pull request overview
Migrates the Python SDK’s tracing/span ingestion to the LLM Observability v3 spans API, ensuring enum fields are sent as string values (per the v3 contract) and updating public exports and tests accordingly.
Changes:
- Introduce
StrEnum-backedSpanStatus,SpanSource,ExecutionType, and migrateVerbosityLeveltoStrEnum, including int→enum bridging for OTEL attribute integers. - Update
LlmOpsHttpExporterto post to/api/Traces/v3/spansand to use the newSpanStatustype throughout. - Update re-exports and expand tests to assert v3 URL usage and string-enum payloads end-to-end.
Reviewed changes
Copilot reviewed 9 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/uipath/uv.lock | Bumps local package versions in lockfile to match release increment. |
| packages/uipath/pyproject.toml | Bumps uipath version and updates uipath-platform minimum dependency. |
| packages/uipath/src/uipath/tracing/_otel_exporters.py | Switches exporter to v3 endpoint and uses SpanStatus string enums. |
| packages/uipath/src/uipath/tracing/_live_tracking_processor.py | Updates SpanStatus import and type annotations for status overrides. |
| packages/uipath/src/uipath/tracing/init.py | Re-exports SpanStatus from the new common enum source. |
| packages/uipath/tests/tracing/test_otel_exporters.py | Updates tests for v3 URL + string enums and adds an end-to-end v3 payload assertion. |
| packages/uipath-platform/uv.lock | Bumps uipath-platform version in lockfile. |
| packages/uipath-platform/pyproject.toml | Bumps uipath-platform version. |
| packages/uipath-platform/src/uipath/platform/common/_span_utils.py | Adds string enums + int→enum mappings and updates UiPathSpan serialization for v3. |
| packages/uipath-platform/src/uipath/platform/common/init.py | Exposes new public enum types (SpanStatus, SpanSource, ExecutionType, VerbosityLevel). |
| packages/uipath-platform/tests/services/test_span_utils.py | Updates/extends unit tests for StrEnum behavior, v3 field names, and string serialization. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- _enum_from_int helper now backs executionType/verbosityLevel/source so all three ignore bool (int subclass) identically — previously only source was guarded, so executionType=True/verbosityLevel=True still mapped to the value-1 member (Runtime/Trace) - to_dict now omits OrganizationId/TenantId/FolderKey when None instead of emitting null; corrected the stale comment that claimed they were omitted - Tests: bool-attributes-do-not-map; None Guid keys omitted from to_dict; set Guid keys still present Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016228TgPXqjJTxmZDY4Jtmd
status_override is SpanStatus | None; branch on 'is not None' to match the type contract rather than truthiness (Copilot review). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016228TgPXqjJTxmZDY4Jtmd
|
🚨 Heads up:
|
| 6: VerbosityLevel.OFF, | ||
| } | ||
|
|
||
| _SOURCE_BY_INT: dict[int, SpanSource] = { |
There was a problem hiding this comment.
todo: we should probably expose these enums as a package so there isn't a copy-paste dependency
| "AgentVersion": self.agent_version, | ||
| # v3 ingest (SpanV3Req) has no AgentVersion field; the agent version | ||
| # is carried by ReferenceVersion (pairs with ReferenceId above). | ||
| "ReferenceVersion": self.agent_version, |
There was a problem hiding this comment.
cc @jepadil23 are your referencehierarchy changes in yet?
| # Agent Builder runtime, which keeps interrupted runs Running | ||
| # (ConversationalEngineWorkflow waits via WaitConditionAsync; no | ||
| # terminal status). StatusEnum has no Interrupted member. | ||
| return SpanStatus.RUNNING |
There was a problem hiding this comment.
a little confused, where did Status.INTERRUPTED come from if it's not in the span schema?



Motivation
The UiPath LLM Observability backend is introducing V3 span APIs with insert-only ingestion semantics. The V3 endpoint rejects integer enum values — all status, source, verbosity, and execution type fields must be strings (e.g.
"Ok"not1). This PR migrates the Python SDK's span ingestion pipeline to conform to the V3 contract.Summary
_span_utils.py: ReplaceIntEnum-basedVerbosityLevelwithStrEnum; addSpanStatus,SpanSource,ExecutionTypeasStrEnumtypes with values matching C# server enum names exactly ("Ok","CodedAgents","Information","Runtime"). Add int→StrEnum lookup dicts for bridging raw OTEL attribute integers to enum members. UpdateUiPathSpanfield types fromintto the new enums. Updateotel_span_to_uipath_span()to produce string enum values._otel_exporters.py: Remove the old integerSpanStatusclass and innerStatusclass. ImportSpanStatusfrom_span_utils. Update_build_url()from/api/Traces/spans→/api/Traces/v3/spans. Update_determine_status()return type andupsert_span()parameter type._live_tracking_processor.py: UpdateSpanStatusimport to come from_span_utilsinstead of_otel_exporters. Update_upsert_span_asynctype annotation fromint | NonetoSpanStatus | None.platform/common/__init__.py: ExportExecutionType,SpanSource,SpanStatus(new public types).tracing/__init__.py: Re-exportSpanStatusfrom_span_utilsinstead of_otel_exporters.test_span_utils.pywith StrEnum unit tests and enum value assertions. Updatedtest_otel_exporters.pywith v3 URL assertions, string enum body assertions, and a newTestV3EndToEndintegration test.Verification in OR - hacked-coded
Test plan
packages/uipath-platform: 1212 passed, 0 failedpackages/uipath: 1866 passed, 0 failedmypy src testsclean on both packages (0 errors)ruff check+ruff format --checkclean on both packagesTestV3EndToEnd.test_export_posts_to_v3_url_with_string_enums— end-to-end: real OTel span →LlmOpsHttpExporter→ asserts POST tov3/spanswith"Status": "Ok"and"Source": "CodedAgents"as strings🤖 Generated with Claude Code