# Project export: RELAY

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: RELAY — when disaster strikes, language shouldn't be a barrier to survival. RELAY is a multilingual voice intake that triages crisis calls and routes people to real help, instantly.
- Devpost: https://devpost.com/software/relay-g4i2bc
- GitHub: https://github.com/nysh9/Relay
- Video: https://www.youtube.com/embed/_nhvEufuK18?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — nysh9 (6 commits), Meghna Adduri (6 commits), Person D (2 commits), Claude Opus 4.8 (1 commits)

## Devpost submission (written by the team)

### Inspiration

During Hurricane Harvey in 2017, Houston's 911 system received over 56,000 calls in a single day, which is more than double its normal volume. Operators were overwhelmed. And that was in English. For the tens of thousands of Houston residents who speak Spanish, Vietnamese, Hindi, or Arabic as their primary language, the barrier wasn't just the flood. It was the phone call itself. Relief organizations running mass-care operations face a version of this problem every time a major disaster hits: thousands of simultaneous calls, dozens of languages, and no free human to triage each one. Translation lines exist, but they add minutes per call — minutes that matter when someone hasn't had water in two days. We built RELAY because the problem isn't a lack of resources. It's a routing problem. The shelter exists. The water station is 1.2km away. The gap is connecting the person in crisis to the resource in real time, in their language, without a human bottleneck.

### What it does

RELAY is a multilingual voice intake and routing agent for disaster-relief organizations. A caller in crisis speaks in their own language. RELAY: Listens — transcribes the call in real time using Deepgram streaming STT, with support for multiple languages through automatic language detection Understands — Claude reads the transcript and extracts structured triage data: number of people, injuries, location, and needs. If critical information is missing, RELAY asks a follow-up question in the caller's language rather than triaging on incomplete data Matches — a deterministic routing engine filters real relief resources by availability and capability, ranks by distance, and produces a dispatch Shows — a live map surfaces the caller's location, nearby resources, and a routed line to the best match, color-coded by priority (P1/P2/P3) RELAY is not a 911 replacement. It is the multilingual intake layer for the moment the formal system is overwhelmed — when volume and language barriers are too high for humans to triage alone.

### How we built it

The pipeline has four stages, each owned by one team member and built against locked data contracts so all four could develop in parallel: The Ear (Deepgram) — a Node.js WebSocket server captures browser mic audio, downsamples it to 16-bit PCM at 16kHz, and streams it to Deepgram's streaming STT API. Deepgram handles multilingual transcription, interim results (live "typing" effect), and turn-taking via utterance_end events. A confidence threshold of $c \geq 0.6$ gates whether a transcript is sent forward or triggers a re-prompt. After $N = 2$ failed attempts, the system escalates to a human operator. The Brain (Claude / Anthropic) — on each final utterance, the transcript is sent to Claude with a strict system prompt that returns only a structured Triage JSON object. Claude extracts people count, injuries, location (as stated by the caller, never device GPS), needs, and priority. Required slots are: location, people, needs. If any are missing, Claude sets nextQuestion and readyToRoute: false. The Brain never decides the match — it only extracts facts. The Matchmaker — a pure TypeScript function: $$\text{score}(r) = \text{distanceKm}(\text{caller}, r) \quad \text{subject to: } r.\text{has} \cap \text{needs} \neq \emptyset \text{ and } r.\text{availableCapacity} > 0$$ Candidates are filtered by capability and availability, then ranked by distance using the Haversine formula. The top match and two alternatives are returned as a Dispatch object. If nothing fits, matched is null — the function never fabricates a resource. The Map (Mapbox GL JS) — a Next.js frontend renders the caller pin, resource pins, and an animated routing line to the matched resource. The triage card shows the P1/P2/P3 priority chip, dispatch text, and an escalation banner for "911" or "human" cases. Stack: Next.js (App Router) + TypeScript + Tailwind · Node.js WebSocket server · Deepgram Streaming STT · Anthropic Claude API · Mapbox GL JS + Geocoding API · Redis (session memory)

