Skip to content

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

Merged
viswa-uipath merged 1 commit into
mainfrom
feat/gov-api-client
Jun 23, 2026
Merged

feat(platform): add GovernanceService for agenticgovernance_ ingress#1738
viswa-uipath merged 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

Platform-side foundation for UiPath agent governance — protocols in uipath-core and a concrete GovernanceService + provider adapter in uipath-platform. The runtime-side wrapper that consumes these will land in uipath-runtime in a follow-up PR.

  • uipath-core — declares GovernancePolicyProvider / GovernanceCompensationProvider protocols (runtime_checkable) alongside EvaluatorProtocol. Owns the wire-format models PolicyContext, PolicyResponse, FiredRule, GovernRequest. No registry or concrete provider — uipath-core does not depend on uipath-platform or uipath-runtime.
  • uipath-platform — adds GovernanceService (sdk.governance) for the org-scoped agenticgovernance_ ingress with public ergonomic methods:
    • retrieve_policy(*, is_conversational=None) + async — GET the tenant policy pack.
    • compensate(*, hook, validators, rules, data, trace_id, src_timestamp, agent_name, runtime_id, folder_key=None, job_key=None, process_key=None, reference_id=None, agent_version=None) + async — POST the compensating /runtime/govern call; builds GovernRequest internally; job-context fields auto-fill from UiPathConfig with caller values (including empty strings) taking precedence over the env-backed fallback.
    • Honors UIPATH_SERVICE_URL_AGENTICGOVERNANCE for local dev — the override path injects routing headers since the platform router is bypassed.
  • uipath-platform — adds UiPathPlatformGovernanceProvider as the dedicated adapter that satisfies both core protocols by delegating to the service. Runtime consumers receive this object via constructor injection without importing uipath-platform directly. Construct via UiPathPlatformGovernanceProvider(service=UiPath().governance) or by passing config / execution_context.
  • uipath-platform — 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.

Bumps: uipath-core 0.5.20 → 0.5.21, uipath-platform 0.1.71 → 0.1.73, uipath 2.11.6 → 2.11.9.

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 live in uipath-core. The runtime layer will accept the provider via constructor injection — no global registry, no hidden imports across the layer boundary.

After this lands (and the follow-up runtime PR), 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 + a constructor-injected provider.

Design choices

  • Protocols in uipath-core, concrete provider in uipath-platform — keeps uipath-core as the lowest layer with no upward dependencies; runtime consumers depend only on the protocols. No module-level provider registry — providers move via constructor injection.
  • runtime_checkable Protocols — any object exposing get_policy(context) / compensate(request) satisfies the contract.
  • Public SDK takes individual params; provider takes the objectcompensate(*, hook, ...) is the ergonomic SDK shape that matches retrieve_policy's pattern and stays consistent with the rest of the platform services. The internal _compensate(request) is the worker the provider adapter calls to satisfy the protocol shape without unpacking.
  • UiPathPlatformGovernanceProvider is the dedicated adapter — implements both core protocols by delegating to GovernanceService. The service is the SDK surface; the provider is the runtime-facing contract.
  • 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.
  • Honors UIPATH_SERVICE_URL_AGENTICGOVERNANCE for local dev_build_org_scoped_request resolves the override itself (mirroring what BaseService.request does for paths that fit scope_url) and injects routing headers, because the org-UUID URL shape can't go through the standard scope_url flow.
  • 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: EnforcementMode | None 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.

Out of scope (follow-up PRs)

  • Runtime wrapper: GovernanceRuntime (calls provider.get_policy() at execute/stream start, exposes compensate() to agent code) will land in uipath-runtime, not here. CLI wiring (cli_run / cli_debug constructing the runtime with the injected provider) lives there too.
  • Removing the EnablePythonGovernanceChecker feature flag: kept in place until test-pypi rollout finishes; will be removed in a separate PR.

