Skip to content

feat(platform): add GovernanceService for agenticgovernance_ ingress#1738

Open
viswa-uipath wants to merge 1 commit into
mainfrom
feat/gov-api-client
Open

feat(platform): add GovernanceService for agenticgovernance_ ingress#1738
viswa-uipath wants to merge 1 commit into
mainfrom
feat/gov-api-client

Conversation

@viswa-uipath

@viswa-uipath viswa-uipath commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Three-layer wiring that lets the agent runtime consume UiPath governance through protocols owned by uipath-core, with the HTTP implementation hidden in uipath-platform and registered at CLI startup.

  • uipath-core — declares GovernancePolicyProvider / GovernanceCompensationProvider protocols plus a module-level default-provider registry (set_/get_/reset_default_*_provider). Owns the wire-format models PolicyContext, PolicyResponse, FiredRule, GovernRequest.
  • uipath-platform — adds GovernanceService (sdk.governance) wrapping the org-scoped agenticgovernance_ ingress: retrieve_policy() and compensate() (plus async variants), satisfying both core protocols via get_policy(context) and compensate(request). Adds public resolve_trace_id(fallback) helper in common/_base_service.py so callers can capture the trace id on the hook thread before hopping to a background pool.
  • uipath CLI_governance_setup.register_governance_providers() instantiates GovernanceService and registers it as the default policy + compensation provider. Wired into cli_run / cli_debug startup. No-op when UIPATH_URL / UIPATH_ACCESS_TOKEN are unset (dev machines) or when UiPath() construction fails — governance must never block agent execution.
  • Bumps: uipath-core 0.5.20 → 0.5.21, uipath-platform 0.1.71 → 0.1.73, uipath 2.11.6 → 2.11.7.

Motivation

Replaces hand-rolled urllib HTTP in uipath-runtime-python PRs #121 and #123 per radu-mocanu's review comment: "tenant/org/access token etc. are platform concerns. The runtime's concern is just managing the policies."

The runtime can't import uipath-platform (dependency direction is the other way), so the protocols and default-provider registry live in uipath-core. The CLI binds the concrete platform implementation in at startup; the runtime never reaches across the layer boundary.

After this lands, the runtime gives up urllib, the browser-UA WAF workaround, manual UIPATH_ACCESS_TOKEN reads, manual org/tenant resolution, and the import-time uipath-platform dependency — BaseService provides all of that, and the runtime depends only on uipath-core protocols.

Design choices

  • Three-leg triangle (core protocols / platform impl / CLI wiring) rather than a direct uipath-platform import from runtime. Keeps uipath-core as the lowest layer; lets tests swap in fakes via set_default_*_provider.
  • runtime_checkable Protocols — any object exposing get_policy(context) / compensate(request) satisfies the contract. GovernanceService happens to be the platform-backed implementation but isn't load-bearing on the runtime side.
  • Fail-open registration — missing creds or construction failure leaves the registry empty; the runtime falls back to "no provider → no policies", same end state as before this PR.
  • URL composed directly (not via RequestSpec + Endpoint): the agenticgovernance_ ingress uses {origin}/{org_id_uuid}/agenticgovernance_/... — the org UUID, not the slug UiPathUrl.scope_url produces. Documented in _build_org_scoped_request.
  • is_conversational: bool | None rather than an enum: matches the runtime's existing _agent_is_conversational flag. The True/False/None → conversational/autonomous/omit wire mapping is an implementation detail of the service.
  • resolve_trace_id returns the value, doesn't mutate headers: the existing _inject_trace_context only injects the header. Compensation needs the id in the request body, and must resolve on the hook thread before the pool hop because OpenTelemetry context is thread-local.
  • @cached_property on sdk.governance: matches buckets/attachments/folders. Avoids fresh httpx.Client/AsyncClient per access in the background compensation loop.
  • PolicyResponse.mode: Optional[EnforcementMode] with lenient coercion: a @field_validator(mode="before") maps unknown wire values to None instead of raising, so a server-side mode addition can't break agent startup.

What stays in uipath-runtime-python

The runtime keeps submit_compensation's bounded thread pool, in-flight semaphore, drop-on-saturation, atexit shutdown, and disabled_guardrails(...) domain helper. The worker resolves the registered provider via get_default_compensation_provider() and calls provider.compensate(GovernRequest(...)).