### Challenges we ran into

WebSocket audio streaming was the riskiest piece of plumbing in the project. Getting the mic audio from the browser into a format Deepgram accepts — 16-bit PCM, mono, 16kHz — required a custom AudioWorklet (pcm-worklet.js) to downsample from the browser's native sample rate. A subtle buffering bug caused dropped audio frames that looked like low-confidence transcripts, which took several hours to isolate. Keeping Claude grounded took significant prompt engineering. Early versions of the Brain occasionally invented a location or returned prose mixed into the JSON. The fix was a combination of strict output schema enforcement in the prompt, a JSON parse validation layer, and a fallback triage that escalates to a human rather than guessing when the parse fails. Parallel development across four people meant the data contracts in types/index.ts were locked before a line of UI or audio code was written. Even then, Person B and Person C had subtle type mismatches in the Triage.location shape and the Dispatch.matched nullability that only surfaced at the chain-it integration session. We resolved them by treating B's brain/src/types.ts as the source of truth for Triage and C's types/index.ts as the source of truth for Dispatch and Resource. Git merge conflicts in package.json and package-lock.json appeared on nearly every branch merge because each sub-project (brain/, server/, root) added its own dependencies. The reliable fix was deleting package-lock.json and running npm install fresh after each merge rather than hand-resolving the lock file. Live voice in a noisy room remains the demo's biggest risk. We built and rehearsed a full backup audio clip path through the same Deepgram pipeline so the fallback is indistinguishable from the live run.

### Accomplishments we're proud of

The Matchmaker is fully testable without AI. Because routing is deterministic TypeScript with no Claude dependency, we could prove the guardrails work — especially the matched: null case — with plain unit tests before a single line of audio or UI code existed. Judges can read the code and see exactly why it can never fabricate a resource. The follow-up question loop. When the Brain detects a missing required slot, RELAY asks one calm, targeted follow-up in the caller's language rather than routing on incomplete information. This is the single beat that makes RELAY an agent rather than a transcriber — and it was deliberately scripted into the hero call so judges see it in the demo. Four people, four parallel workstreams, zero blocking. By locking the data contracts on day one and having each person build against typed stubs rather than each other's actual code, we reached the chain-it integration session with four independently working stages. The integration bugs that surfaced were in the contracts, not the implementations — which is exactly what you want. 18 real Houston relief resources with real addresses, coordinates, and phone numbers — scoped to the Texas Medical Center cluster, downtown shelters, and Red Cross distribution points. The dataset is small enough to be fast and honest enough to be credible.

### What we learned

The moat is in the routing, not the translation. Early in the project we spent energy on the voice pipeline. The thing judges respond to is the moment the map lights up — when chaos becomes a routed dispatch. The translation is what gets us in the door; the Matchmaker is the product. Structured output discipline is harder than it looks. Getting an LLM to return only valid JSON, every time, with no markdown fences and no prose, requires more than one instruction in the prompt. It requires schema validation, a fallback path, and a test suite that runs against the real API — not a mock. Escalation is a feature, not a failure. The system's willingness to say "I don't know enough to route this — routing to a human" is the thing that makes it trustworthy in a crisis context. We learned to treat every escalation path as a product decision, not an error case. Git discipline under time pressure saves hours. The practice of one branch per person, merge into an integration branch first, test before merging to main — this slowed down the first merge and saved us on every subsequent one.

### What's next

