Files
promptfoo--promptfoo/examples/integration-google-adk/agent.py
T
wehub-resource-sync 0d3cb498a3
Deploy local.promptfoo.app / Deploy to Cloudflare Pages (push) Waiting to run
Test and Publish Multi-arch Docker Image / test (push) Waiting to run
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-amd64 platform:linux/amd64 runner:ubuntu-latest]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / build-docker-and-push-digests (map[digest-suffix:linux-arm64 platform:linux/arm64 runner:ubuntu-24.04-arm]) (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / merge-docker-digests (push) Blocked by required conditions
Test and Publish Multi-arch Docker Image / Attest Multi-arch Image (push) Blocked by required conditions
Validate Renovate Config / Validate Renovate Configuration (push) Waiting to run
CI / Shell Format Check (push) Has been cancelled
CI / Check Ruby (3.4) (push) Has been cancelled
CI / CI Config (push) Has been cancelled
CI / Test on Node ${{ matrix.node }} and ${{ matrix.os }}${{ matrix.shard && format(' (shard {0}/3)', matrix.shard) || '' }} (push) Has been cancelled
CI / Build on Node ${{ matrix.node }} (push) Has been cancelled
CI / Style Check (push) Has been cancelled
CI / Generate Assets (push) Has been cancelled
CI / Check Python (3.14) (push) Has been cancelled
CI / Check Python (3.9) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Code Scan Action (push) Has been cancelled
CI / Site tests (push) Has been cancelled
CI / webui tests (push) Has been cancelled
CI / Run Integration Tests (push) Has been cancelled
CI / Run Smoke Tests (push) Has been cancelled
CI / Go Tests (push) Has been cancelled
CI / Share Test (push) Has been cancelled
CI / Redteam (Production API) (push) Has been cancelled
CI / Redteam (Staging API) (push) Has been cancelled
CI / GitHub Actions Lint (push) Has been cancelled
CI / Check Ruby (3.0) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / build (push) Has been cancelled
release-please / publish-npm (push) Has been cancelled
release-please / publish-npm-backfill (push) Has been cancelled
release-please / docker (push) Has been cancelled
release-please / publish-code-scan-action (push) Has been cancelled
release-please / attest-code-scan-action (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:08 +08:00

139 lines
4.6 KiB
Python

"""Google ADK agents used by the Promptfoo integration example."""
from __future__ import annotations
from typing import Any
from google.adk.agents import Agent, SequentialAgent
from google.adk.apps import App
from google.adk.plugins import BasePlugin
from google.adk.tools import ToolContext
from google.genai import types
APP_NAME = "promptfoo_adk_demo"
WEATHER_REPORTS = {
"london": "London is cloudy with light drizzle and 14 C temperatures.",
"new york": "New York is sunny with a light breeze and 22 C temperatures.",
"tokyo": "Tokyo is clear with mild humidity and 24 C temperatures.",
}
def _normalize_city(city: str) -> str:
return city.strip().casefold()
def before_agent_callback(callback_context) -> None:
"""Track how often ADK enters the main conversational agent."""
callback_context.state["callback_invocations"] = (
int(callback_context.state.get("callback_invocations", 0)) + 1
)
def get_weather(city: str, tool_context: ToolContext) -> dict[str, Any]:
"""Return sample weather data and remember the most recent city."""
normalized_city = _normalize_city(city)
report = WEATHER_REPORTS.get(
normalized_city,
f"No sample weather is stored for {city}.",
)
tool_context.state["last_city"] = city
return {
"city": city,
"status": "success" if normalized_city in WEATHER_REPORTS else "not_found",
"report": report,
}
async def save_trip_note(
city: str,
summary: str,
tool_context: ToolContext,
) -> dict[str, Any]:
"""Save a short trip note as an ADK artifact."""
filename = f"{_normalize_city(city).replace(' ', '-')}-trip-note.md"
artifact = types.Part.from_bytes(
data=f"# Trip note for {city}\n\n{summary}\n".encode("utf-8"),
mime_type="text/markdown",
)
version = await tool_context.save_artifact(filename=filename, artifact=artifact)
tool_context.state["last_saved_artifact"] = filename
return {
"city": city,
"filename": filename,
"version": version,
}
class AuditPlugin(BasePlugin):
"""Record runner lifecycle callbacks so the provider can expose them."""
def __init__(self) -> None:
super().__init__(name="audit_plugin")
self.events: list[str] = []
async def before_run_callback(self, *, invocation_context) -> None:
self.events.append(f"before_run:{invocation_context.session.id}")
async def after_run_callback(self, *, invocation_context) -> None:
self.events.append(f"after_run:{invocation_context.session.id}")
def build_conversational_app(model: str) -> tuple[App, AuditPlugin]:
"""Build the conversational ADK app used by the main eval."""
audit_plugin = AuditPlugin()
root_agent = Agent(
name="weather_agent",
model=model,
description="Answers weather questions and saves trip notes.",
instruction=(
"You are a concise travel weather assistant. "
"When the user asks about weather, call get_weather. "
"When the user asks to save a note, call save_trip_note using the "
"city already discussed and a brief summary of the weather. "
"When the user asks what city was discussed earlier, answer from the "
"conversation and current session state."
),
tools=[get_weather, save_trip_note],
before_agent_callback=before_agent_callback,
)
return (
App(name=APP_NAME, root_agent=root_agent, plugins=[audit_plugin]),
audit_plugin,
)
def build_workflow_app(model: str) -> App:
"""Build a small SequentialAgent workflow for the workflow eval."""
weather_lookup_agent = Agent(
name="weather_lookup_agent",
model=model,
description="Looks up weather for the requested city.",
instruction=(
"Call get_weather for the city in the user's request, then return a "
"single-sentence weather summary."
),
tools=[get_weather],
output_key="weather_snapshot",
)
briefing_agent = Agent(
name="briefing_agent",
model=model,
description="Turns the weather snapshot into a compact travel brief.",
instruction=(
"Use the weather snapshot from state to write a one-sentence trip "
"brief that includes the city name and a packing suggestion."
),
)
workflow = SequentialAgent(
name="trip_planning_workflow",
description="Looks up weather, then writes a travel brief.",
sub_agents=[weather_lookup_agent, briefing_agent],
)
return App(name=APP_NAME, root_agent=workflow)