Skip to content

Commit 1f99dff

Browse files
authored
Merge pull request #340 from ai-agent-assembly/v0.0.1/AAASM-4839/adk_public_api
[AAASM-4839] ♻️ (examples): Use public google_adk adapter API, drop private internals
2 parents c26f79a + a46c626 commit 1f99dff

2 files changed

Lines changed: 42 additions & 15 deletions

File tree

python/google-adk/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Google ADK drives its agent loop against a **cloud LLM** (Gemini / Vertex AI), w
3131

3232
### Adapter / version note
3333

34-
In ADK 1.x, concrete tools (`FunctionTool` and custom `BaseTool` subclasses) override `run_async`, so patching the `BaseTool` base class alone does not intercept them. This example therefore applies the adapter's tool patch to the concrete demo tool class directly (see `src/governance.py`) — the same mechanism the Agent Assembly SDK's own integration tests use. With a future adapter release that patches concrete tool classes, `init_assembly()`'s auto-detection will wire this for you.
34+
In ADK 1.x, concrete tools (`FunctionTool` and custom `BaseTool` subclasses) override `run_async`, so patching the `BaseTool` base class alone does not intercept them. The public `GoogleADKAdapter` handles this: `register_hooks()` patches `BaseTool` **and** every concrete tool class exported from `google.adk.tools` that overrides `run_async`, and `init_assembly()`'s auto-detection wires it for you against real ADK tools. This example uses that same public adapter (see `src/governance.py`); because its demo tool is a local `BaseTool` subclass rather than a real `google.adk.tools` tool, it registers the class into the adapter's public discovery scope before applying the hooks. A production agent exposing real ADK tools needs no such bridging.
3535

3636
## Prerequisites
3737

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,60 @@
11
"""Governance wiring for the google-adk-governed-agent example.
22
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.
910
1011
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.
1321
"""
1422
from __future__ import annotations
1523

24+
import importlib
1625
from typing import Any
1726

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] = {}
1934

2035

2136
def govern_tool_class(tool_cls: type[Any], interceptor: Any) -> None:
2237
"""Attach Agent Assembly governance to a concrete ADK tool class.
2338
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.
2742
"""
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
2951

3052

3153
def ungovern_tool_class(tool_cls: type[Any]) -> None:
3254
"""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

Comments
 (0)