Real capacity feeds. The current dataset is a realistic mock. FEMA's National Shelter System (NSS) publishes real shelter data during activations. Connecting RELAY to a live NSS feed would turn the mock dataset into ground truth. Phone intake via Twilio. The demo uses a browser mic to keep the stack simple. In real deployment, callers need a phone number — a Twilio IVR that pipes audio into the same Deepgram WebSocket. This is the roadmap item closest to production-ready. The urgency classifier. A TF-IDF + Logistic Regression classifier trained on synthetic labeled transcripts would replace Claude's priority field with a measured, auditable score. The story: "it never confuses a P1 for a P3," demonstrated with a confusion matrix. This is the "real ML, not a wrapper" credibility layer. Human-operator dashboard. The current UI surfaces escalations but doesn't give a human operator a full intake view. A dispatcher panel — showing all active calls, their priority, and one-click confirmation — is the next UI milestone.

## README (from the GitHub repository)

# RELAY

**Built at Berkeley AI Hackathon 2026.**

RELAY is an operator-facing disaster-relief intake dashboard. A caller speaks
in their own language (demo: Hindi) → speech is transcribed live → Claude
extracts a structured triage (who, where, how many, what's needed, how urgent)
→ a deterministic matchmaker routes them to the nearest resource with
capacity → a dispatcher watches it all resolve on a map in real time.

```
LISTEN → UNDERSTAND → MATCH → SHOW
 (Ear)    (Brain)   (Matchmaker) (Map)
```

No AI invents a resource or a dispatch decision — matching is deterministic
TypeScript over real data; Claude only reads the transcript and fills in the
triage. See [`docs/DESIGN_BRIEF.md`](docs/DESIGN_BRIEF.md) for the full product
and design rationale.

## Architecture

RELAY is five independent services plus a shared Redis instance. Each can run
standalone; wired together they form the full pipeline.

| Service | Dir | Role | Port |
|---|---|---|---|
| **Ear** | [`server/`](server) | Mic capture (browser) → WebSocket → Deepgram streaming STT | `8080` |
| **Brain** | [`brain/`](brain) | Transcript → structured Triage JSON via Claude, session memory in Redis | `4001` |
| **Matchmaker** | [`matchmaker/`](matchmaker) | Triage → matched resource via Redis vector search + deterministic ranking | `3002` |
| **Classifier** *(stretch)* | [`classifier/`](classifier) | TF-IDF + LogisticRegression urgency check Brain consults for a second opinion | `8000` |
| **Dashboard** | [`src/`](src) (Next.js) | Operator UI + `/api/*` proxies to Brain/Matchmaker + Deepgram TTS | `3000` |
| **Live UI** | [`frontend/`](frontend) | Figma-designed dashboard, wired end-to-end to the real backend | Vite default |
| Redis Stack | `docker-compose.yml` | Brain session memory (TTL) + Matchmaker vector index | `6379` |

Two UIs exist because of how the team split up work: [`src/`](src) is the
original Next.js dashboard (also demoable standalone with mock data, no
backend needed), and [`frontend/`](frontend) is a Figma-designed rebuild wired
to the live pipeline — this is the one used for the demo. Both call the same
`/api/triage`, `/api/dispatch`, and `/api/speak` routes served by the Next.js
app in `src/`, so that app (or at least its API routes) needs to be running
either way.

## Quick start

**Demo mode (no backend, mock data):**

```bash
npm install
npm run dev
# → http://localhost:3000
```

**Full pipeline (live mic → Claude → dispatch):** see
[`docs/SETUP.md`](docs/SETUP.md) for env vars, Redis, and per-service startup
— or bring every service up together:

```bash
npm run dev:all   # web + brain + ear, concurrently
```

then in separate shells: `cd matchmaker && npm run dev`, `cd frontend && npm run dev`,
and optionally `cd classifier && python server.py`.

## Repo layout

```
relay/
├── src/                  Next.js dashboard (UI + /api/* proxy routes)
├── frontend/              Figma-designed live UI (Vite + React), wired to the real backend
├── server/                Ear — audio capture, WS server, Deepgram STT
├── brain/                 Brain — Claude triage, Redis session memory
├── matchmaker/             Matchmaker — Redis vector search + resource ranking
├── classifier/             Urgency classifier (Python/FastAPI, stretch goal)
├── data/                  Shared demo resource dataset
├── docker-compose.yml     Redis Stack (session memory + vector search)
├── docs/                  Setup guide + design brief
├── AGENTS.md / CLAUDE.md  Agent-facing project instructions
└── .claude/               Claude Code launch config
```

## Docs

- [`docs/SETUP.md`](docs/SETUP.md) — environment variables, Redis, running each
  service, wiring the live backend, demo rehearsal checklist.
- [`docs/DESIGN_BRIEF.md`](docs/DESIGN_BRIEF.md) — full visual design spec
  (color system, layout, component states, animations).
- Each service has its own README with the details specific to it:
  [`server/README.md`](server/README.md), [`classifier/README.md`](classifier/README.md),
  [`brain/HERO_CALL.md`](brain/HERO_CALL.md) (the scripted demo call).

## Stack

Next.js 15 · React 19 · TypeScript · Tailwind · Mapbox GL JS (dashboard) /
Leaflet (live UI) · Express · Redis Stack (RediSearch vector index) ·
Claude (Anthropic SDK) · Deepgram (streaming STT + Aura TTS) · Python /
FastAPI + scikit-learn (classifier).


## Detected evidence (automated analysis)

Indexed codebase: 122 recognized source files, 465 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- Redis (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository; commit authorship or trailers
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 144)

```
.claude/launch.json
.env.example
.gitignore
AGENTS.md
brain/.gitignore
brain/HERO_CALL.md
brain/package.json
brain/README.md
brain/src/brain.ts
brain/src/server.ts
brain/src/session.ts
brain/src/types.ts
brain/tsconfig.json
classifier/data.csv
classifier/generate_data.py
classifier/README.md
classifier/requirements.txt
classifier/server.py
classifier/train.py
CLAUDE.md
data/resources.json
docker-compose.yml
docs/DESIGN_BRIEF.md
docs/SETUP.md
eslint.config.mjs
frontend/ATTRIBUTIONS.md
frontend/default_shadcn_theme.css
frontend/guidelines/Guidelines.md
frontend/index.html
frontend/package.json
frontend/pnpm-workspace.yaml
frontend/postcss.config.mjs
frontend/README.md
frontend/src/app/App.tsx
frontend/src/app/components/figma/ImageWithFallback.tsx
frontend/src/app/components/ui/accordion.tsx
frontend/src/app/components/ui/alert-dialog.tsx
frontend/src/app/components/ui/alert.tsx
frontend/src/app/components/ui/aspect-ratio.tsx
frontend/src/app/components/ui/avatar.tsx
frontend/src/app/components/ui/badge.tsx
frontend/src/app/components/ui/breadcrumb.tsx
frontend/src/app/components/ui/button.tsx
frontend/src/app/components/ui/calendar.tsx
frontend/src/app/components/ui/card.tsx
frontend/src/app/components/ui/carousel.tsx
frontend/src/app/components/ui/chart.tsx
frontend/src/app/components/ui/checkbox.tsx
frontend/src/app/components/ui/collapsible.tsx
frontend/src/app/components/ui/command.tsx
frontend/src/app/components/ui/context-menu.tsx
frontend/src/app/components/ui/dialog.tsx
frontend/src/app/components/ui/drawer.tsx
frontend/src/app/components/ui/dropdown-menu.tsx
frontend/src/app/components/ui/form.tsx
frontend/src/app/components/ui/hover-card.tsx
frontend/src/app/components/ui/input-otp.tsx
frontend/src/app/components/ui/input.tsx
frontend/src/app/components/ui/label.tsx
frontend/src/app/components/ui/menubar.tsx
frontend/src/app/components/ui/navigation-menu.tsx
frontend/src/app/components/ui/pagination.tsx
frontend/src/app/components/ui/popover.tsx
frontend/src/app/components/ui/progress.tsx
frontend/src/app/components/ui/radio-group.tsx
frontend/src/app/components/ui/resizable.tsx
frontend/src/app/components/ui/scroll-area.tsx
frontend/src/app/components/ui/select.tsx
frontend/src/app/components/ui/separator.tsx
frontend/src/app/components/ui/sheet.tsx
frontend/src/app/components/ui/sidebar.tsx
frontend/src/app/components/ui/skeleton.tsx
frontend/src/app/components/ui/slider.tsx
frontend/src/app/components/ui/sonner.tsx
frontend/src/app/components/ui/switch.tsx
frontend/src/app/components/ui/table.tsx
frontend/src/app/components/ui/tabs.tsx
frontend/src/app/components/ui/textarea.tsx
frontend/src/app/components/ui/toggle-group.tsx
frontend/src/app/components/ui/toggle.tsx
frontend/src/app/components/ui/tooltip.tsx
frontend/src/app/components/ui/use-mobile.ts
frontend/src/app/components/ui/utils.ts
frontend/src/app/useRelayLive.ts
frontend/src/imports/pasted_text/claude-relay-rules.md
frontend/src/main.tsx
frontend/src/styles/fonts.css
frontend/src/styles/globals.css
frontend/src/styles/index.css
frontend/src/styles/tailwind.css
frontend/src/styles/theme.css
frontend/vite.config.ts
lib/geocode.ts
lib/matchmaker.ts
lib/pipeline.ts
LICENSE
matchmaker/package.json
matchmaker/README.md
matchmaker/resources.json
matchmaker/src/contracts.ts
matchmaker/src/embeddings.ts
matchmaker/src/geocode.ts
matchmaker/src/match.ts
matchmaker/src/redisStore.ts
matchmaker/src/server.ts
matchmaker/tsconfig.json
next.config.ts
package.json
postcss.config.mjs
README.md
server/.gitignore
server/audio/README.md
server/package.json
server/public/client.js
server/public/index.html
server/public/pcm-worklet.js
server/README.md
server/src/backupAudio.ts
server/src/brainStub.ts
server/src/deepgramClient.ts
[24 more files omitted for size]
```

### Dependencies

- brain/package.json: @anthropic-ai/sdk@^0.30.0, @types/express@^4.17.21, @types/node@^22.0.0, @types/uuid@^10.0.0, dotenv@^16.4.5, express@^4.19.2, ioredis@^5.4.1, ts-node@^10.9.2, typescript@^5.5.4, uuid@^10.0.0
- classifier/requirements.txt: anthropic@>=0.30, fastapi@>=0.110, joblib@>=1.4, matplotlib@>=3.8, numpy@>=1.26, pandas@>=2.2, pydantic@>=2.6, scikit-learn@>=1.5, uvicorn@>=0.29
- frontend/package.json: @emotion/react@11.14.0, @emotion/styled@11.14.1, @mui/icons-material@7.3.5, @mui/material@7.3.5, @popperjs/core@2.11.8, @radix-ui/react-accordion@1.2.3, @radix-ui/react-alert-dialog@1.1.6, @radix-ui/react-aspect-ratio@1.1.2, @radix-ui/react-avatar@1.1.3, @radix-ui/react-checkbox@1.1.4, @radix-ui/react-collapsible@1.1.3, @radix-ui/react-context-menu@2.2.6, @radix-ui/react-dialog@1.1.6, @radix-ui/react-dropdown-menu@2.1.6, @radix-ui/react-hover-card@1.1.6, @radix-ui/react-label@2.1.2, @radix-ui/react-menubar@1.1.6, @radix-ui/react-navigation-menu@1.2.5, @radix-ui/react-popover@1.1.6, @radix-ui/react-progress@1.1.2, @radix-ui/react-radio-group@1.2.3, @radix-ui/react-scroll-area@1.2.3, @radix-ui/react-select@2.1.6, @radix-ui/react-separator@1.1.2, @radix-ui/react-slider@1.2.3, @radix-ui/react-slot@1.1.2, @radix-ui/react-switch@1.1.3, @radix-ui/react-tabs@1.1.3, @radix-ui/react-toggle@1.1.2, @radix-ui/react-toggle-group@1.1.2, @radix-ui/react-tooltip@1.1.8, @tailwindcss/vite@4.1.12, @types/leaflet@^1.9.21, @vitejs/plugin-react@4.7.0, canvas-confetti@1.9.4, class-variance-authority@0.7.1, clsx@2.1.1, cmdk@1.1.1, date-fns@3.6.0, embla-carousel-react@8.6.0, input-otp@1.4.2, leaflet@^1.9.4, lucide-react@0.487.0, motion@12.23.24, next-themes@0.4.6, react@18.3.1, react-day-picker@8.10.1, react-dnd@16.0.1, react-dnd-html5-backend@16.0.1, react-dom@18.3.1, react-hook-form@7.55.0, react-leaflet@^5.0.0, react-popper@2.3.0, react-resizable-panels@2.1.7, react-responsive-masonry@2.7.1, react-router@7.13.0, react-slick@0.31.0, recharts@2.15.2, sonner@2.0.3, tailwind-merge@3.2.0, tailwindcss@4.1.12, tw-animate-css@1.3.8, vaul@1.1.2, vite@6.3.5
- matchmaker/package.json: @types/express@^4.17.21, @types/node@^20.14.0, @xenova/transformers@^2.17.2, express@^4.19.2, ioredis@^5.4.1, ts-node@^10.9.2, typescript@^5.4.5
- package.json: @tailwindcss/postcss@^4.3.1, @types/mapbox-gl@^3.4.0, @types/node@^20, @types/react@^19, @types/react-dom@^19, autoprefixer@^10.4.19, clsx@^2.1.1, concurrently@^10.0.3, eslint@^9, eslint-config-next@16.2.9, mapbox-gl@^3.5.2, next@^15.3.0, postcss@^8, react@19.2.4, react-dom@19.2.4, tailwindcss@^4, typescript@^5
- server/package.json: @deepgram/sdk@^3.9.0, @types/node@^20.14.9, @types/ws@^8.5.10, dotenv@^16.4.5, ts-node@^10.9.2, ts-node-dev@^2.0.0, typescript@^5.5.3, ws@^8.18.0

### Recent commits (newest first)

- Merge pull request #2 from nysh9/fix/docs-and-structure
- Fix docs paths, dedupe postcss config, add service READMEs
- Update README.md
- Add Deepgram voice-back (TTS) for RELAY follow-up questions
- Fix figma to connect to backend
- Merge pull request #1 from nysh9/add-figma-frontend
- Add figma make frontend ui
- Merge remote-tracking branch 'origin/feat/classifier'
- Add logistic-regression urgency classifier + wire into brain
- final hopefully
- Redis session memeory + vector-search matchmaker
- full workflow
- working demo front to end
- merge: resolve conflicts, add Person D scaffold + Mapbox deps
- initial: RELAY Person D scaffold
- person b done
- Merge branch 'main' of https://github.com/nysh9/Relay
- Initial commit from Create Next App
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### CLAUDE.md

```markdown
@AGENTS.md

```

### AGENTS.md

```markdown
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->

```

### docker-compose.yml

```yaml
# RELAY — local Redis Stack (Redis + Query Engine for vector search).
#
# This is what makes "Redis beyond caching" work: redis-stack-server bundles
# the RediSearch module the Matchmaker uses for KNN vector recall, and the Brain
# uses the same instance for per-session agent memory with TTL.
#
#   docker compose up -d redis     # start
#   redis-cli ping                 # -> PONG
#   docker compose down            # stop
#
# Data is intentionally NOT persisted to a volume — session memory is per-call
# and wiped (§2 privacy), and resources are reseeded by the matchmaker on boot.
services:
  redis:
    image: redis/redis-stack-server:latest
    container_name: relay-redis
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

```

### package.json

```
{
  "name": "relay",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "dev:all": "concurrently -n web,brain,ear -c blue,magenta,green \"npm run dev\" \"npm --prefix brain run dev\" \"npm --prefix server run dev\"",
    "build": "next build",
    "start": "next start",
    "lint": "eslint",
    "type-check": "tsc --noEmit"
  },
  "dependencies": {
    "clsx": "^2.1.1",
    "mapbox-gl": "^3.5.2",
    "next": "^15.3.0",
    "react": "19.2.4",
    "react-dom": "19.2.4"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4.3.1",
    "@types/mapbox-gl": "^3.4.0",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "autoprefixer": "^10.4.19",
    "concurrently": "^10.0.3",
    "eslint": "^9",
    "eslint-config-next": "16.2.9",
    "postcss": "^8",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

```

### classifier/requirements.txt

```
scikit-learn>=1.5
pandas>=2.2
numpy>=1.26
matplotlib>=3.8
joblib>=1.4
fastapi>=0.110
uvicorn>=0.29
pydantic>=2.6
anthropic>=0.30

```

### matchmaker/package.json

```
{
  "name": "relay-matchmaker",
  "version": "1.0.0",
  "description": "RELAY Matchmaker — Redis vector-search resource routing (Person C)",
  "main": "dist/server.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/server.js",
    "dev": "ts-node src/server.ts"
  },
  "dependencies": {
    "@xenova/transformers": "^2.17.2",
    "express": "^4.19.2",
    "ioredis": "^5.4.1"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "@types/node": "^20.14.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.4.5"
  }
}

```

### brain/package.json

```
{
  "name": "relay-brain",
  "version": "1.0.0",
  "description": "RELAY Brain — Hindi transcript → structured Triage JSON via Claude",
  "main": "dist/server.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/server.js",
    "dev": "ts-node src/server.ts"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.30.0",
    "dotenv": "^16.4.5",
    "express": "^4.19.2",
    "ioredis": "^5.4.1",
    "uuid": "^10.0.0"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "@types/node": "^22.0.0",
    "@types/uuid": "^10.0.0",
    "ts-node": "^10.9.2",
    "typescript": "^5.5.4"
  }
}

```

### server/package.json

```
{
  "name": "relay-ear",
  "version": "0.1.0",
  "description": "RELAY — Person A: The Ear (Audio + WebSocket + Deepgram streaming STT for Hindi)",
  "private": true,
  "type": "commonjs",
  "scripts": {
    "dev": "ts-node-dev --respawn --transpile-only src/server.ts",
    "build": "tsc -p .",
    "start": "node dist/server.js",
    "backup-clip": "ts-node src/backupAudio.ts"
  },
  "dependencies": {
    "@deepgram/sdk": "^3.9.0",
    "dotenv": "^16.4.5",
    "ws": "^8.18.0"
  },
  "devDependencies": {
    "@types/node": "^20.14.9",
    "@types/ws": "^8.5.10",
    "ts-node": "^10.9.2",
    "ts-node-dev": "^2.0.0",
    "typescript": "^5.5.3"
  }
}

```

### frontend/package.json

```
{
  "name": "@figma/my-make-file",
  "private": true,
  "version": "0.0.1",
  "type": "module",
  "scripts": {
    "build": "vite build",
    "dev": "vite"
  },
  "dependencies": {
    "@emotion/react": "11.14.0",
    "@emotion/styled": "11.14.1",
    "@mui/icons-material": "7.3.5",
    "@mui/material": "7.3.5",
    "@popperjs/core": "2.11.8",
    "@radix-ui/react-accordion": "1.2.3",
    "@radix-ui/react-alert-dialog": "1.1.6",
    "@radix-ui/react-aspect-ratio": "1.1.2",
    "@radix-ui/react-avatar": "1.1.3",
    "@radix-ui/react-checkbox": "1.1.4",
    "@radix-ui/react-collapsible": "1.1.3",
    "@radix-ui/react-context-menu": "2.2.6",
    "@radix-ui/react-dialog": "1.1.6",
    "@radix-ui/react-dropdown-menu": "2.1.6",
    "@radix-ui/react-hover-card": "1.1.6",
    "@radix-ui/react-label": "2.1.2",
    "@radix-ui/react-menubar": "1.1.6",
    "@radix-ui/react-navigation-menu": "1.2.5",
    "@radix-ui/react-popover": "1.1.6",
    "@radix-ui/react-progress": "1.1.2",
    "@radix-ui/react-radio-group": "1.2.3",
    "@radix-ui/react-scroll-area": "1.2.3",
    "@radix-ui/react-select": "2.1.6",
    "@radix-ui/react-separator": "1.1.2",
    "@radix-ui/react-slider": "1.2.3",
    "@radix-ui/react-slot": "1.1.2",
    "@radix-ui/react-switch": "1.1.3",
    "@radix-ui/react-tabs": "1.1.3",
    "@radix-ui/react-toggle": "1.1.2",
    "@radix-ui/react-toggle-group": "1.1.2",
    "@radix-ui/react-tooltip": "1.1.8",
    "@types/leaflet": "^1.9.21",
    "canvas-confetti": "1.9.4",
    "class-variance-authority": "0.7.1",
    "clsx": "2.1.1",
    "cmdk": "1.1.1",
    "date-fns": "3.6.0",
    "embla-carousel-react": "8.6.0",
    "input-otp": "1.4.2",
    "leaflet": "^1.9.4",
    "lucide-react": "0.487.0",
    "motion": "12.23.24",
    "next-themes": "0.4.6",
    "react-day-picker": "8.10.1",
    "react-dnd": "16.0.1",
    "react-dnd-html5-backend": "16.0.1",
    "react-hook-form": "7.55.0",
    "react-leaflet": "^5.0.0",
    "react-popper": "2.3.0",
    "react-resizable-panels": "2.1.7",
    "react-responsive-masonry": "2.7.1",
    "react-router": "7.13.0",
    "react-slick": "0.31.0",
    "recharts": "2.15.2",
    "sonner": "2.0.3",
    "tailwind-merge": "3.2.0",
    "tw-animate-css": "1.3.8",
    "vaul": "1.1.2"
  },
  "devDependencies": {
    "@tailwindcss/vite": "4.1.12",
    "@vitejs/plugin-react": "4.7.0",
    "tailwindcss": "4.1.12",
    "vite": "6.3.5"
  },
  "peerDependencies": {
    "react": "18.3.1",
    "react-dom": "18.3.1"
  },
  "peerDependenciesMeta": {
    "react": {
      "optional": true
    },
    "react-dom": {
      "optional": true
    }
  },
  "pnpm": {
    "overrides": {
      "vite": "6.3.5"
    }
  }
}
```

### types/index.ts

```typescript
export type Resource = {
  id: string;
  name: string;
  type: "shelter" | "medical" | "water" | "supply" | "evacuation";
  lat: number;
  lng: number;
  capacity: number;
  availableCapacity: number;
  has: string[];
  address: string;
  phone?: string;
};

export type CallerLocation = {
  text: string;   // what the caller said — always from the call, never device GPS
  lat: number;
  lng: number;
};

export type ResourceMatch = {
  resourceId: string;
  name: string;
  type: string;
  distanceKm: number;
  available: boolean;
};

export type Dispatch = {
  matched: ResourceMatch | null;   // null = nothing fits (guardrail — never fabricate)
  alternatives: Array<{ resourceId: string; name: string; distanceKm: number }>;
  dispatchText: string;
};
```

[110 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]