-
Notifications
You must be signed in to change notification settings - Fork 26
feat[PLT-105095]: changes for governance #1700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| """Generic adapter contracts for framework integrations. | ||
|
|
||
| This package holds only the abstract contracts — concrete adapter | ||
| implementations live in framework-specific plugin packages (e.g. | ||
| ``uipath-langchain``, ``uipath-openai``) that target the framework they | ||
| integrate with. Plugin packages register their concrete adapters with | ||
| the global :class:`AdapterRegistry` via the | ||
| ``uipath.governance.adapters`` entry-point group. | ||
|
|
||
| Public surface: | ||
|
|
||
| - :class:`BaseAdapter` – abstract base every adapter inherits from. | ||
| - :class:`GovernedAgentBase` – proxy base for governed agent wrappers. | ||
| - :class:`EvaluatorProtocol` – structural protocol the adapter expects | ||
| from any policy evaluator. | ||
| - :class:`AdapterRegistry` – ordered list of adapters that resolves | ||
| the first match for a given agent. | ||
| """ | ||
|
|
||
| from .base import BaseAdapter, GovernedAgentBase | ||
| from .evaluator import EvaluatorProtocol | ||
| from .registry import ( | ||
| AdapterRegistry, | ||
| get_adapter_registry, | ||
| reset_adapter_registry, | ||
| ) | ||
|
|
||
| __all__ = [ | ||
| "BaseAdapter", | ||
| "GovernedAgentBase", | ||
| "EvaluatorProtocol", | ||
| "AdapterRegistry", | ||
| "get_adapter_registry", | ||
| "reset_adapter_registry", | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| """Base adapter contracts for framework-specific integrations. | ||
|
|
||
| An adapter's job: | ||
|
|
||
| 1. Detect whether it can handle a given agent object. | ||
| 2. Attach hooks to that agent (framework-specific). | ||
| 3. Publish events to a policy evaluator when those hooks fire. | ||
|
|
||
| The evaluator subscribes to events and runs policy checks; it never | ||
| knows or cares which adapter fired the event. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from abc import ABC, abstractmethod | ||
| from typing import Any | ||
| from uuid import uuid4 | ||
|
|
||
| from .evaluator import EvaluatorProtocol | ||
|
|
||
|
|
||
| class BaseAdapter(ABC): | ||
| """Base class for framework-specific governance adapters.""" | ||
|
|
||
| #: Higher value = more specific = inserted earlier in the registry. | ||
| #: Plugin authors should set this above ``0`` on adapters that target | ||
| #: a narrower agent type than another already-registered adapter, so | ||
| #: the specific one wins ``can_handle`` resolution regardless of the | ||
| #: order in which plugins happen to be imported. Among adapters with | ||
| #: the same priority, registration order is preserved (stable). | ||
| priority: int = 0 | ||
|
|
||
| #: Set to True on a catch-all adapter that should always sort last in | ||
| #: the registry. The registry uses this flag (not the class name or | ||
| #: :attr:`priority`) to keep the fallback in last position when new | ||
| #: adapters register. | ||
| is_fallback: bool = False | ||
|
|
||
| @property | ||
| def name(self) -> str: | ||
| """Return adapter name for logging.""" | ||
| return self.__class__.__name__ | ||
|
|
||
| @abstractmethod | ||
| def can_handle(self, agent: Any) -> bool: | ||
| """Return True if this adapter knows how to hook into this agent type.""" | ||
|
|
||
| @abstractmethod | ||
| def attach( | ||
| self, | ||
| agent: Any, | ||
| agent_id: str, | ||
| session_id: str, | ||
| evaluator: EvaluatorProtocol, | ||
| ) -> Any: | ||
| """Attach governance hooks to the agent. | ||
|
|
||
| Args: | ||
| agent: The agent to govern. | ||
| agent_id: Unique identifier for the agent. | ||
| session_id: Session identifier for tracing. | ||
| evaluator: Policy evaluator implementing | ||
| :class:`EvaluatorProtocol`. | ||
|
|
||
| Returns: | ||
| A governed proxy (or the original agent with hooks installed). | ||
| """ | ||
|
|
||
| def detach(self, governed: Any) -> Any: | ||
| """Detach governance and return the original agent. | ||
|
|
||
| Default implementation uses the public :attr:`GovernedAgentBase.unwrapped` | ||
| contract; non-proxy adapters that return the original agent from | ||
| :meth:`attach` get back ``governed`` unchanged. | ||
| """ | ||
| return getattr(governed, "unwrapped", governed) | ||
|
|
||
| def _generate_trace_id(self) -> str: | ||
| """Generate a trace ID for governance events.""" | ||
| return str(uuid4()) | ||
|
|
||
|
|
||
| class GovernedAgentBase: | ||
| """Base class for governed agent proxies. | ||
|
|
||
| Provides common functionality for all governed agents: | ||
|
|
||
| - Stores reference to original agent | ||
| - Forwards unknown attributes to original agent | ||
| - Tracks governance metadata | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| agent: Any, | ||
| adapter: BaseAdapter, | ||
| agent_id: str, | ||
| session_id: str, | ||
| evaluator: EvaluatorProtocol, | ||
| ) -> None: | ||
| """Initialize with the wrapped agent and governance metadata.""" | ||
| self._agent = agent | ||
| self._adapter = adapter | ||
| self._agent_id = agent_id | ||
| self._session_id = session_id | ||
| self._evaluator = evaluator | ||
| self._trace_id = adapter._generate_trace_id() | ||
|
|
||
| @property | ||
| def unwrapped(self) -> Any: | ||
| """Get the original unwrapped agent.""" | ||
| return self._agent | ||
|
|
||
| def __getattr__(self, name: str) -> Any: | ||
| """Forward attribute access to the original agent.""" | ||
| return getattr(self._agent, name) |
99 changes: 99 additions & 0 deletions
99
packages/uipath-core/src/uipath/core/adapters/evaluator.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| """Structural contract for the policy evaluator an adapter talks to. | ||
|
|
||
| Framework adapters call into a policy evaluator at each lifecycle hook. | ||
| Concrete evaluator implementations (the native runtime evaluator, a | ||
| Microsoft AGT bridge, a composite, …) live in packages outside | ||
| ``uipath-core`` — adapters depend only on this structural protocol so | ||
| they can be swapped against any of them without code change. | ||
|
|
||
| ``EvaluatorProtocol`` is a :class:`typing.Protocol` so any class whose | ||
| methods match the signatures below satisfies the contract without | ||
| inheritance. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from typing import Any, Protocol, runtime_checkable | ||
|
|
||
|
|
||
| @runtime_checkable | ||
| class EvaluatorProtocol(Protocol): | ||
| """Structural protocol an adapter expects from a policy evaluator. | ||
|
|
||
| Return types are intentionally :class:`typing.Any`: the concrete | ||
| audit record shape lives in the plugin package that owns the | ||
| evaluator and the policy model. Adapters in that package cast the | ||
| return value back to the concrete type they know. | ||
| """ | ||
|
|
||
| def evaluate_before_agent( | ||
| self, | ||
| agent_input: str, | ||
| agent_name: str, | ||
| runtime_id: str, | ||
| trace_id: str, | ||
| model_name: str = "", | ||
| **kwargs: Any, | ||
| ) -> Any: | ||
| """Evaluate BEFORE_AGENT rules.""" | ||
| ... | ||
|
|
||
| def evaluate_after_agent( | ||
| self, | ||
| agent_output: str, | ||
| agent_name: str, | ||
| runtime_id: str, | ||
| trace_id: str, | ||
| **kwargs: Any, | ||
| ) -> Any: | ||
| """Evaluate AFTER_AGENT rules.""" | ||
| ... | ||
|
|
||
| def evaluate_before_model( | ||
| self, | ||
| model_input: str, | ||
| agent_name: str, | ||
| runtime_id: str, | ||
| trace_id: str, | ||
| messages: list[dict[str, Any]] | None = None, | ||
| model_name: str = "", | ||
| **kwargs: Any, | ||
| ) -> Any: | ||
| """Evaluate BEFORE_MODEL rules.""" | ||
| ... | ||
|
|
||
| def evaluate_after_model( | ||
| self, | ||
| model_output: str, | ||
| agent_name: str, | ||
| runtime_id: str, | ||
| trace_id: str, | ||
| **kwargs: Any, | ||
| ) -> Any: | ||
| """Evaluate AFTER_MODEL rules.""" | ||
| ... | ||
|
|
||
| def evaluate_tool_call( | ||
| self, | ||
| tool_name: str, | ||
| tool_args: dict[str, Any], | ||
| agent_name: str, | ||
| runtime_id: str, | ||
| trace_id: str, | ||
| session_state: dict[str, Any] | None = None, | ||
| **kwargs: Any, | ||
| ) -> Any: | ||
| """Evaluate TOOL_CALL rules.""" | ||
| ... | ||
|
|
||
| def evaluate_after_tool( | ||
| self, | ||
| tool_name: str, | ||
| tool_result: str, | ||
| agent_name: str, | ||
| runtime_id: str, | ||
| trace_id: str, | ||
| **kwargs: Any, | ||
| ) -> Any: | ||
| """Evaluate AFTER_TOOL rules.""" | ||
| ... |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.