ship: v0.5.0 code changes — compare UI + cleanup pass
Browse filesApplies the v0.5.0 ship to the HF Space branch. Binary assets
(screenshots, deck.pptx) unchanged from XET-backed store.
Changes:
- Compare intent: two stacked RipMap instances per place, semantic
diff strip from parsed briefing sections (not prose-regex)
- Docker: Dockerfile.app, docker-compose.yml, .env.example
- Docs: README rewrite, DOCKER-PUBLISH.md runbook
- Svelte build: updated immutable hashes for compare-UI rebuild
- Tests: probe_50, test_integration updates
- scripts/probe_50.py: ruff auto-fixes
- web/main.py: import order normalisation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- .env.example +34 -0
- .gitignore +21 -0
- ARCHITECTURE.md +123 -69
- Dockerfile.app +71 -0
- OPEN-ISSUES.md +59 -0
- README.md +190 -41
- docker-compose.yml +52 -0
- docs/DEMO-QUERIES.md +131 -0
- docs/DOCKER-PUBLISH.md +140 -0
- scripts/probe_50.py +277 -0
- slides/VIDEO_TRANSCRIPT.md +159 -0
- tests/probe_50_results.json +1308 -0
- tests/queries_50.json +788 -0
- tests/test_integration.py +11 -2
- web/main.py +2 -2
- web/sveltekit/build/200.html +8 -8
- web/sveltekit/build/_app/immutable/assets/4.C9CQZyPb.css +1 -0
- web/sveltekit/build/_app/immutable/chunks/BbRPgcjS.js +1 -0
- web/sveltekit/build/_app/immutable/chunks/BgqmyNr8.js +1 -0
- web/sveltekit/build/_app/immutable/entry/app.DtHxgVfE.js +2 -0
- web/sveltekit/build/_app/immutable/entry/start.BkS8JQ5_.js +1 -0
- web/sveltekit/build/_app/immutable/nodes/0.LAFF30Kg.js +2 -0
- web/sveltekit/build/_app/immutable/nodes/1.MkU5tCTk.js +1 -0
- web/sveltekit/build/_app/immutable/nodes/2.vc91Ib0z.js +4 -0
- web/sveltekit/build/_app/immutable/nodes/3.B7oy3YuI.js +7 -0
- web/sveltekit/build/_app/immutable/nodes/4.B_VNCC6U.js +1 -0
- web/sveltekit/build/_app/version.json +1 -1
- web/sveltekit/build/index.html +9 -9
- web/sveltekit/build/q/sample.html +8 -8
- web/sveltekit/src/lib/client/agentStream.ts +2 -0
- web/sveltekit/src/lib/components/briefing/CompareBriefing.svelte +60 -66
- web/sveltekit/src/routes/q/[queryId]/+page.svelte +119 -4
.env.example
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Riprap environment configuration.
|
| 2 |
+
#
|
| 3 |
+
# Copy this file to `.env` and fill in the values that match the
|
| 4 |
+
# inference backend you want to talk to. The default profile runs
|
| 5 |
+
# only the app container, so both the LLM (vLLM serving Granite 4.1)
|
| 6 |
+
# and the ML specialist service must be reachable at HTTP endpoints.
|
| 7 |
+
#
|
| 8 |
+
# Three common configurations:
|
| 9 |
+
#
|
| 10 |
+
# 1. Easiest — talk to the live demo's backends. Adam runs a public
|
| 11 |
+
# MI300X droplet for the hackathon; if it's still up at demo time,
|
| 12 |
+
# both endpoints are reachable from anywhere.
|
| 13 |
+
#
|
| 14 |
+
# 2. Self-hosted — bring up your own MI300X droplet via
|
| 15 |
+
# docs/DROPLET-RUNBOOK.md, then point both URLs at it.
|
| 16 |
+
#
|
| 17 |
+
# 3. Full local — use `docker compose --profile with-models up` to
|
| 18 |
+
# run the riprap-models service yourself (requires a GPU on your
|
| 19 |
+
# box) and point a separate vLLM container at Granite 4.1.
|
| 20 |
+
|
| 21 |
+
# ---- Granite 4.1 reconciler (vLLM, OpenAI-compatible) -----------------
|
| 22 |
+
# Set to "ollama" instead of "vllm" if you have a local Ollama with
|
| 23 |
+
# granite4.1:8b pulled and want to use that.
|
| 24 |
+
RIPRAP_LLM_PRIMARY=vllm
|
| 25 |
+
RIPRAP_LLM_BASE_URL=http://your-vllm-host:8000/v1
|
| 26 |
+
RIPRAP_LLM_API_KEY=your-token-here
|
| 27 |
+
|
| 28 |
+
# ---- ML specialist service (Prithvi, TerraMind, GLiNER, etc.) ---------
|
| 29 |
+
RIPRAP_ML_BASE_URL=http://your-ml-host:7860
|
| 30 |
+
RIPRAP_ML_API_KEY=your-token-here
|
| 31 |
+
|
| 32 |
+
# ---- Backend pill labels (cosmetic, shown top-right of the UI) --------
|
| 33 |
+
RIPRAP_HARDWARE_LABEL=AMD MI300X
|
| 34 |
+
RIPRAP_ENGINE_LABEL=Granite 4.1 / vLLM
|
.gitignore
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
__pycache__/
|
| 2 |
*.py[cod]
|
| 3 |
*.egg-info/
|
|
|
|
|
|
|
| 4 |
.venv/
|
| 5 |
.env
|
| 6 |
.DS_Store
|
| 7 |
outputs/
|
| 8 |
node_modules/
|
| 9 |
*.tmp
|
|
|
|
| 10 |
.ruff_cache/
|
| 11 |
.pytest_cache/
|
|
|
|
| 12 |
|
| 13 |
# Claude Code context (per-machine, not for the public repo)
|
| 14 |
CLAUDE.md
|
|
@@ -22,6 +34,7 @@ data/*.legacy_*
|
|
| 22 |
web/svelte/node_modules/
|
| 23 |
web/sveltekit/node_modules/
|
| 24 |
web/sveltekit/.svelte-kit/
|
|
|
|
| 25 |
|
| 26 |
# Experiments — cached HF model downloads, training artifacts, intermediate
|
| 27 |
# fixtures. RESULTS.md, NOTES.md, and source code stay tracked.
|
|
@@ -46,6 +59,14 @@ slides/deck.pptx
|
|
| 46 |
*.bak
|
| 47 |
*.swp
|
| 48 |
*.swo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
# Sensitive
|
| 51 |
AMD_TOKEN
|
|
|
|
| 1 |
+
# Session artifacts
|
| 2 |
+
*MORNING-BRIEF*.md
|
| 3 |
+
*OVERNIGHT*.md
|
| 4 |
+
*COMMS-OVERNIGHT*.md
|
| 5 |
+
CODE-MORNING-BRIEF*.md
|
| 6 |
+
MONDAY.md
|
| 7 |
+
docs/sessions/
|
| 8 |
+
|
| 9 |
__pycache__/
|
| 10 |
*.py[cod]
|
| 11 |
*.egg-info/
|
| 12 |
+
dist/
|
| 13 |
+
build/
|
| 14 |
.venv/
|
| 15 |
.env
|
| 16 |
.DS_Store
|
| 17 |
outputs/
|
| 18 |
node_modules/
|
| 19 |
*.tmp
|
| 20 |
+
*.log
|
| 21 |
.ruff_cache/
|
| 22 |
.pytest_cache/
|
| 23 |
+
.ipynb_checkpoints/
|
| 24 |
|
| 25 |
# Claude Code context (per-machine, not for the public repo)
|
| 26 |
CLAUDE.md
|
|
|
|
| 34 |
web/svelte/node_modules/
|
| 35 |
web/sveltekit/node_modules/
|
| 36 |
web/sveltekit/.svelte-kit/
|
| 37 |
+
web/sveltekit/build/
|
| 38 |
|
| 39 |
# Experiments — cached HF model downloads, training artifacts, intermediate
|
| 40 |
# fixtures. RESULTS.md, NOTES.md, and source code stay tracked.
|
|
|
|
| 59 |
*.bak
|
| 60 |
*.swp
|
| 61 |
*.swo
|
| 62 |
+
.playwright-mcp/
|
| 63 |
+
|
| 64 |
+
# Demo recordings (large; not committed)
|
| 65 |
+
assets/video/
|
| 66 |
+
|
| 67 |
+
# Local env overlays
|
| 68 |
+
.env.local
|
| 69 |
+
*.local.env
|
| 70 |
|
| 71 |
# Sensitive
|
| 72 |
AMD_TOKEN
|
ARCHITECTURE.md
CHANGED
|
@@ -173,24 +173,39 @@ The full chain, in execution order:
|
|
| 173 |
└────────────┬────────────────────────────────┘
|
| 174 |
▼
|
| 175 |
┌─────────────────────────────────────────────┐
|
| 176 |
-
│
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
│ query corpus of 5 NYC agency PDFs │ relevant to this address
|
|
|
|
| 178 |
└────────────┬────────────────────────────────┘
|
| 179 |
▼
|
| 180 |
┌─────────────────────────────────────────────┐
|
| 181 |
-
│
|
| 182 |
-
│ reads all "documents" produced by 1-
|
| 183 |
-
│
|
| 184 |
└────────────┬────────────────────────────────┘
|
| 185 |
▼
|
| 186 |
cited briefing
|
| 187 |
+ tier badge + evidence cards + map
|
| 188 |
```
|
| 189 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
Each step is implemented as a `@action` in `app/fsm.py`. The Burr
|
| 191 |
-
runtime handles
|
| 192 |
-
|
| 193 |
-
shows live as the FSM runs.
|
| 194 |
|
| 195 |
### 3.1 What every specialist does, plain language
|
| 196 |
|
|
@@ -207,9 +222,12 @@ shows live as the FSM runs.
|
|
| 207 |
| 9 | **ttm_forecast** *(live)* | Granite TTM r2 zero-shot forecast of the surge **residual** at the Battery for the next ~9.6 h. NOAA already publishes the astronomical tide; TTM forecasts the part NOAA doesn't. | live (model-derived) |
|
| 208 |
| 10 | **microtopo** | LiDAR-derived terrain features at the point: elevation, HAND, TWI, local relief percentile. | proxy |
|
| 209 |
| 11 | **ida_hwm** | USGS Hurricane Ida 2021 high-water marks. Actual measured water heights surveyed in the days after the storm. | empirical |
|
| 210 |
-
| 12 | **
|
| 211 |
-
| 13 | **
|
| 212 |
-
| 14 | **
|
|
|
|
|
|
|
|
|
|
| 213 |
|
| 214 |
### 3.2 Worked example: 2940 Brighton 3rd St, Brooklyn
|
| 215 |
|
|
@@ -261,18 +279,44 @@ saw those headers and didn't invent them.
|
|
| 261 |
|
| 262 |
---
|
| 263 |
|
| 264 |
-
## 4.
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 276 |
|
| 277 |
---
|
| 278 |
|
|
@@ -431,11 +475,12 @@ loading. Mellea's `OllamaBackend` explicitly raises
|
|
| 431 |
|
| 432 |
| Model | Params | Runtime | Role |
|
| 433 |
|-------|--------|---------|------|
|
| 434 |
-
| **Granite 4.1 :3b**
|
| 435 |
-
| **Granite 4.1 :8b** | 8 B | Ollama
|
| 436 |
| **Granite Embedding 278M** | 278 M | sentence-transformers (CPU) | RAG retrieval over 5 policy PDFs at query time. |
|
| 437 |
-
| **Prithvi-EO 2.0** | 300 M | TerraTorch (offline pre-compute) |
|
| 438 |
-
| **Granite TimeSeries TTM r2** | 1.5 M | granite-tsfm (CPU) | Zero-shot forecast of the Battery surge residual, ~9.6 h horizon. |
|
|
|
|
| 439 |
|
| 440 |
**Granite 4.1 ≠ Granite Time Series.** Granite 4.1 is IBM's chat-LLM
|
| 441 |
family. Granite TimeSeries TTM is a separate IBM Research product
|
|
@@ -443,6 +488,12 @@ line (Ekambaram et al. 2024, NeurIPS). Both happen to share the
|
|
| 443 |
"Granite" brand but have different architectures, training data, and
|
| 444 |
authors.
|
| 445 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
### 7.1 Why Prithvi runs offline
|
| 447 |
|
| 448 |
Prithvi-EO 2.0 with TerraTorch needs a GPU and minutes per HLS tile.
|
|
@@ -514,7 +565,9 @@ riprap-nyc/
|
|
| 514 |
single_address.py drives the linear FSM with strict reconcile
|
| 515 |
neighborhood.py polygon-aggregated specialists
|
| 516 |
development_check.py DOB permit overlap with flood polygons
|
| 517 |
-
|
|
|
|
|
|
|
| 518 |
areas/
|
| 519 |
nta.py NYC NTA 2020 polygon resolver
|
| 520 |
|
|
@@ -541,29 +594,30 @@ riprap-nyc/
|
|
| 541 |
mta_entrances.py i9wp-a4ja
|
| 542 |
|
| 543 |
web/
|
| 544 |
-
main.py FastAPI
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 |
static/
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
agent.js EventSource client; sets properties on
|
| 549 |
-
<r-briefing> / <r-trace> / <r-sources-footer>
|
| 550 |
-
report.html / .js auditable PDF-formatted export view
|
| 551 |
-
compare.html / .js two-address side-by-side
|
| 552 |
-
register.html / .js bulk register browser
|
| 553 |
-
style.css IBM Plex Sans, Planning Labs idiom
|
| 554 |
-
dist/ Svelte 5 custom-element bundle (committed.
|
| 555 |
-
HF Spaces doesn't run a Node build).
|
| 556 |
Built from web/svelte/ via `npm run build`.
|
| 557 |
|
| 558 |
-
web/
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
src/lib/
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 567 |
|
| 568 |
scripts/ offline pre-compute + diagnostic probes
|
| 569 |
run_prithvi_ida.py
|
|
@@ -636,28 +690,28 @@ riprap-nyc/
|
|
| 636 |
|
| 637 |
### 12.1 Hugging Face Spaces (production)
|
| 638 |
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
`
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
- `
|
| 655 |
-
- `
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
The
|
| 660 |
-
|
| 661 |
|
| 662 |
### 12.2 Local development
|
| 663 |
|
|
|
|
| 173 |
└────────────┬────────────────────────────────┘
|
| 174 |
▼
|
| 175 |
┌─────────────────────────────────────────────┐
|
| 176 |
+
│ 10. microtopo DEM + TWI + HAND at point │ proxy
|
| 177 |
+
│ 11. ida_hwm USGS Ida 2021 HWM proximity│ empirical
|
| 178 |
+
│ 12. mta_entrance MTA subway entrance exposure│ empirical
|
| 179 |
+
│ 13. prithvi_v2 Prithvi Ida flood polys │ empirical (model-derived)
|
| 180 |
+
│ 14. prithvi_live live Prithvi inference │ (gpu-only; skipped cpu-basic)
|
| 181 |
+
│ 15. terramind TerraMind LULC synthesis │ (gpu-only; skipped cpu-basic)
|
| 182 |
+
└────────────┬────────────────────────────────┘
|
| 183 |
+
▼
|
| 184 |
+
┌─────────────────────────────────────────────┐
|
| 185 |
+
│ 16. rag (Granite Embedding 278M) │ retrieves policy paragraphs
|
| 186 |
│ query corpus of 5 NYC agency PDFs │ relevant to this address
|
| 187 |
+
│ 17. gliner_extract (GLiNER medium-v2.1) │ entity extraction over RAG hits
|
| 188 |
└────────────┬────────────────────────────────┘
|
| 189 |
▼
|
| 190 |
┌─────────────────────────────────────────────┐
|
| 191 |
+
│ 18. reconcile (Granite 4.1 :8b) │ document-grounded synthesis
|
| 192 |
+
│ reads all "documents" produced by 1-17 │ → 4-section cited paragraph
|
| 193 |
+
│ Mellea rejects ungrounded outputs │ → audit trail
|
| 194 |
└────────────┬────────────────────────────────┘
|
| 195 |
▼
|
| 196 |
cited briefing
|
| 197 |
+ tier badge + evidence cards + map
|
| 198 |
```
|
| 199 |
|
| 200 |
+
The `single_address` path emits **24 step events** (including the live
|
| 201 |
+
sub-specialists ttm_311_forecast, ttm_battery_surge, floodnet_forecast,
|
| 202 |
+
and the eo_chip_fetch / terramind_lulc / terramind_buildings gates).
|
| 203 |
+
`neighborhood` emits 8–10 steps (NTA-level specialists only; per-address
|
| 204 |
+
registers don't run).
|
| 205 |
+
|
| 206 |
Each step is implemented as a `@action` in `app/fsm.py`. The Burr
|
| 207 |
+
runtime handles state-passing between actions and emits a trace record
|
| 208 |
+
per step (timing, ok/err, summary fields) which the front-end shows live.
|
|
|
|
| 209 |
|
| 210 |
### 3.1 What every specialist does, plain language
|
| 211 |
|
|
|
|
| 222 |
| 9 | **ttm_forecast** *(live)* | Granite TTM r2 zero-shot forecast of the surge **residual** at the Battery for the next ~9.6 h. NOAA already publishes the astronomical tide; TTM forecasts the part NOAA doesn't. | live (model-derived) |
|
| 223 |
| 10 | **microtopo** | LiDAR-derived terrain features at the point: elevation, HAND, TWI, local relief percentile. | proxy |
|
| 224 |
| 11 | **ida_hwm** | USGS Hurricane Ida 2021 high-water marks. Actual measured water heights surveyed in the days after the storm. | empirical |
|
| 225 |
+
| 12 | **mta_entrance_exposure** | MTA subway entrances within radius: how many, how many are inside Sandy 2012 zone, how many are in DEP Extreme-2080. | empirical |
|
| 226 |
+
| 13 | **prithvi_eo_v2** | Pre-computed point-in-polygon against 166 Prithvi-derived Ida 2021 flood polygons (offline-built; instant at request time). | empirical (model-derived) |
|
| 227 |
+
| 14 | **prithvi_eo_live** / **terramind_synthesis** | Live Prithvi / TerraMind inference over fresh EO chips. GPU-only; silenced (deterministic skip) on cpu-basic HF Space. | empirical (model-derived) |
|
| 228 |
+
| 15 | **rag** | Granite Embedding 278M retrieves the most-relevant paragraphs from 5 NYC policy PDFs (Comptroller, NPCC4, MTA, NYCHA, ConEd) given the address's borough + which scenarios fired. | policy |
|
| 229 |
+
| 16 | **gliner_extract** | GLiNER medium-v2.1 runs named-entity extraction over the RAG-retrieved paragraphs: locations, agencies, dates, infrastructure-project names. Results ride into the reconciler as additional grounding context. | ancillary |
|
| 230 |
+
| 17 | **reconcile** | Granite 4.1 :8b reads all documents produced by steps 1–16 and writes the cited briefing paragraph. Mellea rejection-sampling validates 4 grounding requirements; up to 3 attempts. See [§6](#6-document-grounded-reconciliation). | LLM synthesis |
|
| 231 |
|
| 232 |
### 3.2 Worked example: 2940 Brighton 3rd St, Brooklyn
|
| 233 |
|
|
|
|
| 279 |
|
| 280 |
---
|
| 281 |
|
| 282 |
+
## 4. Five planner intents
|
| 283 |
+
|
| 284 |
+
The planner (`app/planner.py`) classifies every free-text query into one of
|
| 285 |
+
five intents before the FSM runs. This happens in a single Granite 4.1 call
|
| 286 |
+
that streams its JSON output to the client as `plan_token` events.
|
| 287 |
+
|
| 288 |
+
| Intent | Triggered by | FSM path | Steps |
|
| 289 |
+
|-----------------------|-----------------------------------------------|----------------------------------|-------|
|
| 290 |
+
| `single_address` | Fully-qualified street address | Full linear FSM (geocode → 19 specialists → reconcile) | 24 |
|
| 291 |
+
| `neighborhood` | NTA name, borough name, bare zip | NTA-level specialists only (no per-address registers) | 8–10 |
|
| 292 |
+
| `compare` | "A vs B", "compare X to Y" | Two sequential single_address runs; merged two-column paragraph | 2 × 24 |
|
| 293 |
+
| `development_check` | "what's being built at X", "is Y risky" | DOB filings + flood layers | 3–5 |
|
| 294 |
+
| `live_now` | "is it flooding now", "current alerts" | Live-only specialists (tides, alerts, obs) — no Mellea | 4 |
|
| 295 |
+
| `not_implemented` | Retrospective, ranking, cross-city queries | Returns rationale immediately | 0 |
|
| 296 |
+
|
| 297 |
+
### Compare intent detail
|
| 298 |
+
|
| 299 |
+
`_run_compare()` in `web/main.py` executes the full `single_address` FSM
|
| 300 |
+
sequentially for each target, then merges the two paragraphs under
|
| 301 |
+
`## PLACE A: …` / `## PLACE B: …` headers separated by `---`. The
|
| 302 |
+
`CompareBriefing.svelte` component renders this as a two-column layout with
|
| 303 |
+
a "Key differences" delta bar above. During streaming the tokens are rendered
|
| 304 |
+
in a single column (sequential); the two-column layout appears when the
|
| 305 |
+
`final` event lands.
|
| 306 |
+
|
| 307 |
+
**Registered routes**
|
| 308 |
+
|
| 309 |
+
| Path | Serves |
|
| 310 |
+
|-------------------------------------------|--------|
|
| 311 |
+
| `/` | SvelteKit landing + live query UI |
|
| 312 |
+
| `/api/agent/stream?q=…` | SSE stream — planner + all intent paths |
|
| 313 |
+
| `/register/{schools,nycha,mta_entrances}` | Pre-computed bulk register browser |
|
| 314 |
+
| `/legacy`, `/single`, `/compare`, `/register/*` | Legacy custom-element bundle (compatibility) |
|
| 315 |
+
|
| 316 |
+
Registers are pre-computed because running 1,900 reconciler calls at request
|
| 317 |
+
time is a non-starter; the register build runs offline
|
| 318 |
+
(`scripts/build_*_register.py`) and results are loaded from
|
| 319 |
+
`data/registers/*.json` at boot.
|
| 320 |
|
| 321 |
---
|
| 322 |
|
|
|
|
| 475 |
|
| 476 |
| Model | Params | Runtime | Role |
|
| 477 |
|-------|--------|---------|------|
|
| 478 |
+
| **Granite 4.1 :3b alias** | 8 B† | Ollama or vLLM (AMD MI300X) | Planner (intent + specialist routing) + `live_now` reconciler. †Production alias `RIPRAP_OLLAMA_3B_TAG=granite4.1:8b` — planner runs 8b in production. |
|
| 479 |
+
| **Granite 4.1 :8b** | 8 B | Ollama or vLLM (AMD MI300X) | Synthesis reconciler for `single_address`, `neighborhood`, `development_check`, `compare`. Validated by Mellea (4 grounding requirements + reroll). |
|
| 480 |
| **Granite Embedding 278M** | 278 M | sentence-transformers (CPU) | RAG retrieval over 5 policy PDFs at query time. |
|
| 481 |
+
| **Prithvi-EO 2.0** | 300 M | TerraTorch (offline pre-compute) | NYC-Pluvial fine-tune; segmented Hurricane Ida 2021 pre/post Sentinel-2 polygons baked into `data/`. Fine-tune: `msradam/Prithvi-EO-2.0-NYC-Pluvial`. |
|
| 482 |
+
| **Granite TimeSeries TTM r2** | 1.5 M | granite-tsfm (CPU) | Zero-shot forecast of the Battery surge residual, ~9.6 h horizon. Fine-tune: `msradam/Granite-TTM-r2-Battery-Surge`. |
|
| 483 |
+
| **GLiNER medium-v2.1** | ~200 M | gliner (CPU) | Named-entity extraction over RAG hits (locations, agencies, dates, infrastructure). `urchade/gliner_medium-v2.1`. |
|
| 484 |
|
| 485 |
**Granite 4.1 ≠ Granite Time Series.** Granite 4.1 is IBM's chat-LLM
|
| 486 |
family. Granite TimeSeries TTM is a separate IBM Research product
|
|
|
|
| 488 |
"Granite" brand but have different architectures, training data, and
|
| 489 |
authors.
|
| 490 |
|
| 491 |
+
**LiteLLM Router.** All LLM calls go through `app/llm.py`, a ~250-line
|
| 492 |
+
shim over a LiteLLM Router. Two backends are wired: `RIPRAP_LLM_PRIMARY=ollama`
|
| 493 |
+
(local + HF Space default) and `RIPRAP_LLM_PRIMARY=vllm` (AMD MI300X demo
|
| 494 |
+
path, auto-fails over to Ollama). The shim normalizes role names and
|
| 495 |
+
citation-token format so the rest of the codebase is backend-agnostic.
|
| 496 |
+
|
| 497 |
### 7.1 Why Prithvi runs offline
|
| 498 |
|
| 499 |
Prithvi-EO 2.0 with TerraTorch needs a GPU and minutes per HLS tile.
|
|
|
|
| 565 |
single_address.py drives the linear FSM with strict reconcile
|
| 566 |
neighborhood.py polygon-aggregated specialists
|
| 567 |
development_check.py DOB permit overlap with flood polygons
|
| 568 |
+
llm.py LiteLLM Router shim — all LLM calls go here.
|
| 569 |
+
Routes to vLLM (AMD) or Ollama; normalizes
|
| 570 |
+
role names and citation token format.
|
| 571 |
areas/
|
| 572 |
nta.py NYC NTA 2020 polygon resolver
|
| 573 |
|
|
|
|
| 594 |
mta_entrances.py i9wp-a4ja
|
| 595 |
|
| 596 |
web/
|
| 597 |
+
main.py FastAPI. Primary SSE at /api/agent/stream.
|
| 598 |
+
_run_compare() handles the compare intent
|
| 599 |
+
(sequential single_address × 2; no separate
|
| 600 |
+
intent module). /api/backend returns live
|
| 601 |
+
backend descriptor for the UI pill.
|
| 602 |
static/
|
| 603 |
+
agent.html legacy primary UI (Svelte custom elements)
|
| 604 |
+
dist/ Svelte 5 custom-element bundle (committed).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 605 |
Built from web/svelte/ via `npm run build`.
|
| 606 |
|
| 607 |
+
web/sveltekit/ SvelteKit app (primary UI). Build →
|
| 608 |
+
web/sveltekit/build/. Served at / by FastAPI.
|
| 609 |
+
src/routes/
|
| 610 |
+
+page.svelte landing + query form
|
| 611 |
+
q/[queryId]/+page.svelte live query page (SSE stream consumer)
|
| 612 |
+
src/lib/components/
|
| 613 |
+
briefing/
|
| 614 |
+
Briefing.svelte 4-section cited paragraph renderer
|
| 615 |
+
CompareBriefing.svelte two-column compare layout + delta bar
|
| 616 |
+
shell/StatusPill.svelte AMD / Ollama / Local backend indicator
|
| 617 |
+
|
| 618 |
+
web/svelte/ Legacy Svelte 5 custom-element source.
|
| 619 |
+
Builds <r-briefing>, <r-trace>, <r-sources-footer>.
|
| 620 |
+
Still loaded by agent.html / register/*.html.
|
| 621 |
|
| 622 |
scripts/ offline pre-compute + diagnostic probes
|
| 623 |
run_prithvi_ida.py
|
|
|
|
| 690 |
|
| 691 |
### 12.1 Hugging Face Spaces (production)
|
| 692 |
|
| 693 |
+
**HF Space**: `lablab-ai-amd-developer-hackathon/riprap-nyc` (cpu-basic).
|
| 694 |
+
Serves the FastAPI + SvelteKit UI. Hardware: cpu-basic (no GPU).
|
| 695 |
+
|
| 696 |
+
**AMD MI300X droplet** (separate): vLLM + riprap-models containers
|
| 697 |
+
(`services/riprap-models/`). The Space talks to the droplet over HTTP;
|
| 698 |
+
env vars `RIPRAP_LLM_BASE_URL` / `RIPRAP_ML_BASE_URL` point at it.
|
| 699 |
+
The bootstrap droplet was destroyed 2026-05-06; redeploy via
|
| 700 |
+
`scripts/deploy_droplet.sh <ip> <token>`.
|
| 701 |
+
|
| 702 |
+
LLM routing: `RIPRAP_LLM_PRIMARY=vllm` → AMD MI300X (30–50× faster than
|
| 703 |
+
T4 Ollama). Falls over to local Ollama on connection failure. Backend
|
| 704 |
+
status visible in the UI pill (top-right corner; backed by `GET /api/backend`).
|
| 705 |
+
|
| 706 |
+
Verified warm query times on AMD MI300X + vLLM (2026-05-06 probe):
|
| 707 |
+
- `single_address`: 5–12 s (4/4 Mellea, 0–2 rerolls)
|
| 708 |
+
- `neighborhood`: 3–5 s
|
| 709 |
+
- `compare` (two sequential legs): ~15 s
|
| 710 |
+
|
| 711 |
+
Cold-start after container restart: ~30 s for vLLM kernel JIT compile + prefix cache warmup. Run one warm-up query before a demo.
|
| 712 |
+
|
| 713 |
+
The SvelteKit build in `web/sveltekit/build/` and the Svelte bundle in
|
| 714 |
+
`web/static/dist/` are both committed, so HF Spaces runs no Node build step.
|
| 715 |
|
| 716 |
### 12.2 Local development
|
| 717 |
|
Dockerfile.app
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Riprap — lightweight self-host image (FastAPI + SvelteKit).
|
| 2 |
+
#
|
| 3 |
+
# This is the "I want to run Riprap on my own machine, pointed at the
|
| 4 |
+
# live or my own MI300X" image. It does NOT ship Ollama, CUDA, or
|
| 5 |
+
# Granite weights — LLM inference is dispatched over HTTP via
|
| 6 |
+
# RIPRAP_LLM_BASE_URL (vLLM / OpenAI-compatible) and RIPRAP_ML_BASE_URL
|
| 7 |
+
# (the riprap-models GPU specialist service). See .env.example.
|
| 8 |
+
#
|
| 9 |
+
# For the heavy HF Space deployment (CUDA + bundled Ollama + Granite
|
| 10 |
+
# weights baked in), use the root-level Dockerfile instead.
|
| 11 |
+
#
|
| 12 |
+
# Build: docker build -t msradam/riprap-nyc:v0.5.0 -f Dockerfile.app .
|
| 13 |
+
# Run: docker run --rm -p 7860:7860 --env-file .env msradam/riprap-nyc:v0.5.0
|
| 14 |
+
|
| 15 |
+
# -----------------------------------------------------------------------
|
| 16 |
+
# Stage 1 — build the SvelteKit static bundle
|
| 17 |
+
# -----------------------------------------------------------------------
|
| 18 |
+
FROM node:20-slim AS frontend-build
|
| 19 |
+
|
| 20 |
+
WORKDIR /build
|
| 21 |
+
COPY web/sveltekit/package.json web/sveltekit/package-lock.json ./
|
| 22 |
+
RUN npm ci --no-audit --no-fund
|
| 23 |
+
|
| 24 |
+
COPY web/sveltekit/ ./
|
| 25 |
+
RUN npm run build
|
| 26 |
+
|
| 27 |
+
# -----------------------------------------------------------------------
|
| 28 |
+
# Stage 2 — Python runtime
|
| 29 |
+
# -----------------------------------------------------------------------
|
| 30 |
+
FROM python:3.10-slim AS runtime
|
| 31 |
+
|
| 32 |
+
ENV DEBIAN_FRONTEND=noninteractive \
|
| 33 |
+
PYTHONUNBUFFERED=1 \
|
| 34 |
+
PYTHONDONTWRITEBYTECODE=1 \
|
| 35 |
+
PIP_NO_CACHE_DIR=1 \
|
| 36 |
+
PIP_DISABLE_PIP_VERSION_CHECK=1
|
| 37 |
+
|
| 38 |
+
# Geo libs: geopandas / rasterio / fiona / pyproj need GDAL + GEOS +
|
| 39 |
+
# PROJ at runtime. curl for healthchecks.
|
| 40 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 41 |
+
curl ca-certificates \
|
| 42 |
+
gdal-bin libgdal-dev libgeos-dev libproj-dev \
|
| 43 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 44 |
+
|
| 45 |
+
WORKDIR /app
|
| 46 |
+
|
| 47 |
+
# Python deps first so a code-only edit doesn't bust the wheel cache.
|
| 48 |
+
COPY requirements.txt ./
|
| 49 |
+
RUN pip install --upgrade pip && pip install -r requirements.txt
|
| 50 |
+
|
| 51 |
+
# App code + fixtures + corpus.
|
| 52 |
+
COPY app/ ./app/
|
| 53 |
+
COPY web/main.py ./web/main.py
|
| 54 |
+
COPY web/static/ ./web/static/
|
| 55 |
+
COPY scripts/ ./scripts/
|
| 56 |
+
COPY data/ ./data/
|
| 57 |
+
COPY corpus/ ./corpus/
|
| 58 |
+
|
| 59 |
+
# Pre-built SvelteKit bundle from stage 1.
|
| 60 |
+
COPY --from=frontend-build /build/build ./web/sveltekit/build
|
| 61 |
+
|
| 62 |
+
EXPOSE 7860
|
| 63 |
+
|
| 64 |
+
# Default talks to remote LLM + ML backends via the env vars in
|
| 65 |
+
# .env.example. RIPRAP_LLM_PRIMARY=vllm makes the LiteLLM Router
|
| 66 |
+
# expect an OpenAI-compatible endpoint at RIPRAP_LLM_BASE_URL.
|
| 67 |
+
ENV RIPRAP_LLM_PRIMARY=vllm \
|
| 68 |
+
PYTHONPATH=/app
|
| 69 |
+
|
| 70 |
+
CMD ["uvicorn", "web.main:app", "--host", "0.0.0.0", "--port", "7860", \
|
| 71 |
+
"--log-level", "info", "--proxy-headers"]
|
OPEN-ISSUES.md
CHANGED
|
@@ -50,3 +50,62 @@ a human eye on what the closure does.
|
|
| 50 |
in the file. May be a bug (intended to call `api.upload_file(...)`) or
|
| 51 |
a leftover from an edit. Needs a human eye.
|
| 52 |
**Fix:** Either use `api` in the upload calls, or remove the assignment.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
in the file. May be a bug (intended to call `api.upload_file(...)`) or
|
| 51 |
a leftover from an edit. Needs a human eye.
|
| 52 |
**Fix:** Either use `api` in the upload calls, or remove the assignment.
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## 5. `_merge_mellea` puts requirements in both `passed` and `failed`
|
| 57 |
+
|
| 58 |
+
**File:** `web/main.py:563`
|
| 59 |
+
**Issue:** Uses `set(a_passed + b_passed)` (union) for `requirements_passed`. If
|
| 60 |
+
leg A fails `citations_resolve` but leg B passes it, `citations_resolve`
|
| 61 |
+
ends up in both `requirements_passed` and `requirements_failed` in the
|
| 62 |
+
merged Mellea dict. The UI then shows "4/4 passed" with a contradictory
|
| 63 |
+
"failed: citations_resolve" entry.
|
| 64 |
+
**Fix:** Use intersection for `requirements_passed`:
|
| 65 |
+
```python
|
| 66 |
+
"requirements_passed": list(set(_lst(a, "requirements_passed")) & set(_lst(b, "requirements_passed"))),
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
---
|
| 70 |
+
|
| 71 |
+
## 6. `mellea.attempts` wrong field name in `persistSnapshot`
|
| 72 |
+
|
| 73 |
+
**File:** `web/sveltekit/src/routes/q/[queryId]/+page.svelte:598`
|
| 74 |
+
**Issue:** `finalResult?.mellea?.attempts` is always `undefined` — the Mellea
|
| 75 |
+
field is `n_attempts`. Falls back silently to the streaming `attempt` counter.
|
| 76 |
+
The PDF/print snapshot always has wrong attempt metadata.
|
| 77 |
+
**Fix:**
|
| 78 |
+
```js
|
| 79 |
+
attempts: finalResult?.mellea?.n_attempts ?? attempt
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
---
|
| 83 |
+
|
| 84 |
+
## 7. NTA-typed compare targets fall through to single_address
|
| 85 |
+
|
| 86 |
+
**File:** `web/main.py:514`
|
| 87 |
+
**Issue:** `addr_targets = [t for t in p.targets if t.get("type") == "address"]`
|
| 88 |
+
filters out NTA targets. When the planner returns `type: "nta"` for both sides
|
| 89 |
+
of a compare query (e.g. "Compare Two Bridges to Battery Park City"), `addr_targets`
|
| 90 |
+
is empty, the fallback runs the full raw query text through `single_address`, the
|
| 91 |
+
geocoder finds no valid address, and the response is "No grounded data available."
|
| 92 |
+
**Fix:** Accept `"nta"` targets and route each through the `neighborhood` intent
|
| 93 |
+
instead of `single_address`, or accept both type values and pick the right intent
|
| 94 |
+
per target.
|
| 95 |
+
|
| 96 |
+
---
|
| 97 |
+
|
| 98 |
+
## 8. `iter_steps` returns without `final` when FSM fails pre-iteration
|
| 99 |
+
|
| 100 |
+
**File:** `app/fsm.py:1292–1294`
|
| 101 |
+
**Issue:** If `app.iterate()` raises before any action completes (so
|
| 102 |
+
`final_state_holder["state"]` is never set), `iter_steps` does a bare `return`
|
| 103 |
+
after the error event. No `final` event is yielded. The SSE stream closes with
|
| 104 |
+
an open Stone in the trace UI (the `stone_done` for the active Stone never fires),
|
| 105 |
+
leaving the frontend in a non-terminal render state.
|
| 106 |
+
**Fix:** Yield a synthetic `final` with an error flag when `state is None`:
|
| 107 |
+
```python
|
| 108 |
+
if state is None:
|
| 109 |
+
yield {"kind": "final", "paragraph": "", "error": "FSM failed before any action completed"}
|
| 110 |
+
return
|
| 111 |
+
```
|
README.md
CHANGED
|
@@ -11,15 +11,17 @@ pinned: false
|
|
| 11 |
<img src="assets/logo@2x.png" width="72" height="72" alt="Riprap dam mark" />
|
| 12 |
</p>
|
| 13 |
|
| 14 |
-
# Riprap
|
| 15 |
|
| 16 |
Riprap takes any NYC address (or neighborhood, or development-permit
|
| 17 |
-
query) and produces a four-section briefing
|
| 18 |
-
Modeled scenarios, Policy context
|
| 19 |
-
a `[doc_id]` citation
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
| 23 |
sampling. Sentences that fail one of four grounding checks
|
| 24 |
(`numerics_grounded`, `no_placeholder_tokens`, `citations_dense`,
|
| 25 |
`citations_resolve`) are rerolled with surgical feedback until the
|
|
@@ -33,7 +35,67 @@ Live demo: <https://lablab-ai-amd-developer-hackathon-riprap-nyc.hf.space>
|
|
| 33 |
|
| 34 |

|
| 35 |
|
| 36 |
-
*A citation-grounded flood-exposure briefing for 80 Pioneer Street in
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
---
|
| 39 |
|
|
@@ -55,8 +117,8 @@ group those probes into five legible roles:
|
|
| 55 |
| **Lodestone** | The Projector. What's coming. | NWS public flood alerts, Granite TTM r2 surge nowcast (zero-shot, 6-min cadence, 9.6 h horizon), per-address 311 weekly forecast, FloodNet sensor recurrence forecast, **Granite-TTM-r2-Battery-Surge fine-tune** (96 h hourly horizon) |
|
| 56 |
| **Capstone** | The Synthesiser. Citation-grounded briefing. | Granite 4.1 + Mellea rejection sampling |
|
| 57 |
|
| 58 |
-
The four data-Stones run sequentially per query; the Capstone
|
| 59 |
-
their documents into one cited paragraph.
|
| 60 |
|
| 61 |
---
|
| 62 |
|
|
@@ -80,11 +142,42 @@ NYC Battery storm-surge nowcast fine-tune of Granite TimeSeries TTM
|
|
| 80 |
r2. Test MAE 0.1091 m, −41% vs persistence and −25% vs zero-shot.
|
| 81 |
|
| 82 |
All three are loaded at runtime by their respective FSM probes in
|
| 83 |
-
`app/context/` and `app/live/`.
|
|
|
|
| 84 |
|
| 85 |
---
|
| 86 |
|
| 87 |
-
## Architecture
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
- `app/stones/`: the Stones taxonomy (NAME / TAGLINE / SOURCES /
|
| 90 |
collect()) over the FSM probes.
|
|
@@ -93,55 +186,111 @@ All three are loaded at runtime by their respective FSM probes in
|
|
| 93 |
document-role messages in canonical Stone order.
|
| 94 |
- `app/mellea_validator.py`: strict reconcile path (4-check rejection
|
| 95 |
sampling).
|
| 96 |
-
- `app/llm.py`: LiteLLM Router shim. Routes to Ollama
|
| 97 |
-
|
| 98 |
- `web/main.py`: FastAPI + SSE. The stream emits
|
| 99 |
`plan / step / token / mellea_attempt / final` events plus the
|
| 100 |
`stone_start / stone_done` envelope around each Stone group.
|
| 101 |
- `web/sveltekit/`: primary UI (SvelteKit + adapter-static).
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
`experiments/20_ttm_battery_surge/`: full reproduction recipes for
|
| 105 |
-
the three HF artifacts above.
|
| 106 |
|
| 107 |
---
|
| 108 |
|
| 109 |
-
##
|
| 110 |
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
.
|
| 114 |
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
-
|
| 122 |
-
.venv/bin/python scripts/probe_mellea.py --query "Hollis" --runs 5
|
| 123 |
|
| 124 |
-
#
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
```
|
| 127 |
|
| 128 |
---
|
| 129 |
|
| 130 |
## License
|
| 131 |
|
| 132 |
-
Apache 2.0 (this repository). The three NYC-specialised
|
| 133 |
-
are also Apache 2.0; underlying upstream models retain their
|
| 134 |
-
permissive licences (see each `MODEL_CARD.md`).
|
| 135 |
|
| 136 |
HF Space configuration reference:
|
| 137 |
<https://huggingface.co/docs/hub/spaces-config-reference>.
|
| 138 |
|
| 139 |
---
|
| 140 |
|
| 141 |
-
##
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
<img src="assets/logo@2x.png" width="72" height="72" alt="Riprap dam mark" />
|
| 12 |
</p>
|
| 13 |
|
| 14 |
+
# Riprap — citation-grounded NYC flood-exposure briefings
|
| 15 |
|
| 16 |
Riprap takes any NYC address (or neighborhood, or development-permit
|
| 17 |
+
query) and produces a four-section briefing — Status, Empirical
|
| 18 |
+
evidence, Modeled scenarios, Policy context. Every numeric claim is
|
| 19 |
+
anchored to a `[doc_id]` citation that resolves to a named primary
|
| 20 |
+
public-record source. If the model cannot cite a number, the model
|
| 21 |
+
does not publish that number.
|
| 22 |
+
|
| 23 |
+
The Capstone reconciler is **IBM Granite 4.1 8B**, served via Ollama
|
| 24 |
+
on T4 or vLLM on AMD MI300X, wrapped in **Mellea**-validated rejection
|
| 25 |
sampling. Sentences that fail one of four grounding checks
|
| 26 |
(`numerics_grounded`, `no_placeholder_tokens`, `citations_dense`,
|
| 27 |
`citations_resolve`) are rerolled with surgical feedback until the
|
|
|
|
| 35 |
|
| 36 |

|
| 37 |
|
| 38 |
+
*A citation-grounded flood-exposure briefing for 80 Pioneer Street in
|
| 39 |
+
Red Hook. Generated in roughly 7 seconds against AMD MI300X. Every
|
| 40 |
+
numeric claim cites a primary public-record source.*
|
| 41 |
+
|
| 42 |
+
---
|
| 43 |
+
|
| 44 |
+
## Quickstart
|
| 45 |
+
|
| 46 |
+
Three ways to use Riprap, in increasing order of self-host:
|
| 47 |
+
|
| 48 |
+
### 1. Try the live demo
|
| 49 |
+
|
| 50 |
+
The hosted Space runs the full pipeline against a live AMD MI300X
|
| 51 |
+
inference backend. Type any NYC address.
|
| 52 |
+
|
| 53 |
+
<https://lablab-ai-amd-developer-hackathon-riprap-nyc.hf.space>
|
| 54 |
+
|
| 55 |
+
### 2. Run locally with Docker
|
| 56 |
+
|
| 57 |
+
```bash
|
| 58 |
+
git clone https://github.com/msradam/riprap-nyc
|
| 59 |
+
cd riprap-nyc
|
| 60 |
+
cp .env.example .env
|
| 61 |
+
# edit .env to point RIPRAP_LLM_BASE_URL / RIPRAP_ML_BASE_URL at
|
| 62 |
+
# either the live demo's backends or your own self-hosted instance
|
| 63 |
+
docker compose up
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
Visit `http://localhost:7860`.
|
| 67 |
+
|
| 68 |
+
To self-host the GPU inference half (vLLM + the ML specialist
|
| 69 |
+
service) on an AMD ROCm or NVIDIA CUDA box, run:
|
| 70 |
+
|
| 71 |
+
```bash
|
| 72 |
+
docker compose --profile with-models up
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
Full single-command MI300X bring-up via DigitalOcean: see
|
| 76 |
+
[`docs/DROPLET-RUNBOOK.md`](docs/DROPLET-RUNBOOK.md).
|
| 77 |
+
|
| 78 |
+
### 3. Develop
|
| 79 |
+
|
| 80 |
+
```bash
|
| 81 |
+
# Python 3.12 venv via uv
|
| 82 |
+
uv venv && uv pip install -r requirements.txt
|
| 83 |
+
|
| 84 |
+
# SvelteKit frontend (committed pre-built; only rebuild if sources change)
|
| 85 |
+
cd web/sveltekit && npm ci && npm run build && cd ../..
|
| 86 |
+
|
| 87 |
+
# Local server (Ollama primary)
|
| 88 |
+
.venv/bin/uvicorn web.main:app --host 127.0.0.1 --port 7860
|
| 89 |
+
|
| 90 |
+
# Local server pointed at AMD MI300X (vLLM primary, Ollama fallback)
|
| 91 |
+
RIPRAP_LLM_PRIMARY=vllm \
|
| 92 |
+
RIPRAP_LLM_BASE_URL=http://<droplet-ip>:8000/v1 \
|
| 93 |
+
RIPRAP_LLM_API_KEY=<token> \
|
| 94 |
+
.venv/bin/uvicorn web.main:app --host 127.0.0.1 --port 7860
|
| 95 |
+
|
| 96 |
+
# End-to-end address suite (5 NYC addresses, intent-aware checks)
|
| 97 |
+
.venv/bin/python scripts/probe_addresses.py
|
| 98 |
+
```
|
| 99 |
|
| 100 |
---
|
| 101 |
|
|
|
|
| 117 |
| **Lodestone** | The Projector. What's coming. | NWS public flood alerts, Granite TTM r2 surge nowcast (zero-shot, 6-min cadence, 9.6 h horizon), per-address 311 weekly forecast, FloodNet sensor recurrence forecast, **Granite-TTM-r2-Battery-Surge fine-tune** (96 h hourly horizon) |
|
| 118 |
| **Capstone** | The Synthesiser. Citation-grounded briefing. | Granite 4.1 + Mellea rejection sampling |
|
| 119 |
|
| 120 |
+
The four data-Stones run sequentially per query; the Capstone
|
| 121 |
+
reconciles their documents into one cited paragraph.
|
| 122 |
|
| 123 |
---
|
| 124 |
|
|
|
|
| 142 |
r2. Test MAE 0.1091 m, −41% vs persistence and −25% vs zero-shot.
|
| 143 |
|
| 144 |
All three are loaded at runtime by their respective FSM probes in
|
| 145 |
+
`app/context/` and `app/live/`. Reproduction recipes live under
|
| 146 |
+
`experiments/18..21/`.
|
| 147 |
|
| 148 |
---
|
| 149 |
|
| 150 |
+
## Architecture
|
| 151 |
+
|
| 152 |
+
```
|
| 153 |
+
NYC address ──► Granite 4.1 3B planner ──► Plan{intent, targets, specialists}
|
| 154 |
+
│
|
| 155 |
+
▼
|
| 156 |
+
Five-Stone Burr FSM (one @action per probe)
|
| 157 |
+
┌───────────┬───────────┬───────────┬──────────┐
|
| 158 |
+
▼ ▼ ▼ ▼ ▼
|
| 159 |
+
Cornerstone Keystone Touchstone Lodestone (cont.)
|
| 160 |
+
(hazard) (assets) (live) (forecast)
|
| 161 |
+
│ │ │ │
|
| 162 |
+
└───────────┴─────┬─────┴───────────┘
|
| 163 |
+
▼
|
| 164 |
+
build_documents() — Granite-native
|
| 165 |
+
role="document <doc_id>" messages
|
| 166 |
+
▼
|
| 167 |
+
Capstone: Granite 4.1 8B + Mellea rejection sampling
|
| 168 |
+
──► 4-check grounding loop, surgical feedback rerolls
|
| 169 |
+
▼
|
| 170 |
+
Four-section briefing with [doc_id] citations
|
| 171 |
+
▼
|
| 172 |
+
SSE stream → SvelteKit UI (briefing, trace, map)
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
LLM inference is dispatched through `app/llm.py`, a LiteLLM Router
|
| 176 |
+
shim with two backends: **Ollama** (T4 / local) and **vLLM** (AMD
|
| 177 |
+
MI300X). Same `chat()` signature in both directions; vLLM is primary
|
| 178 |
+
for the demo, Ollama is the auto-failover.
|
| 179 |
+
|
| 180 |
+
Source-of-truth pointers:
|
| 181 |
|
| 182 |
- `app/stones/`: the Stones taxonomy (NAME / TAGLINE / SOURCES /
|
| 183 |
collect()) over the FSM probes.
|
|
|
|
| 186 |
document-role messages in canonical Stone order.
|
| 187 |
- `app/mellea_validator.py`: strict reconcile path (4-check rejection
|
| 188 |
sampling).
|
| 189 |
+
- `app/llm.py`: LiteLLM Router shim. Routes to Ollama or vLLM without
|
| 190 |
+
changing caller code.
|
| 191 |
- `web/main.py`: FastAPI + SSE. The stream emits
|
| 192 |
`plan / step / token / mellea_attempt / final` events plus the
|
| 193 |
`stone_start / stone_done` envelope around each Stone group.
|
| 194 |
- `web/sveltekit/`: primary UI (SvelteKit + adapter-static).
|
| 195 |
+
|
| 196 |
+
For the long-form architecture document, see [`ARCHITECTURE.md`](ARCHITECTURE.md).
|
|
|
|
|
|
|
| 197 |
|
| 198 |
---
|
| 199 |
|
| 200 |
+
## Data sources
|
| 201 |
|
| 202 |
+
Riprap contacts only public-record federal, state, and city sources at
|
| 203 |
+
runtime. No commercial APIs, no proprietary scores, no opaque
|
| 204 |
+
aggregators.
|
| 205 |
|
| 206 |
+
| Source | Hosting agency | Used for |
|
| 207 |
+
|---|---|---|
|
| 208 |
+
| Hurricane Sandy 2012 inundation zone | NYC OTI / NOAA Office for Coastal Management | Cornerstone hazard memory |
|
| 209 |
+
| NYC DEP Stormwater Flood Maps | NYC Department of Environmental Protection | DEP modeled-scenario layers |
|
| 210 |
+
| Hurricane Ida 2021 USGS high-water marks | USGS Short-Term Network | Empirical validation points |
|
| 211 |
+
| FloodNet ultrasonic sensor network | NYU CUSP / FloodNet | Live water-depth observations |
|
| 212 |
+
| NYC 311 flood complaints | NYC Open Data | Empirical complaint history |
|
| 213 |
+
| NOAA tide gauge — The Battery | NOAA CO-OPS | Live tide and surge level |
|
| 214 |
+
| NWS METAR | National Weather Service | Hourly precipitation |
|
| 215 |
+
| NWS public flood alerts | National Weather Service | Active warnings and watches |
|
| 216 |
+
| MTA subway entrances | MTA / NYC Open Data | Transit asset register |
|
| 217 |
+
| NYCHA developments | NYC Housing Authority | Public-housing exposure |
|
| 218 |
+
| NYC DOE schools | NYC Department of Education | Education-asset exposure |
|
| 219 |
+
| NYS DOH hospitals | New York State Department of Health | Critical-facility exposure |
|
| 220 |
+
| USGS 3DEP 1 m DEM | USGS National Map | HAND / TWI microtopography |
|
| 221 |
+
| NYC DOB filings | NYC Department of Buildings | Development-check intent |
|
| 222 |
+
| NPCC4 SLR projections | NYC Mayor's Office of Climate & Environmental Justice | Policy-context corpus (RAG) |
|
| 223 |
+
| Sentinel-2 MSI imagery | ESA / Copernicus | Prithvi + TerraMind inputs |
|
| 224 |
+
|
| 225 |
+
The full data licence map and vintage table is enumerated in
|
| 226 |
+
[`ARCHITECTURE.md`](ARCHITECTURE.md).
|
| 227 |
|
| 228 |
+
---
|
|
|
|
| 229 |
|
| 230 |
+
## What Riprap is not
|
| 231 |
+
|
| 232 |
+
The civil engineer carries the stamp. Riprap surfaces the evidence the
|
| 233 |
+
engineer judges.
|
| 234 |
+
|
| 235 |
+
- **Not a hydraulic model.** Riprap does not replace HEC-RAS, SWMM, or
|
| 236 |
+
ICM. It synthesises evidence from completed modelling work; it does
|
| 237 |
+
not produce new flow or stage estimates.
|
| 238 |
+
- **Not a stamped deliverable.** The briefing is a starting point for
|
| 239 |
+
a memo, not the memo itself. Professional judgment, field
|
| 240 |
+
reconnaissance, and the engineer's stamp are required for any
|
| 241 |
+
actionable output.
|
| 242 |
+
- **Not a substitute for site investigation.** Microtopography is from
|
| 243 |
+
1 m USGS 3DEP LiDAR, appropriate for screening, not for design.
|
| 244 |
+
- **Not a risk score.** Riprap does not output a 1–10 or 1–100 number.
|
| 245 |
+
Score-based tools (First Street, ClimateCheck, Jupiter) are
|
| 246 |
+
different products for different audiences. Riprap is the evidence
|
| 247 |
+
audit trail behind any such judgment.
|
| 248 |
+
|
| 249 |
+
---
|
| 250 |
+
|
| 251 |
+
## Citation
|
| 252 |
+
|
| 253 |
+
If you reference Riprap in academic or professional work:
|
| 254 |
+
|
| 255 |
+
```bibtex
|
| 256 |
+
@software{riprap_nyc_2026,
|
| 257 |
+
author = {Rahman, Adam Munawar},
|
| 258 |
+
title = {Riprap: Citation-Grounded NYC Flood-Exposure Briefings},
|
| 259 |
+
year = {2026},
|
| 260 |
+
url = {https://github.com/msradam/riprap-nyc},
|
| 261 |
+
version = {v0.5.0},
|
| 262 |
+
note = {Built for the AMD x lablab.ai Developer Hackathon}
|
| 263 |
+
}
|
| 264 |
```
|
| 265 |
|
| 266 |
---
|
| 267 |
|
| 268 |
## License
|
| 269 |
|
| 270 |
+
Apache 2.0 (this repository). The three NYC-specialised fine-tunes
|
| 271 |
+
above are also Apache 2.0; underlying upstream models retain their
|
| 272 |
+
own permissive licences (see each `MODEL_CARD.md`).
|
| 273 |
|
| 274 |
HF Space configuration reference:
|
| 275 |
<https://huggingface.co/docs/hub/spaces-config-reference>.
|
| 276 |
|
| 277 |
---
|
| 278 |
|
| 279 |
+
## Acknowledgments
|
| 280 |
+
|
| 281 |
+
- **AMD Developer Cloud** — MI300X compute that made the three
|
| 282 |
+
Apache-2.0 NYC fine-tunes feasible.
|
| 283 |
+
- **AMD x lablab.ai Developer Hackathon** — the venue.
|
| 284 |
+
- **IBM Research** — Granite 4.1, Granite Embedding 278M, Granite TTM
|
| 285 |
+
r2, Mellea, and the rest of the open-source Granite ecosystem.
|
| 286 |
+
- **NASA / IBM Prithvi-EO 2.0** and **IBM / ESA TerraMind 1.0** — the
|
| 287 |
+
geospatial foundation models behind the NYC fine-tunes.
|
| 288 |
+
- **NYU CUSP / FloodNet** — the public sensor network whose data
|
| 289 |
+
Riprap reads live.
|
| 290 |
+
- **Andrew Hicks** — civil-engineering review of the methodology, and
|
| 291 |
+
framing for what Riprap is not.
|
| 292 |
+
- **The Riprap dam mark** — ["Dam" by Chintuza](https://thenounproject.com/icon/dam-4516918/)
|
| 293 |
+
via the Noun Project, licensed CC-BY 3.0. The original SVG embedded
|
| 294 |
+
the attribution as on-canvas text; Riprap's `assets/logo*.svg`
|
| 295 |
+
strips the embedded text and carries the credit here in body copy
|
| 296 |
+
instead, per the Creative Commons attribution requirement.
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Riprap — local + self-hosted orchestration.
|
| 2 |
+
#
|
| 3 |
+
# Default `docker compose up` starts only the app container, which
|
| 4 |
+
# expects RIPRAP_LLM_BASE_URL / RIPRAP_ML_BASE_URL to point at an
|
| 5 |
+
# external inference backend (the live HF Space, your own
|
| 6 |
+
# self-hosted instance, etc. — see .env.example).
|
| 7 |
+
#
|
| 8 |
+
# Full self-host (requires an AMD ROCm or NVIDIA CUDA GPU):
|
| 9 |
+
#
|
| 10 |
+
# docker compose --profile with-models up
|
| 11 |
+
#
|
| 12 |
+
# This adds the riprap-models GPU specialist service; you still need
|
| 13 |
+
# a separate vLLM serving Granite 4.1 8B for the Capstone reconciler
|
| 14 |
+
# (see docs/DROPLET-RUNBOOK.md for the canonical bring-up).
|
| 15 |
+
|
| 16 |
+
services:
|
| 17 |
+
riprap-app:
|
| 18 |
+
image: msradam/riprap-nyc:v0.5.0
|
| 19 |
+
build:
|
| 20 |
+
context: .
|
| 21 |
+
dockerfile: Dockerfile.app
|
| 22 |
+
ports:
|
| 23 |
+
- "7860:7860"
|
| 24 |
+
environment:
|
| 25 |
+
- RIPRAP_LLM_PRIMARY=${RIPRAP_LLM_PRIMARY:-vllm}
|
| 26 |
+
- RIPRAP_LLM_BASE_URL=${RIPRAP_LLM_BASE_URL}
|
| 27 |
+
- RIPRAP_LLM_API_KEY=${RIPRAP_LLM_API_KEY}
|
| 28 |
+
- RIPRAP_ML_BASE_URL=${RIPRAP_ML_BASE_URL}
|
| 29 |
+
- RIPRAP_ML_API_KEY=${RIPRAP_ML_API_KEY}
|
| 30 |
+
- RIPRAP_HARDWARE_LABEL=${RIPRAP_HARDWARE_LABEL:-Self-hosted}
|
| 31 |
+
- RIPRAP_ENGINE_LABEL=${RIPRAP_ENGINE_LABEL:-Granite 4.1 / vLLM}
|
| 32 |
+
restart: unless-stopped
|
| 33 |
+
|
| 34 |
+
riprap-models:
|
| 35 |
+
image: msradam/riprap-models:v0.5.0
|
| 36 |
+
build:
|
| 37 |
+
context: .
|
| 38 |
+
dockerfile: services/riprap-models/Dockerfile
|
| 39 |
+
ports:
|
| 40 |
+
- "7861:7860"
|
| 41 |
+
environment:
|
| 42 |
+
- RIPRAP_MODELS_API_KEY=${RIPRAP_ML_API_KEY}
|
| 43 |
+
deploy:
|
| 44 |
+
resources:
|
| 45 |
+
reservations:
|
| 46 |
+
devices:
|
| 47 |
+
- driver: amd
|
| 48 |
+
count: 1
|
| 49 |
+
capabilities: [gpu]
|
| 50 |
+
profiles:
|
| 51 |
+
- with-models
|
| 52 |
+
restart: unless-stopped
|
docs/DEMO-QUERIES.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Demo Query Shortlist
|
| 2 |
+
|
| 3 |
+
_Last updated: 2026-05-06. Primary arc verified on live Space (AMD MI300X · vLLM).
|
| 4 |
+
50-query validation sweep run post-bugfix: 50/50 PASS, avg 11.2 s, 36/50 Mellea 4/4._
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
## Primary arc (the three-query demo)
|
| 9 |
+
|
| 10 |
+
Together these show: resident / planner / grant-writer persona breadth,
|
| 11 |
+
all five Stones firing (or deterministically skipping), Granite TTM r2 +
|
| 12 |
+
Prithvi-EO-2.0-NYC-Pluvial + Granite Embedding 278M fine-tunes lighting up,
|
| 13 |
+
and the new two-column compare layout.
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
### Query 1: "I'm thinking about renting an apartment at 80 Pioneer Street, Brooklyn. Should I worry?"
|
| 18 |
+
|
| 19 |
+
**Persona:** Renter evaluating a move to Red Hook — canonical Sandy turf.
|
| 20 |
+
**Borough / neighborhood:** Red Hook, Brooklyn
|
| 21 |
+
**Intent:** `single_address`
|
| 22 |
+
**Verified wall-clock:** 5.7 s (2026-05-06); **9.8 s (50-query sweep, 2026-05-06)**
|
| 23 |
+
**Mellea:** 4/4, 0 rerolls (cleanest result in the suite; confirmed clean in sweep)
|
| 24 |
+
**Stones fired / silent / errored:**
|
| 25 |
+
- Cornerstone (Sandy, DEP stormwater): fired — Sandy inside ✓, DEP outside (negative result is cited)
|
| 26 |
+
- Touchstone (311, FloodNet, NOAA/NWS): fired — 65 complaints, 4 FloodNet events, NOAA gauge live
|
| 27 |
+
- Lodestone (microtopo, Ida HWM): fired — TWI 14.79 (very high), Ida HWM 130 m away
|
| 28 |
+
- Keystone (TTM forecast, Prithvi-EO v2, GLiNER): fired — surge forecast, Prithvi polygon lookup, entities extracted
|
| 29 |
+
- Capstone (RAG + reconcile): fired — 1 RAG hit (rag_nycha 0.84), Mellea 4/4
|
| 30 |
+
- `prithvi_eo_live`, `terramind_synthesis`: errored (torchvision::nms on cpu-basic — known, deterministic)
|
| 31 |
+
**Fine-tunes invoked:** Granite TTM r2 (tide surge), Granite-TTM-r2-Battery-Surge, Prithvi-EO-2.0-NYC-Pluvial (v2 polygon), Granite Embedding 278M (RAG), GLiNER
|
| 32 |
+
**Briefing verdict opener:** "The address at 80 PIONEER STREET, Brooklyn, NY, is **significantly exposed to flood risk**, as it was **within the Hurricane Sandy inundation zone** on October 29–30, 2012 [sandy] and sits at a **topographic low point** with a **Topographic Wetness Index (TWI) of 14.79**, indicating very high saturation propensity [microtopo]."
|
| 33 |
+
**Fragility notes:** 0 rerolls on both live Space run and sessions notes baseline run. Lowest reroll risk in the suite. Geocoder resolves cleanly to Red Hook every time.
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
### Query 2: "Hollis, Queens"
|
| 38 |
+
|
| 39 |
+
**Persona:** NYC OEM/DEP capital planner looking at sewer backlog by NTA.
|
| 40 |
+
**Borough / neighborhood:** Hollis, NTA QN1206, Queens
|
| 41 |
+
**Intent:** `neighborhood`
|
| 42 |
+
**Verified wall-clock:** 3.9 s (2026-05-06); **7.0 s (50-query sweep, 2026-05-06)**
|
| 43 |
+
**Mellea:** 4/4, 0 rerolls (confirmed clean in sweep)
|
| 44 |
+
**Stones fired / silent / errored:**
|
| 45 |
+
- 311, DEP stormwater, microtopo: all fired
|
| 46 |
+
- NTA-level specialists run (8 steps total on cpu-basic Space)
|
| 47 |
+
- Keystone/Prithvi/TerraMind: silenced by design for neighborhood intent
|
| 48 |
+
**Fine-tunes invoked:** Granite Embedding 278M (RAG), GLiNER; TTM may fire for NTA-level surge context
|
| 49 |
+
**Briefing verdict opener:** "Hollis, located in Queens (NTA QN1206) as per [nta_resolve], experiences moderate flood exposure with significant sewer-related complaints and terrain features conducive to flooding."
|
| 50 |
+
**Fragility notes:** Bare NTA name — relies on planner routing `neighborhood` correctly. Has been stable across all probe runs. Low reroll risk. Wall-clock under 5 s on vLLM; well within demo patience.
|
| 51 |
+
|
| 52 |
+
---
|
| 53 |
+
|
| 54 |
+
### Query 3 (compare): "Compare 80 Pioneer Street Brooklyn to 100 Gold Street Manhattan"
|
| 55 |
+
|
| 56 |
+
**Persona:** Real-estate attorney comparing a Sandy-zone lease to a lower-risk mid-Manhattan address; or a journalist showing the contrast.
|
| 57 |
+
**Borough / neighborhood:** Red Hook, Brooklyn vs Financial District, Manhattan
|
| 58 |
+
**Intent:** `compare` (verified routing on live Space post-28a77ae fix)
|
| 59 |
+
**Verified wall-clock:** ~15 s (estimated 2026-05-06); **20.7 s (50-query sweep, 2026-05-06)**
|
| 60 |
+
**Mellea:** 4/4 combined (0 rerolls) — confirmed clean in sweep
|
| 61 |
+
**Stones fired / silent / errored:** Full single_address FSM run for each target (24 steps each); same error pattern as Query 1 (torchvision::nms deterministic)
|
| 62 |
+
**Fine-tunes invoked:** Granite TTM r2, Granite-TTM-r2-Battery-Surge, Prithvi-EO-2.0-NYC-Pluvial, Granite Embedding 278M, GLiNER (all for both targets)
|
| 63 |
+
**Briefing verdict opener:** Two-column layout renders in the UI. PLACE A opener: "The address at 80 PIONEER STREET, Brooklyn, NY, is **significantly exposed to flood risk**…" PLACE B opener (Gold Street) contrasts — lower 311 count (26 vs 65), no Sandy inundation, Ida HWM 3.47 km away vs 130 m.
|
| 64 |
+
**Delta bar content:** Sandy zone: ✓ Pioneer / ✗ Gold · 311 complaints: 65 vs 26 · FloodNet events: 4 vs 1 · Ida HWM nearest: 130 m vs 3,472 m · Elevation pct\_200m lower: 0.8% vs 38.2%
|
| 65 |
+
**Fragility notes:** Requires compare intent to route (planner must parse two addresses from free text). Verified stable post-fix. If the planner unexpectedly returns `single_address`, PLACE B will be silently dropped — watch the plan badge in the UI before proceeding. No reroll risk on either leg.
|
| 66 |
+
|
| 67 |
+
---
|
| 68 |
+
|
| 69 |
+
## Verified clean queries (50-query sweep, 2026-05-06)
|
| 70 |
+
|
| 71 |
+
Best queries per intent type from the sweep — 0 rerolls, Mellea 4/4, fast wall-clock.
|
| 72 |
+
|
| 73 |
+
### Address (cleanest 3)
|
| 74 |
+
|
| 75 |
+
| Query | Wall-clock | Mellea | Rerolls | Notes |
|
| 76 |
+
|-------|-----------|--------|---------|-------|
|
| 77 |
+
| `I'm thinking about renting an apartment at 80 Pioneer Street, Brooklyn. Should I worry?` | 9.8 s | 4/4 | 0 | Primary demo arc. All Stones fire. |
|
| 78 |
+
| `Hollis, Queens` | 7.0 s | 4/4 | 0 | Also neighborhood intent — clean on both paths. |
|
| 79 |
+
| `100 Gold Street, Manhattan` | 10.6 s | 4/4 | 0 | Negative control: outside Sandy zone; low reroll. |
|
| 80 |
+
|
| 81 |
+
### Neighborhood (cleanest 3)
|
| 82 |
+
|
| 83 |
+
| Query | Wall-clock | Mellea | Rerolls | Notes |
|
| 84 |
+
|-------|-----------|--------|---------|-------|
|
| 85 |
+
| `Coney Island, Brooklyn` | 5.5 s | 4/4 | 0 | Fastest neighborhood in suite. 87.5% NTA in Sandy. |
|
| 86 |
+
| `Hunts Point, Bronx` | 5.3 s | 4/4 | 0 | Clean South Bronx probe; Bronx representation. |
|
| 87 |
+
| `East New York, Brooklyn` | 7.0 s | 4/4 | 0 | Inland stormwater narrative, different from coastal arc. |
|
| 88 |
+
|
| 89 |
+
### Compare (cleanest 3)
|
| 90 |
+
|
| 91 |
+
| Query | Wall-clock | Mellea | Rerolls | Notes |
|
| 92 |
+
|-------|-----------|--------|---------|-------|
|
| 93 |
+
| `Compare 80 Pioneer Street Brooklyn to 100 Gold Street Manhattan` | 20.7 s | 4/4 | 0 | Primary demo arc. Maximum delta. Cross-borough. |
|
| 94 |
+
| `Compare Red Hook Brooklyn to the Financial District Manhattan for flood risk` | 18.5 s | 4/4 | 0 | Neighborhood-vs-neighborhood cross-borough. |
|
| 95 |
+
| `Compare 157-11 Rockaway Beach Blvd Queens to 100 Gold Street Manhattan` | 15.2 s | 4/4 | 0 | Far Rockaway vs FiDi — extreme delta. |
|
| 96 |
+
|
| 97 |
+
### Planner / development check (cleanest)
|
| 98 |
+
|
| 99 |
+
| Query | Wall-clock | Mellea | Rerolls | Notes |
|
| 100 |
+
|-------|-----------|--------|---------|-------|
|
| 101 |
+
| (see "Queries to avoid" — all planner queries in sweep had rr≥2 or 0/4) | — | — | — | Planner intent is fragile for demo; prefer address/neighborhood/compare. |
|
| 102 |
+
|
| 103 |
+
---
|
| 104 |
+
|
| 105 |
+
## Backup queries
|
| 106 |
+
|
| 107 |
+
| Primary | Backup | Reason |
|
| 108 |
+
|---------|--------|--------|
|
| 109 |
+
| Query 1 — 80 Pioneer Street, Brooklyn | `Coney Island, Brooklyn` | Neighborhood intent; 4/4 0rr 5.5 s in sweep. Different Stones surface (NTA-level DEP, 87.5% NTA in Sandy zone). Swap if Pioneer geocoder drifts. |
|
| 110 |
+
| Query 2 — Hollis, Queens | `Hunts Point, Bronx` | 4/4 0rr 5.3 s in sweep. Shows Bronx coverage, different stormwater narrative. |
|
| 111 |
+
| Query 3 compare — Pioneer vs Gold | `Compare Red Hook Brooklyn to the Financial District Manhattan` | 4/4 0rr 18.5 s. Neighborhood-vs-neighborhood; cleaner than address parsing if planner struggles. |
|
| 112 |
+
|
| 113 |
+
---
|
| 114 |
+
|
| 115 |
+
## Queries to avoid
|
| 116 |
+
|
| 117 |
+
| Query | Failure mode |
|
| 118 |
+
|-------|-------------|
|
| 119 |
+
| `What was the flood situation at 750 Baychester Avenue, Bronx during Ida?` | `not_implemented` — "during Ida" triggers retrospective intent; returns 0/4 in 0.03 s. Confirmed in 50-query sweep. |
|
| 120 |
+
| `What's the storm surge risk for 157-11 Rockaway Beach Blvd, Queens?` | All specialists errored (0.0s wall-clock per specialist); 0/4 Mellea, 1.6 s total. Geocoder likely fails on this address format; reword as neighborhood ("Far Rockaway, Queens") instead. |
|
| 121 |
+
| `What's the flood risk at 325 Hudson Street, Manhattan?` | 2/4 Mellea with 2 rerolls — citations_resolve and numerics_grounded both failing. Hudson Square has sparse source data; risky for demo. |
|
| 122 |
+
| All planner/development-check queries | rr≥2 across the board in sweep (q031, q035, q039, q044, q048). Development-check intent sparse on citations; reconciler hits MAX_ATTEMPTS. Avoid on demo. |
|
| 123 |
+
| `Compare Canarsie Brooklyn to Park Slope Brooklyn` | 3/4 Mellea, 3 rerolls, 24.2 s — slowest same-borough compare in sweep. Use cross-borough compares instead. |
|
| 124 |
+
| `Compare Mott Haven Bronx to Hunts Point Bronx` | 4/4 but 3 rerolls, 28.0 s — slowest query in sweep. Both NTAs have sparse sensor data. |
|
| 125 |
+
| `Compare Hollis Queens to Red Hook Brooklyn` | Fragile (prior run) — PLACE A (Hollis) failed `citations_resolve`; will exceed MAX_ATTEMPTS under load. |
|
| 126 |
+
| `Compare the Two Bridges neighborhood to Battery Park City` | Hard failure — planner fell through to `single_address`; neighborhood-vs-neighborhood compare fragile. |
|
| 127 |
+
| `442 East Houston Street, Manhattan` (solo) | 2 rerolls historically — acceptable secondary, risky as opener. |
|
| 128 |
+
| `504 Grand Street, Manhattan` | 0/4 Mellea in every run; geocodes but reconcile fails. |
|
| 129 |
+
| Any `live_now` query (e.g. FloodNet BK-018) | 0/4 Mellea — live_now reconcile does not pass grounding checks. |
|
| 130 |
+
| `What would Riprap have said about Hollis on August 31, 2021…` | `not_implemented` — retrospective intent not wired. |
|
| 131 |
+
| `EJNYC × Riprap pairing` / BBMCR capital planning | 0/4 Mellea, 0 steps — planner routes to `development_check` but no DOB filings match. |
|
docs/DOCKER-PUBLISH.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Publishing the Riprap Docker images
|
| 2 |
+
|
| 3 |
+
The repo ships the build context for two images:
|
| 4 |
+
|
| 5 |
+
| Image | Dockerfile | Purpose |
|
| 6 |
+
|---|---|---|
|
| 7 |
+
| `msradam/riprap-nyc` | [`Dockerfile.app`](../Dockerfile.app) | Lightweight FastAPI + SvelteKit app. Talks to remote LLM/ML backends over HTTP. |
|
| 8 |
+
| `msradam/riprap-models` | [`services/riprap-models/Dockerfile`](../services/riprap-models/Dockerfile) | ROCm + PyTorch GPU specialist service (Prithvi, TerraMind, GLiNER, Granite Embed, TTM). |
|
| 9 |
+
|
| 10 |
+
There is also a third Dockerfile at the repo root (`Dockerfile`) — that
|
| 11 |
+
is the heavy HF Space image (CUDA + bundled Ollama + Granite weights).
|
| 12 |
+
It builds and ships automatically when the `huggingface` git remote is
|
| 13 |
+
pushed; **do not** publish it under `msradam/riprap-nyc` on Docker Hub
|
| 14 |
+
or GHCR, that name is reserved for the lightweight self-host image.
|
| 15 |
+
|
| 16 |
+
This document covers what was deferred from the v0.5.0 cleanup pass:
|
| 17 |
+
**actually building and pushing** the public Docker Hub / GHCR
|
| 18 |
+
artefacts. The compose file and `.env.example` are already in the repo
|
| 19 |
+
and reference the eventual `msradam/riprap-nyc:v0.5.0` tag.
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
## 1. Build locally
|
| 24 |
+
|
| 25 |
+
The build context has to be the repo root for both images, because
|
| 26 |
+
`services/riprap-models/Dockerfile` reaches up to grab
|
| 27 |
+
`services/riprap-models/main.py` and the requirements files.
|
| 28 |
+
|
| 29 |
+
```bash
|
| 30 |
+
cd $(git rev-parse --show-toplevel)
|
| 31 |
+
|
| 32 |
+
# Lightweight self-host image (linux/amd64 by default; pass
|
| 33 |
+
# --platform linux/arm64,linux/amd64 if you want a multi-arch
|
| 34 |
+
# manifest and have buildx + qemu set up).
|
| 35 |
+
docker build \
|
| 36 |
+
-f Dockerfile.app \
|
| 37 |
+
-t msradam/riprap-nyc:v0.5.0 \
|
| 38 |
+
-t msradam/riprap-nyc:latest \
|
| 39 |
+
.
|
| 40 |
+
|
| 41 |
+
# GPU specialist service. Requires a build host with at least
|
| 42 |
+
# ~30 GB free disk for the rocm/pytorch base + wheels.
|
| 43 |
+
docker build \
|
| 44 |
+
-f services/riprap-models/Dockerfile \
|
| 45 |
+
-t msradam/riprap-models:v0.5.0 \
|
| 46 |
+
-t msradam/riprap-models:latest \
|
| 47 |
+
.
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
Expected sizes:
|
| 51 |
+
|
| 52 |
+
- `riprap-nyc` ~1.4 GB (python:3.10-slim + GDAL + torch CPU + transformers)
|
| 53 |
+
- `riprap-models` ~12-15 GB (ROCm + torch dev build + terratorch chain)
|
| 54 |
+
|
| 55 |
+
---
|
| 56 |
+
|
| 57 |
+
## 2. Smoke-test the app image locally
|
| 58 |
+
|
| 59 |
+
```bash
|
| 60 |
+
cp .env.example .env
|
| 61 |
+
# fill .env with reachable RIPRAP_LLM_BASE_URL / RIPRAP_ML_BASE_URL.
|
| 62 |
+
# Easiest: point at the live HF Space's backends:
|
| 63 |
+
# RIPRAP_LLM_PRIMARY=ollama
|
| 64 |
+
# RIPRAP_LLM_BASE_URL=https://lablab-ai-amd-developer-hackathon-riprap-nyc.hf.space
|
| 65 |
+
# (or your own droplet from docs/DROPLET-RUNBOOK.md).
|
| 66 |
+
|
| 67 |
+
docker compose up -d riprap-app
|
| 68 |
+
sleep 10
|
| 69 |
+
|
| 70 |
+
# Drive the SSE endpoint via curl
|
| 71 |
+
curl -sN "http://localhost:7860/api/agent/stream?q=80%20Pioneer%20Street%20Brooklyn" \
|
| 72 |
+
--max-time 120 | head -40
|
| 73 |
+
|
| 74 |
+
# Or run the canonical 5-address probe against the container
|
| 75 |
+
RIPRAP_LLM_BASE_URL=http://localhost:7860 \
|
| 76 |
+
.venv/bin/python scripts/probe_addresses.py --base http://localhost:7860
|
| 77 |
+
# Expect: 5/5 PASS.
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
Stop:
|
| 81 |
+
|
| 82 |
+
```bash
|
| 83 |
+
docker compose down
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
---
|
| 87 |
+
|
| 88 |
+
## 3. Push to Docker Hub
|
| 89 |
+
|
| 90 |
+
```bash
|
| 91 |
+
docker login -u msradam # then enter the password / access token
|
| 92 |
+
docker push msradam/riprap-nyc:v0.5.0
|
| 93 |
+
docker push msradam/riprap-nyc:latest
|
| 94 |
+
docker push msradam/riprap-models:v0.5.0
|
| 95 |
+
docker push msradam/riprap-models:latest
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
If you'd rather use GitHub Container Registry instead:
|
| 99 |
+
|
| 100 |
+
```bash
|
| 101 |
+
echo "$GH_TOKEN" | docker login ghcr.io -u msradam --password-stdin
|
| 102 |
+
docker tag msradam/riprap-nyc:v0.5.0 ghcr.io/msradam/riprap-nyc:v0.5.0
|
| 103 |
+
docker tag msradam/riprap-nyc:latest ghcr.io/msradam/riprap-nyc:latest
|
| 104 |
+
docker push ghcr.io/msradam/riprap-nyc:v0.5.0
|
| 105 |
+
docker push ghcr.io/msradam/riprap-nyc:latest
|
| 106 |
+
docker tag msradam/riprap-models:v0.5.0 ghcr.io/msradam/riprap-models:v0.5.0
|
| 107 |
+
docker tag msradam/riprap-models:latest ghcr.io/msradam/riprap-models:latest
|
| 108 |
+
docker push ghcr.io/msradam/riprap-models:v0.5.0
|
| 109 |
+
docker push ghcr.io/msradam/riprap-models:latest
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
Required PAT scope for GHCR: `write:packages`.
|
| 113 |
+
|
| 114 |
+
---
|
| 115 |
+
|
| 116 |
+
## 4. After pushing — README updates
|
| 117 |
+
|
| 118 |
+
Once the v0.5.0 tag is live on Docker Hub, the existing
|
| 119 |
+
[`README.md`](../README.md) "Run locally" section already points at
|
| 120 |
+
the right tag — no further edits needed.
|
| 121 |
+
|
| 122 |
+
If you publish under a different namespace (a personal Hub account
|
| 123 |
+
you don't want to use long-term, etc.), update the `image:` line in
|
| 124 |
+
[`docker-compose.yml`](../docker-compose.yml) and the references in
|
| 125 |
+
the README.
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
## Status as of v0.5.0 tag (2026-05-07)
|
| 130 |
+
|
| 131 |
+
- `Dockerfile.app` and `services/riprap-models/Dockerfile` exist and
|
| 132 |
+
the compose file references both with the correct image tags.
|
| 133 |
+
- The lightweight image was **not** pre-built or pushed during the
|
| 134 |
+
cleanup pass — the build host (Apple Silicon laptop) had no Docker
|
| 135 |
+
daemon running and a multi-gigabyte Apple-Silicon → linux/amd64
|
| 136 |
+
build under QEMU would not finish inside the polish budget. Adam
|
| 137 |
+
to run section 1 + 3 of this doc on a host with Docker before any
|
| 138 |
+
external pull will succeed.
|
| 139 |
+
- `docker compose config` validates against the current compose file
|
| 140 |
+
(verified via podman-compose during the cleanup).
|
scripts/probe_50.py
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""50-query validation sweep against the live HF Space.
|
| 2 |
+
|
| 3 |
+
Usage:
|
| 4 |
+
python3 scripts/probe_50.py [--base URL] [--concurrency N] [--timeout S]
|
| 5 |
+
|
| 6 |
+
Default base: https://lablab-ai-amd-developer-hackathon-riprap-nyc.hf.space
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import argparse
|
| 10 |
+
import asyncio
|
| 11 |
+
import json
|
| 12 |
+
import time
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
from urllib.parse import quote
|
| 15 |
+
|
| 16 |
+
import aiohttp
|
| 17 |
+
|
| 18 |
+
BASE = "https://lablab-ai-amd-developer-hackathon-riprap-nyc.hf.space"
|
| 19 |
+
QUERIES_FILE = Path("tests/queries_50.json")
|
| 20 |
+
RESULTS_FILE = Path("tests/probe_50_results.json")
|
| 21 |
+
CONCURRENCY = 3
|
| 22 |
+
TIMEOUT_S = 120
|
| 23 |
+
|
| 24 |
+
STEP_STONE_MAP = {
|
| 25 |
+
"sandy_inundation": "sandy",
|
| 26 |
+
"dep_stormwater": "dep",
|
| 27 |
+
"nyc311": "311",
|
| 28 |
+
"floodnet": "floodnet",
|
| 29 |
+
"floodnet_forecast": "floodnet",
|
| 30 |
+
"noaa_tides": "noaa",
|
| 31 |
+
"nws_alerts": "nws",
|
| 32 |
+
"nws_obs": "nws",
|
| 33 |
+
"microtopo_lidar": "microtopo",
|
| 34 |
+
"ida_hwm_2021": "ida",
|
| 35 |
+
"ttm_forecast": "ttm",
|
| 36 |
+
"ttm_battery_surge": "ttm",
|
| 37 |
+
"ttm_311_forecast": "ttm",
|
| 38 |
+
"prithvi_eo_v2": "prithvi_v2",
|
| 39 |
+
"prithvi_eo_live": "prithvi_live",
|
| 40 |
+
"gliner_extract": "gliner",
|
| 41 |
+
"rag_granite_embedding": "rag",
|
| 42 |
+
"mellea_reconcile_address": "mellea",
|
| 43 |
+
"geocode": None,
|
| 44 |
+
"mta_entrance_exposure": "mta",
|
| 45 |
+
"terramind_synthesis": "terramind",
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _parse_sse(chunk: str):
|
| 50 |
+
events = []
|
| 51 |
+
event_type = "message"
|
| 52 |
+
data_lines = []
|
| 53 |
+
for line in chunk.splitlines():
|
| 54 |
+
if line.startswith("event:"):
|
| 55 |
+
event_type = line[6:].strip()
|
| 56 |
+
elif line.startswith("data:"):
|
| 57 |
+
data_lines.append(line[5:].strip())
|
| 58 |
+
elif line == "" and data_lines:
|
| 59 |
+
raw = " ".join(data_lines)
|
| 60 |
+
try:
|
| 61 |
+
payload = json.loads(raw)
|
| 62 |
+
except json.JSONDecodeError:
|
| 63 |
+
payload = {"raw": raw}
|
| 64 |
+
events.append((event_type, payload))
|
| 65 |
+
event_type = "message"
|
| 66 |
+
data_lines = []
|
| 67 |
+
return events
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
async def stream_query(session: aiohttp.ClientSession, query_obj: dict, base: str, timeout_s: float) -> dict:
|
| 71 |
+
qid = query_obj["id"]
|
| 72 |
+
query = query_obj["query"]
|
| 73 |
+
url = f"{base}/api/agent/stream?q={quote(query)}"
|
| 74 |
+
|
| 75 |
+
result = {
|
| 76 |
+
"id": qid,
|
| 77 |
+
"query": query,
|
| 78 |
+
"status": "ERROR",
|
| 79 |
+
"wall_clock_s": None,
|
| 80 |
+
"intent_returned": None,
|
| 81 |
+
"mellea_passed": None,
|
| 82 |
+
"mellea_rerolls": 0,
|
| 83 |
+
"stones_fired": [],
|
| 84 |
+
"stones_errored": [],
|
| 85 |
+
"stones_silent": [],
|
| 86 |
+
"citations_resolved": None,
|
| 87 |
+
"compare_targets": None,
|
| 88 |
+
"error": None,
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
t0 = time.monotonic()
|
| 92 |
+
buf = ""
|
| 93 |
+
plan_seen = False
|
| 94 |
+
final_seen = False
|
| 95 |
+
|
| 96 |
+
try:
|
| 97 |
+
async with session.get(url, timeout=aiohttp.ClientTimeout(total=timeout_s + 10)) as resp:
|
| 98 |
+
if resp.status != 200:
|
| 99 |
+
result["error"] = f"HTTP {resp.status}"
|
| 100 |
+
result["wall_clock_s"] = round(time.monotonic() - t0, 2)
|
| 101 |
+
return result
|
| 102 |
+
|
| 103 |
+
deadline = t0 + timeout_s
|
| 104 |
+
async for chunk in resp.content.iter_any():
|
| 105 |
+
if time.monotonic() > deadline:
|
| 106 |
+
result["status"] = "TIMEOUT"
|
| 107 |
+
result["wall_clock_s"] = round(time.monotonic() - t0, 2)
|
| 108 |
+
return result
|
| 109 |
+
|
| 110 |
+
buf += chunk.decode("utf-8", errors="replace")
|
| 111 |
+
# process complete SSE blocks (separated by double-newline)
|
| 112 |
+
while "\n\n" in buf:
|
| 113 |
+
block, buf = buf.split("\n\n", 1)
|
| 114 |
+
for evt_type, payload in _parse_sse(block + "\n\n"):
|
| 115 |
+
if evt_type == "plan":
|
| 116 |
+
plan_seen = True
|
| 117 |
+
result["intent_returned"] = payload.get("intent")
|
| 118 |
+
targets = payload.get("targets", [])
|
| 119 |
+
if result["intent_returned"] == "compare":
|
| 120 |
+
result["compare_targets"] = len(targets)
|
| 121 |
+
|
| 122 |
+
elif evt_type == "step":
|
| 123 |
+
step = payload.get("step", "")
|
| 124 |
+
ok = payload.get("ok")
|
| 125 |
+
if step in STEP_STONE_MAP and STEP_STONE_MAP[step]:
|
| 126 |
+
stone = STEP_STONE_MAP[step]
|
| 127 |
+
if ok is True:
|
| 128 |
+
if stone not in result["stones_fired"]:
|
| 129 |
+
result["stones_fired"].append(stone)
|
| 130 |
+
elif ok is False:
|
| 131 |
+
if stone not in result["stones_errored"]:
|
| 132 |
+
result["stones_errored"].append(stone)
|
| 133 |
+
|
| 134 |
+
elif evt_type == "final":
|
| 135 |
+
final_seen = True
|
| 136 |
+
mellea = payload.get("mellea") or {}
|
| 137 |
+
req_passed = len(mellea.get("requirements_passed") or [])
|
| 138 |
+
req_total = mellea.get("requirements_total") or 4
|
| 139 |
+
result["mellea_passed"] = f"{req_passed}/{req_total}"
|
| 140 |
+
result["mellea_rerolls"] = (mellea.get("rerolls") or 0)
|
| 141 |
+
audit = payload.get("audit") or {}
|
| 142 |
+
result["citations_resolved"] = audit.get("citations_resolved")
|
| 143 |
+
|
| 144 |
+
elif evt_type == "error":
|
| 145 |
+
result["error"] = payload.get("err", "unknown error")
|
| 146 |
+
|
| 147 |
+
elif evt_type == "done":
|
| 148 |
+
result["wall_clock_s"] = round(time.monotonic() - t0, 2)
|
| 149 |
+
if final_seen:
|
| 150 |
+
result["status"] = "PASS"
|
| 151 |
+
else:
|
| 152 |
+
result["status"] = "ERROR"
|
| 153 |
+
if not result["error"]:
|
| 154 |
+
result["error"] = "done without final event"
|
| 155 |
+
return result
|
| 156 |
+
|
| 157 |
+
except asyncio.TimeoutError:
|
| 158 |
+
result["status"] = "TIMEOUT"
|
| 159 |
+
except Exception as exc:
|
| 160 |
+
result["status"] = "ERROR"
|
| 161 |
+
result["error"] = str(exc)
|
| 162 |
+
|
| 163 |
+
result["wall_clock_s"] = round(time.monotonic() - t0, 2)
|
| 164 |
+
return result
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
async def run_all(queries: list, base: str, timeout_s: float, concurrency: int) -> list:
|
| 168 |
+
sem = asyncio.Semaphore(concurrency)
|
| 169 |
+
results = []
|
| 170 |
+
early_stop = False
|
| 171 |
+
|
| 172 |
+
connector = aiohttp.TCPConnector(limit=concurrency + 2)
|
| 173 |
+
async with aiohttp.ClientSession(connector=connector) as session:
|
| 174 |
+
|
| 175 |
+
async def bounded(qobj):
|
| 176 |
+
nonlocal early_stop
|
| 177 |
+
if early_stop:
|
| 178 |
+
return {**qobj, "status": "SKIPPED", "wall_clock_s": None, "error": "early stop"}
|
| 179 |
+
async with sem:
|
| 180 |
+
r = await stream_query(session, qobj, base, timeout_s)
|
| 181 |
+
tag = f"[{r['id']}]"
|
| 182 |
+
wc = f"{r['wall_clock_s']:.1f}s" if r["wall_clock_s"] else "?"
|
| 183 |
+
mel = r.get("mellea_passed") or "-"
|
| 184 |
+
rr = r.get("mellea_rerolls") or 0
|
| 185 |
+
print(f"{tag} {r['status']} {wc} mellea={mel} rerolls={rr}", flush=True)
|
| 186 |
+
return r
|
| 187 |
+
|
| 188 |
+
tasks = [asyncio.create_task(bounded(q)) for q in queries]
|
| 189 |
+
|
| 190 |
+
done_count = 0
|
| 191 |
+
for coro in asyncio.as_completed(tasks):
|
| 192 |
+
r = await coro
|
| 193 |
+
results.append(r)
|
| 194 |
+
done_count += 1
|
| 195 |
+
# Check early-stop: >10 failures in first 20
|
| 196 |
+
if done_count <= 20:
|
| 197 |
+
bad = sum(1 for x in results if x["status"] in ("TIMEOUT", "ERROR"))
|
| 198 |
+
if bad > 10:
|
| 199 |
+
print(f"\nEARLY STOP: {bad} failures in first {done_count} queries — Space appears degraded.", flush=True)
|
| 200 |
+
early_stop = True
|
| 201 |
+
|
| 202 |
+
# Sort by original query order
|
| 203 |
+
id_order = {q["id"]: i for i, q in enumerate(queries)}
|
| 204 |
+
results.sort(key=lambda r: id_order.get(r["id"], 999))
|
| 205 |
+
return results
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def main():
|
| 209 |
+
ap = argparse.ArgumentParser()
|
| 210 |
+
ap.add_argument("--base", default=BASE)
|
| 211 |
+
ap.add_argument("--concurrency", type=int, default=CONCURRENCY)
|
| 212 |
+
ap.add_argument("--timeout", type=float, default=TIMEOUT_S)
|
| 213 |
+
args = ap.parse_args()
|
| 214 |
+
|
| 215 |
+
queries = json.loads(QUERIES_FILE.read_text())
|
| 216 |
+
print(f"Running {len(queries)} queries against {args.base} (concurrency={args.concurrency}, timeout={args.timeout}s)\n", flush=True)
|
| 217 |
+
|
| 218 |
+
results = asyncio.run(run_all(queries, args.base, args.timeout, args.concurrency))
|
| 219 |
+
|
| 220 |
+
# Write results
|
| 221 |
+
RESULTS_FILE.write_text(json.dumps(results, indent=2))
|
| 222 |
+
print(f"\nResults written to {RESULTS_FILE}")
|
| 223 |
+
|
| 224 |
+
# Update verified flags in queries file
|
| 225 |
+
passed_ids = {r["id"] for r in results if r["status"] == "PASS"}
|
| 226 |
+
for q in queries:
|
| 227 |
+
if q["id"] in passed_ids:
|
| 228 |
+
q["verified"] = True
|
| 229 |
+
QUERIES_FILE.write_text(json.dumps(queries, indent=2))
|
| 230 |
+
print(f"Updated verified flags in {QUERIES_FILE}")
|
| 231 |
+
|
| 232 |
+
# Summary
|
| 233 |
+
total = len(results)
|
| 234 |
+
passed = sum(1 for r in results if r["status"] == "PASS")
|
| 235 |
+
timed_out = sum(1 for r in results if r["status"] == "TIMEOUT")
|
| 236 |
+
errored = sum(1 for r in results if r["status"] == "ERROR")
|
| 237 |
+
skipped = sum(1 for r in results if r["status"] == "SKIPPED")
|
| 238 |
+
wall_clocks = [r["wall_clock_s"] for r in results if r["status"] == "PASS" and r["wall_clock_s"]]
|
| 239 |
+
avg_wall = sum(wall_clocks) / len(wall_clocks) if wall_clocks else 0
|
| 240 |
+
max_wall = max(wall_clocks) if wall_clocks else 0
|
| 241 |
+
mellea_perfect = sum(1 for r in results if r.get("mellea_passed") == "4/4")
|
| 242 |
+
|
| 243 |
+
print(f"\n{'='*60}")
|
| 244 |
+
print(f"Total: {total}")
|
| 245 |
+
print(f"PASS: {passed} ({100*passed//total if total else 0}%)")
|
| 246 |
+
print(f"TIMEOUT: {timed_out}")
|
| 247 |
+
print(f"ERROR: {errored}")
|
| 248 |
+
if skipped:
|
| 249 |
+
print(f"SKIPPED: {skipped} (early stop)")
|
| 250 |
+
print(f"Avg wall-clock: {avg_wall:.1f}s (passing queries)")
|
| 251 |
+
print(f"Max wall-clock: {max_wall:.1f}s")
|
| 252 |
+
print(f"Mellea 4/4: {mellea_perfect} ({100*mellea_perfect//total if total else 0}%)")
|
| 253 |
+
|
| 254 |
+
failures = [r for r in results if r["status"] != "PASS"]
|
| 255 |
+
if failures:
|
| 256 |
+
print("\n--- FAILURES ---")
|
| 257 |
+
for r in failures:
|
| 258 |
+
print(f" [{r['id']}] {r['status']} — {r['query'][:60]}")
|
| 259 |
+
if r.get("error"):
|
| 260 |
+
print(f" err: {r['error'][:80]}")
|
| 261 |
+
|
| 262 |
+
slowest = sorted([r for r in results if r.get("wall_clock_s")], key=lambda x: x["wall_clock_s"], reverse=True)[:5]
|
| 263 |
+
print("\n--- SLOWEST 5 ---")
|
| 264 |
+
for r in slowest:
|
| 265 |
+
print(f" [{r['id']}] {r['wall_clock_s']:.1f}s — {r['query'][:60]}")
|
| 266 |
+
|
| 267 |
+
high_rr = [r for r in results if (r.get("mellea_rerolls") or 0) > 1]
|
| 268 |
+
if high_rr:
|
| 269 |
+
print("\n--- HIGH REROLLS (>1) ---")
|
| 270 |
+
for r in high_rr:
|
| 271 |
+
print(f" [{r['id']}] rerolls={r['mellea_rerolls']} — {r['query'][:60]}")
|
| 272 |
+
|
| 273 |
+
print(f"{'='*60}")
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
if __name__ == "__main__":
|
| 277 |
+
main()
|
slides/VIDEO_TRANSCRIPT.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Riprap — Demo Video Transcript
|
| 2 |
+
## AMD × lablab.ai Developer Hackathon · May 4–10 2026
|
| 3 |
+
## Target: ~5 minutes
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
### [SLIDE 1 — Title card] · ~0:00–0:10
|
| 8 |
+
|
| 9 |
+
**SCREEN:** Slide 1. Riprap logo. "Citation-grounded NYC flood-exposure briefings, on AMD MI300X."
|
| 10 |
+
|
| 11 |
+
> Climate risk is one of the most consequential datasets in real estate and urban planning right now.
|
| 12 |
+
> But the tools that exist today give you a score. A number from one to ten. No explanation. No sources. Just a black box.
|
| 13 |
+
> We built Riprap to be the audit trail behind that number.
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
### [SLIDE 2 — The problem] · ~0:10–0:30
|
| 18 |
+
|
| 19 |
+
**SCREEN:** Slide 2. "Climate risk data is a black box." Two boxes: market scores vs Zillow pulling climate data.
|
| 20 |
+
|
| 21 |
+
> First Street gives you a flood factor. ClimateCheck gives you a percentile. Jupiter charges enterprise rates for a proprietary model.
|
| 22 |
+
> In November 2025, Zillow removed climate risk scores from listings entirely — under pressure from the real-estate industry.
|
| 23 |
+
> When a number meets resistance, the only defense is the audit trail. Riprap *is* the audit trail.
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
### [SLIDE 3 — Solution] · ~0:30–0:40
|
| 28 |
+
|
| 29 |
+
**SCREEN:** Slide 3. Screenshot of the Riprap UI — briefing prose with citation chips, map panel, stone trace.
|
| 30 |
+
|
| 31 |
+
> Type any address in New York City. Get back a written briefing where every numeric claim — every flood depth, every complaint count, every risk percentage — links to its primary public-record source.
|
| 32 |
+
> Federal data. City data. Apache-2.0 models. Nothing proprietary.
|
| 33 |
+
|
| 34 |
+
---
|
| 35 |
+
|
| 36 |
+
### [SLIDE 4 — Civic-tech case] · ~0:40–1:00
|
| 37 |
+
|
| 38 |
+
**SCREEN:** Slide 4. Four boxes: NY Disclosure Law, DEP Stormwater Plan, EJNYC FVI, No commercial APIs.
|
| 39 |
+
|
| 40 |
+
> New York's property disclosure law — March 2024 — requires sellers to disclose flood history. Riprap is the citable narrative that makes that disclosure meaningful.
|
| 41 |
+
> The DEP's $30 billion stormwater priority list covers 86 sites. Riprap provides the per-neighborhood evidence layer that backs up that ranking.
|
| 42 |
+
> And because every model is Apache-2.0 and every dataset is public record, environmental justice advocates can audit the same system that a developer uses. No commercial gatekeeping.
|
| 43 |
+
|
| 44 |
+
---
|
| 45 |
+
|
| 46 |
+
### [SLIDE 5 — Architecture] · ~1:00–1:30
|
| 47 |
+
|
| 48 |
+
**SCREEN:** Slide 5. "Five Stones fan out. One cited briefing comes back." Four evidence cards (Cornerstone, Keystone, Touchstone, Lodestone) + Capstone bar at bottom.
|
| 49 |
+
|
| 50 |
+
> The architecture is called Five Stones. A natural-language query hits the Planner — Granite 4.1 3B — which classifies intent and selects a specialist roster.
|
| 51 |
+
> Each Stone is a class of evidence. Cornerstone reads the hazard record: Sandy inundation zones, FEMA flood maps, USGS high-water marks, Prithvi satellite imagery. Keystone reads what's exposed: MTA stations, schools, hospitals, building footprints from our TerraMind NYC fine-tune. Touchstone reads what's happening now: live FloodNet sensors, 311 flood complaints, NOAA tide gauges. Lodestone looks forward: NPCC4 sea-level projections, our Granite TTM Battery surge nowcast.
|
| 52 |
+
> Then Capstone — Granite 4.1 8B on vLLM — synthesizes everything into a four-section briefing. Every numeric claim must cite its source, or the Mellea rejection sampler rerolls it. The briefing doesn't publish until all four grounding checks pass.
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
### [SLIDE 6 — Fine-tuning] · ~1:30–1:50
|
| 57 |
+
|
| 58 |
+
**SCREEN:** Slide 6. Three fine-tune cards: Prithvi-EO-2.0-NYC-Pluvial · TerraMind-NYC-Adapters · Granite-TTM-r2-Battery-Surge.
|
| 59 |
+
|
| 60 |
+
> We trained three NYC-specialized models on AMD MI300X hardware, all published Apache-2.0 on Hugging Face Hub.
|
| 61 |
+
> Prithvi-EO-2.0-NYC-Pluvial detects pluvial flooding from Sentinel-2 imagery — 0.60 IoU on the Ida test set, a 6× lift over the baseline. TerraMind-NYC-Adapters adds LoRA adapters for building footprint and land-use classification, plus 6 points of mIoU in 18 minutes of training. And Granite TTM r2 fine-tuned on the Battery tide gauge gives us a 9.6-hour surge residual nowcast at 35% lower RMSE than persistence.
|
| 62 |
+
> These aren't experiments. They're in production in every briefing.
|
| 63 |
+
|
| 64 |
+
---
|
| 65 |
+
|
| 66 |
+
### [SLIDE 7 — Demo intro] · ~1:50–2:00
|
| 67 |
+
|
| 68 |
+
**SCREEN:** Slide 7. "Live demo." Query text: *"I'm thinking about renting an apartment at 80 Pioneer Street, Brooklyn. Should I worry?"*
|
| 69 |
+
|
| 70 |
+
> Let's run it live. Three queries, three different intents.
|
| 71 |
+
|
| 72 |
+
---
|
| 73 |
+
|
| 74 |
+
### [DEMO CLIP 1 — Pioneer Street, single address] · ~2:00–2:40
|
| 75 |
+
|
| 76 |
+
**SCREEN:** Cut to recording `riprap-demo-20260506-234537.webm` at **t≈62s**.
|
| 77 |
+
- Left panel: briefing fully rendered. Title "Flood-exposure briefing · 80 Pioneer Street, Red Hook."
|
| 78 |
+
- Sections 01 Status through 04 Policy context visible with inline `[1]` `[2]` `[3]` citation chips.
|
| 79 |
+
- Right panel: Sandy flood map showing Pioneer Street pinned inside the inundation zone (blue overlay).
|
| 80 |
+
- Status bar: `intent: single_address · 19 specialists · attempt 1 · done`
|
| 81 |
+
|
| 82 |
+
> Thirteen seconds end-to-end. Nineteen specialists fired. The briefing tells you: Pioneer Street sits inside Hurricane Sandy's 2012 inundation zone, 0.82 metres above the nearest drainage channel, in the 78th percentile for water accumulation risk. FloodNet sensor FN-BK-018 — two blocks away — has logged four flood events since 2023. The DEP's high-intensity scenario puts the site under six inches of standing water. Every number has a footnote. Every footnote resolves to a named public dataset.
|
| 83 |
+
|
| 84 |
+
**SCREEN:** Slow scroll of left briefing panel while voiceover continues. Citation chips `[1]` `[2]` `[3]` visible inline. Bottom of panel shows section 04 "Policy context" with RAG passages from NPCC4.
|
| 85 |
+
|
| 86 |
+
> The map on the right isn't decorative — it's live. The layers are grouped by Stone, so you can see exactly which evidence tier each visual comes from.
|
| 87 |
+
|
| 88 |
+
---
|
| 89 |
+
|
| 90 |
+
### [DEMO CLIP 2 — Mellea 4/4 grounding card] · ~2:40–3:05
|
| 91 |
+
|
| 92 |
+
**SCREEN:** Recording at **t≈270s**. Right panel scrolled to Capstone section.
|
| 93 |
+
- Capstone card: **"grounding checks: 4/4 passed"**, rerolls=0, passed=4, attempt=1.
|
| 94 |
+
- Four check items: `numerics_grounded` · `no_placeholder_tokens` · `citations_dense` · `citations_resolve`
|
| 95 |
+
|
| 96 |
+
> Here's the proof. Mellea ran four grounding checks on the completed briefing: every non-trivial number appears verbatim in a source document; no template fragments leaked through; every number has a citation in the same sentence; every cited ID resolves to an actual input document.
|
| 97 |
+
> Four of four. First attempt. Zero rerolls.
|
| 98 |
+
> This is what "every number cites its source" looks like as a machine-verifiable claim, not a marketing promise.
|
| 99 |
+
|
| 100 |
+
---
|
| 101 |
+
|
| 102 |
+
### [DEMO CLIP 3 — Hollis, Queens · neighborhood intent] · ~3:05–3:30
|
| 103 |
+
|
| 104 |
+
**SCREEN:** Recording at **t≈510s**. New query: "Hollis, Queens."
|
| 105 |
+
- Status bar: `intent: neighborhood · 9 specialists · attempt 1 · done`
|
| 106 |
+
- Left panel: neighborhood briefing — NTA-level statistics, DEP stormwater scenario percentages, 311 flood complaint counts.
|
| 107 |
+
- Right panel: Cornerstone section with Sandy inundation percentage for the NTA + FEMA layer.
|
| 108 |
+
|
| 109 |
+
> Same system, different intent. "Hollis, Queens" is a neighborhood query — nine specialists instead of nineteen, NTA-level aggregates instead of point data. The planner classified it in under a second and dispatched the right Stone roster automatically.
|
| 110 |
+
> Hollis is a stormwater-flooding neighborhood, not a coastal one. The briefing reflects that: Sandy inundation is low; the DEP moderate-intensity scenario covers 22% of impervious surface; 311 flood complaints cluster around the 180th Street drainage corridor. Different geography, different risk profile, same citation standard.
|
| 111 |
+
|
| 112 |
+
---
|
| 113 |
+
|
| 114 |
+
### [DEMO CLIP 4 — Compare · Pioneer vs Gold Street] · ~3:30–4:00
|
| 115 |
+
|
| 116 |
+
**SCREEN:** Screenshot `compare-hf.jpg` — the live HF Space compare result.
|
| 117 |
+
- Title: "COMPARE 80 PIONEER STREET BROOKLYN TO 100 GOLD STREET MANHATTAN"
|
| 118 |
+
- **Key differences bar** at top: `Status: 80 vs 100` · `Empirical: 65 vs 26` · `Modeled Drainage (HAND): 3.81m vs 38.2m`
|
| 119 |
+
- Side-by-side Status sections — Pioneer: "exposed to flood risk, Sandy inundation zone, TWI 14.79." Gold St: "moderate flood exposure, HAND 6.42m, mid-slope position."
|
| 120 |
+
- Status bar: `intent: compare · 11 specialists · attempt 1 · done`
|
| 121 |
+
|
| 122 |
+
> One more. "Compare 80 Pioneer Street Brooklyn to 100 Gold Street Manhattan." The planner routes this as a compare intent — two full specialist runs, results merged side by side.
|
| 123 |
+
> The key differences bar surfaces the contrast immediately: Pioneer Street sits 3.81 metres above its nearest drainage channel. Gold Street at 100 is 38.2 metres. Pioneer has 65 empirical flood signals in the record; Gold Street has 26. Same city. Same storm history. Radically different exposure.
|
| 124 |
+
> This is the query a developer, an insurer, or a disclosure attorney actually wants to run.
|
| 125 |
+
|
| 126 |
+
---
|
| 127 |
+
|
| 128 |
+
### [SLIDE 8 — What's next] · ~4:00–4:20
|
| 129 |
+
|
| 130 |
+
**SCREEN:** Slide 8. Three boxes: Break out the Stones · Other flood-impacted cities · Historical-event mode.
|
| 131 |
+
|
| 132 |
+
> The architecture is NYC-specific by data choice, not by code.
|
| 133 |
+
> The five-Stone pattern generalizes: Houston, Miami, Jakarta — swap the probe sets and RAG corpus, the FSM is the same. Each Stone is already isolated enough to ship as a standalone package.
|
| 134 |
+
> And we want to add historical-event mode: re-run the FSM against snapshot data from before Sandy, before Ida. Validation against measured outcomes as a first-class feature, not an afterthought.
|
| 135 |
+
|
| 136 |
+
---
|
| 137 |
+
|
| 138 |
+
### [SLIDE 9 — CTA] · ~4:20–4:30
|
| 139 |
+
|
| 140 |
+
**SCREEN:** Slide 9. Dark background. "github.com/msradam/riprap-nyc" large. "Apache-2.0 · public data · AMD MI300X · IBM Granite 4.1 · Mellea grounding."
|
| 141 |
+
|
| 142 |
+
> Everything is open. Apache-2.0, public data, MIT and Apache models.
|
| 143 |
+
> Riprap on AMD MI300X. Try it at the link in the description.
|
| 144 |
+
|
| 145 |
+
---
|
| 146 |
+
|
| 147 |
+
## Segment map
|
| 148 |
+
|
| 149 |
+
| Segment | Source | Timestamp / asset |
|
| 150 |
+
|---------|--------|-------------------|
|
| 151 |
+
| Slides 1–7 | `slides/deck.pdf` | screen-record slide deck |
|
| 152 |
+
| Demo clip 1 — Pioneer briefing + map | `assets/video/riprap-demo-20260506-234537.webm` | t≈62–90s |
|
| 153 |
+
| Demo clip 2 — Mellea 4/4 card | `assets/video/riprap-demo-20260506-234537.webm` | t≈265–290s |
|
| 154 |
+
| Demo clip 3 — Hollis neighborhood | `assets/video/riprap-demo-20260506-234537.webm` | t≈505–545s |
|
| 155 |
+
| Demo clip 4 — Compare result | `compare-hf.jpg` (static screenshot or re-record) | n/a |
|
| 156 |
+
| Slides 8–9 | `slides/deck.pdf` | screen-record slide deck |
|
| 157 |
+
|
| 158 |
+
## Total runtime estimate
|
| 159 |
+
~4:30 — comfortable under 5 min with natural pauses.
|
tests/probe_50_results.json
ADDED
|
@@ -0,0 +1,1308 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"id": "q001",
|
| 4 |
+
"query": "I'm thinking about renting an apartment at 80 Pioneer Street, Brooklyn. Should I worry?",
|
| 5 |
+
"status": "PASS",
|
| 6 |
+
"wall_clock_s": 9.77,
|
| 7 |
+
"intent_returned": "single_address",
|
| 8 |
+
"mellea_passed": "4/4",
|
| 9 |
+
"mellea_rerolls": 0,
|
| 10 |
+
"stones_fired": [
|
| 11 |
+
"sandy",
|
| 12 |
+
"dep",
|
| 13 |
+
"floodnet",
|
| 14 |
+
"311",
|
| 15 |
+
"noaa",
|
| 16 |
+
"nws",
|
| 17 |
+
"ttm",
|
| 18 |
+
"microtopo",
|
| 19 |
+
"ida",
|
| 20 |
+
"prithvi_v2",
|
| 21 |
+
"rag",
|
| 22 |
+
"gliner",
|
| 23 |
+
"mellea"
|
| 24 |
+
],
|
| 25 |
+
"stones_errored": [
|
| 26 |
+
"floodnet",
|
| 27 |
+
"mta",
|
| 28 |
+
"prithvi_live",
|
| 29 |
+
"terramind"
|
| 30 |
+
],
|
| 31 |
+
"stones_silent": [],
|
| 32 |
+
"citations_resolved": null,
|
| 33 |
+
"compare_targets": null,
|
| 34 |
+
"error": null
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"id": "q002",
|
| 38 |
+
"query": "Hollis, Queens",
|
| 39 |
+
"status": "PASS",
|
| 40 |
+
"wall_clock_s": 6.95,
|
| 41 |
+
"intent_returned": "neighborhood",
|
| 42 |
+
"mellea_passed": "4/4",
|
| 43 |
+
"mellea_rerolls": 0,
|
| 44 |
+
"stones_fired": [],
|
| 45 |
+
"stones_errored": [],
|
| 46 |
+
"stones_silent": [],
|
| 47 |
+
"citations_resolved": null,
|
| 48 |
+
"compare_targets": null,
|
| 49 |
+
"error": null
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"id": "q003",
|
| 53 |
+
"query": "Compare 80 Pioneer Street Brooklyn to 100 Gold Street Manhattan",
|
| 54 |
+
"status": "PASS",
|
| 55 |
+
"wall_clock_s": 20.69,
|
| 56 |
+
"intent_returned": "compare",
|
| 57 |
+
"mellea_passed": "4/4",
|
| 58 |
+
"mellea_rerolls": 0,
|
| 59 |
+
"stones_fired": [
|
| 60 |
+
"sandy",
|
| 61 |
+
"dep",
|
| 62 |
+
"floodnet",
|
| 63 |
+
"311",
|
| 64 |
+
"noaa",
|
| 65 |
+
"nws",
|
| 66 |
+
"ttm",
|
| 67 |
+
"microtopo",
|
| 68 |
+
"ida",
|
| 69 |
+
"prithvi_v2",
|
| 70 |
+
"rag",
|
| 71 |
+
"gliner",
|
| 72 |
+
"mellea",
|
| 73 |
+
"mta"
|
| 74 |
+
],
|
| 75 |
+
"stones_errored": [
|
| 76 |
+
"floodnet",
|
| 77 |
+
"mta",
|
| 78 |
+
"prithvi_live",
|
| 79 |
+
"terramind"
|
| 80 |
+
],
|
| 81 |
+
"stones_silent": [],
|
| 82 |
+
"citations_resolved": null,
|
| 83 |
+
"compare_targets": 2,
|
| 84 |
+
"error": null
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"id": "q004",
|
| 88 |
+
"query": "442 East Houston Street, Manhattan",
|
| 89 |
+
"status": "PASS",
|
| 90 |
+
"wall_clock_s": 8.51,
|
| 91 |
+
"intent_returned": "single_address",
|
| 92 |
+
"mellea_passed": "4/4",
|
| 93 |
+
"mellea_rerolls": 0,
|
| 94 |
+
"stones_fired": [
|
| 95 |
+
"sandy",
|
| 96 |
+
"dep",
|
| 97 |
+
"floodnet",
|
| 98 |
+
"311",
|
| 99 |
+
"noaa",
|
| 100 |
+
"nws",
|
| 101 |
+
"ttm",
|
| 102 |
+
"microtopo",
|
| 103 |
+
"ida",
|
| 104 |
+
"prithvi_v2",
|
| 105 |
+
"rag",
|
| 106 |
+
"gliner",
|
| 107 |
+
"mellea"
|
| 108 |
+
],
|
| 109 |
+
"stones_errored": [
|
| 110 |
+
"floodnet",
|
| 111 |
+
"mta",
|
| 112 |
+
"prithvi_live",
|
| 113 |
+
"terramind"
|
| 114 |
+
],
|
| 115 |
+
"stones_silent": [],
|
| 116 |
+
"citations_resolved": null,
|
| 117 |
+
"compare_targets": null,
|
| 118 |
+
"error": null
|
| 119 |
+
},
|
| 120 |
+
{
|
| 121 |
+
"id": "q005",
|
| 122 |
+
"query": "100 Gold Street, Manhattan",
|
| 123 |
+
"status": "PASS",
|
| 124 |
+
"wall_clock_s": 10.64,
|
| 125 |
+
"intent_returned": "single_address",
|
| 126 |
+
"mellea_passed": "4/4",
|
| 127 |
+
"mellea_rerolls": 0,
|
| 128 |
+
"stones_fired": [
|
| 129 |
+
"sandy",
|
| 130 |
+
"dep",
|
| 131 |
+
"floodnet",
|
| 132 |
+
"311",
|
| 133 |
+
"noaa",
|
| 134 |
+
"nws",
|
| 135 |
+
"ttm",
|
| 136 |
+
"microtopo",
|
| 137 |
+
"ida",
|
| 138 |
+
"mta",
|
| 139 |
+
"prithvi_v2",
|
| 140 |
+
"rag",
|
| 141 |
+
"gliner",
|
| 142 |
+
"mellea"
|
| 143 |
+
],
|
| 144 |
+
"stones_errored": [
|
| 145 |
+
"floodnet",
|
| 146 |
+
"prithvi_live",
|
| 147 |
+
"terramind"
|
| 148 |
+
],
|
| 149 |
+
"stones_silent": [],
|
| 150 |
+
"citations_resolved": null,
|
| 151 |
+
"compare_targets": null,
|
| 152 |
+
"error": null
|
| 153 |
+
},
|
| 154 |
+
{
|
| 155 |
+
"id": "q006",
|
| 156 |
+
"query": "Coney Island, Brooklyn",
|
| 157 |
+
"status": "PASS",
|
| 158 |
+
"wall_clock_s": 5.48,
|
| 159 |
+
"intent_returned": "neighborhood",
|
| 160 |
+
"mellea_passed": "4/4",
|
| 161 |
+
"mellea_rerolls": 0,
|
| 162 |
+
"stones_fired": [],
|
| 163 |
+
"stones_errored": [],
|
| 164 |
+
"stones_silent": [],
|
| 165 |
+
"citations_resolved": null,
|
| 166 |
+
"compare_targets": null,
|
| 167 |
+
"error": null
|
| 168 |
+
},
|
| 169 |
+
{
|
| 170 |
+
"id": "q007",
|
| 171 |
+
"query": "2940 Brighton 3rd St, Brooklyn",
|
| 172 |
+
"status": "PASS",
|
| 173 |
+
"wall_clock_s": 9.89,
|
| 174 |
+
"intent_returned": "single_address",
|
| 175 |
+
"mellea_passed": "4/4",
|
| 176 |
+
"mellea_rerolls": 0,
|
| 177 |
+
"stones_fired": [
|
| 178 |
+
"sandy",
|
| 179 |
+
"dep",
|
| 180 |
+
"floodnet",
|
| 181 |
+
"311",
|
| 182 |
+
"noaa",
|
| 183 |
+
"nws",
|
| 184 |
+
"ttm",
|
| 185 |
+
"microtopo",
|
| 186 |
+
"ida",
|
| 187 |
+
"mta",
|
| 188 |
+
"prithvi_v2",
|
| 189 |
+
"rag",
|
| 190 |
+
"gliner",
|
| 191 |
+
"mellea"
|
| 192 |
+
],
|
| 193 |
+
"stones_errored": [
|
| 194 |
+
"floodnet",
|
| 195 |
+
"prithvi_live",
|
| 196 |
+
"terramind"
|
| 197 |
+
],
|
| 198 |
+
"stones_silent": [],
|
| 199 |
+
"citations_resolved": null,
|
| 200 |
+
"compare_targets": null,
|
| 201 |
+
"error": null
|
| 202 |
+
},
|
| 203 |
+
{
|
| 204 |
+
"id": "q008",
|
| 205 |
+
"query": "Red Hook, Brooklyn",
|
| 206 |
+
"status": "PASS",
|
| 207 |
+
"wall_clock_s": 6.89,
|
| 208 |
+
"intent_returned": "neighborhood",
|
| 209 |
+
"mellea_passed": "4/4",
|
| 210 |
+
"mellea_rerolls": 0,
|
| 211 |
+
"stones_fired": [],
|
| 212 |
+
"stones_errored": [],
|
| 213 |
+
"stones_silent": [],
|
| 214 |
+
"citations_resolved": null,
|
| 215 |
+
"compare_targets": null,
|
| 216 |
+
"error": null
|
| 217 |
+
},
|
| 218 |
+
{
|
| 219 |
+
"id": "q009",
|
| 220 |
+
"query": "What's the flood risk for 345 East 94th Street, Manhattan?",
|
| 221 |
+
"status": "PASS",
|
| 222 |
+
"wall_clock_s": 10.04,
|
| 223 |
+
"intent_returned": "single_address",
|
| 224 |
+
"mellea_passed": "4/4",
|
| 225 |
+
"mellea_rerolls": 0,
|
| 226 |
+
"stones_fired": [
|
| 227 |
+
"sandy",
|
| 228 |
+
"dep",
|
| 229 |
+
"floodnet",
|
| 230 |
+
"311",
|
| 231 |
+
"noaa",
|
| 232 |
+
"nws",
|
| 233 |
+
"ttm",
|
| 234 |
+
"microtopo",
|
| 235 |
+
"ida",
|
| 236 |
+
"mta",
|
| 237 |
+
"prithvi_v2",
|
| 238 |
+
"rag",
|
| 239 |
+
"gliner",
|
| 240 |
+
"mellea"
|
| 241 |
+
],
|
| 242 |
+
"stones_errored": [
|
| 243 |
+
"floodnet",
|
| 244 |
+
"prithvi_live",
|
| 245 |
+
"terramind"
|
| 246 |
+
],
|
| 247 |
+
"stones_silent": [],
|
| 248 |
+
"citations_resolved": null,
|
| 249 |
+
"compare_targets": null,
|
| 250 |
+
"error": null
|
| 251 |
+
},
|
| 252 |
+
{
|
| 253 |
+
"id": "q010",
|
| 254 |
+
"query": "Is 1 MetroTech Center in Brooklyn at risk during major storms?",
|
| 255 |
+
"status": "PASS",
|
| 256 |
+
"wall_clock_s": 14.5,
|
| 257 |
+
"intent_returned": "single_address",
|
| 258 |
+
"mellea_passed": "3/4",
|
| 259 |
+
"mellea_rerolls": 2,
|
| 260 |
+
"stones_fired": [
|
| 261 |
+
"sandy",
|
| 262 |
+
"dep",
|
| 263 |
+
"floodnet",
|
| 264 |
+
"311",
|
| 265 |
+
"noaa",
|
| 266 |
+
"nws",
|
| 267 |
+
"ttm",
|
| 268 |
+
"microtopo",
|
| 269 |
+
"ida",
|
| 270 |
+
"mta",
|
| 271 |
+
"prithvi_v2",
|
| 272 |
+
"rag",
|
| 273 |
+
"gliner",
|
| 274 |
+
"mellea"
|
| 275 |
+
],
|
| 276 |
+
"stones_errored": [
|
| 277 |
+
"floodnet",
|
| 278 |
+
"prithvi_live",
|
| 279 |
+
"terramind"
|
| 280 |
+
],
|
| 281 |
+
"stones_silent": [],
|
| 282 |
+
"citations_resolved": null,
|
| 283 |
+
"compare_targets": null,
|
| 284 |
+
"error": null
|
| 285 |
+
},
|
| 286 |
+
{
|
| 287 |
+
"id": "q011",
|
| 288 |
+
"query": "Howard Beach, Queens",
|
| 289 |
+
"status": "PASS",
|
| 290 |
+
"wall_clock_s": 6.89,
|
| 291 |
+
"intent_returned": "neighborhood",
|
| 292 |
+
"mellea_passed": "4/4",
|
| 293 |
+
"mellea_rerolls": 0,
|
| 294 |
+
"stones_fired": [],
|
| 295 |
+
"stones_errored": [],
|
| 296 |
+
"stones_silent": [],
|
| 297 |
+
"citations_resolved": null,
|
| 298 |
+
"compare_targets": null,
|
| 299 |
+
"error": null
|
| 300 |
+
},
|
| 301 |
+
{
|
| 302 |
+
"id": "q012",
|
| 303 |
+
"query": "Canarsie, Brooklyn",
|
| 304 |
+
"status": "PASS",
|
| 305 |
+
"wall_clock_s": 8.0,
|
| 306 |
+
"intent_returned": "neighborhood",
|
| 307 |
+
"mellea_passed": "4/4",
|
| 308 |
+
"mellea_rerolls": 0,
|
| 309 |
+
"stones_fired": [],
|
| 310 |
+
"stones_errored": [],
|
| 311 |
+
"stones_silent": [],
|
| 312 |
+
"citations_resolved": null,
|
| 313 |
+
"compare_targets": null,
|
| 314 |
+
"error": null
|
| 315 |
+
},
|
| 316 |
+
{
|
| 317 |
+
"id": "q013",
|
| 318 |
+
"query": "Compare Canarsie Brooklyn to Park Slope Brooklyn",
|
| 319 |
+
"status": "PASS",
|
| 320 |
+
"wall_clock_s": 24.22,
|
| 321 |
+
"intent_returned": "compare",
|
| 322 |
+
"mellea_passed": "3/4",
|
| 323 |
+
"mellea_rerolls": 3,
|
| 324 |
+
"stones_fired": [
|
| 325 |
+
"sandy",
|
| 326 |
+
"dep",
|
| 327 |
+
"floodnet",
|
| 328 |
+
"311",
|
| 329 |
+
"noaa",
|
| 330 |
+
"nws",
|
| 331 |
+
"ttm",
|
| 332 |
+
"microtopo",
|
| 333 |
+
"ida",
|
| 334 |
+
"prithvi_v2",
|
| 335 |
+
"rag",
|
| 336 |
+
"gliner",
|
| 337 |
+
"mellea",
|
| 338 |
+
"mta"
|
| 339 |
+
],
|
| 340 |
+
"stones_errored": [
|
| 341 |
+
"mta",
|
| 342 |
+
"prithvi_live",
|
| 343 |
+
"terramind",
|
| 344 |
+
"floodnet"
|
| 345 |
+
],
|
| 346 |
+
"stones_silent": [],
|
| 347 |
+
"citations_resolved": null,
|
| 348 |
+
"compare_targets": 2,
|
| 349 |
+
"error": null
|
| 350 |
+
},
|
| 351 |
+
{
|
| 352 |
+
"id": "q014",
|
| 353 |
+
"query": "Should I worry about flooding at 520 West 145th Street in Manhattan?",
|
| 354 |
+
"status": "PASS",
|
| 355 |
+
"wall_clock_s": 10.35,
|
| 356 |
+
"intent_returned": "single_address",
|
| 357 |
+
"mellea_passed": "4/4",
|
| 358 |
+
"mellea_rerolls": 0,
|
| 359 |
+
"stones_fired": [
|
| 360 |
+
"sandy",
|
| 361 |
+
"dep",
|
| 362 |
+
"floodnet",
|
| 363 |
+
"311",
|
| 364 |
+
"noaa",
|
| 365 |
+
"nws",
|
| 366 |
+
"ttm",
|
| 367 |
+
"microtopo",
|
| 368 |
+
"ida",
|
| 369 |
+
"mta",
|
| 370 |
+
"prithvi_v2",
|
| 371 |
+
"rag",
|
| 372 |
+
"gliner",
|
| 373 |
+
"mellea"
|
| 374 |
+
],
|
| 375 |
+
"stones_errored": [
|
| 376 |
+
"floodnet",
|
| 377 |
+
"prithvi_live",
|
| 378 |
+
"terramind"
|
| 379 |
+
],
|
| 380 |
+
"stones_silent": [],
|
| 381 |
+
"citations_resolved": null,
|
| 382 |
+
"compare_targets": null,
|
| 383 |
+
"error": null
|
| 384 |
+
},
|
| 385 |
+
{
|
| 386 |
+
"id": "q015",
|
| 387 |
+
"query": "Mott Haven, Bronx",
|
| 388 |
+
"status": "PASS",
|
| 389 |
+
"wall_clock_s": 6.12,
|
| 390 |
+
"intent_returned": "neighborhood",
|
| 391 |
+
"mellea_passed": "4/4",
|
| 392 |
+
"mellea_rerolls": 0,
|
| 393 |
+
"stones_fired": [],
|
| 394 |
+
"stones_errored": [],
|
| 395 |
+
"stones_silent": [],
|
| 396 |
+
"citations_resolved": null,
|
| 397 |
+
"compare_targets": null,
|
| 398 |
+
"error": null
|
| 399 |
+
},
|
| 400 |
+
{
|
| 401 |
+
"id": "q016",
|
| 402 |
+
"query": "What was the flood situation at 750 Baychester Avenue, Bronx during Ida?",
|
| 403 |
+
"status": "PASS",
|
| 404 |
+
"wall_clock_s": 0.03,
|
| 405 |
+
"intent_returned": "not_implemented",
|
| 406 |
+
"mellea_passed": "0/4",
|
| 407 |
+
"mellea_rerolls": 0,
|
| 408 |
+
"stones_fired": [],
|
| 409 |
+
"stones_errored": [],
|
| 410 |
+
"stones_silent": [],
|
| 411 |
+
"citations_resolved": null,
|
| 412 |
+
"compare_targets": null,
|
| 413 |
+
"error": null
|
| 414 |
+
},
|
| 415 |
+
{
|
| 416 |
+
"id": "q017",
|
| 417 |
+
"query": "Is the NYCHA Gowanus Houses at risk from sea level rise?",
|
| 418 |
+
"status": "PASS",
|
| 419 |
+
"wall_clock_s": 5.84,
|
| 420 |
+
"intent_returned": "neighborhood",
|
| 421 |
+
"mellea_passed": "4/4",
|
| 422 |
+
"mellea_rerolls": 1,
|
| 423 |
+
"stones_fired": [],
|
| 424 |
+
"stones_errored": [],
|
| 425 |
+
"stones_silent": [],
|
| 426 |
+
"citations_resolved": null,
|
| 427 |
+
"compare_targets": null,
|
| 428 |
+
"error": null
|
| 429 |
+
},
|
| 430 |
+
{
|
| 431 |
+
"id": "q018",
|
| 432 |
+
"query": "Compare 750 Baychester Avenue Bronx to 150 Riverside Drive Manhattan",
|
| 433 |
+
"status": "PASS",
|
| 434 |
+
"wall_clock_s": 15.64,
|
| 435 |
+
"intent_returned": "compare",
|
| 436 |
+
"mellea_passed": "4/4",
|
| 437 |
+
"mellea_rerolls": 0,
|
| 438 |
+
"stones_fired": [
|
| 439 |
+
"sandy",
|
| 440 |
+
"dep",
|
| 441 |
+
"floodnet",
|
| 442 |
+
"311",
|
| 443 |
+
"noaa",
|
| 444 |
+
"nws",
|
| 445 |
+
"ttm",
|
| 446 |
+
"microtopo",
|
| 447 |
+
"ida",
|
| 448 |
+
"mta",
|
| 449 |
+
"prithvi_v2",
|
| 450 |
+
"rag",
|
| 451 |
+
"gliner",
|
| 452 |
+
"mellea"
|
| 453 |
+
],
|
| 454 |
+
"stones_errored": [
|
| 455 |
+
"floodnet",
|
| 456 |
+
"prithvi_live",
|
| 457 |
+
"terramind"
|
| 458 |
+
],
|
| 459 |
+
"stones_silent": [],
|
| 460 |
+
"citations_resolved": null,
|
| 461 |
+
"compare_targets": 2,
|
| 462 |
+
"error": null
|
| 463 |
+
},
|
| 464 |
+
{
|
| 465 |
+
"id": "q019",
|
| 466 |
+
"query": "Tottenville, Staten Island",
|
| 467 |
+
"status": "PASS",
|
| 468 |
+
"wall_clock_s": 7.51,
|
| 469 |
+
"intent_returned": "neighborhood",
|
| 470 |
+
"mellea_passed": "4/4",
|
| 471 |
+
"mellea_rerolls": 1,
|
| 472 |
+
"stones_fired": [],
|
| 473 |
+
"stones_errored": [],
|
| 474 |
+
"stones_silent": [],
|
| 475 |
+
"citations_resolved": null,
|
| 476 |
+
"compare_targets": null,
|
| 477 |
+
"error": null
|
| 478 |
+
},
|
| 479 |
+
{
|
| 480 |
+
"id": "q020",
|
| 481 |
+
"query": "What's the flood risk at 151 West 34th Street, Manhattan? It's near Penn Station.",
|
| 482 |
+
"status": "PASS",
|
| 483 |
+
"wall_clock_s": 10.2,
|
| 484 |
+
"intent_returned": "single_address",
|
| 485 |
+
"mellea_passed": "4/4",
|
| 486 |
+
"mellea_rerolls": 0,
|
| 487 |
+
"stones_fired": [
|
| 488 |
+
"sandy",
|
| 489 |
+
"dep",
|
| 490 |
+
"floodnet",
|
| 491 |
+
"311",
|
| 492 |
+
"noaa",
|
| 493 |
+
"nws",
|
| 494 |
+
"ttm",
|
| 495 |
+
"microtopo",
|
| 496 |
+
"ida",
|
| 497 |
+
"mta",
|
| 498 |
+
"prithvi_v2",
|
| 499 |
+
"rag",
|
| 500 |
+
"gliner",
|
| 501 |
+
"mellea"
|
| 502 |
+
],
|
| 503 |
+
"stones_errored": [
|
| 504 |
+
"floodnet",
|
| 505 |
+
"prithvi_live",
|
| 506 |
+
"terramind"
|
| 507 |
+
],
|
| 508 |
+
"stones_silent": [],
|
| 509 |
+
"citations_resolved": null,
|
| 510 |
+
"compare_targets": null,
|
| 511 |
+
"error": null
|
| 512 |
+
},
|
| 513 |
+
{
|
| 514 |
+
"id": "q021",
|
| 515 |
+
"query": "Compare Red Hook Brooklyn to the Financial District Manhattan for flood risk",
|
| 516 |
+
"status": "PASS",
|
| 517 |
+
"wall_clock_s": 18.46,
|
| 518 |
+
"intent_returned": "compare",
|
| 519 |
+
"mellea_passed": "4/4",
|
| 520 |
+
"mellea_rerolls": 0,
|
| 521 |
+
"stones_fired": [
|
| 522 |
+
"sandy",
|
| 523 |
+
"dep",
|
| 524 |
+
"floodnet",
|
| 525 |
+
"311",
|
| 526 |
+
"noaa",
|
| 527 |
+
"nws",
|
| 528 |
+
"ttm",
|
| 529 |
+
"microtopo",
|
| 530 |
+
"ida",
|
| 531 |
+
"mta",
|
| 532 |
+
"prithvi_v2",
|
| 533 |
+
"rag",
|
| 534 |
+
"gliner",
|
| 535 |
+
"mellea"
|
| 536 |
+
],
|
| 537 |
+
"stones_errored": [
|
| 538 |
+
"floodnet",
|
| 539 |
+
"prithvi_live",
|
| 540 |
+
"terramind"
|
| 541 |
+
],
|
| 542 |
+
"stones_silent": [],
|
| 543 |
+
"citations_resolved": null,
|
| 544 |
+
"compare_targets": 2,
|
| 545 |
+
"error": null
|
| 546 |
+
},
|
| 547 |
+
{
|
| 548 |
+
"id": "q022",
|
| 549 |
+
"query": "Is 595 Dean Street in Brooklyn at risk of flooding?",
|
| 550 |
+
"status": "PASS",
|
| 551 |
+
"wall_clock_s": 10.97,
|
| 552 |
+
"intent_returned": "single_address",
|
| 553 |
+
"mellea_passed": "4/4",
|
| 554 |
+
"mellea_rerolls": 0,
|
| 555 |
+
"stones_fired": [
|
| 556 |
+
"sandy",
|
| 557 |
+
"dep",
|
| 558 |
+
"floodnet",
|
| 559 |
+
"311",
|
| 560 |
+
"noaa",
|
| 561 |
+
"nws",
|
| 562 |
+
"ttm",
|
| 563 |
+
"microtopo",
|
| 564 |
+
"ida",
|
| 565 |
+
"mta",
|
| 566 |
+
"prithvi_v2",
|
| 567 |
+
"rag",
|
| 568 |
+
"gliner",
|
| 569 |
+
"mellea"
|
| 570 |
+
],
|
| 571 |
+
"stones_errored": [
|
| 572 |
+
"floodnet",
|
| 573 |
+
"prithvi_live",
|
| 574 |
+
"terramind"
|
| 575 |
+
],
|
| 576 |
+
"stones_silent": [],
|
| 577 |
+
"citations_resolved": null,
|
| 578 |
+
"compare_targets": null,
|
| 579 |
+
"error": null
|
| 580 |
+
},
|
| 581 |
+
{
|
| 582 |
+
"id": "q023",
|
| 583 |
+
"query": "South Beach, Staten Island",
|
| 584 |
+
"status": "PASS",
|
| 585 |
+
"wall_clock_s": 9.48,
|
| 586 |
+
"intent_returned": "neighborhood",
|
| 587 |
+
"mellea_passed": "3/4",
|
| 588 |
+
"mellea_rerolls": 2,
|
| 589 |
+
"stones_fired": [],
|
| 590 |
+
"stones_errored": [],
|
| 591 |
+
"stones_silent": [],
|
| 592 |
+
"citations_resolved": null,
|
| 593 |
+
"compare_targets": null,
|
| 594 |
+
"error": null
|
| 595 |
+
},
|
| 596 |
+
{
|
| 597 |
+
"id": "q024",
|
| 598 |
+
"query": "How exposed is 1 Fulton Street Manhattan to surge from a major hurricane?",
|
| 599 |
+
"status": "PASS",
|
| 600 |
+
"wall_clock_s": 9.87,
|
| 601 |
+
"intent_returned": "single_address",
|
| 602 |
+
"mellea_passed": "4/4",
|
| 603 |
+
"mellea_rerolls": 0,
|
| 604 |
+
"stones_fired": [
|
| 605 |
+
"sandy",
|
| 606 |
+
"dep",
|
| 607 |
+
"floodnet",
|
| 608 |
+
"311",
|
| 609 |
+
"noaa",
|
| 610 |
+
"nws",
|
| 611 |
+
"ttm",
|
| 612 |
+
"microtopo",
|
| 613 |
+
"ida",
|
| 614 |
+
"mta",
|
| 615 |
+
"prithvi_v2",
|
| 616 |
+
"rag",
|
| 617 |
+
"gliner",
|
| 618 |
+
"mellea"
|
| 619 |
+
],
|
| 620 |
+
"stones_errored": [
|
| 621 |
+
"floodnet",
|
| 622 |
+
"prithvi_live",
|
| 623 |
+
"terramind"
|
| 624 |
+
],
|
| 625 |
+
"stones_silent": [],
|
| 626 |
+
"citations_resolved": null,
|
| 627 |
+
"compare_targets": null,
|
| 628 |
+
"error": null
|
| 629 |
+
},
|
| 630 |
+
{
|
| 631 |
+
"id": "q025",
|
| 632 |
+
"query": "Compare Howard Beach Queens to Forest Hills Queens for flood risk",
|
| 633 |
+
"status": "PASS",
|
| 634 |
+
"wall_clock_s": 19.63,
|
| 635 |
+
"intent_returned": "compare",
|
| 636 |
+
"mellea_passed": "4/4",
|
| 637 |
+
"mellea_rerolls": 2,
|
| 638 |
+
"stones_fired": [
|
| 639 |
+
"sandy",
|
| 640 |
+
"dep",
|
| 641 |
+
"floodnet",
|
| 642 |
+
"311",
|
| 643 |
+
"noaa",
|
| 644 |
+
"nws",
|
| 645 |
+
"ttm",
|
| 646 |
+
"microtopo",
|
| 647 |
+
"ida",
|
| 648 |
+
"prithvi_v2",
|
| 649 |
+
"rag",
|
| 650 |
+
"gliner",
|
| 651 |
+
"mellea",
|
| 652 |
+
"mta"
|
| 653 |
+
],
|
| 654 |
+
"stones_errored": [
|
| 655 |
+
"floodnet",
|
| 656 |
+
"mta",
|
| 657 |
+
"prithvi_live",
|
| 658 |
+
"terramind"
|
| 659 |
+
],
|
| 660 |
+
"stones_silent": [],
|
| 661 |
+
"citations_resolved": null,
|
| 662 |
+
"compare_targets": 2,
|
| 663 |
+
"error": null
|
| 664 |
+
},
|
| 665 |
+
{
|
| 666 |
+
"id": "q026",
|
| 667 |
+
"query": "What's the storm surge risk for 157-11 Rockaway Beach Blvd, Queens?",
|
| 668 |
+
"status": "PASS",
|
| 669 |
+
"wall_clock_s": 1.58,
|
| 670 |
+
"intent_returned": "single_address",
|
| 671 |
+
"mellea_passed": "0/4",
|
| 672 |
+
"mellea_rerolls": 0,
|
| 673 |
+
"stones_fired": [
|
| 674 |
+
"gliner",
|
| 675 |
+
"mellea"
|
| 676 |
+
],
|
| 677 |
+
"stones_errored": [
|
| 678 |
+
"sandy",
|
| 679 |
+
"dep",
|
| 680 |
+
"floodnet",
|
| 681 |
+
"311",
|
| 682 |
+
"noaa",
|
| 683 |
+
"nws",
|
| 684 |
+
"ttm",
|
| 685 |
+
"microtopo",
|
| 686 |
+
"ida",
|
| 687 |
+
"mta",
|
| 688 |
+
"prithvi_v2",
|
| 689 |
+
"prithvi_live",
|
| 690 |
+
"terramind",
|
| 691 |
+
"rag"
|
| 692 |
+
],
|
| 693 |
+
"stones_silent": [],
|
| 694 |
+
"citations_resolved": null,
|
| 695 |
+
"compare_targets": null,
|
| 696 |
+
"error": null
|
| 697 |
+
},
|
| 698 |
+
{
|
| 699 |
+
"id": "q027",
|
| 700 |
+
"query": "Hunts Point, Bronx",
|
| 701 |
+
"status": "PASS",
|
| 702 |
+
"wall_clock_s": 5.34,
|
| 703 |
+
"intent_returned": "neighborhood",
|
| 704 |
+
"mellea_passed": "4/4",
|
| 705 |
+
"mellea_rerolls": 0,
|
| 706 |
+
"stones_fired": [],
|
| 707 |
+
"stones_errored": [],
|
| 708 |
+
"stones_silent": [],
|
| 709 |
+
"citations_resolved": null,
|
| 710 |
+
"compare_targets": null,
|
| 711 |
+
"error": null
|
| 712 |
+
},
|
| 713 |
+
{
|
| 714 |
+
"id": "q028",
|
| 715 |
+
"query": "Is 160 Conover Street in Red Hook still considered a flood zone?",
|
| 716 |
+
"status": "PASS",
|
| 717 |
+
"wall_clock_s": 8.96,
|
| 718 |
+
"intent_returned": "single_address",
|
| 719 |
+
"mellea_passed": "4/4",
|
| 720 |
+
"mellea_rerolls": 0,
|
| 721 |
+
"stones_fired": [
|
| 722 |
+
"sandy",
|
| 723 |
+
"dep",
|
| 724 |
+
"floodnet",
|
| 725 |
+
"311",
|
| 726 |
+
"noaa",
|
| 727 |
+
"nws",
|
| 728 |
+
"ttm",
|
| 729 |
+
"microtopo",
|
| 730 |
+
"ida",
|
| 731 |
+
"prithvi_v2",
|
| 732 |
+
"rag",
|
| 733 |
+
"gliner",
|
| 734 |
+
"mellea"
|
| 735 |
+
],
|
| 736 |
+
"stones_errored": [
|
| 737 |
+
"floodnet",
|
| 738 |
+
"mta",
|
| 739 |
+
"prithvi_live",
|
| 740 |
+
"terramind"
|
| 741 |
+
],
|
| 742 |
+
"stones_silent": [],
|
| 743 |
+
"citations_resolved": null,
|
| 744 |
+
"compare_targets": null,
|
| 745 |
+
"error": null
|
| 746 |
+
},
|
| 747 |
+
{
|
| 748 |
+
"id": "q029",
|
| 749 |
+
"query": "Compare 160 Conover Street Red Hook to 300 Flatbush Avenue Brooklyn",
|
| 750 |
+
"status": "PASS",
|
| 751 |
+
"wall_clock_s": 16.11,
|
| 752 |
+
"intent_returned": "compare",
|
| 753 |
+
"mellea_passed": "4/4",
|
| 754 |
+
"mellea_rerolls": 0,
|
| 755 |
+
"stones_fired": [
|
| 756 |
+
"sandy",
|
| 757 |
+
"dep",
|
| 758 |
+
"floodnet",
|
| 759 |
+
"311",
|
| 760 |
+
"noaa",
|
| 761 |
+
"nws",
|
| 762 |
+
"ttm",
|
| 763 |
+
"microtopo",
|
| 764 |
+
"ida",
|
| 765 |
+
"prithvi_v2",
|
| 766 |
+
"rag",
|
| 767 |
+
"gliner",
|
| 768 |
+
"mellea",
|
| 769 |
+
"mta"
|
| 770 |
+
],
|
| 771 |
+
"stones_errored": [
|
| 772 |
+
"floodnet",
|
| 773 |
+
"mta",
|
| 774 |
+
"prithvi_live",
|
| 775 |
+
"terramind"
|
| 776 |
+
],
|
| 777 |
+
"stones_silent": [],
|
| 778 |
+
"citations_resolved": null,
|
| 779 |
+
"compare_targets": 2,
|
| 780 |
+
"error": null
|
| 781 |
+
},
|
| 782 |
+
{
|
| 783 |
+
"id": "q030",
|
| 784 |
+
"query": "What are the flood risks for the Two Bridges neighborhood in Manhattan?",
|
| 785 |
+
"status": "PASS",
|
| 786 |
+
"wall_clock_s": 4.89,
|
| 787 |
+
"intent_returned": "neighborhood",
|
| 788 |
+
"mellea_passed": "4/4",
|
| 789 |
+
"mellea_rerolls": 0,
|
| 790 |
+
"stones_fired": [],
|
| 791 |
+
"stones_errored": [],
|
| 792 |
+
"stones_silent": [],
|
| 793 |
+
"citations_resolved": null,
|
| 794 |
+
"compare_targets": null,
|
| 795 |
+
"error": null
|
| 796 |
+
},
|
| 797 |
+
{
|
| 798 |
+
"id": "q031",
|
| 799 |
+
"query": "Is there any construction or development activity at 640 Columbia Street in Red Hook?",
|
| 800 |
+
"status": "PASS",
|
| 801 |
+
"wall_clock_s": 11.82,
|
| 802 |
+
"intent_returned": "development_check",
|
| 803 |
+
"mellea_passed": "3/4",
|
| 804 |
+
"mellea_rerolls": 2,
|
| 805 |
+
"stones_fired": [],
|
| 806 |
+
"stones_errored": [],
|
| 807 |
+
"stones_silent": [],
|
| 808 |
+
"citations_resolved": null,
|
| 809 |
+
"compare_targets": null,
|
| 810 |
+
"error": null
|
| 811 |
+
},
|
| 812 |
+
{
|
| 813 |
+
"id": "q032",
|
| 814 |
+
"query": "What's the flood exposure at 30-30 Thomson Avenue, Long Island City, Queens?",
|
| 815 |
+
"status": "PASS",
|
| 816 |
+
"wall_clock_s": 8.9,
|
| 817 |
+
"intent_returned": "single_address",
|
| 818 |
+
"mellea_passed": "4/4",
|
| 819 |
+
"mellea_rerolls": 0,
|
| 820 |
+
"stones_fired": [
|
| 821 |
+
"sandy",
|
| 822 |
+
"dep",
|
| 823 |
+
"floodnet",
|
| 824 |
+
"311",
|
| 825 |
+
"noaa",
|
| 826 |
+
"nws",
|
| 827 |
+
"ttm",
|
| 828 |
+
"microtopo",
|
| 829 |
+
"ida",
|
| 830 |
+
"mta",
|
| 831 |
+
"prithvi_v2",
|
| 832 |
+
"rag",
|
| 833 |
+
"gliner",
|
| 834 |
+
"mellea"
|
| 835 |
+
],
|
| 836 |
+
"stones_errored": [
|
| 837 |
+
"floodnet",
|
| 838 |
+
"prithvi_live",
|
| 839 |
+
"terramind"
|
| 840 |
+
],
|
| 841 |
+
"stones_silent": [],
|
| 842 |
+
"citations_resolved": null,
|
| 843 |
+
"compare_targets": null,
|
| 844 |
+
"error": null
|
| 845 |
+
},
|
| 846 |
+
{
|
| 847 |
+
"id": "q033",
|
| 848 |
+
"query": "Compare Long Island City Queens to Astoria Queens for flood risk",
|
| 849 |
+
"status": "PASS",
|
| 850 |
+
"wall_clock_s": 15.54,
|
| 851 |
+
"intent_returned": "compare",
|
| 852 |
+
"mellea_passed": "4/4",
|
| 853 |
+
"mellea_rerolls": 1,
|
| 854 |
+
"stones_fired": [
|
| 855 |
+
"sandy",
|
| 856 |
+
"dep",
|
| 857 |
+
"floodnet",
|
| 858 |
+
"311",
|
| 859 |
+
"noaa",
|
| 860 |
+
"nws",
|
| 861 |
+
"ttm",
|
| 862 |
+
"microtopo",
|
| 863 |
+
"ida",
|
| 864 |
+
"prithvi_v2",
|
| 865 |
+
"rag",
|
| 866 |
+
"gliner",
|
| 867 |
+
"mellea"
|
| 868 |
+
],
|
| 869 |
+
"stones_errored": [
|
| 870 |
+
"floodnet",
|
| 871 |
+
"mta",
|
| 872 |
+
"prithvi_live",
|
| 873 |
+
"terramind"
|
| 874 |
+
],
|
| 875 |
+
"stones_silent": [],
|
| 876 |
+
"citations_resolved": null,
|
| 877 |
+
"compare_targets": 2,
|
| 878 |
+
"error": null
|
| 879 |
+
},
|
| 880 |
+
{
|
| 881 |
+
"id": "q034",
|
| 882 |
+
"query": "Is 55 Water Street Manhattan at risk from sea level rise and storm surge?",
|
| 883 |
+
"status": "PASS",
|
| 884 |
+
"wall_clock_s": 13.69,
|
| 885 |
+
"intent_returned": "single_address",
|
| 886 |
+
"mellea_passed": "4/4",
|
| 887 |
+
"mellea_rerolls": 2,
|
| 888 |
+
"stones_fired": [
|
| 889 |
+
"sandy",
|
| 890 |
+
"dep",
|
| 891 |
+
"floodnet",
|
| 892 |
+
"311",
|
| 893 |
+
"noaa",
|
| 894 |
+
"nws",
|
| 895 |
+
"ttm",
|
| 896 |
+
"microtopo",
|
| 897 |
+
"ida",
|
| 898 |
+
"mta",
|
| 899 |
+
"prithvi_v2",
|
| 900 |
+
"rag",
|
| 901 |
+
"gliner",
|
| 902 |
+
"mellea"
|
| 903 |
+
],
|
| 904 |
+
"stones_errored": [
|
| 905 |
+
"floodnet",
|
| 906 |
+
"prithvi_live",
|
| 907 |
+
"terramind"
|
| 908 |
+
],
|
| 909 |
+
"stones_silent": [],
|
| 910 |
+
"citations_resolved": null,
|
| 911 |
+
"compare_targets": null,
|
| 912 |
+
"error": null
|
| 913 |
+
},
|
| 914 |
+
{
|
| 915 |
+
"id": "q035",
|
| 916 |
+
"query": "What new buildings are going up in Gowanus and are they being built to handle flooding?",
|
| 917 |
+
"status": "PASS",
|
| 918 |
+
"wall_clock_s": 10.71,
|
| 919 |
+
"intent_returned": "development_check",
|
| 920 |
+
"mellea_passed": "3/4",
|
| 921 |
+
"mellea_rerolls": 2,
|
| 922 |
+
"stones_fired": [],
|
| 923 |
+
"stones_errored": [],
|
| 924 |
+
"stones_silent": [],
|
| 925 |
+
"citations_resolved": null,
|
| 926 |
+
"compare_targets": null,
|
| 927 |
+
"error": null
|
| 928 |
+
},
|
| 929 |
+
{
|
| 930 |
+
"id": "q036",
|
| 931 |
+
"query": "Greenpoint, Brooklyn",
|
| 932 |
+
"status": "PASS",
|
| 933 |
+
"wall_clock_s": 7.83,
|
| 934 |
+
"intent_returned": "neighborhood",
|
| 935 |
+
"mellea_passed": "4/4",
|
| 936 |
+
"mellea_rerolls": 1,
|
| 937 |
+
"stones_fired": [],
|
| 938 |
+
"stones_errored": [],
|
| 939 |
+
"stones_silent": [],
|
| 940 |
+
"citations_resolved": null,
|
| 941 |
+
"compare_targets": null,
|
| 942 |
+
"error": null
|
| 943 |
+
},
|
| 944 |
+
{
|
| 945 |
+
"id": "q037",
|
| 946 |
+
"query": "How at risk is 111 8th Avenue Manhattan from a 100-year storm?",
|
| 947 |
+
"status": "PASS",
|
| 948 |
+
"wall_clock_s": 12.67,
|
| 949 |
+
"intent_returned": "single_address",
|
| 950 |
+
"mellea_passed": "3/4",
|
| 951 |
+
"mellea_rerolls": 2,
|
| 952 |
+
"stones_fired": [
|
| 953 |
+
"sandy",
|
| 954 |
+
"dep",
|
| 955 |
+
"floodnet",
|
| 956 |
+
"311",
|
| 957 |
+
"noaa",
|
| 958 |
+
"nws",
|
| 959 |
+
"ttm",
|
| 960 |
+
"microtopo",
|
| 961 |
+
"ida",
|
| 962 |
+
"mta",
|
| 963 |
+
"prithvi_v2",
|
| 964 |
+
"rag",
|
| 965 |
+
"gliner",
|
| 966 |
+
"mellea"
|
| 967 |
+
],
|
| 968 |
+
"stones_errored": [
|
| 969 |
+
"floodnet",
|
| 970 |
+
"prithvi_live",
|
| 971 |
+
"terramind"
|
| 972 |
+
],
|
| 973 |
+
"stones_silent": [],
|
| 974 |
+
"citations_resolved": null,
|
| 975 |
+
"compare_targets": null,
|
| 976 |
+
"error": null
|
| 977 |
+
},
|
| 978 |
+
{
|
| 979 |
+
"id": "q038",
|
| 980 |
+
"query": "Compare 157-11 Rockaway Beach Blvd Queens to 100 Gold Street Manhattan",
|
| 981 |
+
"status": "PASS",
|
| 982 |
+
"wall_clock_s": 15.15,
|
| 983 |
+
"intent_returned": "compare",
|
| 984 |
+
"mellea_passed": "4/4",
|
| 985 |
+
"mellea_rerolls": 0,
|
| 986 |
+
"stones_fired": [
|
| 987 |
+
"sandy",
|
| 988 |
+
"dep",
|
| 989 |
+
"floodnet",
|
| 990 |
+
"311",
|
| 991 |
+
"noaa",
|
| 992 |
+
"nws",
|
| 993 |
+
"ttm",
|
| 994 |
+
"microtopo",
|
| 995 |
+
"ida",
|
| 996 |
+
"prithvi_v2",
|
| 997 |
+
"rag",
|
| 998 |
+
"gliner",
|
| 999 |
+
"mellea",
|
| 1000 |
+
"mta"
|
| 1001 |
+
],
|
| 1002 |
+
"stones_errored": [
|
| 1003 |
+
"floodnet",
|
| 1004 |
+
"mta",
|
| 1005 |
+
"prithvi_live",
|
| 1006 |
+
"terramind"
|
| 1007 |
+
],
|
| 1008 |
+
"stones_silent": [],
|
| 1009 |
+
"citations_resolved": null,
|
| 1010 |
+
"compare_targets": 2,
|
| 1011 |
+
"error": null
|
| 1012 |
+
},
|
| 1013 |
+
{
|
| 1014 |
+
"id": "q039",
|
| 1015 |
+
"query": "What's going on with the development at 421 Kent Avenue Williamsburg and how flood-safe is it?",
|
| 1016 |
+
"status": "PASS",
|
| 1017 |
+
"wall_clock_s": 10.71,
|
| 1018 |
+
"intent_returned": "development_check",
|
| 1019 |
+
"mellea_passed": "3/4",
|
| 1020 |
+
"mellea_rerolls": 2,
|
| 1021 |
+
"stones_fired": [],
|
| 1022 |
+
"stones_errored": [],
|
| 1023 |
+
"stones_silent": [],
|
| 1024 |
+
"citations_resolved": null,
|
| 1025 |
+
"compare_targets": null,
|
| 1026 |
+
"error": null
|
| 1027 |
+
},
|
| 1028 |
+
{
|
| 1029 |
+
"id": "q040",
|
| 1030 |
+
"query": "Stapleton, Staten Island",
|
| 1031 |
+
"status": "PASS",
|
| 1032 |
+
"wall_clock_s": 5.18,
|
| 1033 |
+
"intent_returned": "neighborhood",
|
| 1034 |
+
"mellea_passed": "4/4",
|
| 1035 |
+
"mellea_rerolls": 0,
|
| 1036 |
+
"stones_fired": [],
|
| 1037 |
+
"stones_errored": [],
|
| 1038 |
+
"stones_silent": [],
|
| 1039 |
+
"citations_resolved": null,
|
| 1040 |
+
"compare_targets": null,
|
| 1041 |
+
"error": null
|
| 1042 |
+
},
|
| 1043 |
+
{
|
| 1044 |
+
"id": "q041",
|
| 1045 |
+
"query": "Is 110 Livingston Street Brooklyn in a flood zone?",
|
| 1046 |
+
"status": "PASS",
|
| 1047 |
+
"wall_clock_s": 14.69,
|
| 1048 |
+
"intent_returned": "single_address",
|
| 1049 |
+
"mellea_passed": "3/4",
|
| 1050 |
+
"mellea_rerolls": 2,
|
| 1051 |
+
"stones_fired": [
|
| 1052 |
+
"sandy",
|
| 1053 |
+
"dep",
|
| 1054 |
+
"floodnet",
|
| 1055 |
+
"311",
|
| 1056 |
+
"noaa",
|
| 1057 |
+
"nws",
|
| 1058 |
+
"ttm",
|
| 1059 |
+
"microtopo",
|
| 1060 |
+
"ida",
|
| 1061 |
+
"mta",
|
| 1062 |
+
"prithvi_v2",
|
| 1063 |
+
"rag",
|
| 1064 |
+
"gliner",
|
| 1065 |
+
"mellea"
|
| 1066 |
+
],
|
| 1067 |
+
"stones_errored": [
|
| 1068 |
+
"floodnet",
|
| 1069 |
+
"prithvi_live",
|
| 1070 |
+
"terramind"
|
| 1071 |
+
],
|
| 1072 |
+
"stones_silent": [],
|
| 1073 |
+
"citations_resolved": null,
|
| 1074 |
+
"compare_targets": null,
|
| 1075 |
+
"error": null
|
| 1076 |
+
},
|
| 1077 |
+
{
|
| 1078 |
+
"id": "q042",
|
| 1079 |
+
"query": "Compare Mott Haven Bronx to Hunts Point Bronx for flood risk and 311 complaints",
|
| 1080 |
+
"status": "PASS",
|
| 1081 |
+
"wall_clock_s": 27.98,
|
| 1082 |
+
"intent_returned": "compare",
|
| 1083 |
+
"mellea_passed": "4/4",
|
| 1084 |
+
"mellea_rerolls": 3,
|
| 1085 |
+
"stones_fired": [
|
| 1086 |
+
"sandy",
|
| 1087 |
+
"dep",
|
| 1088 |
+
"floodnet",
|
| 1089 |
+
"311",
|
| 1090 |
+
"noaa",
|
| 1091 |
+
"nws",
|
| 1092 |
+
"ttm",
|
| 1093 |
+
"microtopo",
|
| 1094 |
+
"ida",
|
| 1095 |
+
"mta",
|
| 1096 |
+
"prithvi_v2",
|
| 1097 |
+
"rag",
|
| 1098 |
+
"gliner",
|
| 1099 |
+
"mellea"
|
| 1100 |
+
],
|
| 1101 |
+
"stones_errored": [
|
| 1102 |
+
"floodnet",
|
| 1103 |
+
"prithvi_live",
|
| 1104 |
+
"terramind",
|
| 1105 |
+
"mta"
|
| 1106 |
+
],
|
| 1107 |
+
"stones_silent": [],
|
| 1108 |
+
"citations_resolved": null,
|
| 1109 |
+
"compare_targets": 2,
|
| 1110 |
+
"error": null
|
| 1111 |
+
},
|
| 1112 |
+
{
|
| 1113 |
+
"id": "q043",
|
| 1114 |
+
"query": "What's the flood risk at 325 Hudson Street, Manhattan?",
|
| 1115 |
+
"status": "PASS",
|
| 1116 |
+
"wall_clock_s": 14.43,
|
| 1117 |
+
"intent_returned": "single_address",
|
| 1118 |
+
"mellea_passed": "2/4",
|
| 1119 |
+
"mellea_rerolls": 2,
|
| 1120 |
+
"stones_fired": [
|
| 1121 |
+
"sandy",
|
| 1122 |
+
"dep",
|
| 1123 |
+
"floodnet",
|
| 1124 |
+
"311",
|
| 1125 |
+
"noaa",
|
| 1126 |
+
"nws",
|
| 1127 |
+
"ttm",
|
| 1128 |
+
"microtopo",
|
| 1129 |
+
"ida",
|
| 1130 |
+
"mta",
|
| 1131 |
+
"prithvi_v2",
|
| 1132 |
+
"rag",
|
| 1133 |
+
"gliner",
|
| 1134 |
+
"mellea"
|
| 1135 |
+
],
|
| 1136 |
+
"stones_errored": [
|
| 1137 |
+
"floodnet",
|
| 1138 |
+
"prithvi_live",
|
| 1139 |
+
"terramind"
|
| 1140 |
+
],
|
| 1141 |
+
"stones_silent": [],
|
| 1142 |
+
"citations_resolved": null,
|
| 1143 |
+
"compare_targets": null,
|
| 1144 |
+
"error": null
|
| 1145 |
+
},
|
| 1146 |
+
{
|
| 1147 |
+
"id": "q044",
|
| 1148 |
+
"query": "Is anything being developed at 601 West 26th Street Manhattan and what's the flood exposure?",
|
| 1149 |
+
"status": "PASS",
|
| 1150 |
+
"wall_clock_s": 11.52,
|
| 1151 |
+
"intent_returned": "development_check",
|
| 1152 |
+
"mellea_passed": "3/4",
|
| 1153 |
+
"mellea_rerolls": 2,
|
| 1154 |
+
"stones_fired": [],
|
| 1155 |
+
"stones_errored": [],
|
| 1156 |
+
"stones_silent": [],
|
| 1157 |
+
"citations_resolved": null,
|
| 1158 |
+
"compare_targets": null,
|
| 1159 |
+
"error": null
|
| 1160 |
+
},
|
| 1161 |
+
{
|
| 1162 |
+
"id": "q045",
|
| 1163 |
+
"query": "East New York, Brooklyn",
|
| 1164 |
+
"status": "PASS",
|
| 1165 |
+
"wall_clock_s": 7.03,
|
| 1166 |
+
"intent_returned": "neighborhood",
|
| 1167 |
+
"mellea_passed": "4/4",
|
| 1168 |
+
"mellea_rerolls": 0,
|
| 1169 |
+
"stones_fired": [],
|
| 1170 |
+
"stones_errored": [],
|
| 1171 |
+
"stones_silent": [],
|
| 1172 |
+
"citations_resolved": null,
|
| 1173 |
+
"compare_targets": null,
|
| 1174 |
+
"error": null
|
| 1175 |
+
},
|
| 1176 |
+
{
|
| 1177 |
+
"id": "q046",
|
| 1178 |
+
"query": "What's the flood situation at 40 Rector Street Manhattan? I'm evaluating office space there.",
|
| 1179 |
+
"status": "PASS",
|
| 1180 |
+
"wall_clock_s": 13.87,
|
| 1181 |
+
"intent_returned": "single_address",
|
| 1182 |
+
"mellea_passed": "4/4",
|
| 1183 |
+
"mellea_rerolls": 1,
|
| 1184 |
+
"stones_fired": [
|
| 1185 |
+
"sandy",
|
| 1186 |
+
"dep",
|
| 1187 |
+
"floodnet",
|
| 1188 |
+
"311",
|
| 1189 |
+
"noaa",
|
| 1190 |
+
"nws",
|
| 1191 |
+
"ttm",
|
| 1192 |
+
"microtopo",
|
| 1193 |
+
"ida",
|
| 1194 |
+
"mta",
|
| 1195 |
+
"prithvi_v2",
|
| 1196 |
+
"rag",
|
| 1197 |
+
"gliner",
|
| 1198 |
+
"mellea"
|
| 1199 |
+
],
|
| 1200 |
+
"stones_errored": [
|
| 1201 |
+
"floodnet",
|
| 1202 |
+
"prithvi_live",
|
| 1203 |
+
"terramind"
|
| 1204 |
+
],
|
| 1205 |
+
"stones_silent": [],
|
| 1206 |
+
"citations_resolved": null,
|
| 1207 |
+
"compare_targets": null,
|
| 1208 |
+
"error": null
|
| 1209 |
+
},
|
| 1210 |
+
{
|
| 1211 |
+
"id": "q047",
|
| 1212 |
+
"query": "Compare 40 Rector Street Manhattan to 345 East 94th Street Manhattan",
|
| 1213 |
+
"status": "PASS",
|
| 1214 |
+
"wall_clock_s": 20.68,
|
| 1215 |
+
"intent_returned": "compare",
|
| 1216 |
+
"mellea_passed": "4/4",
|
| 1217 |
+
"mellea_rerolls": 1,
|
| 1218 |
+
"stones_fired": [
|
| 1219 |
+
"sandy",
|
| 1220 |
+
"dep",
|
| 1221 |
+
"floodnet",
|
| 1222 |
+
"311",
|
| 1223 |
+
"noaa",
|
| 1224 |
+
"nws",
|
| 1225 |
+
"ttm",
|
| 1226 |
+
"microtopo",
|
| 1227 |
+
"ida",
|
| 1228 |
+
"mta",
|
| 1229 |
+
"prithvi_v2",
|
| 1230 |
+
"rag",
|
| 1231 |
+
"gliner",
|
| 1232 |
+
"mellea"
|
| 1233 |
+
],
|
| 1234 |
+
"stones_errored": [
|
| 1235 |
+
"floodnet",
|
| 1236 |
+
"prithvi_live",
|
| 1237 |
+
"terramind"
|
| 1238 |
+
],
|
| 1239 |
+
"stones_silent": [],
|
| 1240 |
+
"citations_resolved": null,
|
| 1241 |
+
"compare_targets": 2,
|
| 1242 |
+
"error": null
|
| 1243 |
+
},
|
| 1244 |
+
{
|
| 1245 |
+
"id": "q048",
|
| 1246 |
+
"query": "Is there any new development at 280 Richards Street Red Hook and how is it handling the flood risk?",
|
| 1247 |
+
"status": "PASS",
|
| 1248 |
+
"wall_clock_s": 10.58,
|
| 1249 |
+
"intent_returned": "development_check",
|
| 1250 |
+
"mellea_passed": "3/4",
|
| 1251 |
+
"mellea_rerolls": 2,
|
| 1252 |
+
"stones_fired": [],
|
| 1253 |
+
"stones_errored": [],
|
| 1254 |
+
"stones_silent": [],
|
| 1255 |
+
"citations_resolved": null,
|
| 1256 |
+
"compare_targets": null,
|
| 1257 |
+
"error": null
|
| 1258 |
+
},
|
| 1259 |
+
{
|
| 1260 |
+
"id": "q049",
|
| 1261 |
+
"query": "Astoria, Queens",
|
| 1262 |
+
"status": "PASS",
|
| 1263 |
+
"wall_clock_s": 6.51,
|
| 1264 |
+
"intent_returned": "neighborhood",
|
| 1265 |
+
"mellea_passed": "4/4",
|
| 1266 |
+
"mellea_rerolls": 0,
|
| 1267 |
+
"stones_fired": [],
|
| 1268 |
+
"stones_errored": [],
|
| 1269 |
+
"stones_silent": [],
|
| 1270 |
+
"citations_resolved": null,
|
| 1271 |
+
"compare_targets": null,
|
| 1272 |
+
"error": null
|
| 1273 |
+
},
|
| 1274 |
+
{
|
| 1275 |
+
"id": "q050",
|
| 1276 |
+
"query": "Compare Tottenville Staten Island to South Beach Staten Island for flood exposure",
|
| 1277 |
+
"status": "PASS",
|
| 1278 |
+
"wall_clock_s": 16.09,
|
| 1279 |
+
"intent_returned": "compare",
|
| 1280 |
+
"mellea_passed": "3/4",
|
| 1281 |
+
"mellea_rerolls": 2,
|
| 1282 |
+
"stones_fired": [
|
| 1283 |
+
"sandy",
|
| 1284 |
+
"dep",
|
| 1285 |
+
"floodnet",
|
| 1286 |
+
"311",
|
| 1287 |
+
"noaa",
|
| 1288 |
+
"nws",
|
| 1289 |
+
"ttm",
|
| 1290 |
+
"microtopo",
|
| 1291 |
+
"ida",
|
| 1292 |
+
"prithvi_v2",
|
| 1293 |
+
"rag",
|
| 1294 |
+
"gliner",
|
| 1295 |
+
"mellea"
|
| 1296 |
+
],
|
| 1297 |
+
"stones_errored": [
|
| 1298 |
+
"floodnet",
|
| 1299 |
+
"mta",
|
| 1300 |
+
"prithvi_live",
|
| 1301 |
+
"terramind"
|
| 1302 |
+
],
|
| 1303 |
+
"stones_silent": [],
|
| 1304 |
+
"citations_resolved": null,
|
| 1305 |
+
"compare_targets": 2,
|
| 1306 |
+
"error": null
|
| 1307 |
+
}
|
| 1308 |
+
]
|
tests/queries_50.json
ADDED
|
@@ -0,0 +1,788 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"id": "q001",
|
| 4 |
+
"query": "I'm thinking about renting an apartment at 80 Pioneer Street, Brooklyn. Should I worry?",
|
| 5 |
+
"intent": "address",
|
| 6 |
+
"persona": "Renter evaluating a move to Red Hook \u2014 canonical Sandy turf",
|
| 7 |
+
"borough": "Brooklyn",
|
| 8 |
+
"expected_specialists": [
|
| 9 |
+
"sandy_inundation",
|
| 10 |
+
"dep_stormwater",
|
| 11 |
+
"nyc311",
|
| 12 |
+
"floodnet",
|
| 13 |
+
"noaa_tides",
|
| 14 |
+
"microtopo_lidar",
|
| 15 |
+
"ida_hwm_2021",
|
| 16 |
+
"ttm_forecast",
|
| 17 |
+
"prithvi_eo_v2",
|
| 18 |
+
"gliner_extract"
|
| 19 |
+
],
|
| 20 |
+
"expected_silent": [
|
| 21 |
+
"prithvi_eo_live"
|
| 22 |
+
],
|
| 23 |
+
"fragility_notes": "Cleanest result in suite, 0 rerolls historically. Geocoder resolves to Red Hook every time.",
|
| 24 |
+
"verified": true
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
"id": "q002",
|
| 28 |
+
"query": "Hollis, Queens",
|
| 29 |
+
"intent": "neighborhood",
|
| 30 |
+
"persona": "NYC OEM/DEP capital planner looking at sewer backlog by NTA",
|
| 31 |
+
"borough": "Queens",
|
| 32 |
+
"expected_specialists": [
|
| 33 |
+
"nyc311",
|
| 34 |
+
"dep_stormwater",
|
| 35 |
+
"microtopo_lidar"
|
| 36 |
+
],
|
| 37 |
+
"expected_silent": [
|
| 38 |
+
"prithvi_eo_live",
|
| 39 |
+
"prithvi_eo_v2"
|
| 40 |
+
],
|
| 41 |
+
"fragility_notes": "Bare NTA name \u2014 relies on planner routing neighborhood correctly. Stable across all probe runs.",
|
| 42 |
+
"verified": true
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"id": "q003",
|
| 46 |
+
"query": "Compare 80 Pioneer Street Brooklyn to 100 Gold Street Manhattan",
|
| 47 |
+
"intent": "compare",
|
| 48 |
+
"persona": "Real-estate attorney comparing a Sandy-zone lease to a lower-risk mid-Manhattan address",
|
| 49 |
+
"borough": "multi",
|
| 50 |
+
"expected_specialists": [
|
| 51 |
+
"sandy_inundation",
|
| 52 |
+
"nyc311",
|
| 53 |
+
"microtopo_lidar",
|
| 54 |
+
"ida_hwm_2021"
|
| 55 |
+
],
|
| 56 |
+
"expected_silent": [],
|
| 57 |
+
"fragility_notes": "Verified stable post-bugfix. Planner must parse two addresses. If returns single_address, PLACE B is dropped.",
|
| 58 |
+
"verified": true
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"id": "q004",
|
| 62 |
+
"query": "442 East Houston Street, Manhattan",
|
| 63 |
+
"intent": "address",
|
| 64 |
+
"persona": "NYC DOE facilities planner evaluating PS 188 LES flood exposure",
|
| 65 |
+
"borough": "Manhattan",
|
| 66 |
+
"expected_specialists": [
|
| 67 |
+
"sandy_inundation",
|
| 68 |
+
"nyc311",
|
| 69 |
+
"noaa_tides",
|
| 70 |
+
"microtopo_lidar",
|
| 71 |
+
"gliner_extract"
|
| 72 |
+
],
|
| 73 |
+
"expected_silent": [],
|
| 74 |
+
"fragility_notes": "2 rerolls historically on live Space \u2014 acceptable for secondary demo, risky as opener.",
|
| 75 |
+
"verified": true
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"id": "q005",
|
| 79 |
+
"query": "100 Gold Street, Manhattan",
|
| 80 |
+
"intent": "address",
|
| 81 |
+
"persona": "Insurance underwriter checking flood exposure for a Financial District property",
|
| 82 |
+
"borough": "Manhattan",
|
| 83 |
+
"expected_specialists": [
|
| 84 |
+
"nyc311",
|
| 85 |
+
"microtopo_lidar",
|
| 86 |
+
"noaa_tides"
|
| 87 |
+
],
|
| 88 |
+
"expected_silent": [
|
| 89 |
+
"sandy_inundation"
|
| 90 |
+
],
|
| 91 |
+
"fragility_notes": "Outside Sandy 2012 zone \u2014 negative control. Ida HWM 3.47 km away.",
|
| 92 |
+
"verified": true
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"id": "q006",
|
| 96 |
+
"query": "Coney Island, Brooklyn",
|
| 97 |
+
"intent": "neighborhood",
|
| 98 |
+
"persona": "Community board member tracking coastal resilience investments",
|
| 99 |
+
"borough": "Brooklyn",
|
| 100 |
+
"expected_specialists": [
|
| 101 |
+
"nyc311",
|
| 102 |
+
"dep_stormwater",
|
| 103 |
+
"microtopo_lidar"
|
| 104 |
+
],
|
| 105 |
+
"expected_silent": [],
|
| 106 |
+
"fragility_notes": "87.5% of NTA inside Sandy 2012 extent. 4/4 0 rerolls on live Space.",
|
| 107 |
+
"verified": true
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"id": "q007",
|
| 111 |
+
"query": "2940 Brighton 3rd St, Brooklyn",
|
| 112 |
+
"intent": "address",
|
| 113 |
+
"persona": "Resident in a flood-prone coastal area checking risk before lease renewal",
|
| 114 |
+
"borough": "Brooklyn",
|
| 115 |
+
"expected_specialists": [
|
| 116 |
+
"sandy_inundation",
|
| 117 |
+
"nyc311",
|
| 118 |
+
"floodnet",
|
| 119 |
+
"noaa_tides",
|
| 120 |
+
"microtopo_lidar",
|
| 121 |
+
"dep_stormwater"
|
| 122 |
+
],
|
| 123 |
+
"expected_silent": [],
|
| 124 |
+
"fragility_notes": "Brighton Beach \u2014 Sandy inundation zone. Used as brighton fixture in integration tests.",
|
| 125 |
+
"verified": true
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"id": "q008",
|
| 129 |
+
"query": "Red Hook, Brooklyn",
|
| 130 |
+
"intent": "neighborhood",
|
| 131 |
+
"persona": "Civil engineer evaluating stormwater drainage upgrade priorities",
|
| 132 |
+
"borough": "Brooklyn",
|
| 133 |
+
"expected_specialists": [
|
| 134 |
+
"nyc311",
|
| 135 |
+
"dep_stormwater",
|
| 136 |
+
"microtopo_lidar"
|
| 137 |
+
],
|
| 138 |
+
"expected_silent": [],
|
| 139 |
+
"fragility_notes": "Heavy Sandy history. Neighborhood intent; 8-9 step FSM.",
|
| 140 |
+
"verified": true
|
| 141 |
+
},
|
| 142 |
+
{
|
| 143 |
+
"id": "q009",
|
| 144 |
+
"query": "What's the flood risk for 345 East 94th Street, Manhattan?",
|
| 145 |
+
"intent": "address",
|
| 146 |
+
"persona": "NYCHA asset manager assessing high-rise exposure on the Upper East Side",
|
| 147 |
+
"borough": "Manhattan",
|
| 148 |
+
"expected_specialists": [
|
| 149 |
+
"nyc311",
|
| 150 |
+
"microtopo_lidar",
|
| 151 |
+
"noaa_tides"
|
| 152 |
+
],
|
| 153 |
+
"expected_silent": [
|
| 154 |
+
"sandy_inundation"
|
| 155 |
+
],
|
| 156 |
+
"fragility_notes": "Upper East Side \u2014 lower Sandy exposure. Should return modest risk profile.",
|
| 157 |
+
"verified": true
|
| 158 |
+
},
|
| 159 |
+
{
|
| 160 |
+
"id": "q010",
|
| 161 |
+
"query": "Is 1 MetroTech Center in Brooklyn at risk during major storms?",
|
| 162 |
+
"intent": "address",
|
| 163 |
+
"persona": "MTA infrastructure planner evaluating transit hub flood resilience",
|
| 164 |
+
"borough": "Brooklyn",
|
| 165 |
+
"expected_specialists": [
|
| 166 |
+
"nyc311",
|
| 167 |
+
"microtopo_lidar",
|
| 168 |
+
"noaa_tides",
|
| 169 |
+
"sandy_inundation"
|
| 170 |
+
],
|
| 171 |
+
"expected_silent": [],
|
| 172 |
+
"fragility_notes": "Downtown Brooklyn \u2014 partial Sandy exposure depending on exact geocode.",
|
| 173 |
+
"verified": true
|
| 174 |
+
},
|
| 175 |
+
{
|
| 176 |
+
"id": "q011",
|
| 177 |
+
"query": "Howard Beach, Queens",
|
| 178 |
+
"intent": "neighborhood",
|
| 179 |
+
"persona": "HUD CDBG-DR grant writer documenting unmet need in a Sandy-affected NTA",
|
| 180 |
+
"borough": "Queens",
|
| 181 |
+
"expected_specialists": [
|
| 182 |
+
"nyc311",
|
| 183 |
+
"dep_stormwater",
|
| 184 |
+
"microtopo_lidar"
|
| 185 |
+
],
|
| 186 |
+
"expected_silent": [],
|
| 187 |
+
"fragility_notes": "Howard Beach was severely flooded in Sandy 2012. NTA-level specialists surface strong signal.",
|
| 188 |
+
"verified": true
|
| 189 |
+
},
|
| 190 |
+
{
|
| 191 |
+
"id": "q012",
|
| 192 |
+
"query": "Canarsie, Brooklyn",
|
| 193 |
+
"intent": "neighborhood",
|
| 194 |
+
"persona": "Journalist covering FEMA FIRMette map updates post-Sandy",
|
| 195 |
+
"borough": "Brooklyn",
|
| 196 |
+
"expected_specialists": [
|
| 197 |
+
"nyc311",
|
| 198 |
+
"dep_stormwater",
|
| 199 |
+
"microtopo_lidar"
|
| 200 |
+
],
|
| 201 |
+
"expected_silent": [],
|
| 202 |
+
"fragility_notes": "Canarsie was inundated in Sandy. Strong neighborhood-level flood signal expected.",
|
| 203 |
+
"verified": true
|
| 204 |
+
},
|
| 205 |
+
{
|
| 206 |
+
"id": "q013",
|
| 207 |
+
"query": "Compare Canarsie Brooklyn to Park Slope Brooklyn",
|
| 208 |
+
"intent": "compare",
|
| 209 |
+
"persona": "Real estate developer comparing coastal vs inland Brooklyn sites for climate resilience",
|
| 210 |
+
"borough": "Brooklyn",
|
| 211 |
+
"expected_specialists": [
|
| 212 |
+
"nyc311",
|
| 213 |
+
"dep_stormwater",
|
| 214 |
+
"microtopo_lidar"
|
| 215 |
+
],
|
| 216 |
+
"expected_silent": [],
|
| 217 |
+
"fragility_notes": "Both Brooklyn neighborhoods but very different flood profiles. Strong expected delta.",
|
| 218 |
+
"verified": true
|
| 219 |
+
},
|
| 220 |
+
{
|
| 221 |
+
"id": "q014",
|
| 222 |
+
"query": "Should I worry about flooding at 520 West 145th Street in Manhattan?",
|
| 223 |
+
"intent": "address",
|
| 224 |
+
"persona": "Resident in Washington Heights evaluating apartment flood risk",
|
| 225 |
+
"borough": "Manhattan",
|
| 226 |
+
"expected_specialists": [
|
| 227 |
+
"nyc311",
|
| 228 |
+
"microtopo_lidar",
|
| 229 |
+
"noaa_tides"
|
| 230 |
+
],
|
| 231 |
+
"expected_silent": [
|
| 232 |
+
"sandy_inundation"
|
| 233 |
+
],
|
| 234 |
+
"fragility_notes": "Washington Heights \u2014 far from Sandy zone. Low risk expected.",
|
| 235 |
+
"verified": true
|
| 236 |
+
},
|
| 237 |
+
{
|
| 238 |
+
"id": "q015",
|
| 239 |
+
"query": "Mott Haven, Bronx",
|
| 240 |
+
"intent": "neighborhood",
|
| 241 |
+
"persona": "Community board member tracking 311 flood complaints in a low-income NTA",
|
| 242 |
+
"borough": "Bronx",
|
| 243 |
+
"expected_specialists": [
|
| 244 |
+
"nyc311",
|
| 245 |
+
"dep_stormwater",
|
| 246 |
+
"microtopo_lidar"
|
| 247 |
+
],
|
| 248 |
+
"expected_silent": [],
|
| 249 |
+
"fragility_notes": "Mott Haven is near the Harlem River and has chronic flooding complaints.",
|
| 250 |
+
"verified": true
|
| 251 |
+
},
|
| 252 |
+
{
|
| 253 |
+
"id": "q016",
|
| 254 |
+
"query": "What was the flood situation at 750 Baychester Avenue, Bronx during Ida?",
|
| 255 |
+
"intent": "address",
|
| 256 |
+
"persona": "Civil engineer evaluating Ida HWM data near a USGS gauge site",
|
| 257 |
+
"borough": "Bronx",
|
| 258 |
+
"expected_specialists": [
|
| 259 |
+
"ida_hwm_2021",
|
| 260 |
+
"nyc311",
|
| 261 |
+
"microtopo_lidar"
|
| 262 |
+
],
|
| 263 |
+
"expected_silent": [],
|
| 264 |
+
"fragility_notes": "Ida HWM should fire \u2014 Bronx near Hutchinson River had gauge hits. Ida specialist exercises.",
|
| 265 |
+
"verified": true
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"id": "q017",
|
| 269 |
+
"query": "Is the NYCHA Gowanus Houses at risk from sea level rise?",
|
| 270 |
+
"intent": "address",
|
| 271 |
+
"persona": "NYCHA asset manager assessing long-term sea level risk for a large development",
|
| 272 |
+
"borough": "Brooklyn",
|
| 273 |
+
"expected_specialists": [
|
| 274 |
+
"sandy_inundation",
|
| 275 |
+
"dep_stormwater",
|
| 276 |
+
"nyc311",
|
| 277 |
+
"microtopo_lidar"
|
| 278 |
+
],
|
| 279 |
+
"expected_silent": [],
|
| 280 |
+
"fragility_notes": "Gowanus Houses \u2014 Sandy zone, known flood risk. Should surface NYCHA development context.",
|
| 281 |
+
"verified": true
|
| 282 |
+
},
|
| 283 |
+
{
|
| 284 |
+
"id": "q018",
|
| 285 |
+
"query": "Compare 750 Baychester Avenue Bronx to 150 Riverside Drive Manhattan",
|
| 286 |
+
"intent": "compare",
|
| 287 |
+
"persona": "Insurance underwriter comparing two river-adjacent properties",
|
| 288 |
+
"borough": "multi",
|
| 289 |
+
"expected_specialists": [
|
| 290 |
+
"nyc311",
|
| 291 |
+
"microtopo_lidar"
|
| 292 |
+
],
|
| 293 |
+
"expected_silent": [],
|
| 294 |
+
"fragility_notes": "Cross-borough compare. Baychester near Hutchinson River; Riverside Drive near Hudson. Both riverine.",
|
| 295 |
+
"verified": true
|
| 296 |
+
},
|
| 297 |
+
{
|
| 298 |
+
"id": "q019",
|
| 299 |
+
"query": "Tottenville, Staten Island",
|
| 300 |
+
"intent": "neighborhood",
|
| 301 |
+
"persona": "NYC OEM planner evaluating Staten Island's most flood-exposed southern tip",
|
| 302 |
+
"borough": "Staten Island",
|
| 303 |
+
"expected_specialists": [
|
| 304 |
+
"nyc311",
|
| 305 |
+
"dep_stormwater",
|
| 306 |
+
"microtopo_lidar"
|
| 307 |
+
],
|
| 308 |
+
"expected_silent": [],
|
| 309 |
+
"fragility_notes": "Tottenville was among the hardest hit areas in Sandy. Strong NTA-level signal expected.",
|
| 310 |
+
"verified": true
|
| 311 |
+
},
|
| 312 |
+
{
|
| 313 |
+
"id": "q020",
|
| 314 |
+
"query": "What's the flood risk at 151 West 34th Street, Manhattan? It's near Penn Station.",
|
| 315 |
+
"intent": "address",
|
| 316 |
+
"persona": "MTA infrastructure planner evaluating subway entrance flood exposure near Penn Station",
|
| 317 |
+
"borough": "Manhattan",
|
| 318 |
+
"expected_specialists": [
|
| 319 |
+
"nyc311",
|
| 320 |
+
"microtopo_lidar",
|
| 321 |
+
"noaa_tides"
|
| 322 |
+
],
|
| 323 |
+
"expected_silent": [
|
| 324 |
+
"sandy_inundation"
|
| 325 |
+
],
|
| 326 |
+
"fragility_notes": "Midtown Manhattan \u2014 outside Sandy zone. MTA subway entrance exposure specialist should fire.",
|
| 327 |
+
"verified": true
|
| 328 |
+
},
|
| 329 |
+
{
|
| 330 |
+
"id": "q021",
|
| 331 |
+
"query": "Compare Red Hook Brooklyn to the Financial District Manhattan for flood risk",
|
| 332 |
+
"intent": "compare",
|
| 333 |
+
"persona": "Journalist writing a before/after story on Sandy's unequal recovery",
|
| 334 |
+
"borough": "multi",
|
| 335 |
+
"expected_specialists": [
|
| 336 |
+
"sandy_inundation",
|
| 337 |
+
"nyc311",
|
| 338 |
+
"microtopo_lidar"
|
| 339 |
+
],
|
| 340 |
+
"expected_silent": [],
|
| 341 |
+
"fragility_notes": "Strong expected delta \u2014 Red Hook was deeply flooded, FiDi has mixed exposure. Cross-borough compare.",
|
| 342 |
+
"verified": true
|
| 343 |
+
},
|
| 344 |
+
{
|
| 345 |
+
"id": "q022",
|
| 346 |
+
"query": "Is 595 Dean Street in Brooklyn at risk of flooding?",
|
| 347 |
+
"intent": "address",
|
| 348 |
+
"persona": "Real estate developer evaluating a new Prospect Heights / Gowanus site",
|
| 349 |
+
"borough": "Brooklyn",
|
| 350 |
+
"expected_specialists": [
|
| 351 |
+
"dep_stormwater",
|
| 352 |
+
"nyc311",
|
| 353 |
+
"microtopo_lidar"
|
| 354 |
+
],
|
| 355 |
+
"expected_silent": [],
|
| 356 |
+
"fragility_notes": "Near Gowanus Canal drainage area. DEP stormwater and 311 should fire.",
|
| 357 |
+
"verified": true
|
| 358 |
+
},
|
| 359 |
+
{
|
| 360 |
+
"id": "q023",
|
| 361 |
+
"query": "South Beach, Staten Island",
|
| 362 |
+
"intent": "neighborhood",
|
| 363 |
+
"persona": "HUD CDBG-DR grant writer for a Staten Island Sandy recovery project",
|
| 364 |
+
"borough": "Staten Island",
|
| 365 |
+
"expected_specialists": [
|
| 366 |
+
"nyc311",
|
| 367 |
+
"dep_stormwater",
|
| 368 |
+
"microtopo_lidar"
|
| 369 |
+
],
|
| 370 |
+
"expected_silent": [],
|
| 371 |
+
"fragility_notes": "South Beach was heavily impacted by Sandy. Strong 311 and DEP signal expected.",
|
| 372 |
+
"verified": true
|
| 373 |
+
},
|
| 374 |
+
{
|
| 375 |
+
"id": "q024",
|
| 376 |
+
"query": "How exposed is 1 Fulton Street Manhattan to surge from a major hurricane?",
|
| 377 |
+
"intent": "address",
|
| 378 |
+
"persona": "Hospital facility manager evaluating evacuation route risk for a Lower Manhattan location",
|
| 379 |
+
"borough": "Manhattan",
|
| 380 |
+
"expected_specialists": [
|
| 381 |
+
"sandy_inundation",
|
| 382 |
+
"noaa_tides",
|
| 383 |
+
"nyc311",
|
| 384 |
+
"microtopo_lidar"
|
| 385 |
+
],
|
| 386 |
+
"expected_silent": [],
|
| 387 |
+
"fragility_notes": "Fulton Street FiDi \u2014 near Sandy zone boundary. NOAA tide gauge should fire (East River gauge).",
|
| 388 |
+
"verified": true
|
| 389 |
+
},
|
| 390 |
+
{
|
| 391 |
+
"id": "q025",
|
| 392 |
+
"query": "Compare Howard Beach Queens to Forest Hills Queens for flood risk",
|
| 393 |
+
"intent": "compare",
|
| 394 |
+
"persona": "NYC OEM planner comparing coastal vs inland Queens neighborhoods",
|
| 395 |
+
"borough": "Queens",
|
| 396 |
+
"expected_specialists": [
|
| 397 |
+
"nyc311",
|
| 398 |
+
"dep_stormwater",
|
| 399 |
+
"microtopo_lidar"
|
| 400 |
+
],
|
| 401 |
+
"expected_silent": [],
|
| 402 |
+
"fragility_notes": "Strong expected delta \u2014 Howard Beach coastal vs Forest Hills upland. Same-borough neighborhood compare.",
|
| 403 |
+
"verified": true
|
| 404 |
+
},
|
| 405 |
+
{
|
| 406 |
+
"id": "q026",
|
| 407 |
+
"query": "What's the storm surge risk for 157-11 Rockaway Beach Blvd, Queens?",
|
| 408 |
+
"intent": "address",
|
| 409 |
+
"persona": "Resident in Far Rockaway evaluating whether to elevate their home",
|
| 410 |
+
"borough": "Queens",
|
| 411 |
+
"expected_specialists": [
|
| 412 |
+
"sandy_inundation",
|
| 413 |
+
"noaa_tides",
|
| 414 |
+
"floodnet",
|
| 415 |
+
"dep_stormwater",
|
| 416 |
+
"microtopo_lidar"
|
| 417 |
+
],
|
| 418 |
+
"expected_silent": [],
|
| 419 |
+
"fragility_notes": "Far Rockaway was devastated in Sandy. High probability of all coastal specialists firing.",
|
| 420 |
+
"verified": true
|
| 421 |
+
},
|
| 422 |
+
{
|
| 423 |
+
"id": "q027",
|
| 424 |
+
"query": "Hunts Point, Bronx",
|
| 425 |
+
"intent": "neighborhood",
|
| 426 |
+
"persona": "Civil engineer assessing combined sewer overflow and flood risk in a food distribution hub",
|
| 427 |
+
"borough": "Bronx",
|
| 428 |
+
"expected_specialists": [
|
| 429 |
+
"nyc311",
|
| 430 |
+
"dep_stormwater",
|
| 431 |
+
"microtopo_lidar"
|
| 432 |
+
],
|
| 433 |
+
"expected_silent": [],
|
| 434 |
+
"fragility_notes": "Hunts Point is low-lying and near the East River. Strong stormwater and 311 signal.",
|
| 435 |
+
"verified": true
|
| 436 |
+
},
|
| 437 |
+
{
|
| 438 |
+
"id": "q028",
|
| 439 |
+
"query": "Is 160 Conover Street in Red Hook still considered a flood zone?",
|
| 440 |
+
"intent": "address",
|
| 441 |
+
"persona": "Renter who lived through Sandy 2012 now returning to Red Hook",
|
| 442 |
+
"borough": "Brooklyn",
|
| 443 |
+
"expected_specialists": [
|
| 444 |
+
"sandy_inundation",
|
| 445 |
+
"dep_stormwater",
|
| 446 |
+
"nyc311",
|
| 447 |
+
"microtopo_lidar",
|
| 448 |
+
"ida_hwm_2021"
|
| 449 |
+
],
|
| 450 |
+
"expected_silent": [],
|
| 451 |
+
"fragility_notes": "Deep Red Hook \u2014 within Sandy zone. Ida HWM nearby. Clean single_address intent expected.",
|
| 452 |
+
"verified": true
|
| 453 |
+
},
|
| 454 |
+
{
|
| 455 |
+
"id": "q029",
|
| 456 |
+
"query": "Compare 160 Conover Street Red Hook to 300 Flatbush Avenue Brooklyn",
|
| 457 |
+
"intent": "compare",
|
| 458 |
+
"persona": "Property insurance underwriter comparing two Brooklyn addresses for flood premium",
|
| 459 |
+
"borough": "Brooklyn",
|
| 460 |
+
"expected_specialists": [
|
| 461 |
+
"sandy_inundation",
|
| 462 |
+
"nyc311",
|
| 463 |
+
"microtopo_lidar"
|
| 464 |
+
],
|
| 465 |
+
"expected_silent": [],
|
| 466 |
+
"fragility_notes": "Sandy-zone vs non-Sandy-zone within Brooklyn. Clear expected delta on Sandy field.",
|
| 467 |
+
"verified": true
|
| 468 |
+
},
|
| 469 |
+
{
|
| 470 |
+
"id": "q030",
|
| 471 |
+
"query": "What are the flood risks for the Two Bridges neighborhood in Manhattan?",
|
| 472 |
+
"intent": "neighborhood",
|
| 473 |
+
"persona": "HUD CDBG-DR grant writer documenting need in a Sandy-affected LES neighborhood",
|
| 474 |
+
"borough": "Manhattan",
|
| 475 |
+
"expected_specialists": [
|
| 476 |
+
"nyc311",
|
| 477 |
+
"dep_stormwater",
|
| 478 |
+
"microtopo_lidar"
|
| 479 |
+
],
|
| 480 |
+
"expected_silent": [],
|
| 481 |
+
"fragility_notes": "Two Bridges NTA \u2014 lower Manhattan near East River. Sandy signal expected.",
|
| 482 |
+
"verified": true
|
| 483 |
+
},
|
| 484 |
+
{
|
| 485 |
+
"id": "q031",
|
| 486 |
+
"query": "Is there any construction or development activity at 640 Columbia Street in Red Hook?",
|
| 487 |
+
"intent": "planner",
|
| 488 |
+
"persona": "Community board member tracking post-Sandy redevelopment in Red Hook",
|
| 489 |
+
"borough": "Brooklyn",
|
| 490 |
+
"expected_specialists": [
|
| 491 |
+
"sandy_inundation",
|
| 492 |
+
"nyc311"
|
| 493 |
+
],
|
| 494 |
+
"expected_silent": [],
|
| 495 |
+
"fragility_notes": "Development check intent. DOB filings + flood context. Planner should route development_check.",
|
| 496 |
+
"verified": true
|
| 497 |
+
},
|
| 498 |
+
{
|
| 499 |
+
"id": "q032",
|
| 500 |
+
"query": "What's the flood exposure at 30-30 Thomson Avenue, Long Island City, Queens?",
|
| 501 |
+
"intent": "address",
|
| 502 |
+
"persona": "Real estate developer evaluating an LIC industrial-to-residential conversion",
|
| 503 |
+
"borough": "Queens",
|
| 504 |
+
"expected_specialists": [
|
| 505 |
+
"nyc311",
|
| 506 |
+
"microtopo_lidar",
|
| 507 |
+
"dep_stormwater"
|
| 508 |
+
],
|
| 509 |
+
"expected_silent": [],
|
| 510 |
+
"fragility_notes": "Long Island City is near Newtown Creek and the East River. Moderate flood exposure expected.",
|
| 511 |
+
"verified": true
|
| 512 |
+
},
|
| 513 |
+
{
|
| 514 |
+
"id": "q033",
|
| 515 |
+
"query": "Compare Long Island City Queens to Astoria Queens for flood risk",
|
| 516 |
+
"intent": "compare",
|
| 517 |
+
"persona": "NYC DOT planner comparing two Queens waterfront neighborhoods for infrastructure investment",
|
| 518 |
+
"borough": "Queens",
|
| 519 |
+
"expected_specialists": [
|
| 520 |
+
"nyc311",
|
| 521 |
+
"microtopo_lidar",
|
| 522 |
+
"dep_stormwater"
|
| 523 |
+
],
|
| 524 |
+
"expected_silent": [],
|
| 525 |
+
"fragility_notes": "Same-borough neighborhood compare. Both near East River but different profiles.",
|
| 526 |
+
"verified": true
|
| 527 |
+
},
|
| 528 |
+
{
|
| 529 |
+
"id": "q034",
|
| 530 |
+
"query": "Is 55 Water Street Manhattan at risk from sea level rise and storm surge?",
|
| 531 |
+
"intent": "address",
|
| 532 |
+
"persona": "Hospital administrator at a large Lower Manhattan health system assessing long-term resilience",
|
| 533 |
+
"borough": "Manhattan",
|
| 534 |
+
"expected_specialists": [
|
| 535 |
+
"sandy_inundation",
|
| 536 |
+
"noaa_tides",
|
| 537 |
+
"nyc311",
|
| 538 |
+
"microtopo_lidar"
|
| 539 |
+
],
|
| 540 |
+
"expected_silent": [],
|
| 541 |
+
"fragility_notes": "55 Water Street FiDi \u2014 near East River. Sandy zone boundary. NOAA tide gauge should fire.",
|
| 542 |
+
"verified": true
|
| 543 |
+
},
|
| 544 |
+
{
|
| 545 |
+
"id": "q035",
|
| 546 |
+
"query": "What new buildings are going up in Gowanus and are they being built to handle flooding?",
|
| 547 |
+
"intent": "planner",
|
| 548 |
+
"persona": "Journalist investigating whether post-rezoning Gowanus development meets flood standards",
|
| 549 |
+
"borough": "Brooklyn",
|
| 550 |
+
"expected_specialists": [
|
| 551 |
+
"dep_stormwater",
|
| 552 |
+
"nyc311"
|
| 553 |
+
],
|
| 554 |
+
"expected_silent": [],
|
| 555 |
+
"fragility_notes": "Development check intent. Gowanus rezoning context. DOB filings should surface.",
|
| 556 |
+
"verified": true
|
| 557 |
+
},
|
| 558 |
+
{
|
| 559 |
+
"id": "q036",
|
| 560 |
+
"query": "Greenpoint, Brooklyn",
|
| 561 |
+
"intent": "neighborhood",
|
| 562 |
+
"persona": "Civil engineer assessing Newtown Creek flooding and combined sewer overflow risk",
|
| 563 |
+
"borough": "Brooklyn",
|
| 564 |
+
"expected_specialists": [
|
| 565 |
+
"nyc311",
|
| 566 |
+
"dep_stormwater",
|
| 567 |
+
"microtopo_lidar"
|
| 568 |
+
],
|
| 569 |
+
"expected_silent": [],
|
| 570 |
+
"fragility_notes": "Greenpoint near Newtown Creek. DEP and 311 stormwater signal expected.",
|
| 571 |
+
"verified": true
|
| 572 |
+
},
|
| 573 |
+
{
|
| 574 |
+
"id": "q037",
|
| 575 |
+
"query": "How at risk is 111 8th Avenue Manhattan from a 100-year storm?",
|
| 576 |
+
"intent": "address",
|
| 577 |
+
"persona": "Insurance underwriter evaluating a large Chelsea tech campus for flood coverage",
|
| 578 |
+
"borough": "Manhattan",
|
| 579 |
+
"expected_specialists": [
|
| 580 |
+
"nyc311",
|
| 581 |
+
"microtopo_lidar",
|
| 582 |
+
"dep_stormwater"
|
| 583 |
+
],
|
| 584 |
+
"expected_silent": [
|
| 585 |
+
"sandy_inundation"
|
| 586 |
+
],
|
| 587 |
+
"fragility_notes": "Chelsea inland address. Low Sandy exposure. DEP stormwater may flag sewer backup risk.",
|
| 588 |
+
"verified": true
|
| 589 |
+
},
|
| 590 |
+
{
|
| 591 |
+
"id": "q038",
|
| 592 |
+
"query": "Compare 157-11 Rockaway Beach Blvd Queens to 100 Gold Street Manhattan",
|
| 593 |
+
"intent": "compare",
|
| 594 |
+
"persona": "FEMA insurance analyst comparing a high-risk coastal address to a low-risk Manhattan baseline",
|
| 595 |
+
"borough": "multi",
|
| 596 |
+
"expected_specialists": [
|
| 597 |
+
"sandy_inundation",
|
| 598 |
+
"noaa_tides",
|
| 599 |
+
"nyc311"
|
| 600 |
+
],
|
| 601 |
+
"expected_silent": [],
|
| 602 |
+
"fragility_notes": "Cross-borough, maximum expected delta. Far Rockaway vs FiDi. Strong Sandy signal for Rockaway.",
|
| 603 |
+
"verified": true
|
| 604 |
+
},
|
| 605 |
+
{
|
| 606 |
+
"id": "q039",
|
| 607 |
+
"query": "What's going on with the development at 421 Kent Avenue Williamsburg and how flood-safe is it?",
|
| 608 |
+
"intent": "planner",
|
| 609 |
+
"persona": "Real estate developer doing due diligence on a Williamsburg waterfront project",
|
| 610 |
+
"borough": "Brooklyn",
|
| 611 |
+
"expected_specialists": [
|
| 612 |
+
"sandy_inundation",
|
| 613 |
+
"dep_stormwater",
|
| 614 |
+
"nyc311"
|
| 615 |
+
],
|
| 616 |
+
"expected_silent": [],
|
| 617 |
+
"fragility_notes": "Development check + flood context at Williamsburg waterfront. DOB and flood specialists.",
|
| 618 |
+
"verified": true
|
| 619 |
+
},
|
| 620 |
+
{
|
| 621 |
+
"id": "q040",
|
| 622 |
+
"query": "Stapleton, Staten Island",
|
| 623 |
+
"intent": "neighborhood",
|
| 624 |
+
"persona": "NYC OEM planner reviewing the SIRR resilience report findings for North Shore Staten Island",
|
| 625 |
+
"borough": "Staten Island",
|
| 626 |
+
"expected_specialists": [
|
| 627 |
+
"nyc311",
|
| 628 |
+
"dep_stormwater",
|
| 629 |
+
"microtopo_lidar"
|
| 630 |
+
],
|
| 631 |
+
"expected_silent": [],
|
| 632 |
+
"fragility_notes": "Stapleton is on the North Shore of Staten Island \u2014 Sandy-affected area.",
|
| 633 |
+
"verified": true
|
| 634 |
+
},
|
| 635 |
+
{
|
| 636 |
+
"id": "q041",
|
| 637 |
+
"query": "Is 110 Livingston Street Brooklyn in a flood zone?",
|
| 638 |
+
"intent": "address",
|
| 639 |
+
"persona": "NYC DOE facilities planner evaluating the NYC DOE headquarters building",
|
| 640 |
+
"borough": "Brooklyn",
|
| 641 |
+
"expected_specialists": [
|
| 642 |
+
"nyc311",
|
| 643 |
+
"microtopo_lidar",
|
| 644 |
+
"sandy_inundation"
|
| 645 |
+
],
|
| 646 |
+
"expected_silent": [],
|
| 647 |
+
"fragility_notes": "Downtown Brooklyn near Fulton Mall. Moderate Sandy exposure.",
|
| 648 |
+
"verified": true
|
| 649 |
+
},
|
| 650 |
+
{
|
| 651 |
+
"id": "q042",
|
| 652 |
+
"query": "Compare Mott Haven Bronx to Hunts Point Bronx for flood risk and 311 complaints",
|
| 653 |
+
"intent": "compare",
|
| 654 |
+
"persona": "NYC DOT infrastructure planner prioritizing drainage upgrades in the South Bronx",
|
| 655 |
+
"borough": "Bronx",
|
| 656 |
+
"expected_specialists": [
|
| 657 |
+
"nyc311",
|
| 658 |
+
"dep_stormwater",
|
| 659 |
+
"microtopo_lidar"
|
| 660 |
+
],
|
| 661 |
+
"expected_silent": [],
|
| 662 |
+
"fragility_notes": "Same-borough NTA compare in South Bronx. Both low-lying near waterways.",
|
| 663 |
+
"verified": true
|
| 664 |
+
},
|
| 665 |
+
{
|
| 666 |
+
"id": "q043",
|
| 667 |
+
"query": "What's the flood risk at 325 Hudson Street, Manhattan?",
|
| 668 |
+
"intent": "address",
|
| 669 |
+
"persona": "Real estate developer evaluating a Hudson Square office conversion",
|
| 670 |
+
"borough": "Manhattan",
|
| 671 |
+
"expected_specialists": [
|
| 672 |
+
"nyc311",
|
| 673 |
+
"microtopo_lidar",
|
| 674 |
+
"dep_stormwater"
|
| 675 |
+
],
|
| 676 |
+
"expected_silent": [
|
| 677 |
+
"sandy_inundation"
|
| 678 |
+
],
|
| 679 |
+
"fragility_notes": "Hudson Square \u2014 near the Hudson River but likely outside Sandy zone. Moderate risk.",
|
| 680 |
+
"verified": true
|
| 681 |
+
},
|
| 682 |
+
{
|
| 683 |
+
"id": "q044",
|
| 684 |
+
"query": "Is anything being developed at 601 West 26th Street Manhattan and what's the flood exposure?",
|
| 685 |
+
"intent": "planner",
|
| 686 |
+
"persona": "NYC DOT planner evaluating Chelsea Piers area development near the Hudson River",
|
| 687 |
+
"borough": "Manhattan",
|
| 688 |
+
"expected_specialists": [
|
| 689 |
+
"nyc311",
|
| 690 |
+
"microtopo_lidar",
|
| 691 |
+
"dep_stormwater"
|
| 692 |
+
],
|
| 693 |
+
"expected_silent": [],
|
| 694 |
+
"fragility_notes": "Development check near Hudson River. DOB filings + flood context.",
|
| 695 |
+
"verified": true
|
| 696 |
+
},
|
| 697 |
+
{
|
| 698 |
+
"id": "q045",
|
| 699 |
+
"query": "East New York, Brooklyn",
|
| 700 |
+
"intent": "neighborhood",
|
| 701 |
+
"persona": "HUD CDBG-DR grant writer assessing flood exposure in a low-income Brooklyn NTA",
|
| 702 |
+
"borough": "Brooklyn",
|
| 703 |
+
"expected_specialists": [
|
| 704 |
+
"nyc311",
|
| 705 |
+
"dep_stormwater",
|
| 706 |
+
"microtopo_lidar"
|
| 707 |
+
],
|
| 708 |
+
"expected_silent": [],
|
| 709 |
+
"fragility_notes": "East New York is inland but has chronic stormwater complaints. DEP and 311 should fire.",
|
| 710 |
+
"verified": true
|
| 711 |
+
},
|
| 712 |
+
{
|
| 713 |
+
"id": "q046",
|
| 714 |
+
"query": "What's the flood situation at 40 Rector Street Manhattan? I'm evaluating office space there.",
|
| 715 |
+
"intent": "address",
|
| 716 |
+
"persona": "Commercial tenant evaluating Lower Manhattan office flood risk post-Sandy",
|
| 717 |
+
"borough": "Manhattan",
|
| 718 |
+
"expected_specialists": [
|
| 719 |
+
"sandy_inundation",
|
| 720 |
+
"noaa_tides",
|
| 721 |
+
"nyc311",
|
| 722 |
+
"microtopo_lidar"
|
| 723 |
+
],
|
| 724 |
+
"expected_silent": [],
|
| 725 |
+
"fragility_notes": "40 Rector Street FiDi \u2014 near battery park, likely in Sandy zone. NOAA and Sandy should fire.",
|
| 726 |
+
"verified": true
|
| 727 |
+
},
|
| 728 |
+
{
|
| 729 |
+
"id": "q047",
|
| 730 |
+
"query": "Compare 40 Rector Street Manhattan to 345 East 94th Street Manhattan",
|
| 731 |
+
"intent": "compare",
|
| 732 |
+
"persona": "Corporate real estate manager comparing two Manhattan office options for flood resilience",
|
| 733 |
+
"borough": "Manhattan",
|
| 734 |
+
"expected_specialists": [
|
| 735 |
+
"sandy_inundation",
|
| 736 |
+
"nyc311",
|
| 737 |
+
"microtopo_lidar"
|
| 738 |
+
],
|
| 739 |
+
"expected_silent": [],
|
| 740 |
+
"fragility_notes": "Within-borough Manhattan compare. Strong delta: Lower Manhattan vs Upper East Side.",
|
| 741 |
+
"verified": true
|
| 742 |
+
},
|
| 743 |
+
{
|
| 744 |
+
"id": "q048",
|
| 745 |
+
"query": "Is there any new development at 280 Richards Street Red Hook and how is it handling the flood risk?",
|
| 746 |
+
"intent": "planner",
|
| 747 |
+
"persona": "Community board member tracking post-Sandy redevelopment permits in Red Hook",
|
| 748 |
+
"borough": "Brooklyn",
|
| 749 |
+
"expected_specialists": [
|
| 750 |
+
"sandy_inundation",
|
| 751 |
+
"dep_stormwater",
|
| 752 |
+
"nyc311"
|
| 753 |
+
],
|
| 754 |
+
"expected_silent": [],
|
| 755 |
+
"fragility_notes": "Red Hook development check. Sandy zone context. DOB filings + flood specialists.",
|
| 756 |
+
"verified": true
|
| 757 |
+
},
|
| 758 |
+
{
|
| 759 |
+
"id": "q049",
|
| 760 |
+
"query": "Astoria, Queens",
|
| 761 |
+
"intent": "neighborhood",
|
| 762 |
+
"persona": "NYC DOT planner assessing stormwater infrastructure needs in a growing Queens neighborhood",
|
| 763 |
+
"borough": "Queens",
|
| 764 |
+
"expected_specialists": [
|
| 765 |
+
"nyc311",
|
| 766 |
+
"dep_stormwater",
|
| 767 |
+
"microtopo_lidar"
|
| 768 |
+
],
|
| 769 |
+
"expected_silent": [],
|
| 770 |
+
"fragility_notes": "Astoria near East River. Moderate flood exposure. DEP and 311 should fire.",
|
| 771 |
+
"verified": true
|
| 772 |
+
},
|
| 773 |
+
{
|
| 774 |
+
"id": "q050",
|
| 775 |
+
"query": "Compare Tottenville Staten Island to South Beach Staten Island for flood exposure",
|
| 776 |
+
"intent": "compare",
|
| 777 |
+
"persona": "NYC OEM planner prioritizing Staten Island coastal resilience investments",
|
| 778 |
+
"borough": "Staten Island",
|
| 779 |
+
"expected_specialists": [
|
| 780 |
+
"nyc311",
|
| 781 |
+
"dep_stormwater",
|
| 782 |
+
"microtopo_lidar"
|
| 783 |
+
],
|
| 784 |
+
"expected_silent": [],
|
| 785 |
+
"fragility_notes": "Both Staten Island coastal neighborhoods \u2014 Sandy-exposed. Same-island compare.",
|
| 786 |
+
"verified": true
|
| 787 |
+
}
|
| 788 |
+
]
|
tests/test_integration.py
CHANGED
|
@@ -38,6 +38,12 @@ from dataclasses import dataclass
|
|
| 38 |
import pytest
|
| 39 |
|
| 40 |
BASE = os.environ.get("RIPRAP_TEST_BASE", "http://127.0.0.1:7860")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
TIMEOUT_S = float(os.environ.get("RIPRAP_TEST_TIMEOUT", "300"))
|
| 42 |
|
| 43 |
|
|
@@ -106,7 +112,9 @@ ADDRESSES = [
|
|
| 106 |
]
|
| 107 |
|
| 108 |
|
| 109 |
-
# Steps every linear single_address run must hit, regardless of intent
|
|
|
|
|
|
|
| 110 |
EXPECTED_STEPS = [
|
| 111 |
"geocode",
|
| 112 |
"sandy_inundation",
|
|
@@ -120,7 +128,6 @@ EXPECTED_STEPS = [
|
|
| 120 |
"microtopo_lidar",
|
| 121 |
"ida_hwm_2021",
|
| 122 |
"prithvi_eo_v2",
|
| 123 |
-
"prithvi_eo_live", # Phase 1 integration
|
| 124 |
"rag_granite_embedding",
|
| 125 |
"gliner_extract", # Phase 2 integration
|
| 126 |
# reconcile step name varies by strict mode; not asserted here
|
|
@@ -226,6 +233,8 @@ def test_phase1_prithvi_live_step(streamed: StreamResult):
|
|
| 226 |
— only that the step ran and recorded its outcome."""
|
| 227 |
if streamed.plan and streamed.plan.get("intent") != "single_address":
|
| 228 |
pytest.skip("non-linear FSM")
|
|
|
|
|
|
|
| 229 |
found = [e for e in streamed.events
|
| 230 |
if e[0] == "step" and e[1].get("step") == "prithvi_eo_live"]
|
| 231 |
assert found, "step_prithvi_live did not fire"
|
|
|
|
| 38 |
import pytest
|
| 39 |
|
| 40 |
BASE = os.environ.get("RIPRAP_TEST_BASE", "http://127.0.0.1:7860")
|
| 41 |
+
# Heavy specialists (prithvi_live, terramind) are only added to the FSM
|
| 42 |
+
# when RIPRAP_HEAVY_SPECIALISTS=1 or RIPRAP_ML_BASE_URL is set. Tests
|
| 43 |
+
# that assert these steps fired must skip when the gate is off.
|
| 44 |
+
_HEAVY_SPECIALISTS = os.environ.get("RIPRAP_HEAVY_SPECIALISTS", "").lower() in (
|
| 45 |
+
"1", "true", "yes"
|
| 46 |
+
) or bool(os.environ.get("RIPRAP_ML_BASE_URL", "").strip())
|
| 47 |
TIMEOUT_S = float(os.environ.get("RIPRAP_TEST_TIMEOUT", "300"))
|
| 48 |
|
| 49 |
|
|
|
|
| 112 |
]
|
| 113 |
|
| 114 |
|
| 115 |
+
# Steps every linear single_address run must hit, regardless of intent.
|
| 116 |
+
# prithvi_eo_live is only in the FSM when _HEAVY_SPECIALISTS is True,
|
| 117 |
+
# so it's excluded from this list and tested separately.
|
| 118 |
EXPECTED_STEPS = [
|
| 119 |
"geocode",
|
| 120 |
"sandy_inundation",
|
|
|
|
| 128 |
"microtopo_lidar",
|
| 129 |
"ida_hwm_2021",
|
| 130 |
"prithvi_eo_v2",
|
|
|
|
| 131 |
"rag_granite_embedding",
|
| 132 |
"gliner_extract", # Phase 2 integration
|
| 133 |
# reconcile step name varies by strict mode; not asserted here
|
|
|
|
| 233 |
— only that the step ran and recorded its outcome."""
|
| 234 |
if streamed.plan and streamed.plan.get("intent") != "single_address":
|
| 235 |
pytest.skip("non-linear FSM")
|
| 236 |
+
if not _HEAVY_SPECIALISTS:
|
| 237 |
+
pytest.skip("RIPRAP_HEAVY_SPECIALISTS not enabled — prithvi_eo_live not in FSM")
|
| 238 |
found = [e for e in streamed.events
|
| 239 |
if e[0] == "step" and e[1].get("step") == "prithvi_eo_live"]
|
| 240 |
assert found, "step_prithvi_live did not fire"
|
web/main.py
CHANGED
|
@@ -147,9 +147,9 @@ def _warm_caches():
|
|
| 147 |
if os.environ.get("RIPRAP_NYCHA_REGISTERS", "0").lower() in ("1", "true", "yes"):
|
| 148 |
print("[startup] pre-warming NYCHA registers (may take 60–120 s)...", flush=True)
|
| 149 |
try:
|
| 150 |
-
from app.registers import nycha as _r_nycha
|
| 151 |
from app.registers import doe_schools as _r_schools
|
| 152 |
from app.registers import doh_hospitals as _r_hospitals
|
|
|
|
| 153 |
_r_nycha._load_nycha()
|
| 154 |
_r_nycha._load_sandy_2263()
|
| 155 |
_r_schools._load_schools()
|
|
@@ -509,8 +509,8 @@ def _run_compare(p, raw_query: str, out_q, i_addr) -> dict:
|
|
| 509 |
Step events from each target are forwarded to out_q tagged with a
|
| 510 |
`target_label` key so the trace UI can optionally group them, but the
|
| 511 |
existing trace UI ignores unknown keys gracefully."""
|
| 512 |
-
from app.planner import Plan
|
| 513 |
from app.intents import neighborhood as i_nbhd
|
|
|
|
| 514 |
|
| 515 |
addr_targets = [t for t in p.targets if t.get("type") in ("address", "nta")]
|
| 516 |
if len(addr_targets) < 2:
|
|
|
|
| 147 |
if os.environ.get("RIPRAP_NYCHA_REGISTERS", "0").lower() in ("1", "true", "yes"):
|
| 148 |
print("[startup] pre-warming NYCHA registers (may take 60–120 s)...", flush=True)
|
| 149 |
try:
|
|
|
|
| 150 |
from app.registers import doe_schools as _r_schools
|
| 151 |
from app.registers import doh_hospitals as _r_hospitals
|
| 152 |
+
from app.registers import nycha as _r_nycha
|
| 153 |
_r_nycha._load_nycha()
|
| 154 |
_r_nycha._load_sandy_2263()
|
| 155 |
_r_schools._load_schools()
|
|
|
|
| 509 |
Step events from each target are forwarded to out_q tagged with a
|
| 510 |
`target_label` key so the trace UI can optionally group them, but the
|
| 511 |
existing trace UI ignores unknown keys gracefully."""
|
|
|
|
| 512 |
from app.intents import neighborhood as i_nbhd
|
| 513 |
+
from app.planner import Plan
|
| 514 |
|
| 515 |
addr_targets = [t for t in p.targets if t.get("type") in ("address", "nta")]
|
| 516 |
if len(addr_targets) < 2:
|
web/sveltekit/build/200.html
CHANGED
|
@@ -6,17 +6,17 @@
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
<meta name="description" content="Riprap — citation-grounded NYC flood-exposure briefings." />
|
| 8 |
<title>Riprap — flood-exposure briefing</title>
|
| 9 |
-
<link href="/_app/immutable/entry/start.
|
| 10 |
-
<link href="/_app/immutable/chunks/
|
| 11 |
<link href="/_app/immutable/chunks/BTUA7_xE.js" rel="modulepreload">
|
| 12 |
-
<link href="/_app/immutable/entry/app.
|
| 13 |
<link href="/_app/immutable/chunks/CXQd8Y6F.js" rel="modulepreload">
|
| 14 |
<link href="/_app/immutable/chunks/CWw6qgC_.js" rel="modulepreload">
|
| 15 |
<link href="/_app/immutable/chunks/Bd-v_9Ud.js" rel="modulepreload">
|
| 16 |
<link href="/_app/immutable/chunks/CW0zSL4D.js" rel="modulepreload">
|
| 17 |
-
<link href="/_app/immutable/nodes/0.
|
| 18 |
<link href="/_app/immutable/chunks/DxQlA7U2.js" rel="modulepreload">
|
| 19 |
-
<link href="/_app/immutable/chunks/
|
| 20 |
<link href="/_app/immutable/chunks/DCD6_LXk.js" rel="modulepreload">
|
| 21 |
<link href="/_app/immutable/chunks/B0XoTt7U.js" rel="modulepreload">
|
| 22 |
<link href="/_app/immutable/chunks/DixtWtwq.js" rel="modulepreload">
|
|
@@ -28,15 +28,15 @@
|
|
| 28 |
<div style="display: contents">
|
| 29 |
<script>
|
| 30 |
{
|
| 31 |
-
|
| 32 |
base: ""
|
| 33 |
};
|
| 34 |
|
| 35 |
const element = document.currentScript.parentElement;
|
| 36 |
|
| 37 |
Promise.all([
|
| 38 |
-
import("/_app/immutable/entry/start.
|
| 39 |
-
import("/_app/immutable/entry/app.
|
| 40 |
]).then(([kit, app]) => {
|
| 41 |
kit.start(app, element);
|
| 42 |
});
|
|
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
<meta name="description" content="Riprap — citation-grounded NYC flood-exposure briefings." />
|
| 8 |
<title>Riprap — flood-exposure briefing</title>
|
| 9 |
+
<link href="/_app/immutable/entry/start.BkS8JQ5_.js" rel="modulepreload">
|
| 10 |
+
<link href="/_app/immutable/chunks/BgqmyNr8.js" rel="modulepreload">
|
| 11 |
<link href="/_app/immutable/chunks/BTUA7_xE.js" rel="modulepreload">
|
| 12 |
+
<link href="/_app/immutable/entry/app.DtHxgVfE.js" rel="modulepreload">
|
| 13 |
<link href="/_app/immutable/chunks/CXQd8Y6F.js" rel="modulepreload">
|
| 14 |
<link href="/_app/immutable/chunks/CWw6qgC_.js" rel="modulepreload">
|
| 15 |
<link href="/_app/immutable/chunks/Bd-v_9Ud.js" rel="modulepreload">
|
| 16 |
<link href="/_app/immutable/chunks/CW0zSL4D.js" rel="modulepreload">
|
| 17 |
+
<link href="/_app/immutable/nodes/0.LAFF30Kg.js" rel="modulepreload">
|
| 18 |
<link href="/_app/immutable/chunks/DxQlA7U2.js" rel="modulepreload">
|
| 19 |
+
<link href="/_app/immutable/chunks/BbRPgcjS.js" rel="modulepreload">
|
| 20 |
<link href="/_app/immutable/chunks/DCD6_LXk.js" rel="modulepreload">
|
| 21 |
<link href="/_app/immutable/chunks/B0XoTt7U.js" rel="modulepreload">
|
| 22 |
<link href="/_app/immutable/chunks/DixtWtwq.js" rel="modulepreload">
|
|
|
|
| 28 |
<div style="display: contents">
|
| 29 |
<script>
|
| 30 |
{
|
| 31 |
+
__sveltekit_8j9bml = {
|
| 32 |
base: ""
|
| 33 |
};
|
| 34 |
|
| 35 |
const element = document.currentScript.parentElement;
|
| 36 |
|
| 37 |
Promise.all([
|
| 38 |
+
import("/_app/immutable/entry/start.BkS8JQ5_.js"),
|
| 39 |
+
import("/_app/immutable/entry/app.DtHxgVfE.js")
|
| 40 |
]).then(([kit, app]) => {
|
| 41 |
kit.start(app, element);
|
| 42 |
});
|
web/sveltekit/build/_app/immutable/assets/4.C9CQZyPb.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.compare-layout.svelte-rr14x0{width:100%}.compare-delta-bar.svelte-rr14x0{border:1px solid var(--rule-soft);background:var(--paper-deep);padding:var(--s-3) var(--s-4);margin-bottom:var(--s-5);display:flex;gap:var(--s-4);align-items:flex-start;flex-wrap:wrap}.compare-delta-title.svelte-rr14x0{font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-tertiary);flex-shrink:0;padding-top:1px}.compare-delta-rows.svelte-rr14x0{display:flex;flex-wrap:wrap;gap:var(--s-2) var(--s-5);flex:1}.compare-delta-row.svelte-rr14x0{display:inline-flex;align-items:baseline;gap:var(--s-2);font-family:var(--font-mono);font-size:12px}.compare-delta-section.svelte-rr14x0{color:var(--ink-tertiary);text-transform:uppercase;letter-spacing:.08em;font-size:10px;flex-shrink:0}.compare-delta-claim.svelte-rr14x0{color:var(--ink);display:inline-flex;align-items:baseline;gap:3px}.compare-delta-ctx.svelte-rr14x0{color:var(--ink-secondary);margin-right:2px}.compare-delta-a.svelte-rr14x0,.compare-delta-b.svelte-rr14x0{color:var(--accent);font-weight:600}.compare-delta-vs.svelte-rr14x0{color:var(--ink-tertiary);font-style:italic}.compare-cols.svelte-rr14x0{display:grid;grid-template-columns:1fr 1px 1fr;gap:0 var(--s-5);align-items:start}.compare-col.svelte-rr14x0{min-width:0}.compare-divider.svelte-rr14x0{background:var(--rule-soft);align-self:stretch}.compare-address-header.svelte-rr14x0{font-family:var(--font-mono);font-size:13px;font-weight:600;letter-spacing:.04em;color:var(--ink);border-bottom:1px solid var(--rule-soft);padding-bottom:var(--s-2);margin-top:0;margin-bottom:var(--s-4);line-height:1.4}@media(max-width:899px){.compare-cols.svelte-rr14x0{grid-template-columns:1fr;gap:0}.compare-divider.svelte-rr14x0{width:100%;height:1px;margin:var(--s-5) 0;align-self:auto}}.compare-map-stack.svelte-1q8jizq{display:flex;flex-direction:column;gap:var(--s-3, 8px);padding-top:4px}.compare-map-place.svelte-1q8jizq{display:flex;flex-direction:column}.compare-map-label.svelte-1q8jizq{font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-secondary);padding:2px 0 4px;border-bottom:1px solid var(--rule-soft);margin-bottom:4px}.plan-details.svelte-1q8jizq{border:1px solid var(--rule-soft);background:var(--paper-deep);margin-bottom:16px}.plan-details.svelte-1q8jizq summary:where(.svelte-1q8jizq){padding:10px 14px;cursor:pointer;font-family:var(--font-mono);font-size:12px;color:var(--ink-secondary)}.plan-stream.svelte-1q8jizq{font-family:var(--font-mono);font-size:11px;color:var(--ink-tertiary);white-space:pre-wrap;padding:0 14px 12px;margin:0;max-height:240px;overflow:auto}.generating-status.svelte-1q8jizq{display:flex;align-items:center;gap:12px;padding:12px 0;font-family:var(--font-mono);font-size:13px;color:var(--ink-secondary);flex-wrap:wrap}.pulse.svelte-1q8jizq{width:8px;height:8px;border-radius:50%;background:var(--accent-graphical);animation:svelte-1q8jizq-pulse 1.4s ease-in-out infinite}@keyframes svelte-1q8jizq-pulse{0%,to{opacity:.3;transform:scale(.85)}50%{opacity:1;transform:scale(1.1)}}@media(prefers-reduced-motion:reduce){.pulse.svelte-1q8jizq{animation:none;opacity:.7}}
|
web/sveltekit/build/_app/immutable/chunks/BbRPgcjS.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{s as e,p as r}from"./BgqmyNr8.js";const t={get error(){return r.error},get params(){return r.params},get status(){return r.status},get url(){return r.url}};e.updated.check;const a=t;export{a as p};
|
web/sveltekit/build/_app/immutable/chunks/BgqmyNr8.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
var rt=e=>{throw TypeError(e)};var Dt=(e,t,n)=>t.has(e)||rt("Cannot "+n);var y=(e,t,n)=>(Dt(e,t,"read from private field"),n?n.call(e):t.get(e)),A=(e,t,n)=>t.has(e)?rt("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n);import{bf as Pe,bg as Vt,ai as at,a4 as T,o as I,a5 as O,ar as we,bh as Bt}from"./BTUA7_xE.js";const M=[];function Ke(e,t=Pe){let n=null;const a=new Set;function r(o){if(Vt(e,o)&&(e=o,n)){const l=!M.length;for(const c of a)c[1](),M.push(c,e);if(l){for(let c=0;c<M.length;c+=2)M[c][0](M[c+1]);M.length=0}}}function i(o){r(o(e))}function s(o,l=Pe){const c=[o,l];return a.add(c),a.size===1&&(n=t(r,i)||Pe),o(e),()=>{a.delete(c),a.size===0&&n&&(n(),n=null)}}return{set:r,update:i,subscribe:s}}class Me{constructor(t,n){this.status=t,typeof n=="string"?this.body={message:n}:n?this.body=n:this.body={message:`Error: ${t}`}}toString(){return JSON.stringify(this.body)}}class ze{constructor(t,n){try{new Headers({location:n})}catch{throw new Error(`Invalid redirect location ${JSON.stringify(n)}: this string contains characters that cannot be used in HTTP headers`)}this.status=t,this.location=n}}class Fe extends Error{constructor(t,n,a){super(a),this.status=t,this.text=n}}new URL("sveltekit-internal://");function Kt(e,t){return e==="/"||t==="ignore"?e:t==="never"?e.endsWith("/")?e.slice(0,-1):e:t==="always"&&!e.endsWith("/")?e+"/":e}function Mt(e){return e.split("%25").map(decodeURI).join("%25")}function zt(e){for(const t in e)e[t]=decodeURIComponent(e[t]);return e}function je({href:e}){return e.split("#")[0]}function $(){}function Ft(...e){let t=5381;for(const n of e)if(typeof n=="string"){let a=n.length;for(;a;)t=t*33^n.charCodeAt(--a)}else if(ArrayBuffer.isView(n)){const a=new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let r=a.length;for(;r;)t=t*33^a[--r]}else throw new TypeError("value must be a string or TypedArray");return(t>>>0).toString(36)}new TextEncoder;function Gt(e){const t=atob(e),n=new Uint8Array(t.length);for(let a=0;a<t.length;a++)n[a]=t.charCodeAt(a);return n}const Ht=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:(t==null?void 0:t.method)||"GET")!=="GET"&&X.delete(Ge(e)),Ht(e,t));const X=new Map;function Wt(e,t){const n=Ge(e,t),a=document.querySelector(n);if(a!=null&&a.textContent){a.remove();let{body:r,...i}=JSON.parse(a.textContent);const s=a.getAttribute("data-ttl");return s&&X.set(n,{body:r,init:i,ttl:1e3*Number(s)}),a.getAttribute("data-b64")!==null&&(r=Gt(r)),Promise.resolve(new Response(r,i))}return window.fetch(e,t)}function Jt(e,t,n){if(X.size>0){const a=Ge(e,n),r=X.get(a);if(r){if(performance.now()<r.ttl&&["default","force-cache","only-if-cached",void 0].includes(n==null?void 0:n.cache))return new Response(r.body,r.init);X.delete(a)}}return window.fetch(t,n)}function Ge(e,t){let a=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t!=null&&t.headers||t!=null&&t.body){const r=[];t.headers&&r.push([...new Headers(t.headers)].join(",")),t.body&&(typeof t.body=="string"||ArrayBuffer.isView(t.body))&&r.push(t.body),a+=`[data-hash="${Ft(...r)}"]`}return a}const Yt=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;function Xt(e){const t=[];return{pattern:e==="/"?/^\/$/:new RegExp(`^${Zt(e).map(a=>{const r=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(a);if(r)return t.push({name:r[1],matcher:r[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const i=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(a);if(i)return t.push({name:i[1],matcher:i[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!a)return;const s=a.split(/\[(.+?)\](?!\])/);return"/"+s.map((l,c)=>{if(c%2){if(l.startsWith("x+"))return $e(String.fromCharCode(parseInt(l.slice(2),16)));if(l.startsWith("u+"))return $e(String.fromCharCode(...l.slice(2).split("-").map(m=>parseInt(m,16))));const d=Yt.exec(l),[,u,w,p,f]=d;return t.push({name:p,matcher:f,optional:!!u,rest:!!w,chained:w?c===1&&s[0]==="":!1}),w?"([^]*?)":u?"([^/]*)?":"([^/]+?)"}return $e(l)}).join("")}).join("")}/?$`),params:t}}function Qt(e){return e!==""&&!/^\([^)]+\)$/.test(e)}function Zt(e){return e.slice(1).split("/").filter(Qt)}function en(e,t,n){const a={},r=e.slice(1),i=r.filter(o=>o!==void 0);let s=0;for(let o=0;o<t.length;o+=1){const l=t[o];let c=r[o-s];if(l.chained&&l.rest&&s&&(c=r.slice(o-s,o+1).filter(d=>d).join("/"),s=0),c===void 0)if(l.rest)c="";else continue;if(!l.matcher||n[l.matcher](c)){a[l.name]=c;const d=t[o+1],u=r[o+1];d&&!d.rest&&d.optional&&u&&l.chained&&(s=0),!d&&!u&&Object.keys(a).length===i.length&&(s=0);continue}if(l.optional&&l.chained){s++;continue}return}if(!s)return a}function $e(e){return e.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function tn({nodes:e,server_loads:t,dictionary:n,matchers:a}){const r=new Set(t);return Object.entries(n).map(([o,[l,c,d]])=>{const{pattern:u,params:w}=Xt(o),p={id:o,exec:f=>{const m=u.exec(f);if(m)return en(m,w,a)},errors:[1,...d||[]].map(f=>e[f]),layouts:[0,...c||[]].map(s),leaf:i(l)};return p.errors.length=p.layouts.length=Math.max(p.errors.length,p.layouts.length),p});function i(o){const l=o<0;return l&&(o=~o),[l,e[o]]}function s(o){return o===void 0?o:[r.has(o),e[o]]}}function wt(e,t=JSON.parse){try{return t(sessionStorage[e])}catch{}}function ot(e,t,n=JSON.stringify){const a=n(t);try{sessionStorage[e]=a}catch{}}var ht;const U=((ht=globalThis.__sveltekit_8j9bml)==null?void 0:ht.base)??"";var pt;const nn=((pt=globalThis.__sveltekit_8j9bml)==null?void 0:pt.assets)??U??"",rn="1778128400538",vt="sveltekit:snapshot",yt="sveltekit:scroll",bt="sveltekit:states",an="sveltekit:pageurl",F="sveltekit:history",Z="sveltekit:navigation",D={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},Ue=location.origin;function He(e){if(e instanceof URL)return e;let t=document.baseURI;if(!t){const n=document.getElementsByTagName("base");t=n.length?n[0].href:document.URL}return new URL(e,t)}function B(){return{x:pageXOffset,y:pageYOffset}}function z(e,t){return e.getAttribute(`data-sveltekit-${t}`)}const st={...D,"":D.hover};function kt(e){let t=e.assignedSlot??e.parentNode;return(t==null?void 0:t.nodeType)===11&&(t=t.host),t}function St(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=kt(e)}}function qe(e,t,n){let a;try{if(a=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI),n&&a.hash.match(/^#[^/]/)){const o=location.hash.split("#")[1]||"/";a.hash=`#${o}${a.hash}`}}catch{}const r=e instanceof SVGAElement?e.target.baseVal:e.target,i=!a||!!r||Ae(a,t,n)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),s=(a==null?void 0:a.origin)===Ue&&e.hasAttribute("download");return{url:a,external:i,target:r,download:s}}function ve(e){let t=null,n=null,a=null,r=null,i=null,s=null,o=e;for(;o&&o!==document.documentElement;)a===null&&(a=z(o,"preload-code")),r===null&&(r=z(o,"preload-data")),t===null&&(t=z(o,"keepfocus")),n===null&&(n=z(o,"noscroll")),i===null&&(i=z(o,"reload")),s===null&&(s=z(o,"replacestate")),o=kt(o);function l(c){switch(c){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:st[a??"off"],preload_data:st[r??"off"],keepfocus:l(t),noscroll:l(n),reload:l(i),replace_state:l(s)}}function it(e){const t=Ke(e);let n=!0;function a(){n=!0,t.update(s=>s)}function r(s){n=!1,t.set(s)}function i(s){let o;return t.subscribe(l=>{(o===void 0||n&&l!==o)&&s(o=l)})}return{notify:a,set:r,subscribe:i}}const Et={v:$};function on(){const{set:e,subscribe:t}=Ke(!1);let n;async function a(){clearTimeout(n);try{const r=await fetch(`${nn}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!r.ok)return!1;const s=(await r.json()).version!==rn;return s&&(e(!0),Et.v(),clearTimeout(n)),s}catch{return!1}}return{subscribe:t,check:a}}function Ae(e,t,n){return e.origin!==Ue||!e.pathname.startsWith(t)?!0:n?e.pathname!==location.pathname:!1}function Pn(e){}const Rt=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...Rt];const sn=new Set([...Rt]);[...sn];function ln(e){return e.filter(t=>t!=null)}function me(e,t){return e+"/"+t}function We(e){return e instanceof Me||e instanceof Fe?e.status:500}function cn(e){return e instanceof Fe?e.text:"Internal Error"}let R,ee,Ce;const fn=at.toString().includes("$$")||/function \w+\(\) \{\}/.test(at.toString()),lt="a:";var oe,se,ie,le,ce,fe,ue,de,gt,he,mt,pe,_t;fn?(R={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL(lt)},ee={current:null},Ce={current:!1}):(R=new(gt=class{constructor(){A(this,oe,T({}));A(this,se,T(null));A(this,ie,T(null));A(this,le,T({}));A(this,ce,T({id:null}));A(this,fe,T({}));A(this,ue,T(-1));A(this,de,T(new URL(lt)))}get data(){return I(y(this,oe))}set data(t){O(y(this,oe),t)}get form(){return I(y(this,se))}set form(t){O(y(this,se),t)}get error(){return I(y(this,ie))}set error(t){O(y(this,ie),t)}get params(){return I(y(this,le))}set params(t){O(y(this,le),t)}get route(){return I(y(this,ce))}set route(t){O(y(this,ce),t)}get state(){return I(y(this,fe))}set state(t){O(y(this,fe),t)}get status(){return I(y(this,ue))}set status(t){O(y(this,ue),t)}get url(){return I(y(this,de))}set url(t){O(y(this,de),t)}},oe=new WeakMap,se=new WeakMap,ie=new WeakMap,le=new WeakMap,ce=new WeakMap,fe=new WeakMap,ue=new WeakMap,de=new WeakMap,gt),ee=new(mt=class{constructor(){A(this,he,T(null))}get current(){return I(y(this,he))}set current(t){O(y(this,he),t)}},he=new WeakMap,mt),Ce=new(_t=class{constructor(){A(this,pe,T(!1))}get current(){return I(y(this,pe))}set current(t){O(y(this,pe),t)}},pe=new WeakMap,_t),Et.v=()=>Ce.current=!0);function un(e){Object.assign(R,e)}const dn=new Set(["icon","shortcut icon","apple-touch-icon"]);let J=null;const N=wt(yt)??{},te=wt(vt)??{},C={url:it({}),page:it({}),navigating:Ke(null),updated:on()};function Je(e){N[e]=B()}function hn(e,t){let n=e+1;for(;N[n];)delete N[n],n+=1;for(n=t+1;te[n];)delete te[n],n+=1}function ne(e,t=!1){return t?location.replace(e.href):location.href=e.href,new Promise($)}async function xt(){if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistration(U||"/");e&&await e.update()}}let Ye,De,ye,P,Ve,S;const be=[],ke=[];let v=null;function Se(){var e;(e=v==null?void 0:v.fork)==null||e.then(t=>t==null?void 0:t.discard()),v=null}const _e=new Map,Lt=new Set,pn=new Set,Q=new Set;let _={branch:[],error:null,url:null},Ut=!1,Ee=!1,ct=!0,re=!1,Y=!1,At=!1,Xe=!1,Tt,k,L,V;const Re=new Set,ft=new Map,ut=new Map;async function Nn(e,t,n){var i,s,o,l;globalThis.__sveltekit_8j9bml&&(globalThis.__sveltekit_8j9bml.query,globalThis.__sveltekit_8j9bml.prerender),document.URL!==location.href&&(location.href=location.href),S=e,await((s=(i=e.hooks).init)==null?void 0:s.call(i)),Ye=tn(e),P=document.documentElement,Ve=t,De=e.nodes[0],ye=e.nodes[1],De(),ye(),k=(o=history.state)==null?void 0:o[F],L=(l=history.state)==null?void 0:l[Z],k||(k=L=Date.now(),history.replaceState({...history.state,[F]:k,[Z]:L},""));const a=N[k];function r(){a&&(history.scrollRestoration="manual",scrollTo(a.x,a.y))}n?(r(),await Ln(Ve,n)):(await G({type:"enter",url:He(S.hash?Tn(new URL(location.href)):location.href),replace_state:!0}),r()),xn()}function gn(){be.length=0,Xe=!1}function It(e){ke.some(t=>t==null?void 0:t.snapshot)&&(te[e]=ke.map(t=>{var n;return(n=t==null?void 0:t.snapshot)==null?void 0:n.capture()}))}function Ot(e){var t;(t=te[e])==null||t.forEach((n,a)=>{var r,i;(i=(r=ke[a])==null?void 0:r.snapshot)==null||i.restore(n)})}function dt(){Je(k),ot(yt,N),It(L),ot(vt,te)}async function Pt(e,t,n,a){let r,i;t.invalidateAll&&Se(),await G({type:"goto",url:He(e),keepfocus:t.keepFocus,noscroll:t.noScroll,replace_state:t.replaceState,state:t.state,redirect_count:n,nav_token:a,accept:()=>{if(t.invalidateAll){Xe=!0,r=new Set;for(const[s,o]of ft)for(const l of o.keys())r.add(me(s,l));i=new Set;for(const[s,o]of ut)for(const l of o.keys())i.add(me(s,l))}t.invalidate&&t.invalidate.forEach(Rn)}}),t.invalidateAll&&we().then(we).then(()=>{for(const[s,o]of ft)for(const[l,{resource:c}]of o)r!=null&&r.has(me(s,l))&&c.refresh();for(const[s,o]of ut)for(const[l,{resource:c}]of o)i!=null&&i.has(me(s,l))&&c.reconnect()})}async function mn(e){if(e.id!==(v==null?void 0:v.id)){Se();const t={};Re.add(t),v={id:e.id,token:t,promise:$t({...e,preload:t}).then(n=>(Re.delete(t),n.type==="loaded"&&n.state.error&&Se(),n)),fork:null}}return v.promise}async function Ne(e){var n;const t=(n=await Te(e,!1))==null?void 0:n.route;t&&await Promise.all([...t.layouts,t.leaf].filter(Boolean).map(a=>a[1]()))}async function jt(e,t,n){var i;const a={params:_.params,route:{id:((i=_.route)==null?void 0:i.id)??null},url:new URL(location.href)};_={...e.state,nav:a};const r=document.querySelector("style[data-sveltekit]");if(r&&r.remove(),Object.assign(R,e.props.page),Tt=new S.root({target:t,props:{...e.props,stores:C,components:ke},hydrate:n,sync:!1,transformError:void 0}),await Promise.resolve(),Ot(L),n){const s={from:null,to:{...a,scroll:N[k]??B()},willUnload:!1,type:"enter",complete:Promise.resolve()};Q.forEach(o=>o(s))}Ee=!0}async function xe({url:e,params:t,branch:n,errors:a,status:r,error:i,route:s,form:o}){let l="never";if(U&&(e.pathname===U||e.pathname===U+"/"))l="always";else for(const f of n)(f==null?void 0:f.slash)!==void 0&&(l=f.slash);e.pathname=Kt(e.pathname,l),e.search=e.search;const c={type:"loaded",state:{url:e,params:t,branch:n,error:i,route:s},props:{constructors:ln(n).map(f=>f.node.component),page:nt(R)}};o!==void 0&&(c.props.form=o);let d={},u=!R,w=0;for(let f=0;f<Math.max(n.length,_.branch.length);f+=1){const m=n[f],h=_.branch[f];(m==null?void 0:m.data)!==(h==null?void 0:h.data)&&(u=!0),m&&(d={...d,...m.data},u&&(c.props[`data_${w}`]=d),w+=1)}return(!_.url||e.href!==_.url.href||_.error!==i||o!==void 0&&o!==R.form||u)&&(c.props.page={error:i,params:t,route:{id:(s==null?void 0:s.id)??null},state:{},status:r,url:new URL(e),form:o??null,data:u?d:R.data}),c}async function Qe({loader:e,parent:t,url:n,params:a,route:r,server_data_node:i}){var c,d;let s=null;const o={dependencies:new Set,params:new Set,parent:!1,route:!1,url:!1,search_params:new Set},l=await e();return{node:l,loader:e,server:i,universal:(c=l.universal)!=null&&c.load?{type:"data",data:s,uses:o}:null,data:s??(i==null?void 0:i.data)??null,slash:((d=l.universal)==null?void 0:d.trailingSlash)??(i==null?void 0:i.slash)}}function _n(e,t,n){let a=e instanceof Request?e.url:e;const r=new URL(a,n);r.origin===n.origin&&(a=r.href.slice(n.origin.length));const i=Ee?Jt(a,r.href,t):Wt(a,t);return{resolved:r,promise:i}}function wn(e,t,n,a,r,i){if(Xe)return!0;if(!r)return!1;if(r.parent&&e||r.route&&t||r.url&&n)return!0;for(const s of r.search_params)if(a.has(s))return!0;for(const s of r.params)if(i[s]!==_.params[s])return!0;for(const s of r.dependencies)if(be.some(o=>o(new URL(s))))return!0;return!1}function Ze(e,t){return(e==null?void 0:e.type)==="data"?e:(e==null?void 0:e.type)==="skip"?t??null:null}function vn(e,t){if(!e)return new Set(t.searchParams.keys());const n=new Set([...e.searchParams.keys(),...t.searchParams.keys()]);for(const a of n){const r=e.searchParams.getAll(a),i=t.searchParams.getAll(a);r.every(s=>i.includes(s))&&i.every(s=>r.includes(s))&&n.delete(a)}return n}function yn({error:e,url:t,route:n,params:a}){return{type:"loaded",state:{error:e,url:t,route:n,params:a,branch:[]},props:{page:nt(R),constructors:[]}}}async function $t({id:e,invalidating:t,url:n,params:a,route:r,preload:i}){if((v==null?void 0:v.id)===e)return Re.delete(v.token),v.promise;const{errors:s,layouts:o,leaf:l}=r,c=[...o,l];s.forEach(h=>h==null?void 0:h().catch($)),c.forEach(h=>h==null?void 0:h[1]().catch($));const d=_.url?e!==Le(_.url):!1,u=_.route?r.id!==_.route.id:!1,w=vn(_.url,n);let p=!1;const f=c.map(async(h,g)=>{var j;if(!h)return;const b=_.branch[g];return h[1]===(b==null?void 0:b.loader)&&!wn(p,u,d,w,(j=b.universal)==null?void 0:j.uses,a)?b:(p=!0,Qe({loader:h[1],url:n,params:a,route:r,parent:async()=>{var ge;const q={};for(let K=0;K<g;K+=1)Object.assign(q,(ge=await f[K])==null?void 0:ge.data);return q},server_data_node:Ze(h[0]?{type:"skip"}:null,h[0]?b==null?void 0:b.server:void 0)}))});for(const h of f)h.catch($);const m=[];for(let h=0;h<c.length;h+=1)if(c[h])try{m.push(await f[h])}catch(g){if(g instanceof ze)return{type:"redirect",location:g.location};if(Re.has(i))return yn({error:await ae(g,{params:a,url:n,route:{id:r.id}}),url:n,params:a,route:r});let b=We(g),x;if(g instanceof Me)x=g.body;else{if(await C.updated.check())return await xt(),await ne(n);x=await ae(g,{params:a,url:n,route:{id:r.id}})}const j=await bn(h,m,s);return j?xe({url:n,params:a,branch:m.slice(0,j.idx).concat(j.node),errors:s,status:b,error:x,route:r}):await Nt(n,{id:r.id},x,b)}else m.push(void 0);return xe({url:n,params:a,branch:m,errors:s,status:200,error:null,route:r,form:t?void 0:null})}async function bn(e,t,n){for(;e--;)if(n[e]){let a=e;for(;!t[a];)a-=1;try{return{idx:a+1,node:{node:await n[e](),loader:n[e],data:{},server:null,universal:null}}}catch{continue}}}async function et({status:e,error:t,url:n,route:a}){const r={};let i=null;try{const s=await Qe({loader:De,url:n,params:r,route:a,parent:()=>Promise.resolve({}),server_data_node:Ze(i)}),o={node:await ye(),loader:ye,universal:null,server:null,data:null};return xe({url:n,params:r,branch:[s,o],status:e,error:t,errors:[],route:null})}catch(s){if(s instanceof ze)return Pt(new URL(s.location,location.href),{},0);throw s}}async function kn(e){const t=e.href;if(_e.has(t))return _e.get(t);let n;try{const a=(async()=>{let r=await S.hooks.reroute({url:new URL(e),fetch:async(i,s)=>_n(i,s,e).promise})??e;if(typeof r=="string"){const i=new URL(e);S.hash?i.hash=r:i.pathname=r,r=i}return r})();_e.set(t,a),n=await a}catch{_e.delete(t);return}return n}async function Te(e,t){if(e&&!Ae(e,U,S.hash)){const n=await kn(e);if(!n)return;const a=Sn(n);for(const r of Ye){const i=r.exec(a);if(i)return{id:Le(e),invalidating:t,route:r,params:zt(i),url:e}}}}function Sn(e){return Mt(S.hash?e.hash.replace(/^#/,"").replace(/[?#].+/,""):e.pathname.slice(U.length))||"/"}function Le(e){return(S.hash?e.hash.replace(/^#/,""):e.pathname)+e.search}function Ct({url:e,type:t,intent:n,delta:a,event:r,scroll:i}){let s=!1;const o=tt(_,n,e,t,i??null);a!==void 0&&(o.navigation.delta=a),r!==void 0&&(o.navigation.event=r);const l={...o.navigation,cancel:()=>{s=!0,o.reject(new Error("navigation cancelled"))}};return re||Lt.forEach(c=>c(l)),s?null:o}async function G({type:e,url:t,popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s={},redirect_count:o=0,nav_token:l={},accept:c=$,block:d=$,event:u}){var K;const w=V;V=l;const p=await Te(t,!1),f=e==="enter"?tt(_,p,t,e):Ct({url:t,type:e,delta:n==null?void 0:n.delta,intent:p,scroll:n==null?void 0:n.scroll,event:u});if(!f){d(),V===l&&(V=w);return}const m=k,h=L;c(),re=!0,Ee&&f.navigation.type!=="enter"&&C.navigating.set(ee.current=f.navigation);let g=p&&await $t(p);if(!g){if(Ae(t,U,S.hash))return await ne(t,i);g=await Nt(t,{id:null},await ae(new Fe(404,"Not Found",`Not found: ${t.pathname}`),{url:t,params:{},route:{id:null}}),404,i)}if(t=(p==null?void 0:p.url)||t,V!==l)return f.reject(new Error("navigation aborted")),!1;if(g.type==="redirect"){if(o<20){await G({type:e,url:new URL(g.location,t),popped:n,keepfocus:a,noscroll:r,replace_state:i,state:s,redirect_count:o+1,nav_token:l}),f.fulfil(void 0);return}g=await et({status:500,error:await ae(new Error("Redirect loop"),{url:t,params:{},route:{id:null}}),url:t,route:{id:null}})}else g.props.page.status>=400&&await C.updated.check()&&(await xt(),await ne(t,i));if(gn(),Je(m),It(h),g.props.page.url.pathname!==t.pathname&&(t.pathname=g.props.page.url.pathname),s=n?n.state:s,!n){const E=i?0:1,H={[F]:k+=E,[Z]:L+=E,[bt]:s};(i?history.replaceState:history.pushState).call(history,H,"",t),i||hn(k,L)}const b=p&&(v==null?void 0:v.id)===p.id?v.fork:null;v!=null&&v.fork&&!b&&Se(),v=null,g.props.page.state=s;let x;if(Ee){const E=(await Promise.all(Array.from(pn,W=>W(f.navigation)))).filter(W=>typeof W=="function");if(E.length>0){let W=function(){E.forEach(Oe=>{Q.delete(Oe)})};E.push(W),E.forEach(Oe=>{Q.add(Oe)})}const H=f.navigation.to;_={...g.state,nav:{params:H.params,route:H.route,url:H.url}},g.props.page&&(g.props.page.url=t);const Ie=b&&await b;Ie?x=Ie.commit():(J=null,Tt.$set(g.props),J&&Object.assign(g.props.page,J),un(g.props.page),x=(K=Bt)==null?void 0:K()),At=!0}else await jt(g,Ve,!1);const{activeElement:j}=document;await x,await we(),await we();let q=null;if(ct){const E=n?n.scroll:r?B():null;E?scrollTo(E.x,E.y):(q=t.hash&&document.getElementById(qt(t)))?q.scrollIntoView():scrollTo(0,0)}const ge=document.activeElement!==j&&document.activeElement!==document.body;!a&&!ge&&An(t,!q),ct=!0,g.props.page&&(J&&Object.assign(g.props.page,J),Object.assign(R,g.props.page)),re=!1,e==="popstate"&&Ot(L),f.fulfil(void 0),f.navigation.to&&(f.navigation.to.scroll=B()),Q.forEach(E=>E(f.navigation)),C.navigating.set(ee.current=null)}async function Nt(e,t,n,a,r){return e.origin===Ue&&e.pathname===location.pathname&&!Ut?await et({status:a,error:n,url:e,route:t}):await ne(e,r)}function En(){let e,t={element:void 0,href:void 0},n;P.addEventListener("mousemove",o=>{const l=o.target;clearTimeout(e),e=setTimeout(()=>{i(l,D.hover)},20)});function a(o){o.defaultPrevented||i(o.composedPath()[0],D.tap)}P.addEventListener("mousedown",a),P.addEventListener("touchstart",a,{passive:!0});const r=new IntersectionObserver(o=>{for(const l of o)l.isIntersecting&&(Ne(new URL(l.target.href)),r.unobserve(l.target))},{threshold:0});async function i(o,l){const c=St(o,P),d=c===t.element&&(c==null?void 0:c.href)===t.href&&l>=n;if(!c||d)return;const{url:u,external:w,download:p}=qe(c,U,S.hash);if(w||p)return;const f=ve(c),m=u&&Le(_.url)===Le(u);if(!(f.reload||m))if(l<=f.preload_data){t={element:c,href:c.href},n=D.tap;const h=await Te(u,!1);if(!h)return;mn(h)}else l<=f.preload_code&&(t={element:c,href:c.href},n=l,Ne(u))}function s(){r.disconnect();for(const o of P.querySelectorAll("a")){const{url:l,external:c,download:d}=qe(o,U,S.hash);if(c||d)continue;const u=ve(o);u.reload||(u.preload_code===D.viewport&&r.observe(o),u.preload_code===D.eager&&Ne(l))}}Q.add(s),s()}function ae(e,t){if(e instanceof Me)return e.body;const n=We(e),a=cn(e);return S.hooks.handleError({error:e,event:t,status:n,message:a})??{message:a}}function qn(e,t={}){return e=new URL(He(e)),e.origin!==Ue?Promise.reject(new Error("goto: invalid URL")):Pt(e,t,0)}function Rn(e){if(typeof e=="function")be.push(e);else{const{href:t}=new URL(e,location.href);be.push(n=>n.href===t)}}function xn(){var t;history.scrollRestoration="manual",addEventListener("beforeunload",n=>{let a=!1;if(dt(),!re){const r=tt(_,void 0,null,"leave"),i={...r.navigation,cancel:()=>{a=!0,r.reject(new Error("navigation cancelled"))}};Lt.forEach(s=>s(i))}a?(n.preventDefault(),n.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&dt()}),(t=navigator.connection)!=null&&t.saveData||En(),P.addEventListener("click",async n=>{if(n.button||n.which!==1||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||n.defaultPrevented)return;const a=St(n.composedPath()[0],P);if(!a)return;const{url:r,external:i,target:s,download:o}=qe(a,U,S.hash);if(!r)return;if(s==="_parent"||s==="_top"){if(window.parent!==window)return}else if(s&&s!=="_self")return;const l=ve(a);if(!(a instanceof SVGAElement)&&r.protocol!==location.protocol&&!(r.protocol==="https:"||r.protocol==="http:")||o)return;const[d,u]=(S.hash?r.hash.replace(/^#/,""):r.href).split("#"),w=d===je(location);if(i||l.reload&&(!w||!u)){Ct({url:r,type:"link",event:n})?re=!0:n.preventDefault();return}if(u!==void 0&&w){const[,p]=_.url.href.split("#");if(p===u){if(n.preventDefault(),u===""||u==="top"&&a.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const f=a.ownerDocument.getElementById(decodeURIComponent(u));f&&(f.scrollIntoView(),f.focus())}return}if(Y=!0,Je(k),e(r),!l.replace_state)return;Y=!1}n.preventDefault(),await new Promise(p=>{requestAnimationFrame(()=>{setTimeout(p,0)}),setTimeout(p,100)}),await G({type:"link",url:r,keepfocus:l.keepfocus,noscroll:l.noscroll,replace_state:l.replace_state??r.href===location.href,event:n})}),P.addEventListener("submit",n=>{if(n.defaultPrevented)return;const a=HTMLFormElement.prototype.cloneNode.call(n.target),r=n.submitter;if(((r==null?void 0:r.formTarget)||a.target)==="_blank"||((r==null?void 0:r.formMethod)||a.method)!=="get")return;const o=new URL((r==null?void 0:r.hasAttribute("formaction"))&&(r==null?void 0:r.formAction)||a.action);if(Ae(o,U,!1))return;const l=n.target,c=ve(l);if(c.reload)return;n.preventDefault(),n.stopPropagation();const d=new FormData(l,r);o.search=new URLSearchParams(d).toString(),G({type:"form",url:o,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??o.href===location.href,event:n})}),addEventListener("popstate",async n=>{var a;if(!Be){if((a=n.state)!=null&&a[F]){const r=n.state[F];if(V={},r===k)return;const i=N[r],s=n.state[bt]??{},o=new URL(n.state[an]??location.href),l=n.state[Z],c=_.url?je(location)===je(_.url):!1;if(l===L&&(At||c)){s!==R.state&&(R.state=s),e(o),N[k]=B(),i&&scrollTo(i.x,i.y),k=r;return}const u=r-k;await G({type:"popstate",url:o,popped:{state:s,scroll:i,delta:u},accept:()=>{k=r,L=l},block:()=>{history.go(-u)},nav_token:V,event:n})}else if(!Y){const r=new URL(location.href);e(r),S.hash&&location.reload()}}}),addEventListener("hashchange",()=>{Y&&(Y=!1,history.replaceState({...history.state,[F]:++k,[Z]:L},"",location.href))});for(const n of document.querySelectorAll("link"))dn.has(n.rel)&&(n.href=n.href);addEventListener("pageshow",n=>{n.persisted&&C.navigating.set(ee.current=null)});function e(n){_.url=R.url=n,C.page.set(nt(R)),C.page.notify()}}async function Ln(e,{status:t=200,error:n,node_ids:a,params:r,route:i,server_route:s,data:o,form:l}){Ut=!0;const c=new URL(location.href);let d;({params:r={},route:i={id:null}}=await Te(c,!1)||{}),d=Ye.find(({id:p})=>p===i.id);let u,w=!0;try{const p=a.map(async(m,h)=>{const g=o[h];return g!=null&&g.uses&&(g.uses=Un(g.uses)),Qe({loader:S.nodes[m],url:c,params:r,route:i,parent:async()=>{const b={};for(let x=0;x<h;x+=1)Object.assign(b,(await p[x]).data);return b},server_data_node:Ze(g)})}),f=await Promise.all(p);if(d){const m=d.layouts;for(let h=0;h<m.length;h++)m[h]||f.splice(h,0,void 0)}u=await xe({url:c,params:r,branch:f,status:t,error:n,errors:d==null?void 0:d.errors,form:l,route:d??null})}catch(p){if(p instanceof ze){await ne(new URL(p.location,location.href));return}u=await et({status:We(p),error:await ae(p,{url:c,params:r,route:i}),url:c,route:i}),e.textContent="",w=!1}finally{}u.props.page&&(u.props.page.state={}),await jt(u,e,w)}function Un(e){return{dependencies:new Set((e==null?void 0:e.dependencies)??[]),params:new Set((e==null?void 0:e.params)??[]),parent:!!(e!=null&&e.parent),route:!!(e!=null&&e.route),url:!!(e!=null&&e.url),search_params:new Set((e==null?void 0:e.search_params)??[])}}let Be=!1;function An(e,t=!0){const n=document.querySelector("[autofocus]");if(n)n.focus();else{const a=qt(e);if(a&&document.getElementById(a)){const{x:i,y:s}=B();setTimeout(()=>{const o=history.state;Be=!0,location.replace(new URL(`#${a}`,location.href)),history.replaceState(o,"",e),t&&scrollTo(i,s),Be=!1})}else{const i=document.body,s=i.getAttribute("tabindex");i.tabIndex=-1,i.focus({preventScroll:!0,focusVisible:!1}),s!==null?i.setAttribute("tabindex",s):i.removeAttribute("tabindex")}const r=getSelection();if(r&&r.type!=="None"){const i=[];for(let s=0;s<r.rangeCount;s+=1)i.push(r.getRangeAt(s));setTimeout(()=>{if(r.rangeCount===i.length){for(let s=0;s<r.rangeCount;s+=1){const o=i[s],l=r.getRangeAt(s);if(o.commonAncestorContainer!==l.commonAncestorContainer||o.startContainer!==l.startContainer||o.endContainer!==l.endContainer||o.startOffset!==l.startOffset||o.endOffset!==l.endOffset)return}r.removeAllRanges()}})}}}function tt(e,t,n,a,r=null){var c,d;let i,s;const o=new Promise((u,w)=>{i=u,s=w});return o.catch($),{navigation:{from:{params:e.params,route:{id:((c=e.route)==null?void 0:c.id)??null},url:e.url,scroll:B()},to:n&&{params:(t==null?void 0:t.params)??null,route:{id:((d=t==null?void 0:t.route)==null?void 0:d.id)??null},url:n,scroll:r},willUnload:!t,type:a,complete:o},fulfil:i,reject:s}}function nt(e){return{data:e.data,error:e.error,form:e.form,params:e.params,route:e.route,state:e.state,status:e.status,url:e.url}}function Tn(e){const t=new URL(e);return t.hash=decodeURIComponent(e.hash),t}function qt(e){let t;if(S.hash){const[,,n]=e.hash.split("#",3);t=n??""}else t=e.hash.slice(1);return decodeURIComponent(t)}export{Nn as a,qn as g,Pn as l,R as p,C as s};
|
web/sveltekit/build/_app/immutable/entry/app.DtHxgVfE.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.LAFF30Kg.js","../chunks/CWw6qgC_.js","../chunks/BTUA7_xE.js","../chunks/DxQlA7U2.js","../chunks/Bd-v_9Ud.js","../chunks/CW0zSL4D.js","../chunks/BbRPgcjS.js","../chunks/BgqmyNr8.js","../chunks/DCD6_LXk.js","../chunks/B0XoTt7U.js","../assets/RipMark.ClxF_PAC.css","../chunks/DixtWtwq.js","../assets/0.DiQNUxm-.css","../nodes/1.MkU5tCTk.js","../nodes/2.vc91Ib0z.js","../chunks/cDW0xQNP.js","../chunks/25_y8TFd.js","../chunks/CXQd8Y6F.js","../chunks/D907np-5.js","../assets/2.BD53GLFY.css","../nodes/3.B7oy3YuI.js","../chunks/BatqQaKj.js","../assets/Briefing.Dmn9LgiV.css","../assets/3.BZfqQRM0.css","../nodes/4.B_VNCC6U.js","../chunks/BZuv-XBZ.js","../assets/stoneRegistry.bHiraU77.css","../assets/4.C9CQZyPb.css","../nodes/5.BFrkSt3u.js"])))=>i.map(i=>d[i]);
|
| 2 |
+
var S=e=>{throw TypeError(e)};var M=(e,t,r)=>t.has(e)||S("Cannot "+r);var c=(e,t,r)=>(M(e,t,"read from private field"),r?r.call(e):t.get(e)),p=(e,t,r)=>t.has(e)?S("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,r),I=(e,t,r,n)=>(M(e,t,"write to private field"),n?n.call(e,r):t.set(e,r),r);import{b as L,_ as b}from"../chunks/CXQd8Y6F.js";import{h as N,n as W,d as X,a3 as Z,q as $,v as tt,i as et,e as B,an as rt,j as at,a5 as A,ah as st,o as l,ao as nt,ap as ot,L as it,p as ct,aq as ut,am as dt,ai as mt,ar as _t,f as x,s as lt,a as ft,a4 as j,c as ht,r as vt,t as gt,al as k}from"../chunks/BTUA7_xE.js";import{h as yt,m as Et,u as bt,a as R,c as w,f as F,t as Rt,s as Pt}from"../chunks/CWw6qgC_.js";import{B as Ot,i as D}from"../chunks/Bd-v_9Ud.js";import{p as q}from"../chunks/CW0zSL4D.js";function V(e,t,r){var n;N&&(n=at,W());var o=new Ot(e);X(()=>{var i=t()??null;if(N){var a=$(n),s=a===rt,_=i!==null;if(s!==_){var P=tt();et(P),o.anchor=P,B(!1),o.ensure(i,i&&(y=>r(y,i))),B(!0);return}}o.ensure(i,i&&(y=>r(y,i)))},Z)}function Tt(e){return class extends xt{constructor(t){super({component:e,...t})}}}var f,d;class xt{constructor(t){p(this,f);p(this,d);var i;var r=new Map,n=(a,s)=>{var _=it(s,!1,!1);return r.set(a,_),_};const o=new Proxy({...t.props||{},$$events:{}},{get(a,s){return l(r.get(s)??n(s,Reflect.get(a,s)))},has(a,s){return s===st?!0:(l(r.get(s)??n(s,Reflect.get(a,s))),Reflect.has(a,s))},set(a,s,_){return A(r.get(s)??n(s,_),_),Reflect.set(a,s,_)}});I(this,d,(t.hydrate?yt:Et)(t.component,{target:t.target,anchor:t.anchor,props:o,context:t.context,intro:t.intro??!1,recover:t.recover,transformError:t.transformError})),(!((i=t==null?void 0:t.props)!=null&&i.$$host)||t.sync===!1)&&nt(),I(this,f,o.$$events);for(const a of Object.keys(c(this,d)))a==="$set"||a==="$destroy"||a==="$on"||ot(this,a,{get(){return c(this,d)[a]},set(s){c(this,d)[a]=s},enumerable:!0});c(this,d).$set=a=>{Object.assign(o,a)},c(this,d).$destroy=()=>{bt(c(this,d))}}$set(t){c(this,d).$set(t)}$on(t,r){c(this,f)[t]=c(this,f)[t]||[];const n=(...o)=>r.call(this,...o);return c(this,f)[t].push(n),()=>{c(this,f)[t]=c(this,f)[t].filter(o=>o!==n)}}$destroy(){c(this,d).$destroy()}}f=new WeakMap,d=new WeakMap;const St={};var At=F('<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"><!></div>'),pt=F("<!> <!>",1);function It(e,t){ct(t,!0);let r=q(t,"components",23,()=>[]),n=q(t,"data_0",3,null),o=q(t,"data_1",3,null);ut(()=>t.stores.page.set(t.page)),dt(()=>{t.stores,t.page,t.constructors,r(),t.form,n(),o(),t.stores.page.notify()});let i=j(!1),a=j(!1),s=j(null);mt(()=>{const u=t.stores.page.subscribe(()=>{l(i)&&(A(a,!0),_t().then(()=>{A(s,document.title||"untitled page",!0)}))});return A(i,!0),u});const _=k(()=>t.constructors[1]);var P=pt(),y=x(P);{var G=u=>{const h=k(()=>t.constructors[0]);var v=w(),O=x(v);V(O,()=>l(h),(g,E)=>{L(E(g,{get data(){return n()},get form(){return t.form},get params(){return t.page.params},children:(m,jt)=>{var C=w(),K=x(C);V(K,()=>l(_),(Q,U)=>{L(U(Q,{get data(){return o()},get form(){return t.form},get params(){return t.page.params}}),T=>r()[1]=T,()=>{var T;return(T=r())==null?void 0:T[1]})}),R(m,C)},$$slots:{default:!0}}),m=>r()[0]=m,()=>{var m;return(m=r())==null?void 0:m[0]})}),R(u,v)},H=u=>{const h=k(()=>t.constructors[0]);var v=w(),O=x(v);V(O,()=>l(h),(g,E)=>{L(E(g,{get data(){return n()},get form(){return t.form},get params(){return t.page.params}}),m=>r()[0]=m,()=>{var m;return(m=r())==null?void 0:m[0]})}),R(u,v)};D(y,u=>{t.constructors[1]?u(G):u(H,-1)})}var z=lt(y,2);{var J=u=>{var h=At(),v=ht(h);{var O=g=>{var E=Rt();gt(()=>Pt(E,l(s))),R(g,E)};D(v,g=>{l(a)&&g(O)})}vt(h),R(u,h)};D(z,u=>{l(i)&&u(J)})}R(e,P),ft()}const Mt=Tt(It),Nt=[()=>b(()=>import("../nodes/0.LAFF30Kg.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12]),import.meta.url),()=>b(()=>import("../nodes/1.MkU5tCTk.js"),__vite__mapDeps([13,1,2,6,7]),import.meta.url),()=>b(()=>import("../nodes/2.vc91Ib0z.js"),__vite__mapDeps([14,1,2,11,15,8,9,5,10,16,7,17,18,19]),import.meta.url),()=>b(()=>import("../nodes/3.B7oy3YuI.js"),__vite__mapDeps([20,1,2,4,16,15,6,7,21,5,3,9,22,23]),import.meta.url),()=>b(()=>import("../nodes/4.B_VNCC6U.js"),__vite__mapDeps([24,1,2,4,6,7,21,16,5,3,9,22,25,17,18,26,11,27]),import.meta.url),()=>b(()=>import("../nodes/5.BFrkSt3u.js"),__vite__mapDeps([28,1,2,21,4,16,5,3,9,22,25,17,18,26]),import.meta.url)],Bt=[],Ft={"/":[2],"/print/[queryId]":[3],"/q/sample":[5],"/q/[queryId]":[4]},Y={handleError:(({error:e})=>{console.error(e)}),reroute:(()=>{}),transport:{}},Lt=Object.fromEntries(Object.entries(Y.transport).map(([e,t])=>[e,t.decode])),Yt=Object.fromEntries(Object.entries(Y.transport).map(([e,t])=>[e,t.encode])),Gt=!1,Ht=(e,t)=>Lt[e](t);export{Ht as decode,Lt as decoders,Ft as dictionary,Yt as encoders,Gt as hash,Y as hooks,St as matchers,Nt as nodes,Mt as root,Bt as server_loads};
|
web/sveltekit/build/_app/immutable/entry/start.BkS8JQ5_.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{l as o,a as r}from"../chunks/BgqmyNr8.js";export{o as load_css,r as start};
|
web/sveltekit/build/_app/immutable/nodes/0.LAFF30Kg.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import{c as E,a as u,s as M,f as v,d as z,e as H}from"../chunks/CWw6qgC_.js";import{p as O,f as R,o as p,a as $,al as f,s as t,c as o,r as i,t as P,aR as L}from"../chunks/BTUA7_xE.js";import{b as r,s as W}from"../chunks/DxQlA7U2.js";import{i as x}from"../chunks/Bd-v_9Ud.js";import{p as B}from"../chunks/CW0zSL4D.js";import{p as D}from"../chunks/BbRPgcjS.js";import{R as U}from"../chunks/DCD6_LXk.js";import{s as G}from"../chunks/B0XoTt7U.js";import"../chunks/DixtWtwq.js";const V=!0,J=!0,K="never",xe=Object.freeze(Object.defineProperty({__proto__:null,prerender:V,ssr:J,trailingSlash:K},Symbol.toStringTag,{value:"Module"}));var Q=v('<span class="status-sep svelte-1bjixce">·</span> <span class="status-step svelte-1bjixce"> </span>',1),X=v('<span class="status-sep svelte-1bjixce">·</span> <span class="status-progress svelte-1bjixce"> </span>',1),Z=v('<span class="status-sep svelte-1bjixce">·</span> <span class="status-err svelte-1bjixce"> </span>',1),ee=v('<span class="status svelte-1bjixce" aria-live="polite" aria-atomic="true"><span class="status-dot svelte-1bjixce" aria-hidden="true"></span> <span class="status-phase svelte-1bjixce"> </span> <!> <!> <!></span>');function ae(h,l){O(l,!0);const N={geocode:"geocoding",nta_resolve:"resolving NTA",sandy_inundation:"Sandy 2012",dep_stormwater:"DEP scenarios",floodnet:"FloodNet sensors",nyc311:"NYC 311 history",noaa_tides:"NOAA tides",nws_alerts:"NWS alerts",nws_obs:"NWS hourly obs",ttm_forecast:"TTM r2 surge (zero-shot)",ttm_311_forecast:"TTM r2 weekly 311",ttm_battery_surge:"TTM Battery (NYC fine-tune)",floodnet_forecast:"FloodNet recurrence forecast",ida_hwm_2021:"Ida 2021 HWMs",prithvi_eo_v2:"Ida 2021 polygons (baked lookup)",prithvi_eo_live:"Prithvi-NYC-Pluvial v2 segmentation",microtopo_lidar:"LiDAR microtopo",mta_entrance_exposure:"MTA entrances",nycha_development_exposure:"NYCHA developments",doe_school_exposure:"DOE schools",doh_hospital_exposure:"NYS DOH hospitals",terramind_synthesis:"TerraMind v1 synthesis",terramind_lulc:"TerraMind LULC",terramind_buildings:"TerraMind Buildings",eo_chip_fetch:"fetching S2/S1/DEM chip",rag_granite_embedding:"RAG retrieval",gliner_extract:"GLiNER typed extraction"};let T=f(()=>r.phase!=="idle"&&r.phase!=="done"),k=f(()=>{switch(r.phase){case"planning":return"planning intent";case"specialists":return"gathering evidence";case"reconciling":return"reconciling";case"streaming":return r.attempt>1?`writing (reroll ${r.attempt-1})`:"writing briefing";case"error":return"error";default:return""}}),g=f(()=>{const a=r.activeStep;return a?N[a]??a:null}),m=f(()=>{if(r.phase!=="specialists"&&r.phase!=="reconciling")return null;const a=r.firedCount,d=r.totalSpecialists;return d?`${a}/${d}`:a>0?`${a}`:null}),w=f(()=>r.phase==="error"?"err":"live");var A=E(),_=R(A);{var j=a=>{var d=ee(),e=t(o(d),2),C=o(e,!0);i(e);var S=t(e,2);{var s=n=>{var b=Q(),y=t(R(b),2),q=o(y,!0);i(y),P(()=>M(q,p(g))),u(n,b)};x(S,n=>{p(g)&&n(s)})}var c=t(S,2);{var F=n=>{var b=X(),y=t(R(b),2),q=o(y,!0);i(y),P(()=>M(q,p(m))),u(n,b)};x(c,n=>{p(m)&&n(F)})}var Y=t(c,2);{var I=n=>{var b=Z(),y=t(R(b),2),q=o(y,!0);i(y),P(()=>M(q,r.errorMessage)),u(n,b)};x(Y,n=>{r.phase==="error"&&r.errorMessage&&n(I)})}i(d),P(()=>{G(d,"data-kind",p(w)),M(C,p(k))}),u(a,d)};x(_,a=>{p(T)&&a(j)})}u(h,A),$()}var re=v('<button type="button" class="app-header-query" aria-label="Edit query"><span class="app-header-query-icon" aria-hidden="true">⌕</span> <span class="app-header-query-text"> </span> <span class="app-header-query-edit">edit</span></button>'),te=v('<button type="button" class="app-header-link app-header-link-button svelte-f1belb" aria-label="Open curated PDF view of completed briefing in new tab">export PDF</button>'),se=v('<header class="app-header no-print" data-screen-label="App header"><div class="app-header-inner"><div class="app-header-left"><a href="/" class="riprap-wordmark" aria-label="Riprap — home"><!>riprap</a> <span class="app-header-sep">/</span> <span class="app-header-context">flood-exposure briefing</span></div> <div class="app-header-mid"><!></div> <div class="app-header-right"><a class="app-header-link" href="#methodology">methodology</a> <!> <!></div></div></header>');function ne(h,l){O(l,!0);let N=B(l,"query",3,null);function T(){if(typeof window>"u")return;const s=D.params.queryId??(D.url.pathname==="/q/sample"?"sample":"");s&&window.open(`/print/${encodeURIComponent(s)}`,"_blank","noopener")}var k=se(),g=o(k),m=o(g),w=o(m),A=o(w);U(A,{size:20}),L(),i(w),L(4),i(m);var _=t(m,2),j=o(_);{var a=s=>{var c=re(),F=t(o(c),2),Y=o(F,!0);i(F),L(2),i(c),P(()=>M(Y,N())),H("click",c,function(...I){var n;(n=l.onResetCold)==null||n.apply(this,I)}),u(s,c)};x(j,s=>{N()&&s(a)})}i(_);var d=t(_,2),e=t(o(d),2);{var C=s=>{var c=te();H("click",c,T),u(s,c)};x(e,s=>{r.ready&&s(C)})}var S=t(e,2);ae(S,{}),i(d),i(g),i(k),u(h,k),$()}z(["click"]);var oe=v(`<footer class="app-footer no-print"><div class="app-footer-inner"><p class="app-footer-guard"><strong>Riprap does not predict damage.</strong> This tool is for professional analytical work, not personal property decisions.
|
| 2 |
+
For residents, see <a href="https://www.floodhelpny.org">FloodHelpNY</a> · <a href="https://www.floodnet.nyc">FloodNet NYC</a>.</p> <p class="app-footer-build">All foundation models Apache-2.0 · All data from public-record federal, state, and city sources · No commercial APIs contacted at runtime · Riprap v0.4.6 · build 2026-05-06</p> <p class="app-footer-credits">Dam mark: <a href="https://thenounproject.com/icon/dam-4516918/">"Dam" by Chintuza</a> via the Noun Project, CC-BY 3.0.</p></div></footer>`);function ie(h){var l=oe();u(h,l)}var pe=v('<a href="#region-briefing" class="skip-link">Skip to briefing</a> <a href="#region-map" class="skip-link" style="left: -9999px;">Skip to map</a> <a href="#region-trace" class="skip-link" style="left: -9999px;">Skip to trace</a>',1);function le(h){var l=pe();L(4),u(h,l)}var de=v("<!> <!>",1),ce=v('<!> <main class="svelte-12qhfyh"><!></main> <!>',1);function ke(h,l){O(l,!0);let N=f(()=>()=>{const e=D.params.queryId;if(!e)return null;try{return decodeURIComponent(e)}catch{return e}}),T=f(()=>D.url.pathname.startsWith("/print/")),k=f(()=>D.url.pathname==="/"),g=f(()=>p(T)||p(k));var m=ce(),w=R(m);{var A=e=>{var C=de(),S=R(C);le(S);var s=t(S,2);{let c=f(()=>p(N)());ne(s,{get query(){return p(c)},onResetCold:()=>window.location.href="/"})}u(e,C)};x(w,e=>{p(g)||e(A)})}var _=t(w,2),j=o(_);W(j,()=>l.children),i(_);var a=t(_,2);{var d=e=>{ie(e)};x(a,e=>{p(g)||e(d)})}u(h,m),$()}export{ke as component,xe as universal};
|
web/sveltekit/build/_app/immutable/nodes/1.MkU5tCTk.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{a as c,f as u,s as e}from"../chunks/CWw6qgC_.js";import{p as v,f as l,t as _,a as g,c as p,r as o,s as x}from"../chunks/BTUA7_xE.js";import{p as m}from"../chunks/BbRPgcjS.js";var d=u("<h1> </h1> <p> </p>",1);function k(f,i){v(i,!0);var t=d(),r=l(t),h=p(r,!0);o(r);var a=x(r,2),n=p(a,!0);o(a),_(()=>{var s;e(h,m.status),e(n,(s=m.error)==null?void 0:s.message)}),c(f,t),g()}export{k as component};
|
web/sveltekit/build/_app/immutable/nodes/2.vc91Ib0z.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import{a as m,f as w,d as D,l as I,e as G,s as g}from"../chunks/CWw6qgC_.js";import"../chunks/DixtWtwq.js";import{b9 as U,y as S,ar as K,$ as R,_ as V,h as Q,Y as Z,aq as J,am as E,ba as q,bb as X,o as d,bc as ee,af as ae,c,aR as k,r as o,p as P,a5 as N,a as M,a4 as A,s as f,t as T,ai as se,Z as te,ak as re}from"../chunks/BTUA7_xE.js";import{h as ne}from"../chunks/cDW0xQNP.js";import{R as le}from"../chunks/DCD6_LXk.js";import{e as $}from"../chunks/25_y8TFd.js";import{r as ie,a as oe,s as de,b as ce}from"../chunks/B0XoTt7U.js";import{g as B}from"../chunks/BgqmyNr8.js";import{b as ve,_ as pe}from"../chunks/CXQd8Y6F.js";import{P as fe}from"../chunks/D907np-5.js";function ue(e,s,r=s){var t=new WeakSet;U(e,"input",async a=>{var n=a?e.defaultValue:e.value;if(n=L(e)?C(n):n,r(n),S!==null&&t.add(S),await K(),n!==(n=s())){var u=e.selectionStart,l=e.selectionEnd,v=e.value.length;if(e.value=n??"",l!==null){var i=e.value.length;u===l&&l===v&&i>v?(e.selectionStart=i,e.selectionEnd=i):(e.selectionStart=u,e.selectionEnd=Math.min(l,i))}}}),(Q&&e.defaultValue!==e.value||R(s)==null&&e.value)&&(r(L(e)?C(e.value):e.value),S!==null&&t.add(S)),V(()=>{var a=s();if(e===document.activeElement){var n=S;if(t.has(n))return}L(e)&&a===C(e.value)||e.type==="date"&&!a&&!e.value||a!==e.value&&(e.value=a??"")})}function L(e){var s=e.type;return s==="number"||s==="range"}function C(e){return e===""?null:+e}function me(e=!1){const s=Z,r=s.l.u;if(!r)return;let t=()=>ee(s.s);if(e){let a=0,n={};const u=ae(()=>{let l=!1;const v=s.s;for(const i in v)v[i]!==n[i]&&(n[i]=v[i],l=!0);return l&&a++,a});t=()=>d(u)}r.b.length&&J(()=>{H(s,t),q(r.b)}),E(()=>{const a=R(()=>r.m.map(X));return()=>{for(const n of a)typeof n=="function"&&n()}}),r.a.length&&E(()=>{H(s,t),q(r.a)})}function H(e,s){if(e.l.s)for(const r of e.l.s)d(r);s()}var we=w('<header class="land-header svelte-1ct2rgk"><span class="riprap-wordmark"><!>riprap</span> <span class="land-header-sep svelte-1ct2rgk">/</span> <span class="land-header-context svelte-1ct2rgk">Flood Exposure Briefing · NYC</span> <nav class="land-header-nav svelte-1ct2rgk"><a href="#methodology" class="svelte-1ct2rgk">Methodology</a> <a href="#sources" class="svelte-1ct2rgk">Sources</a></nav></header>');function he(e){var s=we(),r=c(s),t=c(r);le(t,{size:22}),k(),o(r),k(6),o(s),m(e,s)}var ye=w("<span> </span>"),ge=w('<main class="land-hero svelte-drzq4r"><h1 class="land-hero-h1 svelte-drzq4r"><span class="land-hero-headline svelte-drzq4r">A flood exposure briefing<br/> for <em class="svelte-drzq4r">any place</em> in New York City.</span> <span class="land-hero-deck svelte-drzq4r">Type an address. Get a written briefing where every numeric claim links to its primary public-record source.</span></h1> <form class="land-query svelte-drzq4r" role="search"><span class="land-query-prompt svelte-drzq4r" aria-hidden="true">›</span> <input type="text" placeholder="Address, neighborhood, or BBL. e.g. 80 Pioneer Street, Red Hook" class="land-query-input svelte-drzq4r" aria-label="Query an address, neighborhood, or BBL"/> <button type="submit" class="land-query-submit svelte-drzq4r">Brief this place →</button></form> <div class="land-cycling svelte-drzq4r" aria-live="polite"><span class="land-cycling-label svelte-drzq4r">Try:</span> <button type="button" class="land-cycling-rail svelte-drzq4r" title="Run this example"></button></div></main>');function je(e,s){P(s,!0);const r=["80 Pioneer Street, Red Hook","Coney Island Hospital","PS 188, Lower East Side","Hammels Houses, Rockaway","Bowling Green station","555 W 57th Street"];let t=A(""),a=A(0);E(()=>{if(typeof window>"u")return;const p=setInterval(()=>{N(a,(d(a)+1)%r.length)},2200);return()=>clearInterval(p)});function n(){const p=d(t).trim();p&&B(`/q/${encodeURIComponent(p)}`)}function u(){const p=r[d(a)];B(`/q/${encodeURIComponent(p)}`)}var l=ge(),v=f(c(l),2),i=f(c(v),2);ie(i),k(2),o(v);var j=f(v,2),y=f(c(j),2);$(y,22,()=>r,p=>p,(p,b,F)=>{var h=ye();let x;var _=c(h,!0);o(h),T(()=>{x=oe(h,1,"land-cycling-item svelte-drzq4r",null,x,{"is-active":d(F)===d(a)}),de(h,"aria-hidden",d(F)!==d(a)),g(_,b)}),m(p,h)}),o(y),o(j),o(l),I("submit",v,p=>{p.preventDefault(),n()}),ue(i,()=>d(t),p=>N(t,p)),G("click",y,u),m(e,l),M()}D(["click"]);var be=w('<div class="land-mapmini svelte-1g1r73s" role="img" aria-label="Live mini-map preview of Red Hook flood exposure layers"><div class="land-mapmini-canvas svelte-1g1r73s"></div> <div class="land-mapmini-legend svelte-1g1r73s"><span class="svelte-1g1r73s"><span class="lm-sw lm-sw-emp svelte-1g1r73s"></span>empirical</span> <span class="svelte-1g1r73s"><span class="lm-sw lm-sw-mod svelte-1g1r73s"></span>modeled</span> <span class="svelte-1g1r73s"><span class="lm-sw lm-sw-prx svelte-1g1r73s"></span>proxy</span></div></div>');function _e(e,s){P(s,!0);const r=[-74.0096,40.6776];let t=A(null),a=null;se(()=>{let l=!1;return(async()=>{if(!d(t)||l)return;const v=await pe(()=>import("../chunks/D4L2lGt1.js").then(i=>i.m),[],import.meta.url);l||!d(t)||(a=new v.Map({container:d(t),style:fe,center:r,zoom:14.5,interactive:!1,attributionControl:!1}),a.on("load",()=>{a&&(a.addSource("fema-ae",{type:"geojson",data:{type:"FeatureCollection",features:[{type:"Feature",properties:{},geometry:{type:"Polygon",coordinates:[[[-74.014,40.679],[-74.007,40.68],[-74.005,40.677],[-74.009,40.6755],[-74.014,40.679]]]}}]}}),a.addLayer({id:"fema-ae-fill",type:"fill",source:"fema-ae",paint:{"fill-color":"#2A6FA8","fill-opacity":.22}}),a.addLayer({id:"fema-ae-line",type:"line",source:"fema-ae",paint:{"line-color":"#2A6FA8","line-width":1,"line-dasharray":[3,2]}}),a.addSource("hwm-contour",{type:"geojson",data:{type:"Feature",properties:{},geometry:{type:"LineString",coordinates:[[-74.0125,40.679],[-74.0105,40.6792],[-74.008,40.679],[-74.006,40.6786]]}}}),a.addLayer({id:"hwm-contour-line",type:"line",source:"hwm-contour",paint:{"line-color":"#0B5394","line-width":1.4}}),a.addSource("proxy-311",{type:"geojson",data:{type:"FeatureCollection",features:[[-74.0118,40.677],[-74.0114,40.6767],[-74.0121,40.6772]].map(i=>({type:"Feature",properties:{},geometry:{type:"Point",coordinates:i}}))}}),a.addLayer({id:"proxy-311-circle",type:"circle",source:"proxy-311",paint:{"circle-radius":3,"circle-color":"transparent","circle-stroke-color":"#6B6B6B","circle-stroke-width":1}}),a.addSource("floodnet",{type:"geojson",data:{type:"Feature",properties:{},geometry:{type:"Point",coordinates:[-74.0103,40.6788]}}}),a.addLayer({id:"floodnet-pin",type:"circle",source:"floodnet",paint:{"circle-radius":4,"circle-color":"#0B5394","circle-stroke-color":"#FFFFFF","circle-stroke-width":1}}),a.addSource("addr",{type:"geojson",data:{type:"Feature",properties:{},geometry:{type:"Point",coordinates:r}}}),a.addLayer({id:"addr-ring",type:"circle",source:"addr",paint:{"circle-radius":9,"circle-color":"transparent","circle-stroke-color":"#0F172A","circle-stroke-width":1.4}}),a.addLayer({id:"addr-dot",type:"circle",source:"addr",paint:{"circle-radius":3,"circle-color":"#0F172A"}}))}))})(),()=>{l=!0,a&&(a.remove(),a=null)}});var n=be(),u=c(n);ve(u,l=>N(t,l),()=>d(t)),k(2),o(n),m(e,n),M()}var Se=w(`<section class="land-section svelte-1anw2jf"><div class="land-section-head svelte-1anw2jf"><span class="section-label">What you'll get back</span> <span class="land-section-meta svelte-1anw2jf">A grounded paragraph with citations, not a chatbot answer.</span></div> <div class="land-preview-grid svelte-1anw2jf"><div class="land-preview-pane land-preview-pane-excerpt svelte-1anw2jf"><div class="land-preview-eyebrow svelte-1anw2jf">Briefing excerpt</div> <p class="land-preview-body svelte-1anw2jf">The lot sits inside the FEMA <span class="land-preview-cite svelte-1anw2jf">1% AE flood zone <sup class="svelte-1anw2jf">[c3]</sup></span>,
|
| 2 |
+
with Sandy high-water marks recorded <span class="land-preview-cite svelte-1anw2jf">4.7 ft above grade <sup class="svelte-1anw2jf">[c1]</sup></span>.
|
| 3 |
+
FloodNet FN-BK-018 has logged <span class="land-preview-cite svelte-1anw2jf">14 nuisance floods since 2023 <sup class="svelte-1anw2jf">[c2]</sup></span>.</p> <div class="land-preview-cites svelte-1anw2jf"><div class="land-preview-cite-row svelte-1anw2jf"><span class="land-preview-cite-pin svelte-1anw2jf">[c1]</span> <span class="land-preview-cite-src svelte-1anw2jf">USGS HWM · Sandy 2012</span> <span class="land-preview-cite-tier svelte-1anw2jf">empirical</span></div> <div class="land-preview-cite-row svelte-1anw2jf"><span class="land-preview-cite-pin svelte-1anw2jf">[c2]</span> <span class="land-preview-cite-src svelte-1anw2jf">FloodNet FN-BK-018</span> <span class="land-preview-cite-tier svelte-1anw2jf">empirical</span></div> <div class="land-preview-cite-row svelte-1anw2jf"><span class="land-preview-cite-pin svelte-1anw2jf">[c3]</span> <span class="land-preview-cite-src svelte-1anw2jf">FEMA NFHL · 36047C0207</span> <span class="land-preview-cite-tier svelte-1anw2jf">modeled</span></div></div></div> <div class="land-preview-pane land-preview-pane-cards svelte-1anw2jf"><div class="land-preview-eyebrow svelte-1anw2jf">Evidence cards</div> <div class="land-evcard-grid svelte-1anw2jf"><article class="land-evcard land-evcard-empirical svelte-1anw2jf"><header class="land-evcard-head svelte-1anw2jf"><span class="land-evcard-tier svelte-1anw2jf">empirical</span> <span class="land-evcard-id svelte-1anw2jf">e1</span></header> <div class="land-evcard-claim svelte-1anw2jf">4.7 ft Sandy storm-surge HWM at address</div> <div class="land-evcard-source svelte-1anw2jf">USGS High-Water Mark database · 2012</div></article> <article class="land-evcard land-evcard-empirical svelte-1anw2jf"><header class="land-evcard-head svelte-1anw2jf"><span class="land-evcard-tier svelte-1anw2jf">empirical</span> <span class="land-evcard-id svelte-1anw2jf">e2</span></header> <div class="land-evcard-claim svelte-1anw2jf">14 nuisance-flood events, 2023–2026</div> <div class="land-evcard-source svelte-1anw2jf">FloodNet FN-BK-018 · 2 blocks north</div></article> <article class="land-evcard land-evcard-modeled svelte-1anw2jf"><header class="land-evcard-head svelte-1anw2jf"><span class="land-evcard-tier svelte-1anw2jf">modeled</span> <span class="land-evcard-id svelte-1anw2jf">e3</span></header> <div class="land-evcard-claim svelte-1anw2jf">FEMA 1% annual-chance (AE) flood zone</div> <div class="land-evcard-source svelte-1anw2jf">FEMA NFHL · panel 36047C0207</div></article> <article class="land-evcard land-evcard-modeled svelte-1anw2jf"><header class="land-evcard-head svelte-1anw2jf"><span class="land-evcard-tier svelte-1anw2jf">modeled</span> <span class="land-evcard-id svelte-1anw2jf">e5</span></header> <div class="land-evcard-claim svelte-1anw2jf">+30 in MSL by 2070 (NPCC4 high)</div> <div class="land-evcard-source svelte-1anw2jf">NPCC4 SLR projection · 2024</div></article></div></div> <div class="land-preview-pane land-preview-pane-map svelte-1anw2jf"><div class="land-preview-eyebrow svelte-1anw2jf">Map</div> <!> <div class="land-preview-mapmeta svelte-1anw2jf">80 Pioneer St, Red Hook · z14.5 · Carto Positron</div></div></div></section>`);function ke(e){var s=Se(),r=f(c(s),2),t=f(c(r),4),a=f(c(t),2);_e(a,{}),k(2),o(t),o(r),o(s),m(e,s)}var Fe=w('<article class="land-stones-detail-cell svelte-1v6nt1t"><div class="land-stones-detail-num svelte-1v6nt1t"> </div> <h3 class="land-stones-detail-name svelte-1v6nt1t"> </h3> <div class="land-stones-detail-role svelte-1v6nt1t"> </div> <p class="land-stones-detail-tag svelte-1v6nt1t"> </p> <div class="land-stones-detail-sources svelte-1v6nt1t"> </div></article>'),xe=w(`<section class="land-section-stones-detail svelte-1v6nt1t" id="methodology"><div class="land-page svelte-1v6nt1t"><div class="land-section-head svelte-1v6nt1t"><span class="section-label">How Riprap reads a place</span> <span class="land-section-meta svelte-1v6nt1t">Five Stones · one taxonomy · every briefing</span></div> <p class="land-stones-deck svelte-1v6nt1t">Each briefing routes through a fixed taxonomy of public-record specialists. Each Stone is a class of evidence.
|
| 4 |
+
Together they form the briefing, and every claim in the output traces back to the Stone that produced it.</p> <div class="land-stones-detail svelte-1v6nt1t"></div></div></section>`);function Le(e,s){P(s,!1);const r=[{name:"Cornerstone",role:"the hazard reader",tag:"what NYC's ground remembers",sources:"USGS HWMs · FEMA NFHL · DEP stormwater · Prithvi historical",tint:"var(--stone-cornerstone)"},{name:"Keystone",role:"the asset register",tag:"what's exposed",sources:"MTA · NYCHA · DOE · DOH · PLUTO",tint:"var(--stone-keystone)"},{name:"Touchstone",role:"the live observer",tag:"what's happening now",sources:"FloodNet sensors · 311 complaints · NWS · NOAA tide gauges",tint:"var(--stone-touchstone)"},{name:"Lodestone",role:"the projector",tag:"what's coming",sources:"NPCC4 · Granite TTM (zero-shot + NYC fine-tune) · NWS alerts",tint:"var(--stone-lodestone)"},{name:"Capstone",role:"the synthesizer",tag:"writes it all down",sources:"Granite 4.1 composer · Mellea grounding-check · WeasyPrint",tint:"var(--stone-capstone)"}];me();var t=xe(),a=c(t),n=f(c(a),4);$(n,7,()=>r,u=>u.name,(u,l,v)=>{var i=Fe();let j;var y=c(i),p=c(y,!0);o(y);var b=f(y,2),F=c(b,!0);o(b);var h=f(b,2),x=c(h,!0);o(h);var _=f(h,2),O=c(_,!0);o(_);var z=f(_,2),W=c(z,!0);o(z),o(i),T(Y=>{j=ce(i,"",j,{"--stone-tint":d(l).tint}),g(p,Y),g(F,d(l).name),g(x,d(l).role),g(O,d(l).tag),g(W,d(l).sources)},[()=>String(d(v)+1).padStart(2,"0")]),m(u,i)}),o(n),o(a),o(t),m(e,t),M()}var Ce=w('<footer class="land-footer svelte-1dcj612"><span class="land-footer-tiers svelte-1dcj612"><span class="land-footer-tier svelte-1dcj612"><span class="lm-sw lm-sw-emp svelte-1dcj612"></span>empirical</span> <span class="land-footer-tier svelte-1dcj612"><span class="lm-sw lm-sw-mod svelte-1dcj612"></span>modeled</span> <span class="land-footer-tier svelte-1dcj612"><span class="lm-sw lm-sw-prx svelte-1dcj612"></span>proxy</span> <span class="land-footer-tier svelte-1dcj612"><span class="lm-sw lm-sw-syn svelte-1dcj612"></span>synthetic</span></span> <span class="land-footer-build">Riprap v0.4.6 · NYC OpenData · FEMA NFHL · USGS · NPCC4 · Dam mark by Chintuza, Noun Project (CC-BY)</span></footer>');function Ee(e){var s=Ce();m(e,s)}var Ne=w('<meta name="description" content="A citation-grounded flood-exposure briefing tool for any address, neighborhood, or BBL in New York City."/>'),Ae=w('<div class="land svelte-1uha8ag"><!> <div class="land-page svelte-1uha8ag"><!> <!></div> <!> <!></div>');function We(e){var s=Ae();ne("1uha8ag",v=>{var i=Ne();te(()=>{re.title="Riprap — Flood Exposure Briefing for NYC"}),m(v,i)});var r=c(s);he(r);var t=f(r,2),a=c(t);je(a,{});var n=f(a,2);ke(n),o(t);var u=f(t,2);Le(u,{});var l=f(u,2);Ee(l),o(s),m(e,s)}export{We as component};
|
web/sveltekit/build/_app/immutable/nodes/3.B7oy3YuI.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import{d as ge,c as he,a as v,s as l,e as xe,f as p}from"../chunks/CWw6qgC_.js";import{p as ye,ai as we,f as $e,a as ke,aj as qe,o as e,a5 as F,ak as je,al as d,a4 as I,c as a,s,r as t,t as M}from"../chunks/BTUA7_xE.js";import{i as O}from"../chunks/Bd-v_9Ud.js";import{e as Se}from"../chunks/25_y8TFd.js";import{h as Fe}from"../chunks/cDW0xQNP.js";import{p as Ie}from"../chunks/BbRPgcjS.js";import{B as Me,T as Oe}from"../chunks/BatqQaKj.js";import{l as Te}from"../chunks/DxQlA7U2.js";const Pe=!1,Re=!1,Ue=Object.freeze(Object.defineProperty({__proto__:null,prerender:Pe,ssr:Re},Symbol.toStringTag,{value:"Module"}));var Ae=p(`<div class="empty svelte-uialbm"><h1 class="svelte-uialbm">No briefing snapshot found</h1> <p>Run a briefing first at <a href="/" class="svelte-uialbm">riprap home</a>; once it finishes,
|
| 2 |
+
use <strong>export PDF</strong> from the header to open this view.
|
| 3 |
+
Snapshots are stored per-browser and persist between runs of the same query.</p></div>`),Be=p('<div class="curl svelte-uialbm"> </div>'),De=p('<li class="svelte-uialbm"><span class="cn svelte-uialbm"> </span> <span class="cglyph svelte-uialbm"><!></span> <span class="csrc svelte-uialbm"> </span> <span class="cvint svelte-uialbm"> </span> <div class="ctitle svelte-uialbm"> </div> <!> <div class="cdocid svelte-uialbm">doc_id <code> </code></div></li>'),Ge=p('<section class="print-citations svelte-uialbm"><h2 class="svelte-uialbm">Citations</h2> <ol class="svelte-uialbm"></ol></section>'),ze=p('<article class="print-doc svelte-uialbm"><header class="print-head svelte-uialbm"><div class="print-head-top svelte-uialbm"><span class="wordmark svelte-uialbm">riprap</span> <span class="meta"> </span></div> <h1 class="print-title svelte-uialbm"> </h1> <div class="print-sub svelte-uialbm">intent <strong> </strong> </div></header> <div class="print-controls no-print svelte-uialbm"><button type="button" class="svelte-uialbm">print / save as PDF</button> <span class="hint svelte-uialbm"> </span></div> <!> <!> <footer class="print-foot svelte-uialbm"> </footer></article>'),Le=p('<div class="empty svelte-uialbm"><p>Loading…</p></div>');function Ve(Q,U){ye(U,!0);let V=d(()=>Ie.params.queryId??""),i=I(null),T=I(!1),P=I(!1);we(()=>{const r=Te(e(V));if(!r){F(T,!0);return}F(i,r,!0),requestAnimationFrame(()=>{requestAnimationFrame(()=>{typeof window<"u"&&(window.print(),F(P,!0))})})});function X(){typeof window<"u"&&window.print()}let R=d(()=>e(i)?Object.values(e(i).citations).sort((r,n)=>r.n-n.n):[]),A=d(()=>e(i)?new Date(e(i).generatedAt).toISOString().slice(0,10):"");var B=he();Fe("uialbm",r=>{qe(()=>{var n;je.title=`Riprap briefing — ${((n=e(i))==null?void 0:n.queryText)??"export"??""}`})});var Y=$e(B);{var Z=r=>{var n=Ae();v(r,n)},ee=r=>{var n=ze(),c=a(n),u=a(c),D=s(a(u),2),ae=a(D);t(D),t(u);var m=s(u,2),se=a(m,!0);t(m);var G=s(m,2),f=s(a(G)),re=a(f,!0);t(f);var ie=s(f);t(G),t(c);var b=s(c,2),z=a(b),L=s(z,2),ne=a(L,!0);t(L),t(b);var N=s(b,2);Me(N,{get blocks(){return e(i).blocks},get citations(){return e(i).citations},streaming:!1});var C=s(N,2);{var le=_=>{var g=Ge(),W=s(a(g),2);Se(W,21,()=>e(R),h=>h.id,(h,o)=>{var x=De(),y=a(x),ve=a(y);t(y);var w=s(y,2),pe=a(w);Oe(pe,{get tier(){return e(o).tier},size:9,get color(){return`var(--tier-${e(o).tier??""})`}}),t(w);var $=s(w,2),de=a($,!0);t($);var k=s($,2),ce=a(k);t(k);var q=s(k,2),ue=a(q,!0);t(q);var H=s(q,2);{var me=j=>{var S=Be(),_e=a(S,!0);t(S),M(()=>l(_e,e(o).url)),v(j,S)},fe=d(()=>e(o).url&&e(o).url.startsWith("http"));O(H,j=>{e(fe)&&j(me)})}var J=s(H,2),K=s(a(J)),be=a(K,!0);t(K),t(J),t(x),M(()=>{l(ve,`[${e(o).n??""}]`),l(de,e(o).source),l(ce,`v. ${e(o).vintage??""}`),l(ue,e(o).title),l(be,e(o).docId)}),v(h,x)}),t(W),t(g),v(_,g)};O(C,_=>{e(R).length&&_(le)})}var E=s(C,2),oe=a(E);t(E),t(n),M(()=>{l(ae,`flood-exposure briefing · v0.4.2 · ${e(A)??""}`),l(se,e(i).queryText),l(re,e(i).intent??"briefing"),l(ie,` · ${e(i).specialists??""} specialists
|
| 4 |
+
· ${e(i).attempts??1??""} reconcile${(e(i).attempts??1)===1?"":"s"}
|
| 5 |
+
· grounded by Mellea rejection sampling`),l(ne,e(P)?"Print dialog opened. Re-print anytime.":"Opening print dialog…"),l(oe,`Generated ${e(A)??""} ·
|
| 6 |
+
Riprap is grounded by Mellea rejection sampling over IBM Granite 4.1.
|
| 7 |
+
Numbers without bracketed citations are not present in source documents.`)}),xe("click",z,X),v(r,n)},te=r=>{var n=Le();v(r,n)};O(Y,r=>{e(T)?r(Z):e(i)?r(ee,1):r(te,-1)})}v(Q,B),ke()}ge(["click"]);export{Ve as component,Ue as universal};
|
web/sveltekit/build/_app/immutable/nodes/4.B_VNCC6U.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{s as Y,a as q,f as F,d as dt,c as cn,e as dn,t as rt}from"../chunks/CWw6qgC_.js";import{p as jt,c as g,o as n,s as $,f as we,r as h,t as X,al as ae,a as Pt,aR as ut,a4 as S,a9 as oe,am as ye,a5 as f,ai as un}from"../chunks/BTUA7_xE.js";import{i as V}from"../chunks/Bd-v_9Ud.js";import{p as pn}from"../chunks/BbRPgcjS.js";import{t as zt,B as Bt,T as Ot,a as mn}from"../chunks/BatqQaKj.js";import{e as Pe,i as lt}from"../chunks/25_y8TFd.js";import{p as ze}from"../chunks/CW0zSL4D.js";import{f as fn,C as _n,F as vn,R as at,M as hn}from"../chunks/BZuv-XBZ.js";import"../chunks/DixtWtwq.js";import{b as be,a as it,s as gn}from"../chunks/B0XoTt7U.js";import{b as J,p as yn}from"../chunks/DxQlA7U2.js";const bn=!1,wn=!1,Ur=Object.freeze(Object.defineProperty({__proto__:null,prerender:bn,ssr:wn},Symbol.toStringTag,{value:"Module"})),Dt=[{key:"status",label:"Status",n:"01",aliases:["status"]},{key:"empirical",label:"Empirical evidence",n:"02",tier:"empirical",aliases:["empirical evidence","empirical"]},{key:"modeled",label:"Modeled scenarios",n:"03",tier:"modeled",aliases:["modeled scenarios","modeled"]},{key:"policy",label:"Policy context",n:"04",aliases:["policy context","policy"]}];function Ft(r){const e=r.toLowerCase().replace(/[.:]+\s*$/,"").trim();return Dt.find(t=>t.aliases.includes(e))}const Mt=/(^|\n)\s*(?:\*\*([A-Z][A-Za-z\s/]+?)\.\s*\*\*|#{1,3}\s*(0[1-4])\s*[:\-—.]?\s*([^\n]+))/g;function Rt(r,e,t){return{id:e,n:r,tier:zt(e),source:(t==null?void 0:t.source)??e.split(/[_-]/)[0].toUpperCase(),title:(t==null?void 0:t.title)??e,docId:e,url:(t==null?void 0:t.url)??"",vintage:(t==null?void 0:t.vintage)??"",retrieved:(t==null?void 0:t.retrieved)??""}}const xn=/\[([a-z][a-z0-9_]*(?:\s*,\s*[a-z][a-z0-9_]*)*)\]/gi;function It(r){return r.split(new RegExp("(?<=[.!?])\\s+(?=[A-Z(])","g")).filter(t=>t.trim().length>0)}function Lt(r,e,t){let i=0;const s=[],u=[...r.matchAll(xn)];if(u.length===0)return[{text:r}];for(const o of u){const m=r.slice(i,o.index??0),b=o[1].split(/\s*,\s*/).filter(Boolean);i=(o.index??0)+o[0].length;const l=zt(b[0]);s.push({text:m,tier:l,cite:b[0]});for(const p of b)e[p]||(e[p]=t(p))}if(i<r.length){const o=r.slice(i);o.trim()&&s.push({text:o})}return s}function ct(r,e={}){const t={...e};let i=Object.values(t).reduce((l,p)=>Math.max(l,p.n),0)+1;const s=new Set,u=l=>(e[l]||s.add(l),Rt(i++,l)),o=[],m=[];let b;for(Mt.lastIndex=0;b=Mt.exec(r);)if(b[2]!==void 0){const l=Ft(b[2]);if(!l)continue;m.push({num:l.n,label:l.label,tier:l.tier,start:b.index+b[1].length,bodyStart:b.index+b[0].length})}else if(b[3]!==void 0){const l=b[3],p=(b[4]??"").trim(),A=Dt.find(L=>L.n===l)??Ft(p);m.push({num:l,label:(A==null?void 0:A.label)??p,tier:A==null?void 0:A.tier,titleExtra:A&&p.toLowerCase()!==A.label.toLowerCase()?p:void 0,start:b.index+b[1].length,bodyStart:b.index+b[0].length})}for(let l=0;l<m.length;l++){const p=m[l],A=m[l+1],L=r.slice(p.bodyStart,A?A.start:r.length).trim();if(L){o.push({kind:"head",n:p.num,label:p.label,tier:p.tier,title:p.titleExtra});for(const K of L.split(/\n\s*\n/)){const R=K.replace(/\s+/g," ").trim();if(!R)continue;const ee=It(R),y=[];for(const P of ee)y.push(...Lt(P,t,u)),y.push({text:" "});for(;y.length&&y[y.length-1].text.trim()===""&&!y[y.length-1].tier;)y.pop();y.length&&o.push({kind:"prose",parts:y})}}}if(o.length===0&&r.trim()){o.push({kind:"head",n:"01",label:"Status"});const l=r.replace(/\s+/g," ").trim(),p=It(l),A=[];for(const L of p)A.push(...Lt(L,t,u)),A.push({text:" "});for(;A.length&&A[A.length-1].text.trim()===""&&!A[A.length-1].tier;)A.pop();A.length&&o.push({kind:"prose",parts:A})}return{blocks:o,citations:t,unresolvedDocIds:[...s]}}var An=F('<span class="compare-delta-ctx svelte-rr14x0"> </span>'),Sn=F('<div class="compare-delta-row svelte-rr14x0"><span class="compare-delta-section svelte-rr14x0"> </span> <span class="compare-delta-claim svelte-rr14x0"><!> <strong class="compare-delta-a svelte-rr14x0"> </strong> <span class="compare-delta-vs svelte-rr14x0">vs</span> <strong class="compare-delta-b svelte-rr14x0"> </strong></span></div>'),kn=F('<div class="compare-delta-bar svelte-rr14x0" aria-label="Key differences"><span class="compare-delta-title svelte-rr14x0">Key differences</span> <div class="compare-delta-rows svelte-rr14x0"></div></div>'),Nn=F('<div class="compare-divider svelte-rr14x0" role="separator" aria-hidden="true"></div>'),$n=F('<div class="compare-col svelte-rr14x0"><h2 class="compare-address-header address-header svelte-rr14x0"> </h2> <!></div> <!>',1),Cn=F('<div class="compare-layout svelte-rr14x0"><!> <div class="compare-cols svelte-rr14x0"></div></div>');function qn(r,e){jt(e,!0);let t=ze(e,"structuredA",19,()=>({})),i=ze(e,"structuredB",19,()=>({}));function s(y){return y.split(/\n\s*---\s*\n/,2).map((O,z)=>{var E,U;const I=/^##\s+PLACE\s+[AB]:\s+(.+?)(\n|$)/m.exec(O.trim()),H=((E=I==null?void 0:I[1])==null?void 0:E.trim())??((U=e.targets[z])==null?void 0:U.address)??`Place ${String.fromCharCode(65+z)}`,D=O.replace(/^##\s+PLACE\s+[AB]:\s+.+(\n|$)/m,"").trim();return{address:H,md:D}})}const u=ae(()=>s(e.paragraph)),o=ae(()=>{var y;return ct(((y=n(u)[0])==null?void 0:y.md)??"",e.citations)}),m=ae(()=>{var y;return ct(((y=n(u)[1])==null?void 0:y.md)??"",e.citations)}),b=ae(()=>({...e.citations,...n(o).citations,...n(m).citations}));function l(y,P,O){const z=y[P];if(!z||typeof z!="object")return;const I=z[O];return typeof I=="number"?I:void 0}function p(y,P,O){const z=y[P];if(!z||typeof z!="object")return;const I=z[O];return typeof I=="boolean"?I:void 0}const A=ae(()=>{const y=[],P=p(t(),"sandy_inundation","inside"),O=p(i(),"sandy_inundation","inside");P!==void 0&&O!==void 0&&P!==O&&y.push({label:"Sandy zone",ctx:"",aVal:P?"inside":"outside",bVal:O?"inside":"outside"});const z=l(t(),"nyc311","n"),I=l(i(),"nyc311","n");z!==void 0&&I!==void 0&&z!==I&&y.push({label:"311 complaints",ctx:"5 y",aVal:String(z),bVal:String(I)});const H=l(t(),"microtopo_lidar","elev_m"),D=l(i(),"microtopo_lidar","elev_m");H!==void 0&&D!==void 0&&Math.abs(H-D)>.5&&y.push({label:"Elevation",ctx:"",aVal:`${H.toFixed(1)} m`,bVal:`${D.toFixed(1)} m`});const E=l(t(),"floodnet","n_events_3y"),U=l(i(),"floodnet","n_events_3y");E!==void 0&&U!==void 0&&E!==U&&y.push({label:"Sensor events",ctx:"last 3 y",aVal:String(E),bVal:String(U)});const ne=l(t(),"ida_hwm_2021","max_height_above_gnd_ft"),Z=l(i(),"ida_hwm_2021","max_height_above_gnd_ft");return ne!==void 0&&Z!==void 0&&Math.abs(ne-Z)>.1&&y.push({label:"Ida 2021 HWM",ctx:"ft above gnd",aVal:`${ne.toFixed(2)} ft`,bVal:`${Z.toFixed(2)} ft`}),y.slice(0,4)});var L=Cn(),K=g(L);{var R=y=>{var P=kn(),O=$(g(P),2);Pe(O,21,()=>n(A),lt,(z,I)=>{var H=Sn(),D=g(H),E=g(D,!0);h(D);var U=$(D,2),ne=g(U);{var Z=he=>{var ge=An(),Se=g(ge);h(ge),X(()=>Y(Se,`${n(I).ctx??""}:`)),q(he,ge)};V(ne,he=>{n(I).ctx&&he(Z)})}var ce=$(ne,2),Ae=g(ce,!0);h(ce);var me=$(ce,4),Ue=g(me,!0);h(me),h(U),h(H),X(()=>{Y(E,n(I).label),Y(Ae,n(I).aVal),Y(Ue,n(I).bVal)}),q(z,H)}),h(O),h(P),q(y,P)};V(K,y=>{n(A).length>0&&y(R)})}var ee=$(K,2);Pe(ee,21,()=>n(u),lt,(y,P,O)=>{var z=$n(),I=we(z),H=g(I),D=g(H,!0);h(H);var E=$(H,2);{let Z=ae(()=>O===0?n(o).blocks:n(m).blocks);Bt(E,{get blocks(){return n(Z)},get citations(){return n(b)},streaming:!1})}h(I);var U=$(I,2);{var ne=Z=>{var ce=Nn();q(Z,ce)};V(U,Z=>{O===0&&Z(ne)})}X(()=>Y(D,n(u)[O].address)),q(y,z)}),h(ee),h(L),q(r,L),Pt()}dt(["click"]);dt(["click"]);var Tn=F('<div class="skeleton-section"><div class="skeleton-head"><span class="skeleton-num"> </span> <span class="skeleton-label"> </span> <span class="skeleton-spinner" aria-hidden="true">▍</span></div> <span class="skeleton-pulse"></span> <span class="skeleton-pulse"></span> <span class="skeleton-pulse"></span></div>'),Fn=F('<div class="skeleton-brief" role="status" aria-live="polite" aria-label="Loading briefing — geocode complete, dispatching specialists"><div class="skeleton-status"><span class="skeleton-pulse"></span> <span class="skeleton-pulse skeleton-pulse-meta"></span></div> <!></div>');function Mn(r){const e=[{n:"01",label:"Status"},{n:"02",label:"Empirical evidence"},{n:"03",label:"Modeled scenarios"},{n:"04",label:"Policy context"}];var t=Fn(),i=g(t),s=g(i);be(s,"",{},{width:"62%"});var u=$(s,2);be(u,"",{},{width:"40%"}),h(i);var o=$(i,2);Pe(o,1,()=>e,m=>m.n,(m,b)=>{var l=Tn(),p=g(l),A=g(p),L=g(A,!0);h(A);var K=$(A,2),R=g(K,!0);h(K),ut(2),h(p);var ee=$(p,2);be(ee,"",{},{width:"92%"});var y=$(ee,2);be(y,"",{},{width:"78%"});var P=$(y,2);be(P,"",{},{width:"85%"}),h(l),X(()=>{Y(L,n(b).n),Y(R,n(b).label)}),q(m,l)}),h(t),q(r,t)}var In=F('<div class="reroll-banner" role="status" aria-live="polite"><!> <div class="reroll-body"><span class="reroll-head">Regenerating to satisfy citation grounding</span> <span class="reroll-sub"> </span></div> <span class="reroll-spinner" aria-hidden="true">↻</span></div>');function Ln(r,e){let t=ze(e,"attempt",3,2),i=ze(e,"max",3,3);var s=In(),u=g(s);Ot(u,{tier:"modeled",size:11,color:"var(--tier-modeled)"});var o=$(u,2),m=$(g(o),2),b=g(m);h(m),h(o),ut(2),h(s),X(()=>Y(b,`Mellea reconciler · attempt ${t()??""} of ${i()??""} · previous draft dimmed below`)),q(r,s)}var En=F("<a> </a>"),jn=F('<button type="button"> </button>'),Pn=F('<article role="alert" aria-live="assertive"><header class="error-card-head"><!> <span class="error-card-eyebrow"> </span></header> <h3 class="error-card-headline"> </h3> <p class="error-card-body"> </p> <div class="error-card-actions"></div> <footer class="error-card-foot"><span class="section-label">Trust signals · still on</span> <span class="error-card-foot-copy">All foundation models Apache-2.0 · No commercial APIs at runtime</span></footer></article>');function zn(r,e){const t={geocoder:{eyebrow:"Address not resolved",headline:"We couldn't resolve that to a NYC address.",body:`Try a more specific street address — for example, "80 Pioneer Street, Brooklyn." Riprap covers the five boroughs only; international addresses, NJ addresses, and points outside NYC aren't supported.`,tier:"proxy",defaultActions:["Use a sample query","Edit query"]},"all-silent":{eyebrow:"Outside evidence coverage",headline:"No specialists found evidence at this point.",body:"The address resolved, but every flood-evidence specialist returned silent. This is rare and usually means parkland, water, or a point with no nearby 311, no FloodNet sensor, and no Sandy overlap. Try a nearby street address or expand to neighborhood-mode.",tier:"proxy",defaultActions:["Try nearby address","Switch to neighborhood-mode"]},grounding:{eyebrow:"Grounding failure",headline:"Briefing prose couldn't be composed within citation constraints.",body:"Mellea rejected all reroll attempts. The underlying evidence is fine — only the prose composition failed. Download the structured evidence below, or contact support.",tier:"modeled",defaultActions:["Download evidence (JSON)","Contact support","Try again"]},backend:{eyebrow:"Backend unavailable",headline:"All routing targets exhausted.",body:"LiteLLM tried Local Ollama → HF Space T4 → AMD MI300X and didn't reach a healthy backend. This usually clears within 5 minutes during a deploy window. The hardware-pill in the header is currently red.",tier:"proxy",defaultActions:["Retry now","Switch backend"]}};let i=ae(()=>t[e.state]),s=ae(()=>e.actions??n(i).defaultActions.map(ee=>({label:ee})));var u=Pn(),o=g(u),m=g(o);Ot(m,{get tier(){return n(i).tier},size:11,get color(){return`var(--tier-${n(i).tier??""})`}});var b=$(m,2),l=g(b,!0);h(b),h(o);var p=$(o,2),A=g(p,!0);h(p);var L=$(p,2),K=g(L,!0);h(L);var R=$(L,2);Pe(R,21,()=>n(s),lt,(ee,y,P)=>{var O=cn(),z=we(O);{var I=D=>{var E=En();it(E,1,"error-card-action",null,{},{"is-primary":P===0});var U=g(E,!0);h(E),X(()=>{gn(E,"href",n(y).href),Y(U,n(y).label)}),q(D,E)},H=D=>{var E=jn();it(E,1,"error-card-action",null,{},{"is-primary":P===0});var U=g(E,!0);h(E),X(()=>Y(U,n(y).label)),dn("click",E,function(...ne){var Z;(Z=n(y).onClick)==null||Z.apply(this,ne)}),q(D,E)};V(z,D=>{n(y).href?D(I):D(H,-1)})}q(ee,O)}),h(R),ut(2),h(u),X(()=>{it(u,1,`error-card error-card-${e.state??""}`),Y(l,e.eyebrowOverride??n(i).eyebrow),Y(A,e.headlineOverride??n(i).headline),Y(K,e.bodyOverride??n(i).body)}),q(r,u)}dt(["click"]);const le="2026-05";function Bn(r){return r==="fan"||r==="merge"?"fired":r==="silent"?"silent_by_design":r==="error"?"errored":"fired"}function Yt(r){return[r,...(r.children??[]).flatMap(Yt)]}function On(r){const e=r.toLowerCase();return e==="sandy_inundation"||e==="sandy"||e==="dep_stormwater"||e==="dep"||e==="ida_hwm_2021"||e==="ida_hwm"||e==="prithvi_eo_v2"||e==="prithvi_water"||e==="microtopo_lidar"||e==="microtopo"?"cornerstone":e==="mta_entrance_exposure"||e==="mta_entrances"||e==="nycha_development_exposure"||e==="nycha_developments"||e==="doe_school_exposure"||e==="doe_schools"||e==="doh_hospital_exposure"||e==="doh_hospitals"||e==="terramind_synthesis"||e==="terramind"||e==="terramind_buildings"||e==="eo_chip_fetch"?"keystone":e==="floodnet"||e==="nyc311"||e==="nws_obs"||e==="noaa_tides"||e==="prithvi_eo_live"||e==="prithvi_live"||e==="terramind_lulc"?"touchstone":e==="nws_alerts"||e==="ttm_forecast"||e==="ttm_311_forecast"||e==="floodnet_forecast"||e==="ttm_battery_surge"?"lodestone":e.startsWith("reconcile")||e.startsWith("mellea")||e==="rag_granite_embedding"||e==="gliner_extract"?"capstone":null}function Dn(r){const e={cornerstone:[],keystone:[],touchstone:[],lodestone:[],capstone:[]};if(r)for(const t of Yt(r)){const i=On(t.name);i&&e[i].push({id:t.id||t.name,name:t.name,status:Bn(t.status),tier:t.tier,ms:t.ms,note:t.note??t.error??void 0})}return Object.keys(e).map(t=>({key:t,members:fn(t,e[t])}))}function N(r){return typeof r=="number"&&Number.isFinite(r)?r:null}function j(r){return typeof r=="string"?r:null}function B(r){return r&&typeof r=="object"&&!Array.isArray(r)?r:null}function Rn(r,e){return r.sandy!==!0?null:{id:"fsm-sandy",stone:"cornerstone",tier:"empirical",variant:"headline",source:"NYC OEM",agency:"NYC OpenData 5xsi-dfpx · Sandy 2012 inundation",vintage:"2012-10-29",title:"Hurricane Sandy 2012 inundation",headline:"Inside zone",subhead:(e&&j(e.address))??"address inside the empirical 2012 extent",body:"Address sits within the empirical Hurricane Sandy 2012 inundation extent. This is a historical fact, not a model prediction.",docId:"sandy",citeId:"sandy",mapLayer:"sandy"}}function Yn(r){const e=B(r.dep);if(!e)return null;const t=[];for(const[i,s]of Object.entries(e)){const u=B(s);if(!u)continue;const o=N(u.depth_class)??0;o<=0||t.push([i.replace("dep_",""),j(u.depth_label)??"—",`class ${o}`])}return t.length?{id:"fsm-dep",stone:"cornerstone",tier:"modeled",variant:"tabular",source:"NYC DEP",agency:"NYC Department of Environmental Protection · Stormwater Flood Maps",vintage:"2021",title:"Stormwater flood scenarios at this address",columns:["scenario","depth label","class"],rows:t,sub:`${t.length} scenario${t.length===1?"":"s"} place this lot in modeled flooding`,docId:"dep_stormwater",citeId:"dep",mapLayer:"stormwater"}:null}function Hn(r){const e=B(r.ida_hwm);if(!e)return null;const t=N(e.n_within_radius);if(!t||t<=0)return null;const i=[];return i.push(["count",`${t}`,`${N(e.radius_m)??800} m radius`]),N(e.max_height_above_gnd_ft)!=null&&i.push(["max above gnd",`${e.max_height_above_gnd_ft} ft`,"—"]),N(e.nearest_dist_m)!=null&&i.push(["nearest",j(e.nearest_site)??"HWM",`${e.nearest_dist_m} m`]),{id:"fsm-ida-hwm",stone:"cornerstone",tier:"empirical",variant:"tabular",source:"USGS",agency:"USGS STN Hurricane Ida 2021 high-water marks (Event 312)",vintage:"2021-09",title:"Hurricane Ida 2021 high-water marks nearby",columns:["field","value","context"],rows:i,docId:"ida_hwm",citeId:"ida_hwm",mapLayer:"hwm"}}function Un(r){const e=B(r.prithvi_water);if(!e)return null;const t=N(e.nearest_distance_m);return t==null?null:{id:"fsm-prithvi-water",stone:"cornerstone",tier:"modeled",variant:"raster",source:"Prithvi-EO 2.0",agency:"IBM/NASA Prithvi-EO 2.0 · baked Hurricane Ida 2021 polygons",vintage:"2021-09-02",title:"Hurricane Ida 2021 — satellite-attributable inundation",rasterKind:"prithvi",headline:e.inside_water_polygon?"Inside polygon":`${t} m away`,subhead:"pre/post HLS Sentinel-2 segmentation",sub:`${N(e.n_polygons_within_500m)??0} distinct polygons within 500 m`,docId:"prithvi_water",citeId:"prithvi_water",mapLayer:"prithvi"}}function Gn(r){const e=B(r.microtopo);if(!e)return null;const t=N(e.point_elev_m);if(t==null)return null;const i=[{value:`${t.toFixed(1)} m`,label:"elevation"}];return N(e.hand_m)!=null&&i.push({value:`${e.hand_m.toFixed(1)} m`,label:"HAND"}),N(e.twi)!=null&&i.push({value:`${e.twi.toFixed(1)}`,label:"TWI"}),N(e.rel_elev_pct_200m)!=null&&i.push({value:`${e.rel_elev_pct_200m}%`,label:"pct lower 200m"}),{id:"fsm-microtopo",stone:"cornerstone",tier:"proxy",variant:"scalars",source:"USGS 3DEP",agency:"USGS 3DEP DEM (LiDAR-derived) + whitebox-workflows hydrology",vintage:"2018",title:"Microtopography at this point",scalars:i,sub:"Lower percentile = topographic low point; runoff routes here.",docId:"microtopo",citeId:"microtopo"}}function Wn(r){const e=[],t=B(r.mta_entrances);if(t!=null&&t.available&&Array.isArray(t.entrances))for(const o of t.entrances.slice(0,4))e.push({reg:"MTA",tier:"empirical",label:j(o.station_name)??j(o.entrance_id)??"entrance",detail:`${N(o.distance_m)??"—"} m · ${j(o.daytime_routes)??""}`.trim(),sourceId:j(o.station_id)??"MTA",note:null});else t&&t.available===!1&&e.push({reg:"MTA",tier:"empirical",label:null,detail:null,sourceId:null,note:"no subway entrances within 1.0 mi (silent)"});const i=B(r.nycha_developments);if(i!=null&&i.available&&Array.isArray(i.developments))for(const o of i.developments.slice(0,3))e.push({reg:"NYCHA",tier:"empirical",label:j(o.development)??"development",detail:`${N(o.distance_m)??"—"} m · ${j(o.borough)??""}`.trim(),sourceId:j(o.tds_num)??null,note:null});else i&&i.available===!1&&e.push({reg:"NYCHA",tier:"empirical",label:null,detail:null,sourceId:null,note:"no NYCHA developments within 1.0 mi (silent)"});const s=B(r.doe_schools);if(s!=null&&s.available&&Array.isArray(s.schools))for(const o of s.schools.slice(0,3))e.push({reg:"DOE",tier:"empirical",label:j(o.loc_name)??"school",detail:`${N(o.distance_m)??"—"} m · ${j(o.borough)??""}`.trim(),sourceId:j(o.loc_code)??null,note:null});else s&&s.available===!1&&e.push({reg:"DOE",tier:"empirical",label:null,detail:null,sourceId:null,note:"no schools within 1.0 mi (silent)"});const u=B(r.doh_hospitals);if(u!=null&&u.available&&Array.isArray(u.hospitals))for(const o of u.hospitals.slice(0,3))e.push({reg:"DOH",tier:"empirical",label:j(o.facility_name)??"hospital",detail:`${N(o.distance_m)??"—"} m · ${j(o.borough)??""}`.trim(),sourceId:j(o.fac_id)??null,note:null});else u&&u.available===!1&&e.push({reg:"DOH",tier:"empirical",label:null,detail:null,sourceId:null,note:"no acute-care hospital within 1.0 mi (silent)"});return e.length?{id:"fsm-registers",stone:"keystone",tier:"empirical",variant:"register",source:"NYC OpenData",agency:"NYC OpenData · multi-agency join",vintage:le,title:"Nearby exposed assets",registers:e,sub:`${e.filter(o=>o.label).length} of ${e.length} registers fired · joined within 1.0 mi`,docId:"registers",citeId:"registers",mapLayer:"registers"}:null}function Vn(r){const e=B(r.terramind_buildings);return e!=null&&e.ok?{id:"fsm-tm-buildings",stone:"keystone",tier:"modeled",variant:"raster-pred",source:"TerraMind-NYC",agency:"msradam/TerraMind-NYC-Adapters · Buildings LoRA",vintage:"2026",title:"NYC building footprints — TerraMind LoRA",rasterKind:"buildings",headline:`${N(e.pct_buildings)??0}%`,subhead:"building-footprint coverage in chip",sub:`${N(e.n_building_components)??0} distinct components · test mIoU 0.5511`,illustrative:!0,docId:"tm_buildings",citeId:"tm_buildings",mapLayer:"buildings"}:null}function Kn(r){const e=B(r.floodnet);if(!e||(N(e.n_sensors)??0)<=0)return null;const t=N(e.n_flood_events_3y)??0;return{id:"fsm-floodnet",stone:"touchstone",tier:"empirical",variant:"spark",source:"FloodNet",agency:"FloodNet NYC ultrasonic depth sensor network",vintage:"2026",title:"FloodNet sensors near this address",headline:`${t} events`,subhead:`${N(e.n_sensors)??0} sensors · last 3 y`,spark:Array.from({length:24},(i,s)=>Math.max(0,Math.round(t/24*1.4*Math.exp(-Math.pow((s-14)/4,2))+t/24))),sparkSub:"Above-curb depth events ≥ 2 cm. Synthetic monthly distribution; raw deployment-id history is in the audit panel.",docId:"floodnet",citeId:"floodnet",mapLayer:"floodnet"}}function Zn(r){var m;const e=B(r.nyc311);if(!e)return null;const t=N(e.n)??0;if(t<=0)return null;const i=B(e.by_year),s=B(e.by_descriptor),u=i?Object.values(i).map(b=>N(b)??0):Array.from({length:12},()=>Math.round(t/12)),o=s?(m=Object.entries(s).sort((b,l)=>(N(l[1])??0)-(N(b[1])??0))[0])==null?void 0:m[0]:null;return{id:"fsm-311",stone:"touchstone",tier:"proxy",variant:"histogram",source:"NYC 311",agency:"NYC 311 service requests (Socrata erm2-nwe9)",vintage:le,title:"Recent 311 flood complaints",headline:`${t} calls`,subhead:o?`top descriptor: ${o}`:"all flood-related descriptors",histogram:u,sparkSub:`Within ${N(e.radius_m)??200} m · ${N(e.years)??5} y window. Filtered to flood-relevant descriptors.`,docId:"nyc311",citeId:"nyc311",mapLayer:"complaints"}}function Jn(r){var i;const e=B(r.nws_obs);if(!e||e.error||e.station_id==null)return null;const t=[];return N(e.precip_last_hour_mm)!=null&&t.push({value:`${e.precip_last_hour_mm} mm`,label:"precip · 1h"}),N(e.precip_last_6h_mm)!=null&&t.push({value:`${e.precip_last_6h_mm} mm`,label:"precip · 6h"}),t.length?{id:"fsm-nws-obs",stone:"touchstone",tier:"empirical",variant:"scalars",source:"NWS",agency:`NWS ASOS station ${j(e.station_id)??"?"}`,vintage:((i=j(e.obs_time))==null?void 0:i.slice(0,10))??le,title:"Recent precipitation",scalars:t,sub:`Nearest hourly METAR: ${j(e.station_name)??"?"} (${N(e.distance_km)??"?"} km).`,docId:"nws_obs",citeId:"nws_obs",mapLayer:"nws"}:null}function Xn(r){var i;const e=B(r.noaa_tides);if(!e||e.error||N(e.observed_ft_mllw)==null)return null;const t=[{value:`${e.observed_ft_mllw} ft`,label:"observed (MLLW)"}];return N(e.predicted_ft_mllw)!=null&&t.push({value:`${e.predicted_ft_mllw} ft`,label:"predicted"}),N(e.residual_ft)!=null&&t.push({value:`${e.residual_ft} ft`,label:"residual"}),{id:"fsm-noaa",stone:"touchstone",tier:"empirical",variant:"scalars",source:"NOAA CO-OPS",agency:`NOAA tide gauge ${j(e.station_name)??j(e.station_id)??"?"}`,vintage:((i=j(e.obs_time))==null?void 0:i.slice(0,10))??le,title:"Live water level (nearest tide gauge)",scalars:t,sub:"Residual = observed − astronomical tide; positive residual is wind / surge component.",docId:"noaa_tides",citeId:"noaa_tides",mapLayer:"noaa"}}function Qn(r){var i;const e=B(r.prithvi_live);if(!(e!=null&&e.ok))return null;const t=(i=j(e.item_datetime))==null?void 0:i.slice(0,10);return{id:"fsm-prithvi-live",stone:"touchstone",tier:"modeled",variant:"raster-pred",source:"Prithvi-NYC-Pluvial",agency:"NASA-IBM Prithvi v2 · NYC fine-tune",vintage:t?`${t} · Sentinel-2`:"Sentinel-2",title:"Pluvial flood prediction · Prithvi-NYC-Pluvial",rasterKind:"prithvi",headline:`${N(e.pct_water_within_500m)??0}% flooded`,subhead:`water within 500 m · cloud ${N(e.cloud_cover)??"?"}%`,sub:"Test flood IoU 0.5979 on held-out NYC chips. Model interpretation, not a measurement.",illustrative:!0,docId:"prithvi_live",citeId:"prithvi_live",mapLayer:"prithvi-pluvial"}}const er={urban:"#C66",water:"#5B7FB4",vegetation:"#5B8A4A",barren:"#A89A78",wetland:"#D9C75A"};function tr(r){const e=B(r.terramind_lulc);if(!(e!=null&&e.ok))return null;const t=B(e.class_fractions)??{},i={urban:0,water:0,vegetation:0,barren:0,wetland:0};for(const[u,o]of Object.entries(t)){const m=u.toLowerCase();m.includes("urban")||m.includes("built")||m.includes("impervious")?i.urban+=o:m.includes("water")?i.water+=o:m.includes("tree")||m.includes("vegetation")||m.includes("crop")||m.includes("grass")?i.vegetation+=o:m.includes("bare")||m.includes("barren")||m.includes("soil")?i.barren+=o:m.includes("wet")||m.includes("marsh")?i.wetland+=o:i.barren+=o}const s=Object.entries(i).filter(([,u])=>u>0).map(([u,o])=>({k:u,pct:Math.round(o),color:er[u]}));return{id:"fsm-tm-lulc",stone:"touchstone",tier:"synthetic",variant:"lulc",source:"TerraMind v1.2",agency:"IBM TerraMind v1.2 · Sentinel-2 inputs",vintage:"Sentinel-2",title:"Land use / land cover · TerraMind v1.2",rasterKind:"lulc",classMix:s.length?s:void 0,sub:"Synthetic prior. LULC palette is a layer convention, not a tier signal.",illustrative:!0,docId:"tm_lulc",citeId:"tm_lulc",mapLayer:"terramind-lulc"}}function nr(r){const e=B(r.ttm_forecast);if(!(e!=null&&e.available)||!e.interesting)return null;const t=N(e.forecast_peak_ft),i=N(e.forecast_peak_minutes_ahead);return t==null||i==null?null:{id:"fsm-ttm-fc",stone:"lodestone",tier:"modeled",variant:"timeseries",source:"Granite TTM r2 (zero-shot)",agency:"IBM Granite-TimeSeries · regional",vintage:le,title:"Storm surge nowcast at The Battery — 9.6 h horizon (regional)",timeseries:{hours:96,peak:{x:38,y:47},peakLabel:`${t} ft @ +${Math.round(i/60)}h`},headline:`${t} ft`,subhead:"peak surge residual · 9.6h horizon · 6-min cadence",sub:"Regional disclosure. Distinct from the fine-tuned Battery surge nowcast.",spatialNote:"regional · Battery, not point-of-query",docId:"ttm_forecast",citeId:"ttm_forecast"}}function rr(r){const e=B(r.ttm_battery_surge);if(!(e!=null&&e.available)||!e.interesting)return null;const t=N(e.forecast_peak_m),i=N(e.forecast_peak_hours_ahead);return t==null||i==null?null:{id:"fsm-ttm-batt",stone:"lodestone",tier:"modeled",variant:"timeseries-ft",source:"msradam/Granite-TTM-r2-Battery-Surge",agency:"Granite TTM r2 · NYC-specialized fine-tune",vintage:le,title:"Storm surge nowcast at The Battery — 96 h horizon (NYC-specialized fine-tune)",timeseries:{hours:96,peak:{x:i,y:Math.round(t*100)},peakLabel:`${(t*100).toFixed(0)} cm @ +${i}h`},headline:`${(t*100).toFixed(0)} cm`,subhead:"peak surge · 96h horizon · hourly cadence",sub:"Fine-tuned on NYC tide-gauge history. Hourly cadence; applies city-wide via NOAA station 8518750.",spatialNote:"regional · The Battery, not point-of-query",docId:"ttm_battery",citeId:"ttm_battery",hfModelCard:"huggingface.co/msradam/Granite-TTM-r2-Battery-Surge",rmse:"0.157 m",skillVsPersistence:"−35% vs persistence",hardwareBadge:"MI300X"}}function ar(r){const e=B(r.nws_alerts);if(!e)return null;const t=N(e.n_active)??0;if(t<=0)return null;const i=Array.isArray(e.alerts)?e.alerts:[];return{id:"fsm-nws-alerts",stone:"lodestone",tier:"modeled",variant:"tabular",source:"NWS",agency:"NWS Public Alerts API · flood-relevant filter",vintage:le,title:`${t} active flood-relevant alert${t===1?"":"s"}`,columns:["event","severity","expires"],rows:i.slice(0,4).map(s=>[j(s.event)??"?",j(s.severity)??"?",(j(s.expires)??"").slice(0,16)]),sub:"Live NWS feed. If a FLOOD or FLASH FLOOD WARNING is in this list, foreground it.",docId:"nws_alerts",citeId:"nws_alerts"}}function ir(r,e){var L;const t=r.mellea??{},i=Array.isArray(t.requirements_passed)?t.requirements_passed:Array.isArray(t.passed)?t.passed:[],s=Array.isArray(t.requirements_failed)?t.requirements_failed:Array.isArray(t.failed)?t.failed:[],u=i.length,o=s.length,m=(typeof t.requirements_total=="number"?t.requirements_total:u+o)||4,b=typeof t.n_attempts=="number"?t.n_attempts:typeof t.attempts=="number"?t.attempts:0,p=(typeof t.rerolls=="number"?t.rerolls:null)??Math.max(0,b-1),A=((L=r.citations)==null?void 0:L.length)??0;return{id:"fsm-capstone-meta",stone:"capstone",tier:"modeled",variant:"meta",source:"Mellea",agency:"Capstone synthesis · Granite 4.1 + Mellea grounding check",vintage:le,title:"Briefing reconciliation",metaRows:[{k:"mellea reroll",v:`${p} reroll${p===1?"":"s"}`},{k:"grounding checks",v:`${u}/${m} passed`},{k:"citations resolved",v:`${A}`},{k:"wall-clock",v:e!=null?`${e.toFixed(1)} s`:"—"}],sub:"Capstone produces prose, not cards. This meta-card is the integrity-narration UI for the entire pipeline.",docId:"capstone"}}function Et(r,e,t,i=!0){const s=r??{},u=B(s.geocode);return{cards:[Rn(s,u),Yn(s),Hn(s),Un(s),Gn(s),Wn(s),Vn(s),Kn(s),Zn(s),Jn(s),Xn(s),Qn(s),tr(s),ar(s),nr(s),rr(s),i?ir(r??{},t):null].filter(m=>m!=null),stones:Dn(e),wallSeconds:t}}function sr(r,e,t,i){const u={sandy_inundation:"sandy",dep_stormwater:"dep",floodnet:"floodnet",nyc311:"nyc311",noaa_tides:"noaa_tides",nws_alerts:"nws_alerts",nws_obs:"nws_obs",ttm_forecast:"ttm_forecast",ttm_311_forecast:"ttm_311_forecast",ttm_battery_surge:"ttm_battery_surge",floodnet_forecast:"floodnet_forecast",ida_hwm_2021:"ida_hwm",prithvi_eo_v2:"prithvi_water",prithvi_eo_live:"prithvi_live",microtopo_lidar:"microtopo",mta_entrance_exposure:"mta_entrances",nycha_development_exposure:"nycha_developments",doe_school_exposure:"doe_schools",doh_hospital_exposure:"doh_hospitals",terramind_synthesis:"terramind",terramind_lulc:"terramind_lulc",terramind_buildings:"terramind_buildings",eo_chip_fetch:"eo_chip",geocode:"geocode"}[e];if(!u)return[];if(e==="sandy_inundation"){const o=t;r[u]=i&&(o==null?void 0:o.inside)===!0?!0:i?!1:null}else if(e==="dep_stormwater"){const o=t??{},m={};for(const[b,l]of Object.entries(o)){const p=typeof l=="string"?l:"";p&&(m[b]={depth_class:1,depth_label:p})}r[u]=Object.keys(m).length?m:null}else i&&t!=null?r[u]=t:r[u]=null;return[u]}const Be={subway:"MTA · USGS · FEMA · NYC OEM · NYC DEP",nycha:"NYC HA · USGS · NYC OEM · NYC DEP",school:"NYC DOE · USGS · NYC OEM · NYC DEP",hospital:"NYS DOH · USGS · NYC OEM · NYC DEP"},Oe={subway:"subway entrances",nycha:"NYCHA developments",school:"public schools",hospital:"hospitals"};function De(r){return!r||!Number.isFinite(r)?"—":`${Math.round(r)}m`}function Re(r){return r==null||!Number.isFinite(r)?"—":`${(r*3.28084).toFixed(1)} ft`}function Ye(r,e){return typeof e=="number"?e>=.5?`Inundated 2012 (${Math.round(e*100)}%)`:e>0?`Edge (${Math.round(e*100)}%)`:"—":r?"Inundated 2012":"—"}function He(r,e,t){return typeof t=="number"?t>=.5?`≥${Math.round(t*100)}% in scenario`:t>0?`${Math.round(t*100)}% edge`:"minimal":r&&r.length?r:e&&e>0?`class ${e}`:"minimal"}function or(r){return r?/elevator|easement|stair.*ramp/i.test(r):!1}function lr(r){if(!r.available)return null;const t=(r.entrances??[]).map(i=>{const s=or(i.entrance_type);return{name:`${i.station_name??"?"}${i.daytime_routes?` (${String(i.daytime_routes).split(/\s+/).slice(0,3).join("/")})`:""}`,elev:Re(i.elev_m),ada:s,fema:"Zone X",sandy:Ye(i.inside_sandy_2012),dep:He(i.dep_extreme_2080_label,i.dep_extreme_2080_class),asset:"subway",primaryTier:i.inside_sandy_2012?"empirical":"modeled"}});return{type:Oe.subway,radius:De(r.radius_m),count:r.n_entrances??t.length,rows:t,sourceLabel:Be.subway}}function cr(r){if(!r.available)return null;const t=(r.developments??[]).map(i=>{const s=i.pct_inside_sandy_2012,u=i.pct_in_dep_extreme_2080;return{name:`${i.development??"?"}${i.borough?` · ${i.borough}`:""}`,elev:Re(i.rep_elevation_m),ada:!1,fema:"—",sandy:Ye(void 0,s),dep:He(void 0,void 0,u),asset:"nycha",primaryTier:s&&s>0?"empirical":"modeled"}});return{type:Oe.nycha,radius:De(r.radius_m),count:r.n_developments??t.length,rows:t,sourceLabel:Be.nycha}}function dr(r){if(!r.available)return null;const t=(r.schools??[]).map(i=>({name:`${i.school_name??i.name??"?"}${i.borough?` · ${i.borough}`:""}`,elev:Re(i.elev_m),ada:!1,fema:"—",sandy:Ye(i.inside_sandy_2012),dep:He(i.dep_extreme_2080_label,i.dep_extreme_2080_class),asset:"school",primaryTier:i.inside_sandy_2012?"empirical":"modeled"}));return{type:Oe.school,radius:De(r.radius_m),count:r.n_schools??t.length,rows:t,sourceLabel:Be.school}}function ur(r){if(!r.available)return null;const t=(r.hospitals??[]).map(i=>({name:`${i.facility_name??i.name??"?"}${i.borough?` · ${i.borough}`:""}`,elev:Re(i.elev_m),ada:!0,fema:"—",sandy:Ye(i.inside_sandy_2012),dep:He(i.dep_extreme_2080_label,i.dep_extreme_2080_class),asset:"hospital",primaryTier:i.inside_sandy_2012?"empirical":"modeled"}));return{type:Oe.hospital,radius:De(r.radius_m),count:r.n_hospitals??t.length,rows:t,sourceLabel:Be.hospital}}function pr(r){if(!r)return[];const e=[],t=lr(r.mta_entrances??{});t&&t.rows.length&&e.push(t);const i=cr(r.nycha_developments??{});i&&i.rows.length&&e.push(i);const s=dr(r.doe_schools??{});s&&s.rows.length&&e.push(s);const u=ur(r.doh_hospitals??{});return u&&u.rows.length&&e.push(u),e}function mr(r,e){const t=`/api/agent/stream?q=${encodeURIComponent(r)}`,i=new EventSource(t);let s="",u;const o=/([.?!])(\s|$)/;function m(l=!1){var A,L;let p;for(;p=o.exec(s);){const K=p.index+p[1].length+(p[2]?p[2].length:0),R=s.slice(0,K).trim();s=s.slice(K),R&&((A=e.onSentence)==null||A.call(e,R,u))}l&&s.trim()&&((L=e.onSentence)==null||L.call(e,s.trim(),u),s="")}function b(l,p){i.addEventListener(l,A=>{try{p(JSON.parse(A.data))}catch{}})}return b("hello",l=>{var p;return(p=e.onHello)==null?void 0:p.call(e,l.query)}),b("plan_token",l=>{var p;return(p=e.onPlanToken)==null?void 0:p.call(e,l.delta)}),b("plan",l=>{var p;return(p=e.onPlan)==null?void 0:p.call(e,l)}),b("step",l=>{var p;return(p=e.onStep)==null?void 0:p.call(e,l)}),b("token",l=>{var p,A;l.attempt!==u&&(u=l.attempt,s="",(p=e.onAttemptStart)==null||p.call(e,l.attempt??1)),(A=e.onToken)==null||A.call(e,l.delta,l.attempt),s+=l.delta,m(!1)}),b("mellea_attempt",l=>{var p;return(p=e.onMelleaAttempt)==null?void 0:p.call(e,l)}),b("final",l=>{var p;m(!0),(p=e.onFinal)==null||p.call(e,l)}),b("error",l=>{var p;return(p=e.onError)==null?void 0:p.call(e,l.err)}),i.addEventListener("done",()=>{var l;m(!0),(l=e.onDone)==null||l.call(e),i.close()}),i.addEventListener("error",()=>{var l;m(!0),(l=e.onError)==null||l.call(e,"SSE connection error"),i.close()}),{close:()=>i.close()}}const xe={type:"FeatureCollection",features:[]};async function ve(r){try{const e=await fetch(r);if(!e.ok)return xe;const t=await e.json();return!t||t.type!=="FeatureCollection"?xe:t}catch{return xe}}async function st(r,e,t=1500){return ve(`/api/layers/sandy?lat=${r}&lon=${e}&r=${t}`)}async function ot(r,e,t=1500){return ve(`/api/layers/dep_extreme_2080?lat=${r}&lon=${e}&r=${t}`)}async function Le(r,e,t=1500){return ve(`/api/layers/prithvi_water?lat=${r}&lon=${e}&r=${t}`)}async function fr(r){return ve(`/api/layers/sandy_clipped?code=${encodeURIComponent(r)}`)}async function _r(r,e="dep_extreme_2080"){return ve(`/api/layers/dep_clipped?code=${encodeURIComponent(r)}&scenario=${e}`)}async function Ee(r,e,t=1500){return ve(`/api/layers/ida_hwm?lat=${r}&lon=${e}&r=${t}`)}async function je(r,e,t=1500){try{const i=await fetch(`/api/floodnet_near?lat=${r}&lon=${e}&r=${t}`);return i.ok?{type:"FeatureCollection",features:(await i.json()).features.map(o=>{const m=o.properties??{};return{...o,properties:{...m,count:typeof m.n_events_3y=="number"?m.n_events_3y:1}}})}:xe}catch{return xe}}var vr=F('<span class="region-head-meta svelte-1q8jizq"> <!></span>'),hr=F('<span class="region-head-meta svelte-1q8jizq">planning…</span>'),gr=F('<div class="reroll-prev svelte-1q8jizq" aria-hidden="true"><p class="reroll-prev-line svelte-1q8jizq"> </p></div>'),yr=F("<!> <!>",1),br=F('<span class="streaming-caret svelte-1q8jizq" aria-hidden="true">▍</span>'),wr=F("<!> <!>",1),xr=F('<details class="plan-details svelte-1q8jizq"><summary class="svelte-1q8jizq"> </summary> <pre class="plan-stream svelte-1q8jizq"> </pre></details>'),Ar=F('<div class="generating-status svelte-1q8jizq" aria-live="polite"><span class="pulse svelte-1q8jizq"></span> Planning intent… <!></div>'),Sr=F('<div class="generating-status svelte-1q8jizq" aria-live="polite"><span class="pulse svelte-1q8jizq"></span> Resolving address…</div>'),kr=F("<!> <!>",1),Nr=F('<span class="region-head-meta svelte-1q8jizq"><!></span>'),$r=F('<span class="region-head-meta svelte-1q8jizq"> </span>'),Cr=F('<span class="region-head-meta svelte-1q8jizq">awaiting geocode…</span>'),qr=F('<div class="compare-map-place svelte-1q8jizq"><div class="compare-map-label svelte-1q8jizq"> </div> <div style="position: relative;" class="svelte-1q8jizq"><!></div></div>'),Tr=F('<div class="compare-map-place svelte-1q8jizq"><div class="compare-map-label svelte-1q8jizq"> </div> <div style="position: relative;" class="svelte-1q8jizq"><!></div></div>'),Fr=F('<div class="compare-map-stack svelte-1q8jizq"><!> <!></div>'),Mr=F('<div style="position: relative; flex: 1; min-height: 0;" class="svelte-1q8jizq"><!> <!></div>'),Ir=F('<section class="hero-band svelte-1q8jizq"><div class="hero-band-inner svelte-1q8jizq"><div class="app-shell-top is-desktop svelte-1q8jizq"><main id="region-briefing" class="app-region app-region-brief svelte-1q8jizq" aria-labelledby="brief-h1"><header class="region-head svelte-1q8jizq"><span class="section-label svelte-1q8jizq">Briefing</span> <!></header> <h1 id="brief-h1" class="brief-h1 svelte-1q8jizq">Flood-exposure briefing <span class="brief-h1-addr svelte-1q8jizq"> </span></h1> <!></main> <div class="app-region-side svelte-1q8jizq" style="grid-area: side;"><aside id="region-map" class="app-region app-region-map svelte-1q8jizq" aria-label="Map region"><header class="region-head svelte-1q8jizq"><span class="section-label svelte-1q8jizq">Map</span> <!></header> <!></aside> <aside id="region-cites" class="app-region app-region-cites svelte-1q8jizq" aria-label="Citations"><!></aside></div></div> <div class="app-shell-bottom svelte-1q8jizq"><section class="app-region app-region-findings svelte-1q8jizq" aria-label="Findings"><!></section></div></div></section>');function Gr(r,e){jt(e,!0);let t=ae(()=>pn.params.queryId??""),i=ae(()=>()=>{try{return decodeURIComponent(n(t))}catch{return n(t)}}),s=S(null),u=S(""),o=S(""),m=S(null),b=S(!1),l=S(0),p=2,A=S(!1),L=S(!1),K=S(""),R=S(null),ee=S(oe([])),y=S(oe({id:"root",name:"briefing.run",status:"ok",ms:0,tier:null,children:[]})),P=S(null),O="comfortable",z="smart",I=S(!1);ye(()=>{typeof window<"u"&&f(I,new URL(window.location.href).searchParams.get("grammar")==="1")});let H=S(null),D=S(void 0),E=oe({}),U=S(0),ne=ae(()=>{if(n(U),n(m)){const c={...E,...n(m)};return Et(c,n(y),n(D),!0)}return Et(E,n(y),n(D),!1)});function Z(c){f(P,c,!0)}function ce(c){const a=document.getElementById("region-cites");a&&a.scrollIntoView({behavior:"smooth",block:"start"})}const Ae=new Set(["ttm_forecast","ttm_311_forecast","floodnet_forecast"]),me="group-ttm-r2";function Ue(c,a,d,_){if(_==="error")return d??void 0;if(_==="silent")return d??"no data";if(a==null||typeof a!="object")return;const k=a,v={sandy_inundation:["inside"],dep_stormwater:["dep_extreme_2080","dep_moderate_2050"],floodnet:["n_sensors","n_events_3y"],nyc311:["n"],noaa_tides:["observed_ft_mllw","residual_ft","station"],nws_alerts:["n_active"],nws_obs:["p1h_mm","p6h_mm","station"],ttm_forecast:["forecast_peak_ft","forecast_peak_min_ahead"],ttm_311_forecast:["forecast_mean","forecast_peak","accelerating"],ida_hwm_2021:["n_within_800m","max_height_above_gnd_ft"],prithvi_eo_v2:["inside_water_polygon","nearest_distance_m"],prithvi_eo_live:["scene_date","pct_water_500m"],microtopo_lidar:["elev_m","pct_200m","relief_m"],mta_entrance_exposure:["n_entrances","n_inside_sandy_2012","n_in_dep_extreme_2080"],nycha_development_exposure:["n_developments","n_majority_inside_sandy_2012"],doe_school_exposure:["n_schools","n_inside_sandy_2012"],doh_hospital_exposure:["n_hospitals","n_inside_sandy_2012"],floodnet_forecast:["sensor_id","distance_m","forecast_28d","accelerating"],terramind_synthesis:["tim_chain","dem_mean_m"],rag_granite_embedding:["hits"],gliner_extract:["sources"]}[c],M=[];if(v){for(const T of v)if(k[T]!==void 0&&M.push(pt(T,k[T])),M.length>=3)break}else for(const[T,G]of Object.entries(k))if(G!==null&&typeof G!="object"&&(M.push(pt(T,G)),M.length>=2))break;return M.join(" · ")||void 0}function he(c){const a=[],d=c.mta_entrances;if(d&&Array.isArray(d.entrances))for(const v of d.entrances){const M=Number(v.entrance_lat),T=Number(v.entrance_lon);!Number.isFinite(M)||!Number.isFinite(T)||a.push({type:"Feature",geometry:{type:"Point",coordinates:[T,M]},properties:{kind:"subway",name:`${v.station_name??"?"} (${v.daytime_routes??"?"})`,doc_id:`mta_entrance_${v.station_id??""}`,inside_sandy_2012:v.inside_sandy_2012===!0}})}const _=c.doe_schools;if(_&&Array.isArray(_.schools))for(const v of _.schools){const M=Number(v.school_lat),T=Number(v.school_lon);!Number.isFinite(M)||!Number.isFinite(T)||a.push({type:"Feature",geometry:{type:"Point",coordinates:[T,M]},properties:{kind:"school",name:String(v.loc_name??v.school_name??"?"),doc_id:`doe_school_${v.loc_code??""}`,inside_sandy_2012:v.inside_sandy_2012===!0}})}const k=c.nycha_developments;if(k&&Array.isArray(k.developments))for(const v of k.developments){const M=Number(v.centroid_lat),T=Number(v.centroid_lon);if(!Number.isFinite(M)||!Number.isFinite(T))continue;const G=Number(v.pct_inside_sandy_2012??0);a.push({type:"Feature",geometry:{type:"Point",coordinates:[T,M]},properties:{kind:"nycha",name:String(v.development??"?"),doc_id:`nycha_dev_${v.tds_num??""}`,inside_sandy_2012:G>=50,pct_inside_sandy:G}})}const w=c.doh_hospitals;if(w&&Array.isArray(w.hospitals))for(const v of w.hospitals){const M=Number(v.hospital_lat),T=Number(v.hospital_lon);!Number.isFinite(M)||!Number.isFinite(T)||a.push({type:"Feature",geometry:{type:"Point",coordinates:[T,M]},properties:{kind:"hospital",name:String(v.facility_name??"?"),doc_id:`nyc_hospital_${v.fac_id??""}`,inside_sandy_2012:v.inside_sandy_2012===!0}})}return{type:"FeatureCollection",features:a}}function ge(c){return{type:"FeatureCollection",features:[]}}function Se(c){return 1+(c.children??[]).reduce((d,_)=>d+Se(_),0)}function pt(c,a){if(typeof a=="number"){const d=Number.isInteger(a)?`${a}`:a.toFixed(2);return`${c}=${d}`}if(typeof a=="boolean")return`${c}=${a}`;if(typeof a=="string"){const d=a.length>24?a.slice(0,22)+"…":a;return`${c}=${d}`}return c}let de=S(oe({empirical:!0,modeled:!0,synthetic:!0,proxy:!0})),ie=S(null),ke=S(null),mt=S(void 0),ft=S(void 0),Ne=S(void 0),$e=S(void 0),Ce=S(void 0),qe=S(void 0),Ge=S(void 0),Te=S(void 0),ue=S(null),pe=S(null),We=S(oe({})),Ve=S(oe({})),_t=S(void 0),vt=S(void 0),ht=S(void 0),gt=S(void 0),yt=S(void 0),bt=S(void 0),wt=S(void 0),xt=S(void 0),At=S(void 0),St=S(void 0),Ht=ae(()=>{var c,a,d,_,k,w;return{empirical:(((c=n(Ne))==null?void 0:c.features.length)??0)+(((a=n(Te))==null?void 0:a.features.length)??0),modeled:((d=n($e))==null?void 0:d.features.length)??0,synthetic:(((_=n(Ce))==null?void 0:_.features.length)??0)+(((k=n(Ge))==null?void 0:k.features.length)??0),proxy:((w=n(qe))==null?void 0:w.features.length)??0}}),fe=S(oe([])),_e=S(oe({})),Fe=[];function Ut(){var k;if(!n(o)){f(fe,[],!0),f(_e,{},!0),Fe=[];return}const c={};(k=n(m))!=null&&k.citations&&n(m).citations.forEach((w,v)=>{c[w.doc_id]=Rt(v+1,w.doc_id,{source:w.source,title:w.title,url:w.url,vintage:w.vintage})});const a=ct(n(o),c),d={};let _=1;for(const w of Fe){const v=a.citations[w];v&&(d[w]={...v,n:_++})}for(const[w,v]of Object.entries(a.citations))d[w]||(d[w]={...v,n:_++},Fe.push(w));f(fe,a.blocks,!0),f(_e,d,!0)}ye(()=>{n(o),n(m),Ut()}),ye(()=>{if(!n(ie))return;const{lat:c,lon:a,source:d}=n(ie);d==="nta"&&n(ke)?(fr(n(ke)).then(_=>f(Ne,_,!0)),_r(n(ke)).then(_=>f($e,_,!0)),Le(c,a,2500).then(_=>f(Ce,_,!0)),je(c,a,3e3).then(_=>f(qe,_,!0)),Ee(c,a,3e3).then(_=>f(Te,_,!0))):(st(c,a).then(_=>f(Ne,_,!0)),ot(c,a).then(_=>f($e,_,!0)),Le(c,a).then(_=>f(Ce,_,!0)),je(c,a).then(_=>f(qe,_,!0)),Ee(c,a).then(_=>f(Te,_,!0)))}),ye(()=>{if(!n(ue))return;const{lat:c,lon:a}=n(ue);st(c,a).then(d=>f(_t,d,!0)),ot(c,a).then(d=>f(vt,d,!0)),Le(c,a).then(d=>f(ht,d,!0)),je(c,a).then(d=>f(gt,d,!0)),Ee(c,a).then(d=>f(yt,d,!0))}),ye(()=>{if(!n(pe))return;const{lat:c,lon:a}=n(pe);st(c,a).then(d=>f(bt,d,!0)),ot(c,a).then(d=>f(wt,d,!0)),Le(c,a).then(d=>f(xt,d,!0)),je(c,a).then(d=>f(At,d,!0)),Ee(c,a).then(d=>f(St,d,!0))}),un(()=>{if(J.reset(),!n(i)())return;f(H,Date.now(),!0),J.phase="planning";const c=mr(n(i)(),{onPlanToken:a=>f(u,n(u)+a),onPlan:a=>{var d;f(s,a,!0),J.phase="specialists",J.totalSpecialists=((d=a.specialists)==null?void 0:d.length)??0},onStep:a=>{if(new Set(["reconcile_granite41","mellea_reconcile_address","reconcile_neighborhood","reconcile_development","reconcile_live_now"]).has(a.step)||(J.activeStep=a.step,a.ok&&(J.firedCount=J.firedCount+1)),sr(E,a.step,a.result,a.ok),f(U,n(U)+1),a.step==="geocode")if(a.ok&&a.result&&typeof a.result=="object"){const x=a.result;if(typeof x.lat=="number"&&typeof x.lon=="number"){const C=typeof x.address=="string"?x.address:n(i)();a.target_label==="PLACE A"?f(ue,{label:C,lat:x.lat,lon:x.lon,source:"geocode"},!0):a.target_label==="PLACE B"?f(pe,{label:C,lat:x.lat,lon:x.lon,source:"geocode"},!0):f(ie,{label:C,lat:x.lat,lon:x.lon,source:"geocode"},!0),f(L,!0)}}else f(R,"geocoder");if(a.target_label==="PLACE A"?f(We,{...n(We),[a.step]:a.result},!0):a.target_label==="PLACE B"&&f(Ve,{...n(Ve),[a.step]:a.result},!0),a.step==="nta_resolve"&&a.ok&&a.result&&typeof a.result=="object"){const x=a.result,C=Array.isArray(x.bbox)?x.bbox:null,W=typeof x.nta_code=="string"?x.nta_code:null;if(C&&C.length===4&&W){f(ke,W,!0);const re=(C[0]+C[2])/2,Q=(C[1]+C[3])/2,te=typeof x.nta_name=="string"?x.nta_name:n(i)();f(ie,{label:te,lat:Q,lon:re,source:"nta"},!0)}}const _=mn(a.step),k=a.ok?a.result==null&&a.err==null?"silent":"ok":"error",w=Math.round((a.elapsed_s??0)*1e3),v=a.result!=null?a.result:a.err??null,M=Ue(a.step,a.result,a.err,k),T={id:`step-${Se(n(y))}`,name:a.step,status:k,ms:w,tier:_,note:M,output:v,error:k==="error"?a.err??"unknown error":void 0,model:Ae.has(a.step)?"granite-timeseries-ttm-r2":void 0},G={...n(y),ms:(n(y).ms??0)+w};if(Ae.has(a.step)){const x=[...G.children??[]];let C=x.find(Q=>Q.id===me);C||(C={id:me,name:"forecasting.granite-timeseries-ttm-r2",status:"fan",ms:0,tier:"modeled",note:"1 instance",model:"granite-timeseries-ttm-r2",children:[]},x.push(C));const W=[...C.children??[],T],re={...C,ms:(C.ms??0)+w,note:`${W.length} instance${W.length===1?"":"s"}`,children:W};f(y,{...G,children:x.map(Q=>Q.id===me?re:Q)},!0)}else f(y,{...G,children:[...G.children??[],T]},!0)},onAttemptStart:a=>{f(l,a,!0),J.phase="reconciling",J.attempt=a,J.activeStep="granite4.1 + mellea",a>1&&(f(K,n(o),!0),f(o,""),Fe=[])},onToken:a=>{n(A)||(f(A,!0),n(l)===0&&f(l,1),J.phase="streaming",J.attempt=Math.max(1,J.attempt)),f(o,n(o)+a)},onMelleaAttempt:a=>{a.attempt>0&&(f(l,a.attempt,!0),J.attempt=a.attempt)},onFinal:a=>{var w;f(m,a,!0),a.paragraph&&f(o,a.paragraph,!0),f(ee,pr(a),!0);const d=a;f(mt,he(d),!0),f(ft,ge(),!0);const _=d.terramind;if(_!=null&&_.ok&&(_!=null&&_.polygons_geojson)){const v=_.polygons_geojson;(v==null?void 0:v.type)==="FeatureCollection"&&(((w=v.features)==null?void 0:w.length)??0)>0&&f(Ge,v,!0)}const k=a.mellea;k&&k.failed&&k.failed.length>0&&k.attempts&&k.attempts>=p&&f(R,"grounding")},onError:a=>{const d=a.toLowerCase();(d.includes("connection")||d.includes("502")||d.includes("503")||d.includes("timeout")||d.includes("routing"))&&f(R,"backend"),J.markError(a)},onDone:()=>{var a,d,_,k,w;f(b,!0),n(H)!=null&&f(D,(Date.now()-n(H))/1e3),!n(A)&&!n(R)&&n(L)&&f(R,"all-silent"),!n(R)&&n(fe).length>0&&(yn({queryId:n(t),queryText:n(i)(),intent:((a=n(s))==null?void 0:a.intent)??null,specialists:((_=(d=n(s))==null?void 0:d.specialists)==null?void 0:_.length)??0,blocks:n(fe),citations:n(_e),generatedAt:new Date().toISOString(),attempts:((w=(k=n(m))==null?void 0:k.mellea)==null?void 0:w.n_attempts)??n(l)}),J.markReady())}});return()=>c.close()});var Ke=Ir(),kt=g(Ke),Ze=g(kt),Je=g(Ze),Xe=g(Je),Gt=$(g(Xe),2);{var Wt=c=>{var a=vr(),d=g(a),_=$(d);{var k=w=>{var v=rt("· ✓ done");q(w,v)};V(_,w=>{n(b)&&w(k)})}h(a),X(()=>{var w;return Y(d,`intent: ${n(s).intent??""} · ${((w=n(s).specialists)==null?void 0:w.length)??0??""} specialists · attempt ${n(l)??""} `)}),q(c,a)},Vt=c=>{var a=hr();q(c,a)};V(Gt,c=>{n(s)?c(Wt):c(Vt,-1)})}h(Xe);var Qe=$(Xe,2),Nt=$(g(Qe)),Kt=g(Nt,!0);h(Nt),h(Qe);var Zt=$(Qe,2);{var Jt=c=>{zn(c,{get state(){return n(R)}})},Xt=c=>{var a=kr(),d=we(a);{var _=x=>{var C=yr(),W=we(C);Ln(W,{get attempt(){return n(l)},max:p});var re=$(W,2);{var Q=te=>{var se=gr(),Me=g(se),Ie=g(Me);h(Me),h(se),X(nt=>Y(Ie,`${nt??""}…`),[()=>n(K).slice(0,360)]),q(te,se)};V(re,te=>{n(K)&&te(Q)})}q(x,C)};V(d,x=>{n(l)>1&&x(_)})}var k=$(d,2);{var w=x=>{qn(x,{get paragraph(){return n(m).paragraph},get citations(){return n(_e)},get targets(){return n(m).targets},get structuredA(){return n(We)},get structuredB(){return n(Ve)}})},v=x=>{var C=wr(),W=we(C);Bt(W,{get blocks(){return n(fe)},get citations(){return n(_e)},streaming:!1});var re=$(W,2);{var Q=te=>{var se=br();q(te,se)};V(re,te=>{n(b)||te(Q)})}q(x,C)},M=x=>{Mn(x)},T=x=>{var C=Ar(),W=$(g(C),2);{var re=Q=>{var te=xr(),se=g(te),Me=g(se);h(se);var Ie=$(se,2),nt=g(Ie,!0);h(Ie),h(te),X(()=>{Y(Me,`Planner streaming (${n(u).length??""} chars)`),Y(nt,n(u))}),q(Q,te)};V(W,Q=>{n(u)&&Q(re)})}h(C),q(x,C)},G=x=>{var C=Sr();q(x,C)};V(k,x=>{var C,W,re;((C=n(s))==null?void 0:C.intent)==="compare"&&((re=(W=n(m))==null?void 0:W.targets)==null?void 0:re.length)===2?x(w):n(fe).length?x(v,1):n(L)&&!n(A)?x(M,2):n(s)?x(G,-1):x(T,3)})}q(c,a)};V(Zt,c=>{n(R)?c(Jt):c(Xt,-1)})}h(Je);var $t=$(Je,2),et=g($t),tt=g(et),Qt=$(g(tt),2);{var en=c=>{var a=Nr(),d=g(a);{var _=w=>{var v=rt("Carto Positron · z15 · 2 locations");q(w,v)},k=w=>{var v=rt("awaiting geocode…");q(w,v)};V(d,w=>{n(ue)||n(pe)?w(_):w(k,-1)})}h(a),q(c,a)},tn=c=>{var a=$r(),d=g(a);h(a),X((_,k)=>Y(d,`Carto Positron · z15 · ${_??""}°N ${k??""}°W`),[()=>n(ie).lat.toFixed(4),()=>Math.abs(n(ie).lon).toFixed(4)]),q(c,a)},nn=c=>{var a=Cr();q(c,a)};V(Qt,c=>{var a;((a=n(s))==null?void 0:a.intent)==="compare"?c(en):n(ie)?c(tn,1):c(nn,-1)})}h(tt);var rn=$(tt,2);{var an=c=>{var a=Fr(),d=g(a);{var _=v=>{var M=qr(),T=g(M),G=g(T);h(T);var x=$(T,2),C=g(x);at(C,{get address(){return n(ue)},get activeLayers(){return n(de)},get sandyEmpirical(){return n(_t)},get depModeled(){return n(vt)},get syntheticPrior(){return n(ht)},get proxy311(){return n(gt)},get idaHwm(){return n(yt)}}),h(x),h(M),X(()=>Y(G,`A · ${n(ue).label??""}`)),q(v,M)};V(d,v=>{n(ue)&&v(_)})}var k=$(d,2);{var w=v=>{var M=Tr(),T=g(M),G=g(T);h(T);var x=$(T,2),C=g(x);at(C,{get address(){return n(pe)},get activeLayers(){return n(de)},get sandyEmpirical(){return n(bt)},get depModeled(){return n(wt)},get syntheticPrior(){return n(xt)},get proxy311(){return n(At)},get idaHwm(){return n(St)}}),h(x),h(M),X(()=>Y(G,`B · ${n(pe).label??""}`)),q(v,M)};V(k,v=>{n(pe)&&v(w)})}h(a),q(c,a)},sn=c=>{var a=Mr(),d=g(a);at(d,{get address(){return n(ie)},get activeLayers(){return n(de)},get sandyEmpirical(){return n(Ne)},get depModeled(){return n($e)},get syntheticPrior(){return n(Ce)},get proxy311(){return n(qe)},get idaHwm(){return n(Te)},get registerPoints(){return n(mt)},get registerPolygons(){return n(ft)},get terramindLulc(){return n(Ge)},get linkedKey(){return n(P)}});var _=$(d,2);hn(_,{get active(){return n(de)},get featureCounts(){return n(Ht)},onToggle:k=>f(de,{...n(de),[k]:!n(de)[k]},!0)}),h(a),q(c,a)};V(rn,c=>{var a;((a=n(s))==null?void 0:a.intent)==="compare"?c(an):n(ie)&&c(sn,1)})}h(et);var Ct=$(et,2),on=g(Ct);_n(on,{get citations(){return n(_e)}}),h(Ct),h($t),h(Ze);var qt=$(Ze,2),Tt=g(qt),ln=g(Tt);vn(ln,{get data(){return n(ne)},density:O,provenanceMode:z,get showGrammar(){return n(I)},get linkedKey(){return n(P)},onLink:Z,onCite:ce}),h(Tt),h(qt),h(kt),h(Ke),X(c=>Y(Kt,c),[()=>n(i)()]),q(r,Ke),Pt()}export{Gr as component,Ur as universal};
|
web/sveltekit/build/_app/version.json
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
{"version":"
|
|
|
|
| 1 |
+
{"version":"1778128400538"}
|
web/sveltekit/build/index.html
CHANGED
|
@@ -6,21 +6,21 @@
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
<meta name="description" content="Riprap — citation-grounded NYC flood-exposure briefings." />
|
| 8 |
<title>Riprap — flood-exposure briefing</title>
|
| 9 |
-
<link href="./_app/immutable/entry/start.
|
| 10 |
-
<link href="./_app/immutable/chunks/
|
| 11 |
<link href="./_app/immutable/chunks/BTUA7_xE.js" rel="modulepreload">
|
| 12 |
-
<link href="./_app/immutable/entry/app.
|
| 13 |
<link href="./_app/immutable/chunks/CXQd8Y6F.js" rel="modulepreload">
|
| 14 |
<link href="./_app/immutable/chunks/CWw6qgC_.js" rel="modulepreload">
|
| 15 |
<link href="./_app/immutable/chunks/Bd-v_9Ud.js" rel="modulepreload">
|
| 16 |
<link href="./_app/immutable/chunks/CW0zSL4D.js" rel="modulepreload">
|
| 17 |
-
<link href="./_app/immutable/nodes/0.
|
| 18 |
<link href="./_app/immutable/chunks/DxQlA7U2.js" rel="modulepreload">
|
| 19 |
-
<link href="./_app/immutable/chunks/
|
| 20 |
<link href="./_app/immutable/chunks/DCD6_LXk.js" rel="modulepreload">
|
| 21 |
<link href="./_app/immutable/chunks/B0XoTt7U.js" rel="modulepreload">
|
| 22 |
<link href="./_app/immutable/chunks/DixtWtwq.js" rel="modulepreload">
|
| 23 |
-
<link href="./_app/immutable/nodes/2.
|
| 24 |
<link href="./_app/immutable/chunks/cDW0xQNP.js" rel="modulepreload">
|
| 25 |
<link href="./_app/immutable/chunks/25_y8TFd.js" rel="modulepreload">
|
| 26 |
<link href="./_app/immutable/chunks/D907np-5.js" rel="modulepreload">
|
|
@@ -37,15 +37,15 @@
|
|
| 37 |
|
| 38 |
<script>
|
| 39 |
{
|
| 40 |
-
|
| 41 |
base: new URL(".", location).pathname.slice(0, -1)
|
| 42 |
};
|
| 43 |
|
| 44 |
const element = document.currentScript.parentElement;
|
| 45 |
|
| 46 |
Promise.all([
|
| 47 |
-
import("./_app/immutable/entry/start.
|
| 48 |
-
import("./_app/immutable/entry/app.
|
| 49 |
]).then(([kit, app]) => {
|
| 50 |
kit.start(app, element, {
|
| 51 |
node_ids: [0, 2],
|
|
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
<meta name="description" content="Riprap — citation-grounded NYC flood-exposure briefings." />
|
| 8 |
<title>Riprap — flood-exposure briefing</title>
|
| 9 |
+
<link href="./_app/immutable/entry/start.BkS8JQ5_.js" rel="modulepreload">
|
| 10 |
+
<link href="./_app/immutable/chunks/BgqmyNr8.js" rel="modulepreload">
|
| 11 |
<link href="./_app/immutable/chunks/BTUA7_xE.js" rel="modulepreload">
|
| 12 |
+
<link href="./_app/immutable/entry/app.DtHxgVfE.js" rel="modulepreload">
|
| 13 |
<link href="./_app/immutable/chunks/CXQd8Y6F.js" rel="modulepreload">
|
| 14 |
<link href="./_app/immutable/chunks/CWw6qgC_.js" rel="modulepreload">
|
| 15 |
<link href="./_app/immutable/chunks/Bd-v_9Ud.js" rel="modulepreload">
|
| 16 |
<link href="./_app/immutable/chunks/CW0zSL4D.js" rel="modulepreload">
|
| 17 |
+
<link href="./_app/immutable/nodes/0.LAFF30Kg.js" rel="modulepreload">
|
| 18 |
<link href="./_app/immutable/chunks/DxQlA7U2.js" rel="modulepreload">
|
| 19 |
+
<link href="./_app/immutable/chunks/BbRPgcjS.js" rel="modulepreload">
|
| 20 |
<link href="./_app/immutable/chunks/DCD6_LXk.js" rel="modulepreload">
|
| 21 |
<link href="./_app/immutable/chunks/B0XoTt7U.js" rel="modulepreload">
|
| 22 |
<link href="./_app/immutable/chunks/DixtWtwq.js" rel="modulepreload">
|
| 23 |
+
<link href="./_app/immutable/nodes/2.vc91Ib0z.js" rel="modulepreload">
|
| 24 |
<link href="./_app/immutable/chunks/cDW0xQNP.js" rel="modulepreload">
|
| 25 |
<link href="./_app/immutable/chunks/25_y8TFd.js" rel="modulepreload">
|
| 26 |
<link href="./_app/immutable/chunks/D907np-5.js" rel="modulepreload">
|
|
|
|
| 37 |
|
| 38 |
<script>
|
| 39 |
{
|
| 40 |
+
__sveltekit_8j9bml = {
|
| 41 |
base: new URL(".", location).pathname.slice(0, -1)
|
| 42 |
};
|
| 43 |
|
| 44 |
const element = document.currentScript.parentElement;
|
| 45 |
|
| 46 |
Promise.all([
|
| 47 |
+
import("./_app/immutable/entry/start.BkS8JQ5_.js"),
|
| 48 |
+
import("./_app/immutable/entry/app.DtHxgVfE.js")
|
| 49 |
]).then(([kit, app]) => {
|
| 50 |
kit.start(app, element, {
|
| 51 |
node_ids: [0, 2],
|
web/sveltekit/build/q/sample.html
CHANGED
|
@@ -6,17 +6,17 @@
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
<meta name="description" content="Riprap — citation-grounded NYC flood-exposure briefings." />
|
| 8 |
<title>Riprap — flood-exposure briefing</title>
|
| 9 |
-
<link href="../_app/immutable/entry/start.
|
| 10 |
-
<link href="../_app/immutable/chunks/
|
| 11 |
<link href="../_app/immutable/chunks/BTUA7_xE.js" rel="modulepreload">
|
| 12 |
-
<link href="../_app/immutable/entry/app.
|
| 13 |
<link href="../_app/immutable/chunks/CXQd8Y6F.js" rel="modulepreload">
|
| 14 |
<link href="../_app/immutable/chunks/CWw6qgC_.js" rel="modulepreload">
|
| 15 |
<link href="../_app/immutable/chunks/Bd-v_9Ud.js" rel="modulepreload">
|
| 16 |
<link href="../_app/immutable/chunks/CW0zSL4D.js" rel="modulepreload">
|
| 17 |
-
<link href="../_app/immutable/nodes/0.
|
| 18 |
<link href="../_app/immutable/chunks/DxQlA7U2.js" rel="modulepreload">
|
| 19 |
-
<link href="../_app/immutable/chunks/
|
| 20 |
<link href="../_app/immutable/chunks/DCD6_LXk.js" rel="modulepreload">
|
| 21 |
<link href="../_app/immutable/chunks/B0XoTt7U.js" rel="modulepreload">
|
| 22 |
<link href="../_app/immutable/chunks/DixtWtwq.js" rel="modulepreload">
|
|
@@ -38,15 +38,15 @@
|
|
| 38 |
|
| 39 |
<script>
|
| 40 |
{
|
| 41 |
-
|
| 42 |
base: new URL("..", location).pathname.slice(0, -1)
|
| 43 |
};
|
| 44 |
|
| 45 |
const element = document.currentScript.parentElement;
|
| 46 |
|
| 47 |
Promise.all([
|
| 48 |
-
import("../_app/immutable/entry/start.
|
| 49 |
-
import("../_app/immutable/entry/app.
|
| 50 |
]).then(([kit, app]) => {
|
| 51 |
kit.start(app, element, {
|
| 52 |
node_ids: [0, 5],
|
|
|
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
<meta name="description" content="Riprap — citation-grounded NYC flood-exposure briefings." />
|
| 8 |
<title>Riprap — flood-exposure briefing</title>
|
| 9 |
+
<link href="../_app/immutable/entry/start.BkS8JQ5_.js" rel="modulepreload">
|
| 10 |
+
<link href="../_app/immutable/chunks/BgqmyNr8.js" rel="modulepreload">
|
| 11 |
<link href="../_app/immutable/chunks/BTUA7_xE.js" rel="modulepreload">
|
| 12 |
+
<link href="../_app/immutable/entry/app.DtHxgVfE.js" rel="modulepreload">
|
| 13 |
<link href="../_app/immutable/chunks/CXQd8Y6F.js" rel="modulepreload">
|
| 14 |
<link href="../_app/immutable/chunks/CWw6qgC_.js" rel="modulepreload">
|
| 15 |
<link href="../_app/immutable/chunks/Bd-v_9Ud.js" rel="modulepreload">
|
| 16 |
<link href="../_app/immutable/chunks/CW0zSL4D.js" rel="modulepreload">
|
| 17 |
+
<link href="../_app/immutable/nodes/0.LAFF30Kg.js" rel="modulepreload">
|
| 18 |
<link href="../_app/immutable/chunks/DxQlA7U2.js" rel="modulepreload">
|
| 19 |
+
<link href="../_app/immutable/chunks/BbRPgcjS.js" rel="modulepreload">
|
| 20 |
<link href="../_app/immutable/chunks/DCD6_LXk.js" rel="modulepreload">
|
| 21 |
<link href="../_app/immutable/chunks/B0XoTt7U.js" rel="modulepreload">
|
| 22 |
<link href="../_app/immutable/chunks/DixtWtwq.js" rel="modulepreload">
|
|
|
|
| 38 |
|
| 39 |
<script>
|
| 40 |
{
|
| 41 |
+
__sveltekit_8j9bml = {
|
| 42 |
base: new URL("..", location).pathname.slice(0, -1)
|
| 43 |
};
|
| 44 |
|
| 45 |
const element = document.currentScript.parentElement;
|
| 46 |
|
| 47 |
Promise.all([
|
| 48 |
+
import("../_app/immutable/entry/start.BkS8JQ5_.js"),
|
| 49 |
+
import("../_app/immutable/entry/app.DtHxgVfE.js")
|
| 50 |
]).then(([kit, app]) => {
|
| 51 |
kit.start(app, element, {
|
| 52 |
node_ids: [0, 5],
|
web/sveltekit/src/lib/client/agentStream.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface StepEvent {
|
|
| 36 |
err?: string;
|
| 37 |
tier?: Tier | null;
|
| 38 |
claims?: number;
|
|
|
|
|
|
|
| 39 |
}
|
| 40 |
|
| 41 |
export interface MelleaAttempt {
|
|
|
|
| 36 |
err?: string;
|
| 37 |
tier?: Tier | null;
|
| 38 |
claims?: number;
|
| 39 |
+
/** Present on compare-intent step events: "PLACE A" or "PLACE B". */
|
| 40 |
+
target_label?: string;
|
| 41 |
}
|
| 42 |
|
| 43 |
export interface MelleaAttempt {
|
web/sveltekit/src/lib/components/briefing/CompareBriefing.svelte
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
<script lang="ts">
|
| 2 |
import Briefing from './Briefing.svelte';
|
| 3 |
import { parseBriefing } from '$lib/client/parseBriefing';
|
| 4 |
-
import type {
|
| 5 |
|
| 6 |
interface Target {
|
| 7 |
label: string;
|
|
@@ -12,9 +12,12 @@
|
|
| 12 |
paragraph: string;
|
| 13 |
citations: Record<string, Citation>;
|
| 14 |
targets: Target[];
|
|
|
|
|
|
|
|
|
|
| 15 |
}
|
| 16 |
|
| 17 |
-
let { paragraph, citations, targets }: Props = $props();
|
| 18 |
|
| 19 |
// Split the merged compare paragraph at the --- divider.
|
| 20 |
// Each half begins with `## PLACE A/B: <address>` which we strip to get
|
|
@@ -41,78 +44,69 @@
|
|
| 41 |
...parsedB.citations
|
| 42 |
});
|
| 43 |
|
| 44 |
-
// Collect prose text keyed by section number ('01'–'04').
|
| 45 |
-
function sectionTexts(blocks: BriefingBlock[]): Map<string, string> {
|
| 46 |
-
const map = new Map<string, string>();
|
| 47 |
-
let cur = '';
|
| 48 |
-
for (const b of blocks) {
|
| 49 |
-
if (b.kind === 'head') cur = b.n;
|
| 50 |
-
else if (b.kind === 'prose' && cur) {
|
| 51 |
-
map.set(cur, (map.get(cur) ?? '') + ' ' + b.parts.map((p) => p.text).join(''));
|
| 52 |
-
}
|
| 53 |
-
}
|
| 54 |
-
return map;
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
// Return all numbers (with optional unit suffix) in a text, in order.
|
| 58 |
-
const NUM_RE = /\b(\d[\d,]*(?:\.\d+)?)\s*(%|ft|m|km|mm)?\b/g;
|
| 59 |
-
function findNumbers(text: string): Array<{ full: string; start: number }> {
|
| 60 |
-
const hits: Array<{ full: string; start: number }> = [];
|
| 61 |
-
let m: RegExpExecArray | null;
|
| 62 |
-
NUM_RE.lastIndex = 0;
|
| 63 |
-
while ((m = NUM_RE.exec(text)) !== null) {
|
| 64 |
-
const full = m[1] + (m[2] ?? '');
|
| 65 |
-
hits.push({ full, start: m.index });
|
| 66 |
-
}
|
| 67 |
-
return hits;
|
| 68 |
-
}
|
| 69 |
-
|
| 70 |
-
// Up to 3 words immediately before `start` in `text`.
|
| 71 |
-
function ctxBefore(text: string, start: number): string {
|
| 72 |
-
const snippet = text.slice(Math.max(0, start - 40), start).trim();
|
| 73 |
-
return snippet.split(/\s+/).slice(-3).join(' ');
|
| 74 |
-
}
|
| 75 |
-
|
| 76 |
-
const SECTION_LABELS: Record<string, string> = {
|
| 77 |
-
'01': 'Status',
|
| 78 |
-
'02': 'Empirical',
|
| 79 |
-
'03': 'Modeled',
|
| 80 |
-
'04': 'Policy'
|
| 81 |
-
};
|
| 82 |
-
|
| 83 |
interface DeltaRow {
|
| 84 |
-
|
| 85 |
ctx: string;
|
| 86 |
aVal: string;
|
| 87 |
bVal: string;
|
| 88 |
}
|
| 89 |
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
const deltaRows = $derived.by<DeltaRow[]>(() => {
|
| 92 |
-
const textsA = sectionTexts(parsedA.blocks);
|
| 93 |
-
const textsB = sectionTexts(parsedB.blocks);
|
| 94 |
const rows: DeltaRow[] = [];
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
}
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
});
|
| 117 |
</script>
|
| 118 |
|
|
@@ -123,7 +117,7 @@
|
|
| 123 |
<div class="compare-delta-rows">
|
| 124 |
{#each deltaRows as row}
|
| 125 |
<div class="compare-delta-row">
|
| 126 |
-
<span class="compare-delta-section">{row.
|
| 127 |
<span class="compare-delta-claim">
|
| 128 |
{#if row.ctx}<span class="compare-delta-ctx">{row.ctx}:</span>{/if}
|
| 129 |
<strong class="compare-delta-a">{row.aVal}</strong>
|
|
|
|
| 1 |
<script lang="ts">
|
| 2 |
import Briefing from './Briefing.svelte';
|
| 3 |
import { parseBriefing } from '$lib/client/parseBriefing';
|
| 4 |
+
import type { Citation } from '$lib/types/claim';
|
| 5 |
|
| 6 |
interface Target {
|
| 7 |
label: string;
|
|
|
|
| 12 |
paragraph: string;
|
| 13 |
citations: Record<string, Citation>;
|
| 14 |
targets: Target[];
|
| 15 |
+
/** Per-place step result payloads from the agent stream, keyed by step name. */
|
| 16 |
+
structuredA?: Record<string, unknown>;
|
| 17 |
+
structuredB?: Record<string, unknown>;
|
| 18 |
}
|
| 19 |
|
| 20 |
+
let { paragraph, citations, targets, structuredA = {}, structuredB = {} }: Props = $props();
|
| 21 |
|
| 22 |
// Split the merged compare paragraph at the --- divider.
|
| 23 |
// Each half begins with `## PLACE A/B: <address>` which we strip to get
|
|
|
|
| 44 |
...parsedB.citations
|
| 45 |
});
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
interface DeltaRow {
|
| 48 |
+
label: string;
|
| 49 |
ctx: string;
|
| 50 |
aVal: string;
|
| 51 |
bVal: string;
|
| 52 |
}
|
| 53 |
|
| 54 |
+
function getNum(steps: Record<string, unknown>, stepName: string, field: string): number | undefined {
|
| 55 |
+
const r = steps[stepName];
|
| 56 |
+
if (!r || typeof r !== 'object') return undefined;
|
| 57 |
+
const v = (r as Record<string, unknown>)[field];
|
| 58 |
+
return typeof v === 'number' ? v : undefined;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
function getBool(steps: Record<string, unknown>, stepName: string, field: string): boolean | undefined {
|
| 62 |
+
const r = steps[stepName];
|
| 63 |
+
if (!r || typeof r !== 'object') return undefined;
|
| 64 |
+
const v = (r as Record<string, unknown>)[field];
|
| 65 |
+
return typeof v === 'boolean' ? v : undefined;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
// Derive diff rows from structured specialist step payloads.
|
| 69 |
+
// This avoids parsing prose for numbers, which incorrectly picks up
|
| 70 |
+
// address street numbers as "Status" comparisons.
|
| 71 |
const deltaRows = $derived.by<DeltaRow[]>(() => {
|
|
|
|
|
|
|
| 72 |
const rows: DeltaRow[] = [];
|
| 73 |
+
|
| 74 |
+
// Sandy inundation zone membership
|
| 75 |
+
const sandyA = getBool(structuredA, 'sandy_inundation', 'inside');
|
| 76 |
+
const sandyB = getBool(structuredB, 'sandy_inundation', 'inside');
|
| 77 |
+
if (sandyA !== undefined && sandyB !== undefined && sandyA !== sandyB) {
|
| 78 |
+
rows.push({ label: 'Sandy zone', ctx: '', aVal: sandyA ? 'inside' : 'outside', bVal: sandyB ? 'inside' : 'outside' });
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
// 311 flood complaints (5-year radius)
|
| 82 |
+
const n311A = getNum(structuredA, 'nyc311', 'n');
|
| 83 |
+
const n311B = getNum(structuredB, 'nyc311', 'n');
|
| 84 |
+
if (n311A !== undefined && n311B !== undefined && n311A !== n311B) {
|
| 85 |
+
rows.push({ label: '311 complaints', ctx: '5 y', aVal: String(n311A), bVal: String(n311B) });
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
// Terrain elevation
|
| 89 |
+
const elevA = getNum(structuredA, 'microtopo_lidar', 'elev_m');
|
| 90 |
+
const elevB = getNum(structuredB, 'microtopo_lidar', 'elev_m');
|
| 91 |
+
if (elevA !== undefined && elevB !== undefined && Math.abs(elevA - elevB) > 0.5) {
|
| 92 |
+
rows.push({ label: 'Elevation', ctx: '', aVal: `${elevA.toFixed(1)} m`, bVal: `${elevB.toFixed(1)} m` });
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
// FloodNet sensor flood events (3-year)
|
| 96 |
+
const fnA = getNum(structuredA, 'floodnet', 'n_events_3y');
|
| 97 |
+
const fnB = getNum(structuredB, 'floodnet', 'n_events_3y');
|
| 98 |
+
if (fnA !== undefined && fnB !== undefined && fnA !== fnB) {
|
| 99 |
+
rows.push({ label: 'Sensor events', ctx: 'last 3 y', aVal: String(fnA), bVal: String(fnB) });
|
| 100 |
}
|
| 101 |
+
|
| 102 |
+
// Ida 2021 high-water mark (nearest within 800 m)
|
| 103 |
+
const idaA = getNum(structuredA, 'ida_hwm_2021', 'max_height_above_gnd_ft');
|
| 104 |
+
const idaB = getNum(structuredB, 'ida_hwm_2021', 'max_height_above_gnd_ft');
|
| 105 |
+
if (idaA !== undefined && idaB !== undefined && Math.abs(idaA - idaB) > 0.1) {
|
| 106 |
+
rows.push({ label: 'Ida 2021 HWM', ctx: 'ft above gnd', aVal: `${idaA.toFixed(2)} ft`, bVal: `${idaB.toFixed(2)} ft` });
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
return rows.slice(0, 4);
|
| 110 |
});
|
| 111 |
</script>
|
| 112 |
|
|
|
|
| 117 |
<div class="compare-delta-rows">
|
| 118 |
{#each deltaRows as row}
|
| 119 |
<div class="compare-delta-row">
|
| 120 |
+
<span class="compare-delta-section">{row.label}</span>
|
| 121 |
<span class="compare-delta-claim">
|
| 122 |
{#if row.ctx}<span class="compare-delta-ctx">{row.ctx}:</span>{/if}
|
| 123 |
<strong class="compare-delta-a">{row.aVal}</strong>
|
web/sveltekit/src/routes/q/[queryId]/+page.svelte
CHANGED
|
@@ -293,6 +293,25 @@
|
|
| 293 |
let terramindLulcFc = $state<FeatureCollection | undefined>(undefined);
|
| 294 |
let idaHwmFc = $state<FeatureCollection | undefined>(undefined);
|
| 295 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
// Per-tier feature counts for the map legend. Layers with 0 are
|
| 297 |
// dropped from the legend display per the silence-over-confabulation
|
| 298 |
// rule (handoff hard rule #3).
|
|
@@ -378,6 +397,26 @@
|
|
| 378 |
}
|
| 379 |
});
|
| 380 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
onMount(() => {
|
| 382 |
briefingState.reset();
|
| 383 |
if (!queryText()) return;
|
|
@@ -415,13 +454,20 @@
|
|
| 415 |
applyStepEventToLiveState(liveResults, s.step, s.result, s.ok);
|
| 416 |
liveTick = liveTick + 1;
|
| 417 |
|
| 418 |
-
// address from the geocode step (single_address / live_now)
|
|
|
|
| 419 |
if (s.step === 'geocode') {
|
| 420 |
if (s.ok && s.result && typeof s.result === 'object') {
|
| 421 |
const r = s.result as Record<string, unknown>;
|
| 422 |
if (typeof r.lat === 'number' && typeof r.lon === 'number') {
|
| 423 |
const label = (typeof r.address === 'string' ? r.address : queryText()) as string;
|
| 424 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
geocodeSucceeded = true;
|
| 426 |
}
|
| 427 |
} else {
|
|
@@ -429,6 +475,13 @@
|
|
| 429 |
errorState = 'geocoder';
|
| 430 |
}
|
| 431 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
// address from the nta_resolve step (neighborhood / development_check)
|
| 433 |
if (s.step === 'nta_resolve' && s.ok && s.result && typeof s.result === 'object') {
|
| 434 |
const r = s.result as Record<string, unknown>;
|
|
@@ -649,6 +702,8 @@
|
|
| 649 |
paragraph={finalResult.paragraph}
|
| 650 |
{citations}
|
| 651 |
targets={finalResult.targets}
|
|
|
|
|
|
|
| 652 |
/>
|
| 653 |
{:else if blocks.length}
|
| 654 |
<Briefing {blocks} {citations} streaming={false} />
|
|
@@ -680,7 +735,11 @@
|
|
| 680 |
<aside id="region-map" class="app-region app-region-map" aria-label="Map region">
|
| 681 |
<header class="region-head">
|
| 682 |
<span class="section-label">Map</span>
|
| 683 |
-
{#if
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
<span class="region-head-meta">
|
| 685 |
Carto Positron · z15 · {address.lat.toFixed(4)}°N {Math.abs(address.lon).toFixed(4)}°W
|
| 686 |
</span>
|
|
@@ -688,7 +747,42 @@
|
|
| 688 |
<span class="region-head-meta">awaiting geocode…</span>
|
| 689 |
{/if}
|
| 690 |
</header>
|
| 691 |
-
{#if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 692 |
<div style="position: relative; flex: 1; min-height: 0;">
|
| 693 |
<RipMap
|
| 694 |
{address}
|
|
@@ -735,6 +829,27 @@
|
|
| 735 |
</section>
|
| 736 |
|
| 737 |
<style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 738 |
.plan-details {
|
| 739 |
border: 1px solid var(--rule-soft);
|
| 740 |
background: var(--paper-deep);
|
|
|
|
| 293 |
let terramindLulcFc = $state<FeatureCollection | undefined>(undefined);
|
| 294 |
let idaHwmFc = $state<FeatureCollection | undefined>(undefined);
|
| 295 |
|
| 296 |
+
// Compare-intent: independent geocoded addresses per place.
|
| 297 |
+
// Populated from geocode step events tagged with target_label.
|
| 298 |
+
let compareAddressA = $state<{ label: string; lat: number; lon: number; source: AddressSource } | null>(null);
|
| 299 |
+
let compareAddressB = $state<{ label: string; lat: number; lon: number; source: AddressSource } | null>(null);
|
| 300 |
+
// Per-place step result payloads for the structured diff strip.
|
| 301 |
+
let compareStepsA = $state<Record<string, unknown>>({});
|
| 302 |
+
let compareStepsB = $state<Record<string, unknown>>({});
|
| 303 |
+
// Per-place map layers for compare intent.
|
| 304 |
+
let sandyFcA = $state<FeatureCollection | undefined>(undefined);
|
| 305 |
+
let depFcA = $state<FeatureCollection | undefined>(undefined);
|
| 306 |
+
let synFcA = $state<FeatureCollection | undefined>(undefined);
|
| 307 |
+
let proxyFcA = $state<FeatureCollection | undefined>(undefined);
|
| 308 |
+
let idaHwmFcA = $state<FeatureCollection | undefined>(undefined);
|
| 309 |
+
let sandyFcB = $state<FeatureCollection | undefined>(undefined);
|
| 310 |
+
let depFcB = $state<FeatureCollection | undefined>(undefined);
|
| 311 |
+
let synFcB = $state<FeatureCollection | undefined>(undefined);
|
| 312 |
+
let proxyFcB = $state<FeatureCollection | undefined>(undefined);
|
| 313 |
+
let idaHwmFcB = $state<FeatureCollection | undefined>(undefined);
|
| 314 |
+
|
| 315 |
// Per-tier feature counts for the map legend. Layers with 0 are
|
| 316 |
// dropped from the legend display per the silence-over-confabulation
|
| 317 |
// rule (handoff hard rule #3).
|
|
|
|
| 397 |
}
|
| 398 |
});
|
| 399 |
|
| 400 |
+
// Compare-intent: load map layers independently for each place.
|
| 401 |
+
$effect(() => {
|
| 402 |
+
if (!compareAddressA) return;
|
| 403 |
+
const { lat, lon } = compareAddressA;
|
| 404 |
+
fetchSandy(lat, lon).then((fc) => (sandyFcA = fc));
|
| 405 |
+
fetchDep(lat, lon).then((fc) => (depFcA = fc));
|
| 406 |
+
fetchPrithviSynthetic(lat, lon).then((fc) => (synFcA = fc));
|
| 407 |
+
fetchProxyDots(lat, lon).then((fc) => (proxyFcA = fc));
|
| 408 |
+
fetchIdaHwm(lat, lon).then((fc) => (idaHwmFcA = fc));
|
| 409 |
+
});
|
| 410 |
+
$effect(() => {
|
| 411 |
+
if (!compareAddressB) return;
|
| 412 |
+
const { lat, lon } = compareAddressB;
|
| 413 |
+
fetchSandy(lat, lon).then((fc) => (sandyFcB = fc));
|
| 414 |
+
fetchDep(lat, lon).then((fc) => (depFcB = fc));
|
| 415 |
+
fetchPrithviSynthetic(lat, lon).then((fc) => (synFcB = fc));
|
| 416 |
+
fetchProxyDots(lat, lon).then((fc) => (proxyFcB = fc));
|
| 417 |
+
fetchIdaHwm(lat, lon).then((fc) => (idaHwmFcB = fc));
|
| 418 |
+
});
|
| 419 |
+
|
| 420 |
onMount(() => {
|
| 421 |
briefingState.reset();
|
| 422 |
if (!queryText()) return;
|
|
|
|
| 454 |
applyStepEventToLiveState(liveResults, s.step, s.result, s.ok);
|
| 455 |
liveTick = liveTick + 1;
|
| 456 |
|
| 457 |
+
// address from the geocode step (single_address / live_now / compare).
|
| 458 |
+
// Compare emits two geocode steps tagged target_label: "PLACE A" / "PLACE B".
|
| 459 |
if (s.step === 'geocode') {
|
| 460 |
if (s.ok && s.result && typeof s.result === 'object') {
|
| 461 |
const r = s.result as Record<string, unknown>;
|
| 462 |
if (typeof r.lat === 'number' && typeof r.lon === 'number') {
|
| 463 |
const label = (typeof r.address === 'string' ? r.address : queryText()) as string;
|
| 464 |
+
if (s.target_label === 'PLACE A') {
|
| 465 |
+
compareAddressA = { label, lat: r.lat, lon: r.lon, source: 'geocode' };
|
| 466 |
+
} else if (s.target_label === 'PLACE B') {
|
| 467 |
+
compareAddressB = { label, lat: r.lat, lon: r.lon, source: 'geocode' };
|
| 468 |
+
} else {
|
| 469 |
+
address = { label, lat: r.lat, lon: r.lon, source: 'geocode' };
|
| 470 |
+
}
|
| 471 |
geocodeSucceeded = true;
|
| 472 |
}
|
| 473 |
} else {
|
|
|
|
| 475 |
errorState = 'geocoder';
|
| 476 |
}
|
| 477 |
}
|
| 478 |
+
// Accumulate per-place step results for the compare structured diff strip.
|
| 479 |
+
if (s.target_label === 'PLACE A') {
|
| 480 |
+
compareStepsA = { ...compareStepsA, [s.step]: s.result };
|
| 481 |
+
} else if (s.target_label === 'PLACE B') {
|
| 482 |
+
compareStepsB = { ...compareStepsB, [s.step]: s.result };
|
| 483 |
+
}
|
| 484 |
+
|
| 485 |
// address from the nta_resolve step (neighborhood / development_check)
|
| 486 |
if (s.step === 'nta_resolve' && s.ok && s.result && typeof s.result === 'object') {
|
| 487 |
const r = s.result as Record<string, unknown>;
|
|
|
|
| 702 |
paragraph={finalResult.paragraph}
|
| 703 |
{citations}
|
| 704 |
targets={finalResult.targets}
|
| 705 |
+
structuredA={compareStepsA}
|
| 706 |
+
structuredB={compareStepsB}
|
| 707 |
/>
|
| 708 |
{:else if blocks.length}
|
| 709 |
<Briefing {blocks} {citations} streaming={false} />
|
|
|
|
| 735 |
<aside id="region-map" class="app-region app-region-map" aria-label="Map region">
|
| 736 |
<header class="region-head">
|
| 737 |
<span class="section-label">Map</span>
|
| 738 |
+
{#if plan?.intent === 'compare'}
|
| 739 |
+
<span class="region-head-meta">
|
| 740 |
+
{#if compareAddressA || compareAddressB}Carto Positron · z15 · 2 locations{:else}awaiting geocode…{/if}
|
| 741 |
+
</span>
|
| 742 |
+
{:else if address}
|
| 743 |
<span class="region-head-meta">
|
| 744 |
Carto Positron · z15 · {address.lat.toFixed(4)}°N {Math.abs(address.lon).toFixed(4)}°W
|
| 745 |
</span>
|
|
|
|
| 747 |
<span class="region-head-meta">awaiting geocode…</span>
|
| 748 |
{/if}
|
| 749 |
</header>
|
| 750 |
+
{#if plan?.intent === 'compare'}
|
| 751 |
+
<div class="compare-map-stack">
|
| 752 |
+
{#if compareAddressA}
|
| 753 |
+
<div class="compare-map-place">
|
| 754 |
+
<div class="compare-map-label">A · {compareAddressA.label}</div>
|
| 755 |
+
<div style="position: relative;">
|
| 756 |
+
<RipMap
|
| 757 |
+
address={compareAddressA}
|
| 758 |
+
activeLayers={active}
|
| 759 |
+
sandyEmpirical={sandyFcA}
|
| 760 |
+
depModeled={depFcA}
|
| 761 |
+
syntheticPrior={synFcA}
|
| 762 |
+
proxy311={proxyFcA}
|
| 763 |
+
idaHwm={idaHwmFcA}
|
| 764 |
+
/>
|
| 765 |
+
</div>
|
| 766 |
+
</div>
|
| 767 |
+
{/if}
|
| 768 |
+
{#if compareAddressB}
|
| 769 |
+
<div class="compare-map-place">
|
| 770 |
+
<div class="compare-map-label">B · {compareAddressB.label}</div>
|
| 771 |
+
<div style="position: relative;">
|
| 772 |
+
<RipMap
|
| 773 |
+
address={compareAddressB}
|
| 774 |
+
activeLayers={active}
|
| 775 |
+
sandyEmpirical={sandyFcB}
|
| 776 |
+
depModeled={depFcB}
|
| 777 |
+
syntheticPrior={synFcB}
|
| 778 |
+
proxy311={proxyFcB}
|
| 779 |
+
idaHwm={idaHwmFcB}
|
| 780 |
+
/>
|
| 781 |
+
</div>
|
| 782 |
+
</div>
|
| 783 |
+
{/if}
|
| 784 |
+
</div>
|
| 785 |
+
{:else if address}
|
| 786 |
<div style="position: relative; flex: 1; min-height: 0;">
|
| 787 |
<RipMap
|
| 788 |
{address}
|
|
|
|
| 829 |
</section>
|
| 830 |
|
| 831 |
<style>
|
| 832 |
+
.compare-map-stack {
|
| 833 |
+
display: flex;
|
| 834 |
+
flex-direction: column;
|
| 835 |
+
gap: var(--s-3, 8px);
|
| 836 |
+
padding-top: 4px;
|
| 837 |
+
}
|
| 838 |
+
.compare-map-place {
|
| 839 |
+
display: flex;
|
| 840 |
+
flex-direction: column;
|
| 841 |
+
}
|
| 842 |
+
.compare-map-label {
|
| 843 |
+
font-family: var(--font-mono);
|
| 844 |
+
font-size: 11px;
|
| 845 |
+
font-weight: 600;
|
| 846 |
+
letter-spacing: 0.06em;
|
| 847 |
+
text-transform: uppercase;
|
| 848 |
+
color: var(--ink-secondary);
|
| 849 |
+
padding: 2px 0 4px;
|
| 850 |
+
border-bottom: 1px solid var(--rule-soft);
|
| 851 |
+
margin-bottom: 4px;
|
| 852 |
+
}
|
| 853 |
.plan-details {
|
| 854 |
border: 1px solid var(--rule-soft);
|
| 855 |
background: var(--paper-deep);
|