File size: 16,568 Bytes
c2dcb19
 
 
202b514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0cb5613
 
 
c2dcb19
 
202b514
 
 
 
d81c4fa
 
 
 
202b514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
00b6baf
 
 
 
 
 
0cb5613
 
202b514
 
 
 
00b6baf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0cb5613
00b6baf
 
0cb5613
00b6baf
 
 
 
 
0cb5613
202b514
 
 
 
 
 
 
 
 
00b6baf
 
 
 
 
0cb5613
00b6baf
202b514
 
 
 
 
 
 
 
 
 
 
 
d81c4fa
202b514
 
 
 
 
 
 
 
 
 
 
 
 
 
00b6baf
 
 
 
 
 
 
 
202b514
 
 
 
 
 
 
 
 
00b6baf
202b514
00b6baf
202b514
00b6baf
 
 
 
 
 
 
 
 
202b514
00b6baf
202b514
 
 
00b6baf
202b514
 
00b6baf
 
 
 
 
202b514
00b6baf
202b514
00b6baf
 
 
 
202b514
00b6baf
202b514
 
 
 
00b6baf
202b514
0cb5613
 
202b514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
00b6baf
202b514
 
00b6baf
 
202b514
 
00b6baf
 
 
 
202b514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
00b6baf
202b514
00b6baf
 
202b514
0cb5613
00b6baf
 
 
 
 
 
 
 
 
 
202b514
00b6baf
202b514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
---
title: 6ixPulse
sdk: docker
app_port: 7860
license: other
tags:
  - build-small-hackathon
  - backyard-ai
  - best-agent
  - best-use-of-codex
  - off-brand
  - housing
  - maps
  - agentic-workflow
  - toronto
  - track:backyard
  - track:wood
  - sponsor:openbmb
  - sponsor:openai
  - sponsor:nvidia
  - achievement:offbrand
  - achievement:llama
  - achievement:sharing
  - achievement:fieldnotes
models:
  - nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16
  - Qwen/Qwen3-Coder-30B-A3B-Instruct
  - openbmb/MiniCPM4-0.5B
  - hexgrad/Kokoro-82M
---

# 6ixPulse

6ixPulse is an agentic Toronto housing intelligence map for renters who do not want to choose a neighborhood from vibes, stale listicles, or one-off anecdotes.

## Codex Attribution

This repository was developed with assistance from OpenAI Codex for implementation, debugging, verification, and repository preparation. Product direction, final decisions, and submission review remain human-owned.

## Why I Built This

I am looking to move, and moving means doing a lot of research before committing to a place. Rent is only one part of the decision. I also need to understand commute time, safety signals, neighborhood feel, access to daily needs, and whether an area is getting better or just getting more expensive.

6ixPulse turns that research process into a map-first agent workflow. I can ask something like:

```text
I want to move to Toronto, make $110000, work at Union Station, need safe streets,
cafes, and rent under $2600.
```

The app then ranks Toronto neighborhoods, animates the map to the areas it is researching, and dispatches specialized agents for affordability, commute, safety, lifestyle, future growth, and final recommendation.

## What It Does

- **Plans first.** Before any agent works, the model lays out a task for each City Agent and the source it will use.
- **Discovers neighbourhoods.** The agentic model picks the Toronto areas that fit the prompt and supplies their coordinates — nothing about the candidate set is hardcoded; the map draws what the model found.
- **Scores all eight dimensions** — Affordability, Safety, Commute, Transit, Amenities, Lifestyle, Growth, and an overall Match — for every candidate, each computed from a real, named, no-key source.
- **Fans out a researcher per City Agent.** Affordability, Commute, Safety, Lifestyle and Growth agents each reason over only their own evidence; a **Recommendation agent runs last** with every agent's finding *and* all of their sources.
- **Fails closed.** The UI withholds a rent, commute, safety, growth claim, or score unless the backend can tie it to source evidence — sources are named, never "S1".
- **Shows a real Mapbox GL JS 3D map** with a research tour that zooms into each area while it is being researched, then flies out-and-in to the next.
- **Reads the findings aloud.** A Play button narrates a short spoken summary using **Kokoro-82M** neural TTS running entirely in the browser — no key, no backend.
- **Models, all < 32B.** NVIDIA Nemotron Nano Omni Reasoning is the main brain (with `enable_thinking`); the small OpenBMB model on llama.cpp assists with summarisation; Kokoro-82M handles speech. Falls back to HF when a provider is unavailable.

## Architecture

