Skip to content

feat: Governance runtime implementation#899

Open
viswa-uipath wants to merge 10 commits into
mainfrom
feat/governance
Open

feat: Governance runtime implementation#899
viswa-uipath wants to merge 10 commits into
mainfrom
feat/governance

Conversation

@viswa-uipath

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

Copy link
Copy Markdown

Summary

Adds a LangChain / LangGraph governance adapter to uipath-langchain. It lets UiPath governance evaluate what a LangChain chain or LangGraph agent does at the model and tool level, and block disallowed actions, without the agent author writing governance code. This package contains only the LangChain-specific bridge.

What it does

  • Detects LangChain Runnable and LangGraph CompiledStateGraph objects and wraps them with a governance proxy.
  • Hooks the model and tool lifecycle via LangChain's callback system and maps each event to a governance check:
    LangChain event Governance hook
    on_llm_start / on_chat_model_start BEFORE_MODEL
    on_llm_end AFTER_MODEL
    on_tool_start TOOL_CALL
    on_tool_end AFTER_TOOL
  • Enforces by letting a GovernanceBlockException (raised on a DENY decision) propagate to stop the model call or tool. Any other error in the hook is logged and swallowed, so a governance failure cannot break an otherwise-healthy run.

What it does not do

  • Does not fire agent-level boundaries (BEFORE_AGENT / AFTER_AGENT); those are owned by the governance host.
  • Does not depend on platform, auth, transport, or runtime internals — only on the shared governance contracts.

Development Package

  • Use uipath pack --nolock to get the latest dev build from this PR (requires version range).
  • Add this package as a dependency in your pyproject.toml:
[project]
dependencies = [
  # Exact version:
  "uipath-langchain==0.13.3.dev1008995004",

  # Any version from PR
  "uipath-langchain>=0.13.3.dev1008990000,<0.13.3.dev1009000000"
]

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

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

[tool.uv]
override-dependencies = [
    "uipath-langchain>=0.13.3.dev1008990000,<0.13.3.dev1009000000",
]

Copilot AI review requested due to automatic review settings June 9, 2026 13:53

Copilot AI left a comment

Copy link
Copy Markdown

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 LangChain/LangGraph governance integration layer to uipath-langchain, wiring UiPath’s governance evaluator into LangChain callbacks and exposing registration via an entry point so uipath-runtime can discover the adapter.

Changes:

  • Introduces a LangChainAdapter + governed proxy wrapper and a callback handler that calls EvaluatorProtocol hooks for model/tool events.
  • Adds governance adapter registration (uipath.governance.adapters entry point) with import-time idempotent self-registration.
  • Updates dependency/lock configuration to pull in updated uipath-core and a dev/test build of uipath-runtime (currently with non-portable lockfile sources).

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 9 comments.

File Description
src/uipath_langchain/governance/adapter.py Implements the LangChain/LangGraph adapter, wrapper, and callback handler that triggers governance evaluations.
src/uipath_langchain/governance/__init__.py Registers the governance adapter on import and exposes an entry-point registration function.
pyproject.toml Adds governance adapter entry point; bumps uipath-core; pins uipath-runtime to a dev build and adds a uv TestPyPI source override.
uv.lock Updates locked dependencies, but currently includes a machine-local Windows editable uipath-runtime source.

Comment thread src/uipath_langchain/governance/adapter.py Outdated
Comment thread src/uipath_langchain/governance/adapter.py Outdated
Comment thread src/uipath_langchain/governance/adapter.py Outdated
Comment thread src/uipath_langchain/governance/adapter.py Outdated
Comment thread src/uipath_langchain/governance/adapter.py Outdated
Comment thread src/uipath_langchain/governance/callbacks.py
Comment thread src/uipath_langchain/governance/adapter.py Outdated
Comment thread pyproject.toml Outdated
Comment thread pyproject.toml Outdated
Registers a LangChain/LangGraph adapter with the uipath-core adapter
registry so GovernanceRuntime can attach BEFORE_MODEL / AFTER_MODEL /
TOOL_CALL / AFTER_TOOL hooks via LangChain's callback system. Exposed
as a uipath.governance.adapters entry point and self-registers on
import. Bumps uipath-core to 0.6.x and uipath-runtime to 0.11.x to
pick up the new adapter contracts.

