feat(platform): add GovernanceService for agenticgovernance_ ingress#1738
feat(platform): add GovernanceService for agenticgovernance_ ingress#1738viswa-uipath wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
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.GovernanceServicewith sync/asyncretrieve_policy()andcompensate()APIs, plus Pydantic request/response models. - Adds public
resolve_trace_id(fallback)helper touipath.platform.commonfor 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-platformversion0.1.71→0.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.
…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>
27d012b to
cfc92c0
Compare
cfc92c0 to
cf5c49e
Compare
🚨 Heads up:
|
🚨 Heads up:
|
cf5c49e to
c49cfc6
Compare
c49cfc6 to
8e2c28c
Compare
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>
8e2c28c to
d4ca685
Compare
|



Summary
Three-layer wiring that lets the agent runtime consume UiPath governance through protocols owned by
uipath-core, with the HTTP implementation hidden inuipath-platformand registered at CLI startup.uipath-core— declaresGovernancePolicyProvider/GovernanceCompensationProviderprotocols plus a module-level default-provider registry (set_/get_/reset_default_*_provider). Owns the wire-format modelsPolicyContext,PolicyResponse,FiredRule,GovernRequest.uipath-platform— addsGovernanceService(sdk.governance) wrapping the org-scopedagenticgovernance_ingress:retrieve_policy()andcompensate()(plus async variants), satisfying both core protocols viaget_policy(context)andcompensate(request). Adds publicresolve_trace_id(fallback)helper incommon/_base_service.pyso callers can capture the trace id on the hook thread before hopping to a background pool.uipathCLI —_governance_setup.register_governance_providers()instantiatesGovernanceServiceand registers it as the default policy + compensation provider. Wired intocli_run/cli_debugstartup. No-op whenUIPATH_URL/UIPATH_ACCESS_TOKENare unset (dev machines) or whenUiPath()construction fails — governance must never block agent execution.uipath-core0.5.20 → 0.5.21,uipath-platform0.1.71 → 0.1.73,uipath2.11.6 → 2.11.7.Motivation
Replaces hand-rolled
urllibHTTP inuipath-runtime-pythonPRs #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 inuipath-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, manualUIPATH_ACCESS_TOKENreads, manual org/tenant resolution, and the import-timeuipath-platformdependency —BaseServiceprovides all of that, and the runtime depends only onuipath-coreprotocols.Design choices
uipath-platformimport from runtime. Keepsuipath-coreas the lowest layer; lets tests swap in fakes viaset_default_*_provider.runtime_checkableProtocols — any object exposingget_policy(context)/compensate(request)satisfies the contract.GovernanceServicehappens to be the platform-backed implementation but isn't load-bearing on the runtime side.RequestSpec+Endpoint): theagenticgovernance_ingress uses{origin}/{org_id_uuid}/agenticgovernance_/...— the org UUID, not the slugUiPathUrl.scope_urlproduces. Documented in_build_org_scoped_request.is_conversational: bool | Nonerather than an enum: matches the runtime's existing_agent_is_conversationalflag. TheTrue/False/None → conversational/autonomous/omitwire mapping is an implementation detail of the service.resolve_trace_idreturns the value, doesn't mutate headers: the existing_inject_trace_contextonly 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_propertyonsdk.governance: matchesbuckets/attachments/folders. Avoids freshhttpx.Client/AsyncClientper access in the background compensation loop.PolicyResponse.mode: Optional[EnforcementMode]with lenient coercion: a@field_validator(mode="before")maps unknown wire values toNoneinstead of raising, so a server-side mode addition can't break agent startup.What stays in
uipath-runtime-pythonThe runtime keeps
submit_compensation's bounded thread pool, in-flight semaphore, drop-on-saturation, atexit shutdown, anddisabled_guardrails(...)domain helper. The worker resolves the registered provider viaget_default_compensation_provider()and callsprovider.compensate(GovernRequest(...)).Test plan
ruff check ./ruff format --check .— cleanmypy src tests— cleanpytest --no-cov— full suite greentests/governance/test_providers.py): protocol conformance (runtime_checkableaccept/reject),PolicyContextdefaults + extra-field tolerance,PolicyResponsedefaults + parsing all three knownEnforcementModevalues + unknown-mode coercion toNone,GovernRequestwire aliases (camelCase + intentional snake_casesrc_timestamp, optional job-context fields excluded whenNone), registry set/get/clear/reset, end-to-end dispatch.tests/services/test_governance_service.py): policy fetch happy path, default field handling, header + bearer wiring,agentTypequery 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;GovernanceServicesatisfies both protocols viaget_policy/get_policy_async/compensate; 5resolve_trace_idcases.tests/cli/test_governance_setup.py): no-op whenUIPATH_URL/UIPATH_ACCESS_TOKENmissing (each independently), both registered to the sameGovernanceServiceinstance when both set, re-registration overwrites the previous provider, construction failure swallowed silently.urllib— separate PRs.🤖 Generated with Claude Code
Development Packages
uipath-core
uipath
uipath-platform