Refactors the Chicago-only local_agent into a city-aware system driven
by a registry. Each registry entry encodes the city's Socrata dataset
IDs, field names, flood-related category codes, and a context blurb
that gets injected into the local-agent prompt so Gemma 4 reasons
about each city's actual sewer system (combined vs separated, dominant
flood mode, local hazard pattern).
Honest per-city availability β what each city's dossier actually shows:
Chicago: full β 311 (WIB/SFL) + permits with reported_cost
(e.g. $80M new medical office at 4822 S Cottage Grove)
NYC: 311 β 391 sewer-flood reports per 350 5th Ave query.
Permits intentionally deferred: DOB datasets are
fragmented (legacy is stale, modern lacks dates,
active is filtered to currently-open jobs).
SF: full β 941 sewer reports + 75 permits at $366M (Mission
test point: '595 basement reports, increaβ¦
v0.8: Gemma 4 bounding-box detection on Street Viewβ
The streetview agent now returns a 2D bounding box per identified
indicator using Gemma 4's native object-detection capability. The
frontend overlays the boxes on the actual image as an SVG so users
can see exactly which pixels Gemma 4 saw.
Smoke-tested against Drexel Blvd Chicago: returns
- basement-level windows [moderate] box_2d=[400, 205, 458, 242]
- sloping driveway [low] box_2d=[455, 395, 580, 552]
both in the Gemini-convention yxyx 0-1000 normalized format.
Backend (streetview_agent.py + scripts/smoke_test_bbox.py):
- System prompt now describes the bounding-box convention
explicitly (yxyx, normalized 0-1000, top-left origin) β verified
via smoke test that Gemma 4 follows the explicit instruction
reliably while being unreliable about self-describing format.
- User prompt schema adds box_2d field per indicator entry.
- Post-parse validator drops box_2d if it isn't a valid 4-tuple of
numbers in [0, 1000] with ymin<ymax and xmin<xmax. Indicator
itself iβ¦
Both google/gemma-4-31b-it:free and google/gemma-4-26b-a4b-it:free
support multimodal input through the OpenAI-compatible image_url
content-part. Cost stays at $0 with BYOK.
Critically, the model declines to fabricate flood-risk indicators
when given an irrelevant image (test used a Picsum patio shot;
Gemma returned indicators=[] with high confidence and an honest
'insufficient data' summary). That's the property we need before
building a Street View agent on top.
Image-fetch pattern in the smoke test is the production pattern:
download server-side with httpx, base64-encode, send inline as a
data: URL. Avoids upstream-fetcher 404s/UA-blocks (Wikimedia bit
us when we tried passing a public URL directly).
Unblocks the Street View flood-indicator agent from the Gemma 4
depth review.
The frontend already used "FlutIQ" in the chrome wordmark and the
repo name has always been Gemma4Good_FlutIQ; the rest of the code
(backend app title, system prompts, smoke-test headers, skill
metadata, env-var name) had drifted into "FloodIQ" from the
original spec. Standardizing on FlutIQ β "Flut" is German for
flood/tide.
What changed:
- backend/app: APP_NAME, USER_AGENT, app title, agent + prompt
module docstrings
- backend/scripts: smoke-test User-Agent + display headers
- backend/README.md: HF Spaces frontmatter
- SKILL.md: skill name in YAML frontmatter (floodiq β flutiq)
- index.html: window.FLOODIQ_API β window.FLUTIQ_API
- floodiq-skill.tar β flutiq-skill.tar (git mv)
- placeholder domain: floodiq.pages.dev β flutiq.pages.dev
- new STATUS.md β working snapshot of architecture, fixed bugs,
known limitations, and what's left before submission
Intentionally NOT renamed:
- FLOODIQ_BACKEND_SPEC.md β historical build spec, referenced in
earlier commit messages; renaming woβ¦
backend: full 7-agent pipeline working end-to-endβ
Adds the four remaining data agents (local/weather/news/archive),
the risk-analyst agent (Gemma 4 reasoning mode), and the advisor
agent. Orchestrator runs the 5 data agents in parallel, then risk,
then advisor, streaming agent_update SSE events along the way and
emitting the compiled dossier on complete.
Verified against 4521 S Drexel Blvd, Chicago β full assessment in
~30s, all 7 agents return structured JSON, and the risk agent's
reasoning_trace shows actual step-by-step math:
P = 1 - (1 - 0.005)^30
P = 1 - (0.995)^30
0.995^30 β 0.860
P β 0.14 (14%)
It also unit-converted the precipitation forecast (1.02 mm/hr β
0.04 in/hr) and compared to the 0.67 in/hr Chicago combined-sewer
overwhelm threshold from the system prompt before concluding low
near-term risk. Advisor wrote recs at 5th-grade level as instructed.
Spec deviations:
- archive_agent uses GDELT 24-month timespan as a Storm Events DB
proxy. NCEI Storm Events has no clean public JSON API (CSV bulk
per year only)β¦
Stands up the FastAPI backend with one agent fully wired through the
SSE pipe so the architecture is proven before we fan out the rest:
POST /api/assess β geocode β FEMA NFHL β Gemma 4 interpret
β agent_update events β complete{dossier}
Verified end-to-end against two cases:
- Houston Allen Pkwy (Zone AE) β is_sfha=true, requires_insurance=true
- Chicago S Drexel (Zone X) β gap_warning generated unprompted,
surfacing the urban-sewer-backup blind spot that is the whole pitch
Smoke-tested Gemma 4 on OpenRouter free tier (scripts/smoke_test.py):
basic completion, reasoning mode, and OpenAI-format tool calls all
pass on google/gemma-4-31b-it:free.
Spec bugs fixed while building:
- llm/client.extract_reasoning read message.reasoning_details[i].content;
the actual key is .text. Also falls back to top-level .reasoning string.
- tools/fema requested VERSION_ID, which does not exist on NFHL layer 28.
ArcGIS returned HTTP 200 with an {"error": ...} bβ¦