Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/guides/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ This directory contains specific developer guides for the ADK Python implementat
* [MongoDbToolset](integrations/mongodb/mongodb_toolset/index.md) - Vector and hybrid search over a MongoDB database, with the query text embedded on the way through.
* [OpenAIDecisionsClassifier](integrations/openai/openai_decisions_classifier/index.md) - Answering `Choice`, `Binary`, and `Score` questions with the OpenAI Decisions API, including questions OpenAI declines to answer.
* [RedisSessionService](integrations/redis/redis_session_service/index.md) - Sharing sessions across processes through Redis, including the expiry every other backend lacks.
* [StreetViewToolset](integrations/street_view/index.md) - Multimodal spatial verification, Street View outdoor baselines, address classification, and reverse geocoding.
* [TeamsRunner](integrations/teams/teams_runner/index.md) - Running an agent as a Microsoft Teams bot, and why replies arrive after the request is acknowledged.
* [TypesafeClassifier](integrations/typesafe/typesafe_classifier/index.md) - Answering `Choice`, `Binary`, and `Score` questions with TypeSafe's Jev decision models instead of a model turn.

Expand Down
153 changes: 153 additions & 0 deletions docs/guides/integrations/street_view/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# StreetViewToolset

`StreetViewToolset` connects ADK agents to Google Maps Platform APIs—Street View Static API, Address Validation API, and Reverse Geocoding API—for multimodal spatial verification, cold-start visual baselines, and property boundary classification.

## Introduction

Agents that verify physical locations (such as disputed deliveries, insurance claims, merchant storefront checks, or field-service inspections) often encounter two gaps:

1. **Cold-start visual baselines**: Newly serviced addresses have no historical ground-truth photos to compare against a submitted photo.
2. **Property boundary ambiguity**: A single-family home has a tight drop-off boundary around its porch or garage, whereas a multi-unit apartment complex has distributed drop-off locations across lobbies, mailrooms, and parcel lockers.

`StreetViewToolset` bundles three tools (`street_view_fallback_tool`, `property_classification_tool`, and `reverse_geocode_tool`) backed by `GoogleMapsConfig` and structured Pydantic models (`StreetViewResult`, `PropertyClassification`, and `VisualAuditVerdict`).

## Get started

Core HTTP and validation features work out of the box with `google-adk`. If you resolve API keys from Google Cloud Secret Manager, install the `gcp` extra:

```shell
pip install 'google-adk[gcp]'
```

Configure `MAPS_API_KEY` (or pass `GoogleMapsConfig`) and attach `StreetViewToolset` to an `LlmAgent`:

```python
from google.adk.agents import LlmAgent
from google.adk.integrations.street_view import GoogleMapsConfig
from google.adk.integrations.street_view import StreetViewToolset
from google.adk.integrations.street_view import default_config

agent = LlmAgent(
name="spatial_auditor",
instruction=(
"Classify the recipient address, verify GPS telemetry with reverse "
"geocoding, and capture a Street View baseline when prior imagery is "
"unavailable."
),
tools=[
StreetViewToolset(
config=GoogleMapsConfig(
default_pitch=default_config.default_pitch,
default_fov=120,
)
)
],
)
```

You can also register individual standalone tool functions directly on an agent:

```python
from google.adk.agents import LlmAgent
from google.adk.integrations.street_view import property_classification_tool
from google.adk.integrations.street_view import reverse_geocode_tool
from google.adk.integrations.street_view import street_view_fallback_tool

agent = LlmAgent(
name="lightweight_auditor",
tools=[
street_view_fallback_tool,
property_classification_tool,
reverse_geocode_tool,
],
)
```

## How it works

### Two-step Street View pre-flight and compass bearing

`fetch_street_view_baseline` queries the Street View Metadata endpoint (`source="outdoor"`) before requesting image tiles:

1. Coordinates are checked with `validate_coordinates` to ensure finite WGS84 bounds.
2. If no outdoor panorama exists (`ZERO_RESULTS`), the call returns `StreetViewResult(available=False, ...)` immediately without billing Static API image quota or downloading gray placeholder tiles.
3. When outdoor imagery exists, `calculate_compass_bearing` computes the forward azimuth (0–360°) from the Street View camera vehicle's road coordinates toward the target property coordinates so the panorama faces the building rather than down the street.
4. `street_view_fallback_tool` saves the JPEG bytes as an ADK artifact named `STREET_VIEW_ARTIFACT_FILENAME` (`"street_view_baseline.jpg"`) and writes JSON-safe metadata (`street_view_url`, `street_view_pano_id`, `street_view_imagery_date`, `street_view_heading`) into `tool_context.state`.

### Address validation and regex fallback

`classify_property_address_async` (and synchronous `classify_property_address`) sends the raw address to the Address Validation API (`validateAddress`) using the `X-Goog-Api-Key` header:

- Extracts normalized street, locality, state, postal code, and subpremise components into `PropertyClassification` with `AddressType.SINGLE_FAMILY` or `AddressType.MULTI_UNIT`.
- Checks `missingComponentTypes` for `"subpremise"` or `"room"` to flag multi-unit buildings where the customer omitted an apartment or suite number (`is_missing_subunit=True`).
- Falls back to deterministic regex sub-unit parsing if `MAPS_API_KEY` is unconfigured or the API returns a transient error (without caching failed lookups).

### Reverse geocoding and caching

`reverse_geocode_coordinates_async` (and synchronous `reverse_geocode_coordinates`) resolves GPS telemetry rounded to 5 decimal places (~1.1 m precision) into a formatted street address. Successful lookups are stored in a bounded LRU cache partitioned by a non-reversible SHA-256 key fingerprint; call `clear_caches` in tests or credential rotation hooks to reset cached entries.

### Multimodal baseline comparison

For custom workflows or tool pipelines, `audit_photo_with_fallback` compares a target photo against historical photos (or automatically falls back to Street View when `historical_photos` is empty) using `BASELINE_SYNTHESIS_PROMPT` and `AUDIT_VERDICT_PROMPT`, returning a structured `VisualAuditVerdict` dictionary classified by `VerdictLabel`:

```python
from google import genai
from google.adk.integrations.street_view import AUDIT_VERDICT_PROMPT
from google.adk.integrations.street_view import BASELINE_SYNTHESIS_PROMPT
from google.adk.integrations.street_view import STREET_VIEW_ARTIFACT_FILENAME
from google.adk.integrations.street_view import audit_photo_with_fallback
from google.adk.integrations.street_view import calculate_compass_bearing
from google.adk.integrations.street_view import classify_property_address
from google.adk.integrations.street_view import classify_property_address_async
from google.adk.integrations.street_view import clear_caches
from google.adk.integrations.street_view import fetch_street_view_baseline
from google.adk.integrations.street_view import reverse_geocode_coordinates
from google.adk.integrations.street_view import reverse_geocode_coordinates_async
from google.adk.integrations.street_view import validate_coordinates


async def run_visual_audit(target_jpeg: bytes, tool_context):
client = genai.Client()
verdict = await audit_photo_with_fallback(
target_photo_bytes=target_jpeg,
historical_photos=None,
latitude=37.4220656,
longitude=-122.0840897,
tool_context=tool_context,
genai_client=client,
)
return verdict
```

## Public API reference