Test plan

  • ruff check . / ruff format --check . — clean
  • mypy src tests — clean
  • pytest --no-cov — full suite green
  • 18 uipath-core tests (tests/governance/test_providers.py): protocol conformance (runtime_checkable accept/reject), PolicyContext defaults + extra-field tolerance, PolicyResponse defaults + parsing all three known EnforcementMode values + unknown-mode coercion to None, GovernRequest wire aliases (camelCase + intentional snake_case src_timestamp, optional job-context fields excluded when None), registry set/get/clear/reset, end-to-end dispatch.
  • 26 uipath-platform tests (tests/services/test_governance_service.py): policy fetch happy path, default field handling, header + bearer wiring, agentType query param (parameterized True/False), missing org/tenant validation errors, HTTP errors, async; compensate payload aliasing, job-context auto-fill, caller override precedence, caller empty-string preserved over env, omitted keys, HTTP error, missing org id, async; GovernanceService satisfies both protocols via get_policy / get_policy_async / compensate; 5 resolve_trace_id cases.
  • 5 uipath CLI tests (tests/cli/test_governance_setup.py): no-op when UIPATH_URL / UIPATH_ACCESS_TOKEN missing (each independently), both registered to the same GovernanceService instance when both set, re-registration overwrites the previous provider, construction failure swallowed silently.
  • Integration: runtime-side PRs tracer: fix lint #121/cli: pack should include mermaid files #123 to be refactored to consume the registered protocols instead of inline urllib — separate PRs.

🤖 Generated with Claude Code

Development Packages

uipath-core

[project]
dependencies = [
  # Exact version (copy-paste ready):
  "uipath-core==0.5.21.dev1017386862",

  # Any version from this PR (uncomment to use a range instead):
  # "uipath-core>=0.5.21.dev1017380000,<0.5.21.dev1017390000",
]