```mermaid
flowchart TB
  subgraph FE["Frontend · React 19 + Vite"]
    Prompt["Renter prompt<br/>budget · commute · safety · vibe"]
    MapUI["Mapbox GL JS + deck.gl<br/>3D map · research tour"]
    Panels["Agent cards · 8-dimension scores<br/>research brief · compare · listings"]
  end

  subgraph Space["Hugging Face Space · Docker"]
    Gradio["gradio.Server (FastAPI)<br/>serves the React UI + /run_agent MCP tool"]
    API["Node agent backend<br/>POST /api/agent/run"]
  end

  Prompt --> Gradio --> API

  subgraph Pipeline["Agentic pipeline · every step is traced"]
    direction TB
    Plan["1 · Plan<br/>intent + a task per City Agent"]
    Discover["2 · Discover neighbourhoods<br/>model picks Toronto areas + coordinates<br/>(nothing hardcoded)"]
    Research["3 · Gather official evidence"]
    Score["4 · Score 8 dimensions<br/>from named sources"]
    Fanout["5 · City Agent fan-out<br/>each researches its own dimension"]
    Recommend["6 · Recommendation agent<br/>weighs every agent + all their sources"]
    Policy["7 · Evidence policy<br/>fail-closed: no source, no claim"]
    Plan --> Discover --> Research --> Score --> Fanout --> Recommend --> Policy
  end

  API --> Plan
  Policy --> API --> Gradio --> Panels
  Discover -. coordinates .-> MapUI

  subgraph Sources["No-key data sources"]
    Police["Toronto Police Service<br/>crime rates → Safety"]
    OSM["OpenStreetMap (Overpass)<br/>cafes · parks · transit · builds<br/>→ Amenities / Lifestyle / Transit / Growth"]
    Transit["TTC + GO / distance to Union<br/>→ Commute"]
    CMHC["CMHC + density model<br/>→ Affordability"]
    OpenData["Toronto Open Data + StatCan<br/>permits · GTFS · census"]
    Wiki["Wikipedia REST<br/>→ coordinates"]
  end
  Research --- OpenData
  Research --- Wiki
  Score --- Police
  Score --- OSM
  Score --- Transit
  Score --- CMHC

  subgraph Models["Models · all under 32B"]
    Main["Main: NVIDIA Nemotron Nano Omni Reasoning<br/>temp 0.6 · enable_thinking · reasoning_budget<br/>↳ fallback: HF Qwen3-Coder"]
    Assist["Assistant: OpenBMB MiniCPM via llama.cpp<br/>summarises each agent's evidence"]
    Speech["Voice: Kokoro-82M neural TTS<br/>in-browser, narrates the summary"]
  end
  Discover --- Main
  Fanout --- Assist
  Fanout --- Main
  Recommend --- Main
  Panels -. Play .-> Speech
```

## Tech Stack

- Frontend: React 19, TypeScript, Vite
- Map: Mapbox GL JS, custom Mapbox style, deck.gl
- UI: custom CSS, lucide-react icons, map-matched monochrome palette
- Backend: Node HTTP server
- Space wrapper: `gradio.Server` on FastAPI, Docker Space
- Agent orchestration: plan → discover → research → score → fan-out → recommend, fully traced
- Data: Toronto Open Data, Toronto Police, OpenStreetMap (Overpass), CMHC, StatCan, Wikipedia — all no-key
- Models (all < 32B):
  - Main agentic brain: `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning` (build.nvidia.com)
  - Summarisation assistant: OpenBMB MiniCPM via llama.cpp (`openbmb/MiniCPM4-*-GGUF`)
  - Speech: `hexgrad/Kokoro-82M` neural TTS in the browser (transformers.js)
  - Fallback: `Qwen/Qwen3-Coder-30B-A3B-Instruct` on the HF Router

## Build Small Hackathon Readiness

The official Build Small Field Guide requires every model to stay under 32B parameters, a Gradio app in the Build Small Hugging Face org, a demo video, a social post, and README tags/write-up.

| Requirement | Status | Notes |
| --- | --- | --- |
| Model under 32B | On track | Primary model is `nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16`. The NVIDIA model card describes it as a 31B / A3B MoE with about 3B active parameters per token. |
| Practical track | On track | This fits Backyard AI: a personal daily-life tool for choosing where to live. |
| Agentic app | On track | Multi-step planning, tool use, domain-scoped search, evidence policy, and model synthesis. |
| Custom UI | On track | The app is a custom map-first interface rather than default Gradio components. |
| README tags | Done | Tags are in the YAML block at the top of this README. |
| Codex prize | Done | This README includes Codex attribution, and the repo is being prepared and pushed through Codex. Keep Codex-attributed commits in the connected GitHub repo or Space history. |
| Gradio Space | Uploaded | Live Space: https://huggingface.co/spaces/build-small-hackathon/6ixPulse. Add Space secrets before judging so Mapbox and NVIDIA calls are live. |
| Demo video | Pending | Add a public demo video link before submission. |
| Social post | Pending | Add a social post link before submission. |