| Export | Description |
| :--- | :--- |
| `StreetViewToolset` | `BaseToolset` implementation exposing configurable Maps & Street View `FunctionTool` instances with `tool_filter` and `tool_name_prefix` support. |
| `GoogleMapsConfig` | Credential and request configuration resolving keys from `api_key`, `MAPS_API_KEY`, or Secret Manager (`MAPS_API_KEY_SECRET_RESOURCE`). |
| `default_config` | Module-level default `GoogleMapsConfig` instance used by standalone tools. |
| `street_view_fallback_tool` | Async ADK tool function that captures outdoor Street View imagery into session artifacts and state. |
| `property_classification_tool` | Async ADK tool function that standardizes addresses and classifies single-family vs. multi-unit boundaries. |
| `reverse_geocode_tool` | Async ADK tool function that resolves GPS coordinates into a street address in session state. |
| `audit_photo_with_fallback` | Two-stage Gemini Vision helper that synthesizes a visual baseline and scores a target photo. |
| `fetch_street_view_baseline` | Low-level async service executing the Street View Metadata pre-flight check and image fetch. |
| `classify_property_address_async` | Async Address Validation service with deterministic local regex fallback. |
| `classify_property_address` | Synchronous counterpart to `classify_property_address_async`. |
| `reverse_geocode_coordinates_async` | Async Reverse Geocoding service with bounded LRU caching. |
| `reverse_geocode_coordinates` | Synchronous counterpart to `reverse_geocode_coordinates_async`. |
| `calculate_compass_bearing` | Computes forward compass heading (0–360°) from camera coordinates to target coordinates. |
| `validate_coordinates` | Validates finite WGS84 latitude `[-90, 90]` and longitude `[-180, 180]` bounds. |
| `clear_caches` | Clears the in-memory bounded LRU caches for address validation and reverse geocoding. |
| `AddressType` | `StrEnum` with `SINGLE_FAMILY` and `MULTI_UNIT` values. |
| `PropertyClassification` | Pydantic model representing normalized address components and sub-unit flags. |
| `StreetViewResult` | Pydantic model holding panorama bytes, deep-link URL, `pano_id`, `imagery_date`, and `heading`. |
| `VerdictLabel` | `Literal` union of `CONFIRMED_MATCH`, `CONFIRMED_MISMATCH`, `PLAUSIBLE_ALTERNATE_LOCATION`, and `INSUFFICIENT_VISUAL_CONTEXT`. |
| `VisualAuditVerdict` | Pydantic model returned by multimodal baseline comparison. |
| `STREET_VIEW_ARTIFACT_FILENAME` | Default ADK artifact filename (`"street_view_baseline.jpg"`). |
| `BASELINE_SYNTHESIS_PROMPT` | Reference prompt template for extracting stable architectural features from baseline imagery. |
| `AUDIT_VERDICT_PROMPT` | Reference prompt template for scoring target imagery against property boundaries. |

## Limitations

- **Outdoor coverage only**: `fetch_street_view_baseline` filters with `source="outdoor"` to avoid indoor photospheres, so gated communities or private roads without public Street View coverage return `available=False`.
- **Imagery recency**: Street View panoramas reflect the capture date reported in `StreetViewResult.imagery_date` (`YYYY-MM`) rather than real-time conditions; visual comparison prompts therefore focus on stable architectural features rather than transient objects or seasonal foliage.
117 changes: 117 additions & 0 deletions src/google/adk/integrations/street_view/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Street View & Google Maps Platform Integration for ADK

Integrates **Google Maps Platform APIs** (Street View Static API, Address Validation API, and Geocoding API) with **Google GenAI / Gemini Vision** for multimodal spatial and physical verification workflows in ADK.

## Architecture Overview

In multimodal spatial and physical verification workflows (such as proof-of-delivery audits, field inspections, real estate assessments, and insurance claim reviews), agents often face two distinct challenges:

1. **Cold-Start Visual Disconnect ($N = 0$)**: When an agent needs to visually audit a target scene against historical baseline photos, newly serviced addresses or sparse historical data offer zero prior imagery.
2. **Ambiguous Property Boundaries**: Knowing whether an address is a **single-family home** (tight 50–150 ft drop-off boundary) or a **multi-unit apartment complex** (loose, distributed drop-off points across leasing offices, parcel lockers, or building clusters) determines the spatial decision boundary.

```text
┌──────────────────────────────────────────────┐
│ Incoming Event │
│ (GPS Lat/Lng, Raw Address) │
└──────────────────────┬───────────────────────┘
│
┌─────────────────────┴──────────────────────┐
▼ ▼
┌───────────────────────────────┐ ┌───────────────────────────────┐
│ Address Validation API │ │ Reverse Geocoding API │
│ (Standardize, classify unit, │ │ (Resolve drop-off GPS into │
│ detect omitted apartment #) │ │ physical street address) │
└───────────────┬───────────────┘ └───────────────┬───────────────┘
│ │
▼ ▼
[Property Class] [Street Context]
(SINGLE_FAMILY vs. (Saved to ADK State)
MULTI_UNIT) │
│ │
└─────────────────────┬──────────────────────┘
│
┌─────────────────────┴──────────────────────┐
│ Historical Imagery Available? │
└──────┬──────────────────────────────┬──────┘
Yes │ │ No (Cold Start)
▼ ▼
┌──────────────────────────┐ ┌───────────────────────────┐
│ GCS Historical Deliveries│ │ Street View Static API │
│ (1..N Prior Photos) │ │ • Pre-flight metadata chk│
└────────────┬─────────────┘ │ • Compute camera heading │
│ │ • Fetch outdoor panorama │
│ │ • Save ADK Artifact │
│ └─────────────┬─────────────┘
│ │
└──────────────┬────────────────┘
│
▼
┌─────────────────────────────┐
│ Gemini Vision Baseline │
│ (Generate visual features │
│ + score target photo) │
└─────────────────────────────┘
```