Key behaviour:
- BEFORE_MODEL is scoped to the latest message only. Earlier passes
  concatenated the entire prompt history, which re-fired violations
  from older turns on every subsequent LLM call. The callback now
  takes the last entry of the last batched prompt (mirrors the
  BEFORE_AGENT latest_only contract in uipath-runtime).
- List-of-blocks content (multimodal, OpenAI function-call, Anthropic
  tool_use, Claude extended thinking) is walked via _extract_block_text
  instead of str(msg.content), so structured shapes produce clean text
  with no dict-repr noise that breaks regex/field rules.
- Both on_chat_model_start and on_llm_start cap extracted input at
  _BEFORE_MODEL_TEXT_CAP = 64000 (matches the runtime side).
- register_governance_adapter is idempotent — uses the AdapterRegistry
  instance state as the single source of truth (no module-level flag).

Packaging:
- prerelease = "allow" plus override-dependencies for uipath-runtime
  under [tool.uv] so the dev pin can satisfy the umbrella's stable-only
  constraint. Re-locked so uipath-runtime resolves from testpypi rather
  than the local editable Windows path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aditik0303 and others added 2 commits June 22, 2026 16:41
Revert uipath-runtime to the stable range (>=0.11.0,<0.12.0) and drop the
temporary testpypi source, prerelease allowance, and override-dependencies
that were only needed for in-PR testing. Version will be bumped to the
governance-bearing release once uipath-runtime goes live publicly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve conflicts in pyproject.toml (take main's dependency version bumps:
uipath<2.12.0, uipath-core>=0.5.20, uipath-platform>=0.1.71, version 0.12.3;
keep the governance adapter entry-point) and regenerate uv.lock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
except ImportError:
pass

# Duck-typed fallback: anything with invoke + ainvoke that isn't

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this broad duck-typed fallback. a LangChain adapter should only claim LangChain/LangGraph objects, or an explicit marker/registration path. invoke + ainvoke is too generic and can attach LangChain governance to other frameworks that use the same method names.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — dropped the duck-typed fallback; can_handle now only claims a LangGraph CompiledStateGraph or a langchain_core Runnable.

logger.debug("Registered uipath-langchain governance adapter")


# Side-effect registration on module import.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove side-effect registration on import. this package already declares the uipath.governance.adapters entry point, so adapter registration should happen through the governance adapter discovery path. importing this module should not mutate the global registry.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — removed the import-time registration; the adapter is registered only through the uipath.governance.adapters entry-point discovery path. Importing the module no longer mutates the global registry.

- ``on_llm_start`` / ``on_chat_model_start`` / ``on_llm_end`` → BEFORE_MODEL / AFTER_MODEL
- ``on_tool_start`` / ``on_tool_end`` → TOOL_CALL / AFTER_TOOL

Chain-level boundaries (BEFORE_AGENT / AFTER_AGENT) are intentionally

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep this package focused on the LangChain adapter. remove references that tie it to a specific runtime wrapper location. uipath-langchain should implement the framework-specific hook bridge; runtime/core should discover and call it, not the adapter documenting runtime internals.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — the docstring now refers to the generic "governance host" and no longer documents the runtime wrapper / its internals.

aditik0303 and others added 3 commits June 23, 2026 18:03
- can_handle: drop the broad duck-typed invoke/ainvoke fallback; only
  claim LangChain Runnable / LangGraph CompiledStateGraph objects.
- __init__: remove side-effect registration on import; the adapter is
  registered only through the uipath.governance.adapters entry point /
  discovery path. Importing the module no longer mutates the registry.