Suggested target categories:

- Backyard AI
- Best Agent
- Best Use of Codex
- Off Brand
- Best Demo, once the demo video and social post are ready

## Runtime Flow

1. The user writes a housing prompt in the Ask 6ixPulse composer; the frontend calls `POST /api/agent/run`.
2. **Plan** — the model parses intent (budget, commute cap, priority weights) and lays out a task for each City Agent.
3. **Discover** — the model names the Toronto neighbourhoods that fit and supplies their coordinates. No candidate list is hardcoded; the map renders what the model returns.
4. **Gather evidence** — official Toronto data is pulled per area: Toronto Police crime rates, building permits, TTC GTFS, StatCan, and Wikipedia coordinates.
5. **Score** — for every candidate, all eight dimensions are computed from named sources (see below) and a weighted Match.
6. **Fan-out** — each City Agent reasons over only its dimension's evidence; the **Recommendation agent runs last** with every finding and all sources, using the reasoning model's thinking mode.
7. **Evidence policy** — any claim without a matched source or computed fact is hidden ("needs source").
8. The map, agent cards, 8-dimension scores, research brief, and comparison update from the same structured, source-cited result.

## Source-Backed Display Policy

6ixPulse intentionally fails closed.

Local neighborhood rows seed the workflow, but they are not treated as truth. The app does not present a rent range, commute score, safety claim, growth claim, or final agent score unless the backend can connect that category to source evidence or computed facts.

This matters because housing decisions are high-stakes. A pretty map with fake confidence is worse than a map that admits where research is incomplete.

## Data Sources & Scoring

Every dimension shown in the UI is computed from a real, no-key source and labelled with that source's name (never "S1"):

| Dimension | Source |
| --- | --- |
| Safety | **Toronto Police Service** — Neighbourhood Crime Rates (Toronto Open Data) |
| Commute | **Distance to Union Station** over the TTC + GO (Metrolinx) network |
| Transit | **TTC** stops & stations via **OpenStreetMap** |
| Amenities · Lifestyle · Growth | **OpenStreetMap (Overpass)** — cafés, restaurants, parks, groceries, construction sites |
| Affordability | **CMHC** rental-market context + a distance-to-core / density model |
| Match | weighted blend of the above, against the renter's stated priorities |
| Coordinates | **Wikipedia REST** (for placing discovered areas on the map) |

These are all official public APIs, so the hosted Space runs **without any scraping or bypass tooling**. Optional keyed web search (Google CSE, SerpAPI, Brave, Tavily) can be enabled for extra context, but it is off by default and never required.

## Models

Two tiers, both under 32B parameters:

```bash
# Main agentic brain: NVIDIA Nemotron Nano Omni Reasoning (build.nvidia.com).
# Used for discovery, the per-agent findings, and the final recommendation (with thinking).
AGENT_MODEL_PROVIDER=auto              # tries Nemotron first, then HF
NVIDIA_API_KEY=your_nvapi_key
NVIDIA_MODEL=nvidia/nemotron-3-nano-omni-30b-a3b-reasoning
NVIDIA_BASE_URL=https://integrate.api.nvidia.com/v1
NVIDIA_ENABLE_THINKING=1              # temperature 0.6, top_p 0.95, reasoning_budget

# Summarisation assistant: small OpenBMB model through the llama.cpp runtime.
LLAMACPP_ENABLED=1
LLAMACPP_MODEL=openbmb/MiniCPM4-0.5B  # or MiniCPM4-8B-GGUF for stronger summaries
AGENT_SUMMARIZER_PROVIDER=llamacpp

# Fallback when no key/runtime is available, so the app always responds.
HF_TOKEN=your_hugging_face_token
HF_MODEL=Qwen/Qwen3-Coder-30B-A3B-Instruct
```

The reasoning model uses the Nemotron recipe (matching `ChatNVIDIA`): `temperature=0.6`, `top_p=0.95`, `chat_template_kwargs={"enable_thinking": true}`, and a `reasoning_budget`; its `<think>` reasoning is captured separately from the JSON answer. Thinking is enabled only for the decisions (discovery, recommendation, synthesis), and the per-agent fan-out runs in parallel on hosted APIs, so a full run stays within the response budget.

