# Next Session: Static Map Images on Telegram/WhatsApp/Text

**Priority:** MEDIUM — additive visual enhancement, all tools work without it
**Parallel session:** Independent of other sessions
**Estimated effort:** 2-3 hours (research per-channel image pipelines + implement + test)

## What

When Annie gives directions, weather, or AQI results, she should also send a **visual map image** showing the location. Google Static Maps API generates PNG images from a URL — no JavaScript needed. The image gets sent through the existing per-channel image pipeline (Telegram `sendPhoto`, WhatsApp image sender, text chat inline).

## Why

"How long to Skandagiri?" currently returns text-only directions. A map with markers for origin (A) and destination (B) makes it instantly understandable. Similarly, weather/AQI results for a non-home location benefit from showing WHERE on a map.

## Google Static Maps API

Already enabled (part of the 33 APIs). Same `GOOGLE_MAPS_API_KEY`. Simple GET request that returns a PNG:

```
GET https://maps.googleapis.com/maps/api/staticmap?
  center=12.91,77.64
  &zoom=13
  &size=400x300
  &markers=color:red|label:A|12.91,77.64
  &markers=color:green|label:B|13.35,77.57
  &key=$KEY
```

Returns raw PNG bytes — save to temp file → send via channel's image pipeline.

## Implementation Plan

### Step 1: `_get_static_map_url()` helper in `maps_tools.py`

Build the URL from coordinates. Support:
- **Single marker** (weather/AQI — show the location)
- **Two markers + path** (directions — show origin A and destination B)
- Size: 400x300 (good for mobile)
- Zoom: auto-fit when using markers (omit `center` + `zoom`, Google auto-fits)

### Step 2: `_download_map_image()` async helper

```python
async def _download_map_image(url: str) -> bytes | None:
    """Download static map PNG. Returns None on failure."""
```

Uses `httpx.AsyncClient`, returns raw bytes. Save to `/tmp/maps_<hash>.png`.

### Step 3: Return map image path alongside text result

The tool handlers currently return `str`. To include an image, options:
- **Option A**: Return a structured dict `{"text": "...", "image": "/tmp/maps_xxx.png"}` — requires changing the handler return type and dispatch handling
- **Option B**: Save the image and emit a side-channel event (observability or data channel) — keeps handler return type as `str`
- **Option C**: Use the existing `render_table` / visual_tools pattern — sends image via data channel separately from text

**Research needed**: How does `send_image` work on each channel today? Check:
- `services/telegram-bot/` — `sendPhoto` API
- `services/whatsapp-agent/wa_sender.py` — image sending via Playwright
- `services/annie-voice/visual_tools.py` — data channel for web UI
- `services/annie-voice/bot.py` — how visual outputs are sent

### Step 4: Per-channel image delivery

| Channel | Mechanism | Complexity |
|---------|-----------|------------|
| **Telegram** | `bot.send_photo(chat_id, photo=open(path, 'rb'))` | LOW — well-documented API |
| **WhatsApp** | `wa_sender.send_image(contact, path)` — already implemented | LOW — pipeline exists |
| **Text chat** | Return image URL or base64 in response? Or data channel event? | MEDIUM — depends on frontend |
| **Phone** | Not applicable — voice-only, no image display | N/A |

### Step 5: Tests

- `_get_static_map_url()` — URL construction with 1 marker, 2 markers, encoding
- `_download_map_image()` — mocked httpx, timeout, error handling
- Integration tests per channel (mocked send)

## Key Questions to Research

1. **How does Telegram bot currently send images?** Find the `sendPhoto` call pattern in `services/telegram-bot/`.
2. **How does `visual_tools.py` send images?** The data channel pattern may be reusable.
3. **Should the map image be optional?** (e.g., skip if download fails — don't block the text response)
4. **Should we cache map images?** Same coordinates = same image. TTL cache on URL hash?
5. **Return type change**: If handlers must return `str`, how to include image path? Side channel vs structured return?

## Files to Modify

| File | Change |
|------|--------|
| `maps_tools.py` | `_get_static_map_url()`, `_download_map_image()`, call from directions/weather/AQI |
| `text_llm.py` or `tool_dispatch.py` | Handle image return alongside text (if structured return) |
| `services/telegram-bot/llm_route.py` | Send image after tool result (if side channel) |
| `services/whatsapp-agent/wa_sender.py` | Already has image sending — wire it up |
| `tests/test_maps_tools.py` | URL builder + download tests |

## Verification

```bash
# 1. Directions with map
curl -s -N -X POST http://localhost:7860/v1/chat \
  -H "X-Internal-Token: $CE_TOKEN" -H "Content-Type: application/json" \
  -d '{"message": "How do I get to Skandagiri?"}'
# Expected: text directions + map image sent

# 2. Weather with map (non-home location)
curl -s -N -X POST http://localhost:7860/v1/chat \
  -H "X-Internal-Token: $CE_TOKEN" -H "Content-Type: application/json" \
  -d '{"message": "What is the weather in Mumbai?"}'
# Expected: weather text + map pin on Mumbai

# 3. Telegram: same queries via Telegram bot → photo appears in chat
# 4. WhatsApp: same queries → image appears in WhatsApp
```