- docstrings/comments: stop documenting runtime-internal locations
  (GovernanceRuntime, uipath.runtime.governance.wrapper); refer to the
  governance host generically so this package stays focused on the
  LangChain hook bridge.
- tests: update can_handle expectations to match (duck-typed and
  import-failure cases now return False).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Rename unused LangChain-callback parameter serialized -> _serialized in
  on_llm_start / on_chat_model_start (S1172).
- Reduce cognitive complexity (S3776): extract _blocks_to_text from
  _latest_message_input and _collect_generations_text from on_llm_end.
  Behavior unchanged; covered by existing tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract the shared "accumulate text up to the scan cap" loop into
_join_within_cap, used by both _blocks_to_text and
_collect_generations_text. This removes the nested-loop cognitive
complexity Sonar flagged on _collect_generations_text (17 -> under 15).
Behavior unchanged; existing tests cover both paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment on lines +127 to +130
# Only LangChain/LangGraph objects are claimed. No duck-typed
# fallback: matching on generic ``invoke`` / ``ainvoke`` would let
# this adapter attach to other frameworks that share those method
# names.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for this comment now. this just shows tech debt (what you just removed)

@aditik0303 aditik0303 Jun 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done removed.

self._tool_runs.pop(str(run_id), None)
logger.warning("Tool error in governed session %s: %s", self._session_id, error)

# Chain-level callbacks (on_chain_start / on_chain_end / on_chain_error)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this comment. the module docstring and the ignore_chain setting already make it clear that chain callbacks are not handled here.

general note for this file: remove comments when the code is self explanatory. keep comments concise only when they explain current intent or a non-obvious constraint, not removed behavior / debt history.

@aditik0303 aditik0303 Jun 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done removed

run_inline: bool = True
raise_error: bool = False
ignore_llm: bool = False
# Chain-level events (BEFORE_AGENT / AFTER_AGENT) are owned by the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shorten this comment to the invariant: chain-level events are owned by the governance host, so this handler skips them to avoid duplicate boundary evaluations. no need to mention absent methods / AttributeError noise.

@aditik0303 aditik0303 Jun 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

aditik0303 and others added 3 commits June 24, 2026 16:32
Address radu's review on the LangChain adapter: drop comments that document removed behavior / tech debt and keep code self-explanatory.
- can_handle: remove the 'no duck-typed fallback' explanatory comment (the code already only claims LangChain/LangGraph).
- ignore_chain descriptor: shorten to the invariant (chain events owned by the governance host → skipped to avoid duplicate boundary evaluations); drop the AttributeError-noise wording.
- module docstring: drop the same AttributeError-noise tail.
- remove the dangling end-of-file comment re-explaining why chain callbacks aren't implemented (docstring + ignore_chain already cover it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove comments that only restate the code: the two isinstance() labels in can_handle and the 'copy to a list' note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@radu-mocanu radu-mocanu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please squash into 1 commit and rebase on top of main. make sure to bump the uipath-langchain version and regenerate the uv.lock

@viswa-uipath viswa-uipath requested a review from radu-mocanu June 25, 2026 03:08
…r kwarg

Per the governance architecture review (uipath-python PR 1761):
LangChainAdapter, GovernedLangChainAgent, the _inject_callback proxy,
and the uipath.governance.adapters entry-point are removed. The
surviving GovernanceCallbackHandler now subclasses
langchain_core.callbacks.BaseCallbackHandler directly, and
UiPathLangGraphRuntimeFactory.new_runtime gains an
evaluator: EvaluatorProtocol | None = None kwarg — when supplied,
the factory builds the handler and hands it to UiPathLangGraphRuntime
through the existing callbacks constructor arg.

Pins uipath-core==0.5.23.dev1017616946 (the PR 1761 dev build) via
testpypi, since BaseAdapter and the adapter registry no longer exist
in uipath-core.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants