mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-02-14 17:15:11 -05:00
Compare commits
46 Commits
dev-update
...
adk-python
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acf56382d7 | ||
|
|
fe2dd13968 | ||
|
|
bf3fb86fd2 | ||
|
|
d3d62a76d4 | ||
|
|
8fa8840d77 | ||
|
|
1f8019c50a | ||
|
|
1ecc3cd604 | ||
|
|
acbb9c15ed | ||
|
|
7e175c62a0 | ||
|
|
b0947b2adc | ||
|
|
3665b78a4b | ||
|
|
d7d5e75324 | ||
|
|
ad05a12a67 | ||
|
|
0b23fc950f | ||
|
|
632dd10180 | ||
|
|
9771aa47df | ||
|
|
a212aedd19 | ||
|
|
9210e5555c | ||
|
|
b43af71793 | ||
|
|
da1f463dd1 | ||
|
|
3265f7e3a6 | ||
|
|
336743f747 | ||
|
|
911069ae8d | ||
|
|
cee59d52c3 | ||
|
|
9517daba09 | ||
|
|
3c61ee0597 | ||
|
|
19271eb9ee | ||
|
|
3a150c77ca | ||
|
|
ca6f31a192 | ||
|
|
d7faf7700f | ||
|
|
37a60ea2a6 | ||
|
|
8de16976ae | ||
|
|
49cb2f39f7 | ||
|
|
f169874e53 | ||
|
|
db8c3a3c77 | ||
|
|
8b33b0c67f | ||
|
|
35fa73516b | ||
|
|
66df3bfd21 | ||
|
|
73e0edc3cd | ||
|
|
3f32a9aab6 | ||
|
|
28006fc9b2 | ||
|
|
56c69131b4 | ||
|
|
ad4a509340 | ||
|
|
d39acac96c | ||
|
|
6df2ad28a9 | ||
|
|
8416378613 |
66
DEVELOPER.md
66
DEVELOPER.md
@@ -358,72 +358,6 @@ preview link will be automatically added as a comment to your PR.
|
||||
docker run -d toolbox:dev
|
||||
```
|
||||
|
||||
### Cross-Compilation
|
||||
|
||||
To cross-compile binaries for different operating systems and architectures (e.g., Linux, macOS, Windows), this project uses [Zig](https://ziglang.org/) as a C cross-compiler to handle CGO dependencies.
|
||||
|
||||
You can refer to the [Continuous Release Cloud Build configuration](.ci/continuous.release.cloudbuild.yaml) for the specific `zig` commands and flags used for each target.
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
* **Zig:** Download and extract Zig (e.g., version 0.15.2).
|
||||
* **macOS SDK:** Required for cross-compiling to macOS.
|
||||
|
||||
#### Build Instructions
|
||||
|
||||
Below are examples of how to compile for different targets using Zig. Adjust `ZIG_PATH` and other paths to match your environment.
|
||||
|
||||
**1. Windows (x86_64)**
|
||||
|
||||
```bash
|
||||
# Example setup:
|
||||
# curl https://ziglang.org/download/0.15.2/zig-x86_64-linux-0.15.2.tar.xz --output zig.tar.xz
|
||||
# tar xfv zig.tar.xz
|
||||
|
||||
export ZIG_PATH="$HOME/zig-x86_64-linux-0.15.2"
|
||||
export CC="$ZIG_PATH/zig cc -target x86_64-windows-gnu"
|
||||
export CXX="$ZIG_PATH/zig c++ -target x86_64-windows-gnu"
|
||||
export CGO_ENABLED=1
|
||||
|
||||
GOOS=windows GOARCH=amd64 go build -o toolbox.exe
|
||||
```
|
||||
|
||||
**2. Linux (x86_64)**
|
||||
|
||||
```bash
|
||||
export ZIG_PATH="$HOME/zig-x86_64-linux-0.15.2"
|
||||
export CC="$ZIG_PATH/zig cc -target x86_64-linux-gnu"
|
||||
export CXX="$ZIG_PATH/zig c++ -target x86_64-linux-gnu"
|
||||
export CGO_ENABLED=1
|
||||
|
||||
GOOS=linux GOARCH=amd64 go build -o toolbox-linux
|
||||
```
|
||||
|
||||
**3. macOS (ARM64)**
|
||||
|
||||
Requires `MacOSX14.5.sdk` (or similar) to be available locally.
|
||||
|
||||
```bash
|
||||
export ZIG_PATH="$HOME/zig-x86_64-linux-0.15.2"
|
||||
export SDK_PATH="/path/to/MacOSX14.5.sdk"
|
||||
export MACOS_MIN_VER="10.14"
|
||||
|
||||
export CGO_LDFLAGS="-mmacosx-version-min=$MACOS_MIN_VER --sysroot $SDK_PATH -F$SDK_PATH/System/Library/Frameworks -L/usr/lib"
|
||||
export CC="$ZIG_PATH/zig cc -mmacosx-version-min=$MACOS_MIN_VER -target aarch64-macos.11.0.0-none -isysroot $SDK_PATH -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
export CXX="$ZIG_PATH/zig c++ -mmacosx-version-min=$MACOS_MIN_VER -target aarch64-macos.11.0.0-none -isysroot $SDK_PATH -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
export CGO_ENABLED=1
|
||||
|
||||
GOOS=darwin GOARCH=arm64 go build -trimpath -buildmode=pie -o toolbox-darwin-arm64
|
||||
```
|
||||
|
||||
#### Transferring Binaries
|
||||
|
||||
After compilation, you can transfer the binary to the target machine using `scp` or similar tools:
|
||||
|
||||
```bash
|
||||
scp toolbox.exe user@remote-host:/path/to/destination
|
||||
```
|
||||
|
||||
## Developing Toolbox SDKs
|
||||
|
||||
Refer to the [SDK developer
|
||||
|
||||
@@ -115,7 +115,7 @@ pip install google-genai
|
||||
|
||||
1. Update `my_agent/agent.py` with the following content to connect to Toolbox:
|
||||
```py
|
||||
{{< include "quickstart/python/adk/quickstart.py" >}}
|
||||
{{< regionInclude "quickstart/python/adk/quickstart.py" "quickstart" >}}
|
||||
```
|
||||
<br/>
|
||||
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
# [START quickstart]
|
||||
import asyncio
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.apps import App
|
||||
from google.adk.runners import InMemoryRunner
|
||||
from google.adk.tools.toolbox_toolset import ToolboxToolset
|
||||
from google.genai.types import Content, Part
|
||||
|
||||
prompt = """
|
||||
You're a helpful hotel assistant. You handle hotel searching, booking and
|
||||
cancellations. When the user searches for a hotel, mention it's name, id,
|
||||
location and price tier. Always mention hotel ids while performing any
|
||||
searches. This is very important for any operations. For any bookings or
|
||||
cancellations, please provide the appropriate confirmation. Be sure to
|
||||
update checkin or checkout dates if mentioned by the user.
|
||||
Don't ask for confirmations from the user.
|
||||
"""
|
||||
|
||||
# TODO(developer): update the TOOLBOX_URL to your toolbox endpoint
|
||||
toolset = ToolboxToolset(
|
||||
@@ -8,10 +23,35 @@ toolset = ToolboxToolset(
|
||||
)
|
||||
|
||||
root_agent = Agent(
|
||||
name='root_agent',
|
||||
name='hotel_assistant',
|
||||
model='gemini-2.5-flash',
|
||||
instruction="You are a helpful AI assistant designed to provide accurate and useful information.",
|
||||
instruction=prompt,
|
||||
tools=[toolset],
|
||||
)
|
||||
|
||||
app = App(root_agent=root_agent, name="my_agent")
|
||||
# [END quickstart]
|
||||
|
||||
queries = [
|
||||
"Find hotels in Basel with Basel in its name.",
|
||||
"Can you book the Hilton Basel for me?",
|
||||
"Oh wait, this is too expensive. Please cancel it and book the Hyatt Regency instead.",
|
||||
"My check in dates would be from April 10, 2024 to April 19, 2024.",
|
||||
]
|
||||
|
||||
async def main():
|
||||
runner = InMemoryRunner(app=app)
|
||||
session = await runner.session_service.create_session(
|
||||
app_name=app.name, user_id="test_user"
|
||||
)
|
||||
|
||||
for query in queries:
|
||||
print(f"\nUser: {query}")
|
||||
user_message = Content(parts=[Part.from_text(text=query)])
|
||||
|
||||
async for event in runner.run_async(user_id="test_user", session_id=session.id, new_message=user_message):
|
||||
if event.is_final_response() and event.content and event.content.parts:
|
||||
print(f"Agent: {event.content.parts[0].text}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -41,31 +41,29 @@ def golden_keywords():
|
||||
class TestExecution:
|
||||
"""Test framework execution and output validation."""
|
||||
|
||||
_cached_output = None
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def script_output(self, capsys):
|
||||
"""Run the quickstart function and return its output."""
|
||||
|
||||
# TODO: Add better validation for ADK once we have a way to capture its
|
||||
# output.
|
||||
if ORCH_NAME == "adk":
|
||||
return quickstart.app.root_agent.name
|
||||
else:
|
||||
if TestExecution._cached_output is None:
|
||||
asyncio.run(quickstart.main())
|
||||
|
||||
return capsys.readouterr()
|
||||
out, err = capsys.readouterr()
|
||||
TestExecution._cached_output = (out, err)
|
||||
|
||||
class Output:
|
||||
def __init__(self, out, err):
|
||||
self.out = out
|
||||
self.err = err
|
||||
|
||||
return Output(*TestExecution._cached_output)
|
||||
|
||||
def test_script_runs_without_errors(self, script_output):
|
||||
"""Test that the script runs and produces no stderr."""
|
||||
if ORCH_NAME == "adk":
|
||||
return
|
||||
assert script_output.err == "", f"Script produced stderr: {script_output.err}"
|
||||
|
||||
def test_keywords_in_output(self, script_output, golden_keywords):
|
||||
"""Test that expected keywords are present in the script's output."""
|
||||
|
||||
if ORCH_NAME == "adk":
|
||||
assert script_output == "root_agent"
|
||||
return
|
||||
output = script_output.out
|
||||
missing_keywords = [kw for kw in golden_keywords if kw not in output]
|
||||
assert not missing_keywords, f"Missing keywords in output: {missing_keywords}"
|
||||
|
||||
@@ -83,15 +83,12 @@ Toolbox instead of the local address.
|
||||
|
||||
2. Open your agent file (`my_agent/agent.py`).
|
||||
|
||||
3. Update the `ToolboxSyncClient` initialization to use your Cloud Run URL.
|
||||
3. Update the `ToolboxToolset` initialization to point to your Cloud Run service URL. Replace the existing initialization code with the following:
|
||||
|
||||
{{% alert color="info" %}}
|
||||
Since Cloud Run services are secured by default, you also need to provide an
|
||||
authentication token.
|
||||
{{% alert color="info" title="Note" %}}
|
||||
Since Cloud Run services are secured by default, you also need to provide a workload identity.
|
||||
{{% /alert %}}
|
||||
|
||||
Replace your existing client initialization code with the following:
|
||||
|
||||
```python
|
||||
from google.adk import Agent
|
||||
from google.adk.apps import App
|
||||
@@ -132,14 +129,14 @@ app = App(root_agent=root_agent, name="my_agent")
|
||||
Run the deployment command:
|
||||
|
||||
```bash
|
||||
make backend
|
||||
make deploy
|
||||
```
|
||||
|
||||
This command will build your agent's container image and deploy it to Vertex AI.
|
||||
|
||||
## Step 6: Test your Deployment
|
||||
|
||||
Once the deployment command (`make backend`) completes, it will output the URL
|
||||
Once the deployment command (`make deploy`) completes, it will output the URL
|
||||
for the Agent Engine Playground. You can click on this URL to open the
|
||||
Playground in your browser and start chatting with your agent to test the tools.
|
||||
|
||||
|
||||
@@ -16,7 +16,14 @@ This guide demonstrates how to implement these patterns in your Toolbox applicat
|
||||
|
||||
{{< tabpane persist=header >}}
|
||||
{{% tab header="ADK" text=true %}}
|
||||
Coming soon.
|
||||
The following example demonstrates how to use `ToolboxToolset` with ADK's pre and post processing hooks to implement pre and post processing for tool calls.
|
||||
|
||||
```py
|
||||
{{< include "python/adk/agent.py" >}}
|
||||
```
|
||||
You can also add model-level (`before_model_callback`, `after_model_callback`) and agent-level (`before_agent_callback`, `after_agent_callback`) hooks to intercept messages at different stages of the execution loop.
|
||||
|
||||
For more information, see the [ADK Callbacks documentation](https://google.github.io/adk-docs/callbacks/types-of-callbacks/).
|
||||
{{% /tab %}}
|
||||
{{% tab header="Langchain" text=true %}}
|
||||
The following example demonstrates how to use `ToolboxClient` with LangChain's middleware to implement pre- and post- processing for tool calls.
|
||||
|
||||
137
docs/en/samples/pre_post_processing/python/adk/agent.py
Normal file
137
docs/en/samples/pre_post_processing/python/adk/agent.py
Normal file
@@ -0,0 +1,137 @@
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional
|
||||
from copy import deepcopy
|
||||
|
||||
from google.adk import Agent
|
||||
from google.adk.apps import App
|
||||
from google.adk.runners import Runner
|
||||
from google.adk.sessions.in_memory_session_service import InMemorySessionService
|
||||
from google.adk.tools.tool_context import ToolContext
|
||||
from google.genai import types
|
||||
from toolbox_adk import CredentialStrategy, ToolboxToolset, ToolboxTool
|
||||
|
||||
SYSTEM_PROMPT = """
|
||||
You're a helpful hotel assistant. You handle hotel searching, booking and
|
||||
cancellations. When the user searches for a hotel, mention it's name, id,
|
||||
location and price tier. Always mention hotel ids while performing any
|
||||
searches. This is very important for any operations. For any bookings or
|
||||
cancellations, please provide the appropriate confirmation. Be sure to
|
||||
update checkin or checkout dates if mentioned by the user.
|
||||
Don't ask for confirmations from the user.
|
||||
"""
|
||||
|
||||
|
||||
# Pre processing
|
||||
async def before_tool_callback(
|
||||
tool: ToolboxTool, args: Dict[str, Any], tool_context: ToolContext
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Callback fired before a tool is executed.
|
||||
Enforces business logic: Max stay duration is 14 days.
|
||||
"""
|
||||
tool_name = tool.name
|
||||
print(f"POLICY CHECK: Intercepting '{tool_name}'")
|
||||
|
||||
if tool_name == "update-hotel" and "checkin_date" in args and "checkout_date" in args:
|
||||
start = datetime.fromisoformat(args["checkin_date"])
|
||||
end = datetime.fromisoformat(args["checkout_date"])
|
||||
duration = (end - start).days
|
||||
|
||||
if duration > 14:
|
||||
print("BLOCKED: Stay too long")
|
||||
return {"result": "Error: Maximum stay duration is 14 days."}
|
||||
return None
|
||||
|
||||
|
||||
# Post processing
|
||||
async def after_tool_callback(
|
||||
tool: ToolboxTool,
|
||||
args: Dict[str, Any],
|
||||
tool_context: ToolContext,
|
||||
tool_response: Any,
|
||||
) -> Optional[Any]:
|
||||
"""
|
||||
Callback fired after a tool execution.
|
||||
Enriches response for successful bookings.
|
||||
"""
|
||||
if isinstance(tool_response, dict):
|
||||
result = tool_response.get("result", "")
|
||||
elif isinstance(tool_response, str):
|
||||
result = tool_response
|
||||
else:
|
||||
return None
|
||||
|
||||
tool_name = tool.name
|
||||
if isinstance(result, str) and "Error" not in result:
|
||||
if tool_name == "book-hotel":
|
||||
loyalty_bonus = 500
|
||||
enriched_result = f"Booking Confirmed!\n You earned {loyalty_bonus} Loyalty Points with this stay.\n\nSystem Details: {result}"
|
||||
|
||||
if isinstance(tool_response, dict):
|
||||
modified_response = deepcopy(tool_response)
|
||||
modified_response["result"] = enriched_result
|
||||
return modified_response
|
||||
else:
|
||||
return enriched_result
|
||||
return None
|
||||
|
||||
|
||||
async def run_chat_turn(
|
||||
runner: Runner, session_id: str, user_id: str, message_text: str
|
||||
):
|
||||
"""Executes a single chat turn and prints the interaction."""
|
||||
print(f"\nUSER: '{message_text}'")
|
||||
response_text = ""
|
||||
async for event in runner.run_async(
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
new_message=types.Content(role="user", parts=[types.Part(text=message_text)]),
|
||||
):
|
||||
if event.content and event.content.parts:
|
||||
for part in event.content.parts:
|
||||
if part.text:
|
||||
response_text += part.text
|
||||
|
||||
print(f"AI: {response_text}")
|
||||
|
||||
|
||||
async def main():
|
||||
toolset = ToolboxToolset(
|
||||
server_url="http://127.0.0.1:5000",
|
||||
toolset_name="my-toolset",
|
||||
credentials=CredentialStrategy.toolbox_identity(),
|
||||
)
|
||||
tools = await toolset.get_tools()
|
||||
root_agent = Agent(
|
||||
name="root_agent",
|
||||
model="gemini-2.5-flash",
|
||||
instruction=SYSTEM_PROMPT,
|
||||
tools=tools,
|
||||
# add any pre and post processing callbacks
|
||||
before_tool_callback=before_tool_callback,
|
||||
after_tool_callback=after_tool_callback,
|
||||
)
|
||||
app = App(root_agent=root_agent, name="my_agent")
|
||||
runner = Runner(app=app, session_service=InMemorySessionService())
|
||||
session_id = "test-session"
|
||||
user_id = "test-user"
|
||||
await runner.session_service.create_session(
|
||||
app_name=app.name, user_id=user_id, session_id=session_id
|
||||
)
|
||||
|
||||
# First turn: Successful booking
|
||||
await run_chat_turn(runner, session_id, user_id, "Book hotel with id 3.")
|
||||
print("-" * 50)
|
||||
# Second turn: Policy violation (stay > 14 days)
|
||||
await run_chat_turn(
|
||||
runner,
|
||||
session_id,
|
||||
user_id,
|
||||
"Book a hotel with id 5 with checkin date 2025-01-18 and checkout date 2025-02-10",
|
||||
)
|
||||
await toolset.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,3 @@
|
||||
google-adk[toolbox]==1.23.0
|
||||
toolbox-adk==0.5.8
|
||||
google-genai==1.62.0
|
||||
Reference in New Issue
Block a user