## Enterprise Use Cases

| Vertical | Use Case | How `integrations.street_view` Solves It |
| :--- | :--- | :--- |
| **Logistics & Delivery** | Disputed Delivery ("Item Not Received") Audits | Classifies `SINGLE_FAMILY` vs. `MULTI_UNIT` boundaries, catches missing apartment numbers, reverse-geocodes driver GPS telemetry, and pulls Street View cold-start baselines ($N=0$). |
| **Property & Casualty Insurance** | Exterior Claim Triage & Underwriting | Captures pre-loss outdoor Street View imagery (including `imagery_date` metadata) to verify claimant-submitted property photos. |
| **FinTech & Marketplaces** | Merchant KYB & Storefront Verification | Validates `BUSINESS` vs. `RESIDENTIAL` address metadata and captures curb-view exterior imagery to confirm physical commercial signage. |
| **Field Services & Utilities** | Work-Order Completion Verification | Confirms technician inspection photos (EV chargers, solar installs, telecom drops) align with dispatched coordinates and surrounding streetscape. |
| **Real Estate & Lending** | Automated Desktop Appraisal Checks | Normalizes parcel/unit addresses and orients the Street View camera directly toward the target coordinates (`heading`) to cross-check listing imagery. |

## Prerequisites & Setup

Enable the required Google Maps Platform APIs on your Google Cloud project:

- **Street View Static API**
- **Address Validation API**
- **Geocoding API**

```shell
gcloud services enable \
streetviewpublish.googleapis.com \
static-maps-backend.googleapis.com \
addressvalidation.googleapis.com \
geocoding-backend.googleapis.com
```

Configure credentials via environment variable or Google Cloud Secret Manager:

```shell
export MAPS_API_KEY="your-maps-api-key"

# Optional: fetch from Secret Manager instead (requires pip install 'google-adk[gcp]')
export MAPS_API_KEY_SECRET_RESOURCE="projects/my-project/secrets/maps-api-key/versions/latest"
```

## Usage

### Registering `StreetViewToolset` on an ADK Agent

```python
from google.adk.agents import LlmAgent
from google.adk.integrations.street_view import GoogleMapsConfig
from google.adk.integrations.street_view import StreetViewToolset

agent = LlmAgent(
name="spatial_auditor",
instruction="Verify delivery addresses, GPS telemetry, and outdoor imagery.",
tools=[
StreetViewToolset(
config=GoogleMapsConfig(default_pitch=10, default_fov=120),
)
],
)
```

### Session State & Artifacts Written by Tools

| Tool | Session State / Artifact Keys | Description |
| :--- | :--- | :--- |
| `street_view_fallback_tool` | Artifact `street_view_baseline.jpg`, `state["street_view_baseline_used"]`, `state["street_view_url"]`, `state["street_view_pano_id"]`, `state["street_view_imagery_date"]`, `state["street_view_heading"]` | Pre-flight checks `source="outdoor"`, computes compass bearing from the camera vehicle to target coordinates, and persists the panorama. |
| `property_classification_tool` | `state["address_classification"]`, `state["is_multi_unit"]`, `state["is_missing_subunit"]`, `state["standardized_address"]` | Standardizes the street address, classifies `SINGLE_FAMILY` vs. `MULTI_UNIT`, and detects omitted sub-units. |
| `reverse_geocode_tool` | `state["reverse_geocoded_address"]`, `state["dropoff_coordinates"]` | Resolves raw GPS latitude/longitude into a formatted street address. |
Loading
Loading