Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions docs/runtime-wrapper-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Governance Integration Point

`uipath-runtime` wraps runtimes with governance via a single direct
function, `apply_governance_wrapper`, gated by the
`EnablePythonGovernanceChecker` feature flag.

Governance contracts (feature-flag, exceptions, models) live in
`uipath.core.governance` (in `uipath-core`); the runtime-side wrapper
lives here in `uipath.runtime.governance`. Runtime has **no separate
`uipath-governance` dependency** — the contracts namespace is always
available because `uipath-core` is already a hard dep. When the flag
is off, `uipath.runtime.governance.wrapper` is **not imported** — its
transitive cost stays off the startup path.

## How it works

```
UiPathRuntimeFactoryRegistry.get(...)
↓ returns
UiPathWrappedRuntimeFactory.new_runtime(...)
↓ calls
apply_governance_wrapper(runtime, context, runtime_id)
if _is_governance_enabled():
from uipath.runtime.governance.wrapper import governance_wrapper # lazy
return governance_wrapper(runtime, context, runtime_id)
else:
return runtime # unwrapped, no governance import
```

## Feature flag

| Setting | Effect |
|---|---|
| `FeatureFlags.configure_flags({"EnablePythonGovernanceChecker": True})` (typically via gitops) | Governance is applied |
| `UIPATH_FEATURE_EnablePythonGovernanceChecker=true` env var | Governance is applied (fallback when no programmatic config) |
| Neither set | Governance **not** applied; `uipath.runtime.governance.wrapper` is **not imported** |

Resolution and fallback semantics come from `uipath-core`'s
`FeatureFlags.is_flag_enabled(..., default=False)`. Programmatic
configuration beats env var.

## API

```python
from uipath.runtime import (
GOVERNANCE_FEATURE_FLAG, # "EnablePythonGovernanceChecker"
apply_governance_wrapper, # the call-site
)
```

`apply_governance_wrapper(runtime, context, runtime_id)` is an
`async` function. It returns the original runtime untouched when the
flag is off or when the wrapper itself raises — governance failures
must never break agent execution.

## Why deferred-import matters

When the flag is off, `apply_governance_wrapper` returns before the
`from uipath.runtime.governance.wrapper import governance_wrapper` line
ever runs. That keeps governance's transitive imports — audit,
evaluator, OpenTelemetry, the policy index — entirely off the startup
hot path.

## Testing

Force the flag on/off per test via `FeatureFlags`:

```python
from uipath.core.feature_flags import FeatureFlags
from uipath.runtime.wrapper import GOVERNANCE_FEATURE_FLAG

# Force enable
FeatureFlags.configure_flags({GOVERNANCE_FEATURE_FLAG: True})

# Force disable
FeatureFlags.configure_flags({GOVERNANCE_FEATURE_FLAG: False})

# Reset (typically in a teardown fixture)
FeatureFlags.reset_flags()
```

Use `sys.modules` patching to stub `uipath.runtime.governance.wrapper`
when you need to assert against the wrapper invocation without
actually importing it — see `tests/test_wrapper.py` for the fixture.
7 changes: 7 additions & 0 deletions src/uipath/runtime/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
)
from uipath.runtime.schema import UiPathRuntimeSchema
from uipath.runtime.storage import UiPathRuntimeStorageProtocol
from uipath.runtime.wrapper import (
GOVERNANCE_FEATURE_FLAG,
apply_governance_wrapper,
)

__all__ = [
"UiPathExecuteOptions",
Expand Down Expand Up @@ -73,4 +77,7 @@
"UiPathResumeTriggerName",
"UiPathChatProtocol",
"UiPathChatRuntime",
# Governance integration (direct, FF-gated, lazy import)
"GOVERNANCE_FEATURE_FLAG",
"apply_governance_wrapper",
]
Loading
Loading