[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
publish-url = "https://test.pypi.org/legacy/"
explicit = true

[tool.uv.sources]
uipath-core = { index = "testpypi" }

uipath

[project]
dependencies = [
  # Exact version (copy-paste ready):
  "uipath==2.11.7.dev1017386862",

  # Any version from this PR (uncomment to use a range instead):
  # "uipath>=2.11.7.dev1017380000,<2.11.7.dev1017390000",
]

[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
publish-url = "https://test.pypi.org/legacy/"
explicit = true

[tool.uv.sources]
uipath = { index = "testpypi" }
uipath-platform = { index = "testpypi" }
uipath-core = { index = "testpypi" }

[tool.uv]
override-dependencies = ["uipath-platform==0.1.73.dev1017386862", "uipath-core==0.5.21.dev1017386862"]

uipath-platform

[project]
dependencies = [
  # Exact version (copy-paste ready):
  "uipath-platform==0.1.73.dev1017386862",

  # Any version from this PR (uncomment to use a range instead):
  # "uipath-platform>=0.1.73.dev1017380000,<0.1.73.dev1017390000",
]

[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
publish-url = "https://test.pypi.org/legacy/"
explicit = true

[tool.uv.sources]
uipath-platform = { index = "testpypi" }
uipath-core = { index = "testpypi" }

[tool.uv]
override-dependencies = ["uipath-core==0.5.21.dev1017386862"]

Copilot AI review requested due to automatic review settings June 22, 2026 09:28
@github-actions github-actions Bot added test:uipath-langchain Triggers tests in the uipath-langchain-python repository test:uipath-integrations labels Jun 22, 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 GovernanceService to uipath-platform to wrap the org-scoped agenticgovernance_ ingress (policy retrieval + compensating govern call), and exposes a new resolve_trace_id() helper so callers can capture the effective trace id before moving work off-thread.

Changes:

  • Introduces uipath.platform.governance.GovernanceService with sync/async retrieve_policy() and compensate() APIs, plus Pydantic request/response models.
  • Adds public resolve_trace_id(fallback) helper to uipath.platform.common for retrieving the current trace id as a normalized 32-char hex string.
  • Adds a comprehensive test suite for governance + trace-id resolution and bumps uipath-platform version 0.1.710.1.72.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/uipath-platform/tests/services/test_governance_service.py Adds coverage for governance policy retrieval, compensation payload behavior, and resolve_trace_id() normalization/fallbacks.
packages/uipath-platform/src/uipath/platform/governance/policy.py Defines PolicyResponse response model with safe defaults and extra-field tolerance.
packages/uipath-platform/src/uipath/platform/governance/compensate.py Defines FiredRule and GovernRequest models with wire-format aliases for compensation POST payloads.
packages/uipath-platform/src/uipath/platform/governance/_governance_service.py Implements the GovernanceService API, URL/header composition, agentType param mapping, and job-context auto-fill.
packages/uipath-platform/src/uipath/platform/governance/init.py Exposes governance service/models as a public module.
packages/uipath-platform/src/uipath/platform/common/_base_service.py Adds resolve_trace_id() helper alongside existing trace header injection logic.
packages/uipath-platform/src/uipath/platform/common/init.py Re-exports resolve_trace_id from uipath.platform.common.
packages/uipath-platform/src/uipath/platform/_uipath.py Adds UiPath.governance cached_property for convenient access from the main client.
packages/uipath-platform/pyproject.toml Bumps package version to 0.1.72.

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

@viswa-uipath viswa-uipath added the build:dev Create a dev build from the pr label Jun 22, 2026
viswa-uipath added a commit that referenced this pull request Jun 22, 2026
…nsate

- Regenerate uv.lock in uipath-platform and uipath after the
  0.1.71 -> 0.1.72 bump so `uv sync --locked` (CI lint check) passes.
- In `_build_govern_payload`, use `wire_key in payload` instead of
  truthiness — `exclude_none=True` already drops caller-None fields,
  so presence is the right "was it supplied?" signal. The previous
  truthiness check would silently replace a caller-supplied empty
  string with the env-backed UiPathConfig value, violating the
  documented precedence (caller > UiPathConfig > omit). Caught by
  Copilot review on PR #1738.
- Add regression test pinning the empty-string contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@viswa-uipath viswa-uipath force-pushed the feat/gov-api-client branch from 27d012b to cfc92c0 Compare June 22, 2026 12:59
@viswa-uipath viswa-uipath added build:dev Create a dev build from the pr and removed build:dev Create a dev build from the pr labels Jun 22, 2026
@viswa-uipath viswa-uipath force-pushed the feat/gov-api-client branch from cfc92c0 to cf5c49e Compare June 22, 2026 13:08
@viswa-uipath viswa-uipath added build:dev Create a dev build from the pr and removed build:dev Create a dev build from the pr labels Jun 22, 2026
@github-actions

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

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 →

@viswa-uipath viswa-uipath force-pushed the feat/gov-api-client branch from cf5c49e to c49cfc6 Compare June 22, 2026 13:33
@viswa-uipath viswa-uipath removed the build:dev Create a dev build from the pr label Jun 22, 2026
@viswa-uipath viswa-uipath force-pushed the feat/gov-api-client branch from c49cfc6 to 8e2c28c Compare June 22, 2026 15:02
Declare GovernancePolicyProvider / GovernanceCompensationProvider in
uipath-core so the runtime depends only on the lowest layer. Implement
both in uipath-platform via UiPathPlatformGovernanceProvider, backed by
the GovernanceService HTTP client.

Inject the providers into the runtime by construction rather than a
global registry: a new uipath._governance package owns the
GovernanceRuntime wrapper and a wrap_with_governance() helper that the
CLI (uipath run / debug) calls to wrap the delegate. The helper
fails open — when UIPATH_URL / UIPATH_ACCESS_TOKEN are unset or the
platform client cannot be built, the delegate is returned unchanged so
governance is never on the critical path for agent execution.

GovernanceRuntime caches the policy pack on the first execute/stream
call for the lifetime of the wrapper (one per CLI session). Policies
are tenant-stable; refresh requires constructing a fresh wrapper. The
cache flag is set on first attempt regardless of outcome, so a
transient fetch failure keeps the wrapper running policy-less in the
documented fail-open mode rather than retrying on every call.

Also adds resolve_trace_id() in platform common to capture the
canonical trace id before hopping off the OTel-context-owning thread.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@viswa-uipath viswa-uipath force-pushed the feat/gov-api-client branch from 8e2c28c to d4ca685 Compare June 22, 2026 17:46
@sonarqubecloud

Copy link
Copy Markdown

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 test:uipath-runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants