|
1 | 1 | """Governance wiring for the google-adk-governed-agent example. |
2 | 2 |
|
3 | | -The ``GoogleADKAdapter.register_hooks`` patches ``google.adk.tools.BaseTool``, |
4 | | -but ADK 1.x concrete tools (``FunctionTool`` and custom ``BaseTool`` |
5 | | -subclasses) override ``run_async``, so the base-class patch does not intercept |
6 | | -them. To exercise the *real* governance code path offline, this module applies |
7 | | -the adapter's tool patch to the concrete demo tool class directly — the same |
8 | | -mechanism the Agent Assembly SDK's own integration tests use. |
| 3 | +Governance is installed through the SDK's **public** Google ADK adapter — |
| 4 | +``GoogleADKAdapter.register_hooks`` / ``unregister_hooks`` — the same entrypoint |
| 5 | +``init_assembly`` uses to wire ADK governance in a live deployment. The adapter |
| 6 | +patches ``google.adk.tools.BaseTool.run_async`` and every concrete tool class |
| 7 | +exported from ``google.adk.tools`` that overrides ``run_async``, routing each |
| 8 | +invocation through the interceptor's ``check_tool_start`` / |
| 9 | +``wait_for_tool_approval`` before the tool body runs. |
9 | 10 |
|
10 | 11 | This keeps the demo fully offline (no Gemini / Vertex AI credentials, no |
11 | | -network) while still running the genuine allow / deny / pending governance |
12 | | -logic from the Google ADK adapter. |
| 12 | +network) while running the genuine allow / deny / pending governance logic. |
| 13 | +
|
| 14 | +SDK gap (AAASM-4839): the public adapter has no entrypoint to govern a *single* |
| 15 | +caller-supplied tool class — it only auto-discovers concrete tools exported from |
| 16 | +``google.adk.tools``. This demo's ``DemoTool`` is a local subclass defined in the |
| 17 | +example, so ``govern_tool_class`` registers it into that public discovery scope |
| 18 | +before ``register_hooks`` runs. A production agent instead exposes real |
| 19 | +``google.adk.tools`` tools (e.g. ``FunctionTool``), which the adapter governs |
| 20 | +with no bridging; a public per-class governance API would remove even this step. |
13 | 21 | """ |
14 | 22 | from __future__ import annotations |
15 | 23 |
|
| 24 | +import importlib |
16 | 25 | from typing import Any |
17 | 26 |
|
18 | | -from agent_assembly.adapters.google_adk import patch as google_adk_patch |
| 27 | +from agent_assembly.adapters.google_adk import GoogleADKAdapter |
| 28 | + |
| 29 | +_ADK_TOOLS_MODULE = "google.adk.tools" |
| 30 | + |
| 31 | +# One adapter instance per governed class: ``unregister_hooks`` only reverts what |
| 32 | +# an adapter's own ``register_hooks`` installed, so the instance must be retained. |
| 33 | +_ADAPTERS: dict[type[Any], GoogleADKAdapter] = {} |
19 | 34 |
|
20 | 35 |
|
21 | 36 | def govern_tool_class(tool_cls: type[Any], interceptor: Any) -> None: |
22 | 37 | """Attach Agent Assembly governance to a concrete ADK tool class. |
23 | 38 |
|
24 | | - Wraps the class's ``run_async`` so every invocation is checked against the |
25 | | - interceptor (``check_tool_start`` / ``wait_for_tool_approval``) before the |
26 | | - tool body runs. |
| 39 | + Wires the class through the public ``GoogleADKAdapter`` so every |
| 40 | + ``run_async`` invocation is checked against the interceptor |
| 41 | + (``check_tool_start`` / ``wait_for_tool_approval``) before the tool body runs. |
27 | 42 | """ |
28 | | - google_adk_patch._apply_tool_run_async_patch(tool_cls, interceptor) |
| 43 | + adk_tools = importlib.import_module(_ADK_TOOLS_MODULE) |
| 44 | + # Bridge the local demo tool into the adapter's public discovery scope — see |
| 45 | + # the module docstring's "SDK gap" note for why this is needed here but not |
| 46 | + # for a production agent using real ``google.adk.tools`` tools. |
| 47 | + setattr(adk_tools, tool_cls.__name__, tool_cls) |
| 48 | + adapter = GoogleADKAdapter() |
| 49 | + adapter.register_hooks(interceptor) |
| 50 | + _ADAPTERS[tool_cls] = adapter |
29 | 51 |
|
30 | 52 |
|
31 | 53 | def ungovern_tool_class(tool_cls: type[Any]) -> None: |
32 | 54 | """Revert governance hooks installed by ``govern_tool_class``.""" |
33 | | - google_adk_patch._revert_tool_run_async_patch(tool_cls) |
| 55 | + adapter = _ADAPTERS.pop(tool_cls, None) |
| 56 | + if adapter is not None: |
| 57 | + adapter.unregister_hooks() |
| 58 | + adk_tools = importlib.import_module(_ADK_TOOLS_MODULE) |
| 59 | + if getattr(adk_tools, tool_cls.__name__, None) is tool_cls: |
| 60 | + delattr(adk_tools, tool_cls.__name__) |
0 commit comments