For speech, the Play button uses **`hexgrad/Kokoro-82M`** neural TTS via `transformers.js`, running fully in the browser (ONNX/WASM). It is lazy-loaded — the ~80 MB model only downloads on the first Play, then is cached — and falls back to the browser's built-in speech while it loads or where it can't run. No key, no backend, 82M parameters.

## Gradio Space

This repo is prepared for a Docker-backed Gradio Space using `gradio.Server`, which is designed for custom frontends like React while still giving the project Gradio's API engine, queuing, MCP support, and Hugging Face Spaces hosting.

Live Space:

```text
https://huggingface.co/spaces/build-small-hackathon/6ixPulse
```

The Space entrypoint is:

```text
app.py
```

What it does:

- starts the Node agent backend on `127.0.0.1:8787`
- serves the built React app from `dist/`
- injects runtime Mapbox config from Space secrets
- proxies the existing frontend calls to `/api/agent/run`
- exposes a Gradio API endpoint and MCP tool named `/run_agent`

Required Space secrets:

```bash
VITE_MAPBOX_TOKEN=your_mapbox_token
NVIDIA_API_KEY=your_nvidia_api_key
```

Local secrets are intentionally not committed or uploaded. Add these in the Space settings before final judging.

Recommended Space variables (the Docker image already sets these):

```bash
AGENT_MODEL_PROVIDER=auto
NVIDIA_MODEL=nvidia/nemotron-3-nano-omni-30b-a3b-reasoning
NVIDIA_BASE_URL=https://integrate.api.nvidia.com/v1
NVIDIA_ENABLE_THINKING=1
AGENT_DISCOVER=1
AGENT_FANOUT=1
SEARCH_PROVIDER=disabled        # official-data-only; no scraping on the hosted Space
OFFICIAL_DATA_ENABLED=1
```

Local Space-style run:

```bash
npm install
npm run build
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py
```

Open:

```text
http://127.0.0.1:7860/
```

## Running Locally

```bash
npm install
cp .env.example .env
npm run dev:full
```

Open:

```text
http://127.0.0.1:5173/
```

Set your Mapbox token before running:

```bash
VITE_MAPBOX_TOKEN=your_token_here
VITE_MAPBOX_STYLE_URL=mapbox://styles/ownpath/cmqe4wg8h005001s4bjx9461m
```

Run services separately:

```bash
npm run dev:api
npm run dev
```

Health checks:

```bash
curl http://127.0.0.1:8787/api/agent/health
curl http://127.0.0.1:8787/api/agent/search/health
```

## Project Structure

```text
app.py                         Gradio Server wrapper for the Space runtime
Dockerfile                     Docker Space image with Node + Python
src/App.tsx                    app shell, agent panels, scores, listings
src/components/MapCanvas.tsx   Mapbox/deck.gl map + research tour
src/lib/agentApi.ts            frontend API client types
src/lib/tts.ts                 Kokoro-82M in-browser narration
server/index.mjs               agent API server; orchestrates the pipeline
server/discover.mjs            model-driven neighbourhood discovery (+ coordinates)
server/score-tools.mjs         8-dimension scoring from named no-key sources
server/agent-fanout.mjs        per-agent City Agents + Recommendation agent
server/model-chat.mjs          provider routing: main brain vs. summariser
server/agent-core.mjs          plan, trace, and the fail-closed evidence policy
server/research-tools.mjs      Toronto Open Data + crime rates + coordinates
server/nvidia-client.mjs       NVIDIA Nemotron (reasoning) client
server/llamacpp-client.mjs     llama.cpp / OpenBMB client
server/hf-client.mjs           Hugging Face Router fallback client
scripts/dev-full.mjs           starts frontend and backend together
scripts/llama-server.sh        launches a local OpenBMB GGUF via llama.cpp
```

## Verification

```bash
node --check server/open-websearch-mcp.mjs
node --check server/research-tools.mjs
node --check server/index.mjs
npm run build
```

Current local verification:

- Node syntax checks pass.
- TypeScript + Vite production build passes.
- Browser UI check: map-focus mode collapses the side panels into edge tabs and restores them.

## Submission To Finish

Before final Build Small submission:

1. Add `VITE_MAPBOX_TOKEN` and `NVIDIA_API_KEY` in the Hugging Face Space secrets panel.
2. Wait for the Docker Space build to finish and confirm the live map loads.
3. Record and link a demo video.
4. Publish and link a social post.
5. Add `NVIDIA_API_KEY` (nvapi-…) to run Nemotron (`nvidia/nemotron-3-nano-omni-30b-a3b-reasoning`), or run a small GGUF locally via `npm run llama:serve` (llama.cpp / OpenBMB).
6. Keep the GitHub repo or Space history connected with Codex-attributed commits for the Codex prize.