Test plan

  • ruff check . / ruff format --check . — clean across all three packages
  • mypy src tests — clean
  • pytest --no-cov — full suite green
  • 31 uipath-core tests (tests/governance/test_providers.py): protocol conformance (runtime_checkable accept/reject), wire-format models (defaults, camelCase aliasing + intentional snake_case src_timestamp, optional job-context excluded when None, lenient EnforcementMode coercion).
  • 37 uipath-platform tests across tests/services/test_governance_service.py (28) and tests/services/test_governance_provider.py (9): policy fetch happy path / defaults / header + bearer wiring / agentType query param (parameterized True/False) / missing org-tenant / HTTP errors / async; compensate payload aliasing / job-context auto-fill from env / caller override precedence / caller empty-string preserved over env / omitted keys / HTTP errors / async; GovernanceService.get_policy satisfies the policy protocol; UiPathPlatformGovernanceProvider construction (existing service, built-from-config, requires service or full kwargs) + both-protocol conformance + delegation; UIPATH_SERVICE_URL_AGENTICGOVERNANCE redirects both policy fetch and compensate POST; 5 resolve_trace_id cases.
  • Integration: runtime-side PRs (uipath-runtime-python tracer: fix lint #121/cli: pack should include mermaid files #123) to be refactored to consume the constructor-injected provider — separate PRs.

🤖 Generated with Claude Code

Development Packages

Dev-version numbers below are tied to a previous CI build; the next CI run on this branch will regenerate them.

uipath-core

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

  # 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.9.dev1017386896",

  # Any version from this PR (uncomment to use a range instead):
  # "uipath>=2.11.9.dev1017380000,<2.11.9.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.dev1017386896", "uipath-core==0.5.21.dev1017386896"]

uipath-platform

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

  # 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.dev1017386896"]

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 →

@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 4 times, most recently from 41bd919 to 08e0d09 Compare June 23, 2026 04:19
Comment thread packages/uipath-core/src/uipath/core/governance/providers.py Outdated
Comment thread packages/uipath-core/src/uipath/core/governance/providers.py Outdated
Comment thread packages/uipath-core/src/uipath/core/governance/providers.py Outdated
@viswa-uipath viswa-uipath force-pushed the feat/gov-api-client branch from 08e0d09 to 6b9a74a Compare June 23, 2026 08:33
Comment thread packages/uipath-core/src/uipath/core/governance/providers.py
Comment thread packages/uipath/src/uipath/_governance/setup.py Outdated
Comment thread packages/uipath-platform/src/uipath/platform/governance/_governance_service.py Outdated
Comment thread packages/uipath-platform/src/uipath/platform/governance/_governance_service.py Outdated
Comment thread packages/uipath-platform/src/uipath/platform/_uipath.py Outdated
Comment thread packages/uipath/src/uipath/_governance/setup.py Outdated
Comment thread packages/uipath/src/uipath/_governance/runtime.py Outdated
Comment thread packages/uipath-core/src/uipath/core/governance/providers.py
Comment thread packages/uipath-platform/tests/services/test_governance_provider.py Outdated
…governance_ ingress

Declare GovernancePolicyProvider / GovernanceCompensationProvider in
uipath-core alongside EvaluatorProtocol — runtime consumers depend on
the lowest layer; concrete providers live outside this package.

Add GovernanceService in uipath-platform for the agenticgovernance_
ingress:

- retrieve_policy(*, is_conversational=None) — GET the tenant policy
  pack.
- compensate(*, hook, validators, rules, data, trace_id, ...) — POST
  the compensating /runtime/govern call when a guardrail_fallback rule
  matches; job-context fields auto-fill from UiPathConfig with caller
  values (including empty strings) taking precedence over the
  env-backed fallback.
- Honors UIPATH_SERVICE_URL_AGENTICGOVERNANCE for local dev — the
  override path injects routing headers since the platform router is
  bypassed.

Add UiPathPlatformGovernanceProvider as the dedicated adapter that
satisfies both core protocols by delegating to the service; runtime
consumers receive this object via constructor injection without
importing uipath-platform directly.

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

The runtime-side wrapper that consumes these protocols is intentionally
not in this PR — it will land in uipath-runtime in a follow-up.

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 acec4dd to 39bfaba Compare June 23, 2026 10:50
@sonarqubecloud

Copy link
Copy Markdown

@viswa-uipath viswa-uipath added the build:dev Create a dev build from the pr label Jun 23, 2026
@viswa-uipath viswa-uipath merged commit 968e4ae into main Jun 23, 2026
258 of 261 checks passed
@viswa-uipath viswa-uipath deleted the feat/gov-api-client branch June 23, 2026 11:15
@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 →

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

Labels

build:dev Create a dev build from the pr 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.

3 participants