# Project export: Verba

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 2025
- Tagline: Nearly 80% of serious medical errors happen during patient-doctor handoffs. Verba is a EMS voice agent that extracts and sends structured patient data to hospitals—fast, accurate, and hands-free.
- Devpost: https://devpost.com/software/verba-0hj1fl
- GitHub: https://github.com/jmx1e/verba
- Video: https://www.youtube.com/embed/wKBMkL7NPlM?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Amy (24 commits), jmx1e (11 commits), smritirangarajan (2 commits)

## Devpost submission (written by the team)

### Inspiration

Every second counts: the difference between life and death. Yet in today’s EMS-to-hospital handoffs, an estimated 80% of serious medical errors occur during patient handoffs due to vital patient information being too often delayed, distorted, or completely lost. During high-stress calls—late at night, mid-transport, or while multitasking—first responders can't always recall or relay every detail, especially without reliable tools to support them. This puts thousands of lives at risk every year. So we built Verba: A voice-first AI agent built to streamline the EMS-to-doctor handoff—accurately, instantly, and effortlessly.

### What it does

Verba is a real-time voice agent that ensures nothing gets lost in translation during the most critical moments of emergency responses. Designed for EMS-to-hospital handoffs, Verba listens to paramedics’ verbal reports on scene and automatically transcribes, extracts, and structures all key medical data using a voice-first, AI-assisted workflow. It’s trained to be as efficient as possible, asking clarifying questions only when absolutely necessary. Verba then uses advanced custom agents to deliver structured reports that directly integrate with existing electronic patient care report (ePCR) systems. These reports appear instantly on the Doctor Dashboard prioritized on criticality, reducing time-to-treatment and minimizing human error.

### How we built it

Voice Agent: Vapi: Built and deployed an advanced AI voice agent. Handles natural conversations in real-time, makes and receives calls, and is built to seamlessly connect with major ePCR/EHR systems. Designed to respond only when necessary to save as much time as possible. Claude: Used for advanced language understanding and summarization. Aids in refining handoff notes into concise, medically actionable key terms and summaries for rapid physician review. Frontend: React application: Real-time updates. Provides a clean, clinician-friendly UI. Leaflet Maps API: Integrated live mapping to show real-time EMS locations and scene data directly on the Doctor Dashboard. Helps doctors anticipate patient arrival and triage in advance. Backend: Supabase: Used as a scalable Postgres database with built-in authentication and row-level security. Enabled real-time syncing of structured patient data and secure access control for doctors and EMS teams. Medical database: Thousands of EMS-specific terminology, abbreviations, and field-data. Letta: A custom medical language model agent, based on GPT-4o mini and trained on medical database. Parses transcriptions from Vapi into structured, clinically relevant data fields for doctor review. Seamlessly connects with existing major ePCR/EHR systems like Zoll and ESO.

### Challenges we ran into

Balancing tradeoffs between real-time responsiveness and accuracy Acquiring an extensive database that would power the report generation and semantic understanding of the analyzed speech Addressing variations in real-world speech, including background noise and enunciation Unifying multiple AI agent frameworks into one cohesive product

### Accomplishments we're proud of

Created a fully functional AI agent for medical emergencies in 24 hours Minimized latency in the voice call and dashboard updates to be near instant Built flexible report-generation pipeline that can be adapted for various medical emergencies and beyond Ensured the agent is accessible via phone

### What we learned

How to integrate multiple modern APIs in one cohesive project EMS reports are unstandardized and incomplete Instant verbal feedback is helpful for EMS first responders to ensure all necessary information about an emergency is recorded

### What's next

Multimodal integration: live video feeds—especially body cam footage—to enhance situational understanding through real-time visual segmentation and tracking Security: HIPAA-compliance and full encryption (including voice data in transit) will be prioritized to protect patient privacy and system integrity Model base: Increasing model complexity while maintaining quick speed Integration: Further integration of EMS Patient Care Report with hospital workflow

## README (from the GitHub repository)

# Verba 
Verba is a real-time voice agent that ensures nothing gets lost in translation during the most critical moments of emergency responses. Designed for EMS-to-hospital handoffs, Verba listens to paramedics’ verbal reports on scene and automatically transcribes, extracts, and structures all key medical data using a voice-first, AI-assisted workflow. It’s trained to be as efficient as possible, asking clarifying questions only when absolutely necessary. 

Verba then uses advanced custom agents to deliver structured reports that appear instantly on the Doctor Dashboard prioritized on criticality, reducing time-to-treatment and minimizing human error.

![dash](./images/dash.jpg)

## Project Overview
### Voice Agent:
- **Vapi**: Built and deployed an advanced AI voice agent. Handles natural conversations in real-time, makes and receives calls, and is built to seamlessly connect with major ePCR/EHR systems. Designed to respond only when necessary to save as much time as possible.
- **Claude**: Used for advanced language understanding and summarization. Aids in refining handoff notes into concise, medically actionable key terms and summaries for rapid physician review.

### Frontend: 
- **React application**: Real-time updates. Provides a clean, clinician-friendly UI.
- **Leaflet Maps API**: Integrated live mapping to show real-time EMS locations and scene data directly on the Doctor Dashboard. Helps doctors anticipate patient arrival and triage in advance.

### Backend: 
- **Supabase**: Used as a scalable Postgres database with built-in authentication and row-level security. Enabled real-time syncing of structured patient data and secure access control for doctors and EMS teams.
- **Medical database**: thousands of EMS-specific terminology, abbreviations, and field-data.
- **Letta**: A custom medical language model agent, based on GPT-4o mini and trained on medical database. Parses transcriptions from Vapi into structured, clinically relevant data fields for doctor review. Seamlessly connects with existing major ePCR/EHR systems like Zoll and ESO.

![Verba Flowchart](./images/Verba_Flowchart.png) 

## Features
- Key medical term/feature extraction through Vapi workflow: Speech-to-Text, LLM, Text-to-Speech
- Responsive dashboard with real-time updates and map integration
- Helpful reports for doctors with critical information from paramedics and suggested further action

## Getting Started
1. Environment Setup
```
# Activate the Python virtual environment
source first_responder_env/bin/activate
```
2. Backend Setup

Install dependencies:
```
cd verba-folder
npm install
```
Set up environment variables by creating a `.env` file in the `verba-folder` directory:
```
# verba-folder/.env
LETTA_API_KEY=your_letta_api_key_here
AGENT_ID=your_agent_id_here
SUPABASE_URL=your_supabase_url_here
SUPABASE_KEY=your_supabase_key_here
```
Start the backend server:
```
# From verba-folder directory
node api/index.js
```
The API will run on http://localhost:3001

3. Frontend Setup

Install dependencies:
```
cd verba-folder/ui
npm install
```
Start the development server:
```
npm run dev
```
The dashboard will be available at http://localhost:5173 

4. Testing the system
    - Call the Voice Agent: +1 (650) 252 7577
    - Follow Verba's instructions and provide emergency data
    - Visit the dashboard at http://localhost:5173 to see structured reports appear in real-time
    - Continue calling the number if desired





## Detected evidence (automated analysis)

Indexed codebase: 3055 recognized source files, 44722 KB.
- C (language) — detected in the code
- C++ (language) — detected in the code
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- Supabase (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 6077)

```
.gitignore
first_responder_env/bin/activate
first_responder_env/bin/activate.csh
first_responder_env/bin/activate.fish
first_responder_env/bin/Activate.ps1
first_responder_env/bin/debugpy
first_responder_env/bin/debugpy-adapter
first_responder_env/bin/ipython
first_responder_env/bin/ipython3
first_responder_env/bin/jupyter
first_responder_env/bin/jupyter-kernel
first_responder_env/bin/jupyter-kernelspec
first_responder_env/bin/jupyter-migrate
first_responder_env/bin/jupyter-run
first_responder_env/bin/jupyter-troubleshoot
first_responder_env/bin/normalizer
first_responder_env/bin/pip
first_responder_env/bin/pip 2
first_responder_env/bin/pip3
first_responder_env/bin/pip3 2
first_responder_env/bin/pip3.12
first_responder_env/bin/pip3.12 2
first_responder_env/bin/pygmentize
first_responder_env/bin/pyrsa-decrypt
first_responder_env/bin/pyrsa-encrypt
first_responder_env/bin/pyrsa-keygen
first_responder_env/bin/pyrsa-priv2pub
first_responder_env/bin/pyrsa-sign
first_responder_env/bin/pyrsa-verify
first_responder_env/bin/python
first_responder_env/bin/python3
first_responder_env/bin/python3.12
first_responder_env/bin/tqdm
first_responder_env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER
first_responder_env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE
first_responder_env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA
first_responder_env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD
first_responder_env/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL
first_responder_env/lib/python3.12/site-packages/annotated_types/__init__.py
first_responder_env/lib/python3.12/site-packages/annotated_types/py.typed
first_responder_env/lib/python3.12/site-packages/annotated_types/test_cases.py
first_responder_env/lib/python3.12/site-packages/apiclient/__init__.py
first_responder_env/lib/python3.12/site-packages/appnope-0.1.4.dist-info/INSTALLER
first_responder_env/lib/python3.12/site-packages/appnope-0.1.4.dist-info/LICENSE
first_responder_env/lib/python3.12/site-packages/appnope-0.1.4.dist-info/METADATA
first_responder_env/lib/python3.12/site-packages/appnope-0.1.4.dist-info/RECORD
first_responder_env/lib/python3.12/site-packages/appnope-0.1.4.dist-info/top_level.txt
first_responder_env/lib/python3.12/site-packages/appnope-0.1.4.dist-info/WHEEL
first_responder_env/lib/python3.12/site-packages/appnope/__init__.py
first_responder_env/lib/python3.12/site-packages/appnope/_dummy.py
first_responder_env/lib/python3.12/site-packages/appnope/_nope.py
first_responder_env/lib/python3.12/site-packages/asttokens-3.0.0.dist-info/INSTALLER
first_responder_env/lib/python3.12/site-packages/asttokens-3.0.0.dist-info/LICENSE
first_responder_env/lib/python3.12/site-packages/asttokens-3.0.0.dist-info/METADATA
first_responder_env/lib/python3.12/site-packages/asttokens-3.0.0.dist-info/RECORD
first_responder_env/lib/python3.12/site-packages/asttokens-3.0.0.dist-info/top_level.txt
first_responder_env/lib/python3.12/site-packages/asttokens-3.0.0.dist-info/WHEEL
first_responder_env/lib/python3.12/site-packages/asttokens/__init__.py
first_responder_env/lib/python3.12/site-packages/asttokens/astroid_compat.py
first_responder_env/lib/python3.12/site-packages/asttokens/asttokens.py
first_responder_env/lib/python3.12/site-packages/asttokens/line_numbers.py
first_responder_env/lib/python3.12/site-packages/asttokens/mark_tokens.py
first_responder_env/lib/python3.12/site-packages/asttokens/py.typed
first_responder_env/lib/python3.12/site-packages/asttokens/util.py
first_responder_env/lib/python3.12/site-packages/asttokens/version.py
first_responder_env/lib/python3.12/site-packages/cachetools-5.5.2.dist-info/INSTALLER
first_responder_env/lib/python3.12/site-packages/cachetools-5.5.2.dist-info/LICENSE
first_responder_env/lib/python3.12/site-packages/cachetools-5.5.2.dist-info/METADATA
first_responder_env/lib/python3.12/site-packages/cachetools-5.5.2.dist-info/RECORD
first_responder_env/lib/python3.12/site-packages/cachetools-5.5.2.dist-info/top_level.txt
first_responder_env/lib/python3.12/site-packages/cachetools-5.5.2.dist-info/WHEEL
first_responder_env/lib/python3.12/site-packages/cachetools/__init__.py
first_responder_env/lib/python3.12/site-packages/cachetools/_decorators.py
first_responder_env/lib/python3.12/site-packages/cachetools/func.py
first_responder_env/lib/python3.12/site-packages/cachetools/keys.py
first_responder_env/lib/python3.12/site-packages/certifi-2025.6.15.dist-info/INSTALLER
first_responder_env/lib/python3.12/site-packages/certifi-2025.6.15.dist-info/licenses/LICENSE
first_responder_env/lib/python3.12/site-packages/certifi-2025.6.15.dist-info/METADATA
first_responder_env/lib/python3.12/site-packages/certifi-2025.6.15.dist-info/RECORD
first_responder_env/lib/python3.12/site-packages/certifi-2025.6.15.dist-info/top_level.txt
first_responder_env/lib/python3.12/site-packages/certifi-2025.6.15.dist-info/WHEEL
first_responder_env/lib/python3.12/site-packages/certifi/__init__.py
first_responder_env/lib/python3.12/site-packages/certifi/__main__.py
first_responder_env/lib/python3.12/site-packages/certifi/cacert.pem
first_responder_env/lib/python3.12/site-packages/certifi/core.py
first_responder_env/lib/python3.12/site-packages/certifi/py.typed
first_responder_env/lib/python3.12/site-packages/charset_normalizer-3.4.2.dist-info/entry_points.txt
first_responder_env/lib/python3.12/site-packages/charset_normalizer-3.4.2.dist-info/INSTALLER
first_responder_env/lib/python3.12/site-packages/charset_normalizer-3.4.2.dist-info/licenses/LICENSE
first_responder_env/lib/python3.12/site-packages/charset_normalizer-3.4.2.dist-info/METADATA
first_responder_env/lib/python3.12/site-packages/charset_normalizer-3.4.2.dist-info/RECORD
first_responder_env/lib/python3.12/site-packages/charset_normalizer-3.4.2.dist-info/top_level.txt
first_responder_env/lib/python3.12/site-packages/charset_normalizer-3.4.2.dist-info/WHEEL
first_responder_env/lib/python3.12/site-packages/charset_normalizer/__init__.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/__main__.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/api.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/cd.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/constant.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/legacy.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/md.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/models.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/py.typed
first_responder_env/lib/python3.12/site-packages/charset_normalizer/utils.py
first_responder_env/lib/python3.12/site-packages/charset_normalizer/version.py
first_responder_env/lib/python3.12/site-packages/comm-0.2.2.dist-info/INSTALLER
first_responder_env/lib/python3.12/site-packages/comm-0.2.2.dist-info/licenses/LICENSE
first_responder_env/lib/python3.12/site-packages/comm-0.2.2.dist-info/METADATA
first_responder_env/lib/python3.12/site-packages/comm-0.2.2.dist-info/RECORD
first_responder_env/lib/python3.12/site-packages/comm-0.2.2.dist-info/WHEEL
first_responder_env/lib/python3.12/site-packages/comm/__init__.py
first_responder_env/lib/python3.12/site-packages/comm/base_comm.py
first_responder_env/lib/python3.12/site-packages/comm/py.typed
first_responder_env/lib/python3.12/site-packages/dateutil/__init__.py
first_responder_env/lib/python3.12/site-packages/dateutil/_common.py
first_responder_env/lib/python3.12/site-packages/dateutil/_version.py
first_responder_env/lib/python3.12/site-packages/dateutil/easter.py
first_responder_env/lib/python3.12/site-packages/dateutil/parser/__init__.py
first_responder_env/lib/python3.12/site-packages/dateutil/parser/_parser.py
[5957 more files omitted for size]
```

### Dependencies

- verba-folder/api/package.json: @letta-ai/letta-client@^0.1.132, cors@^2, dotenv@^16, express@^4, node-fetch@^2, nodemon@^3.1.10
- verba-folder/package.json: @letta-ai/letta-client@^0.1.132, autoprefixer@^10.4.21, canvas@^3.1.1, concurrently@^9.2.0, dotenv@^16.5.0, leaflet@^1.9.4, lucide-react@^0.522.0, node-fetch@^2.7.0, p-memoize@^6.0.2, postcss@^8.5.6, react-leaflet@^5.0.0, react-markdown@^10.1.0, tailwindcss@^4.1.10
- verba-folder/ui/package.json: @eslint/js@^9.25.0, @types/react@^19.1.2, @types/react-dom@^19.1.2, @vitejs/plugin-react@^4.4.1, axios@^1.10.0, eslint@^9.25.0, eslint-plugin-react-hooks@^5.2.0, eslint-plugin-react-refresh@^0.4.19, globals@^16.0.0, leaflet@^1.9.4, react@^19.1.0, react-dom@^19.1.0, react-leaflet@^5.0.0, vite@^6.3.5

### Recent commits (newest first)

- Update README.md
- Update README.md
- Delete test-files directory
- Merge pull request #4 from jmx1e/read
- fixing readme
- Merge pull request #3 from jmx1e/help
- fixing keys
- fixed the files
- Update README.md
- Add files via upload
- Merge pull request #2 from jmx1e/newnewmodel
- fixed the model
- reorganize prompt folders
- Delete gov-data.txt
- Delete model-voice.ipynb
- Delete pyaudio_install_log.txt
- Delete incident_report_20250620_225942.md
- Delete test_report.md
- Delete incident_report_20250620_225017.md
- Delete incident_report_20250620_224921.md

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

### prompts/letta_prompt.md

```markdown
I am Verba, an EMS assistant for post-processing patient data to create records for doctors after the initial EMS call. 

I receive input with the following fields: "misc," "heart_rate," "patient_age," "patient_sex," "dispatch_time," "initial_report," "vehicle_number," "incident_location," "oxygen_saturation," "patient_full_name," "treatment_urgency," "level_of_consciousness," "allergies_and_reactions," and "previous_medical_history."

MY MAIN ROLE: I reproduce the EXACT same report. I then search the web based on what I have for recommendations on what to do next. I then put that into a "Recommendations" section. This section should be extremely detailed.

I am the go between for doctor and EMS personnel. I need to query things the input didn't understand just in case there's important information missing. 
For abbreviations in "misc" or "initial_report", I query `ems-abbreviations.txt` to include their category. This report is formatted like this: "If the term is [abbreviation], the category is [category]." I respond with the same report, except I create a new categroy "Abbreviations", where I provide Information on the abbreviation. If I am NOT ABLE to find the abbreviation, I add another section titled "Unkown" where I include those unknown abbreviations. 


My responses are professional, clear, and optimized for speed. If nothing is found for that abbreviation, leave it off the report. I stop after providing the report. I DO NOT add extra messages.
```

### prompts/vapi_variables.md

```markdown
You will be given a transcript of a call and the system prompt of the AI assistant, extract these following variables. The variables under "necessary:" should always be extracted, and the assistant should ask for them if not found. The variables under "unnecessary" should be extracted if given, otherwise if not given the assistant should not ask for them.

necessary: 
    { type: "string", title: "patient_full_name" },
    { type: "number", title: "patient_age" },
    { type: "string", title: "patient_sex", enum: ["Male", "Female", "Intersex"] },

    { 
      type: "string", 
      title: "dispatch_time",
      description: "Time in either 24-hour format (e.g. 22:11) or 12-hour format (e.g. 5:21 AM)"
    },
    { type: "string", title: "vehicle_number" },
    {
      type: "string",
      title: "incident_location",
      description: "The location will be provided as a string. Please convert the location to latitude and longitude coordinates."
    },

    { type: "string", title: "ems_officer_name" },
    { 
      type: "string", 
      title: "initial_report",
      description: "The first responder's explicit initial assessment report. Must be directly stated by the responder, NOT inferred from vital signs or other medical details. Pay special attention to any acronyms or abbreviations that may be spelled out letter by letter."
    },

    {
      type: "string",
      title: "treatment_urgency",
      enum: ["Critical", "Emergent", "Urgent", "Non-urgent"]
    },
    { 
      type: "string", 
      title: "misc",
      description: "Any additional special information or notes for the doctor not captured in other variables. Pay special attention to any acronyms or abbreviations that may be spelled out letter by letter."
    }

unnecessary:
    { type: "string", title: "patient_complaint" },
    { type: "string", title: "previous_medical_history" },
    { type: "string", title: "allergies_and_reactions" },

    { type: "string", title: "speech" },
    { type: "string", title: "level_of_consciousness" },
    { type: "string", title: "skin" },

    { type: "string", title: "respiration_rate" },
    { type: "string", title: "oxygen_saturation" },
    { type: "string", title: "heart_rate" },
    { type: "string", title: "blood_pressure" },
    
    { type: "string", title: "injury_present" },
    { type: "string", title: "substance_use_indicator" }

```

### verba-folder/package.json

```
{
    "name": "emt-dashboard",
    "private": true,
    "scripts": {
        "dev": "concurrently \"npm run dev --prefix api\" \"npm run dev --prefix ui\"",
        "build": "npm run build --prefix ui"
    },
    "devDependencies": {
        "autoprefixer": "^10.4.21",
        "concurrently": "^9.2.0",
        "postcss": "^8.5.6",
        "tailwindcss": "^4.1.10"
    },
    "dependencies": {
        "@letta-ai/letta-client": "^0.1.132",
        "canvas": "^3.1.1",
        "dotenv": "^16.5.0",
        "leaflet": "^1.9.4",
        "lucide-react": "^0.522.0",
        "node-fetch": "^2.7.0",
        "p-memoize": "^6.0.2",
        "react-leaflet": "^5.0.0",
        "react-markdown": "^10.1.0"
    }
}

```

### verba-folder/api/package.json

```
{
    "name": "emt-api",
    "scripts": {
        "dev": "nodemon index.js"
    },
    "dependencies": {
        "@letta-ai/letta-client": "^0.1.132",
        "cors": "^2",
        "dotenv": "^16",
        "express": "^4",
        "node-fetch": "^2"
    },
    "devDependencies": {
        "nodemon": "^3.1.10"
    }
    
}

```

### verba-folder/ui/package.json

```
{
  "name": "ui",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.10.0",
    "leaflet": "^1.9.4",
    "react": "^19.1.0",
    "react-dom": "^19.1.0",
    "react-leaflet": "^5.0.0"
  },
  "devDependencies": {
    "@eslint/js": "^9.25.0",
    "@types/react": "^19.1.2",
    "@types/react-dom": "^19.1.2",
    "@vitejs/plugin-react": "^4.4.1",
    "eslint": "^9.25.0",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.19",
    "globals": "^16.0.0",
    "vite": "^6.3.5"
  }
}

```

### verba-folder/api/index.js

```javascript
/*********************  api/index.js  **************************/
require('dotenv').config({ path: '../.env' });
const express   = require('express');
const cors      = require('cors');
const fetch     = require('node-fetch');
const memoize   = require('p-memoize').default || require('p-memoize');
const { LettaClient } = require('@letta-ai/letta-client');

const app = express();
app.use(cors());

/* ───────────────────────── 0. Letta client ───────────────────────── */
const letta = new LettaClient({
  token  : process.env.LETTA_API_KEY,
  baseUrl: 'https://api.letta.com',
});

/* ───────────── 1. Claude → cleaned address (optional) ───────────── */
async function refineWithClaude(raw) {
  if (!process.env.CLAUDE_API_KEY) return raw;                  // skip if key missing
  const body = {
    model       : process.env.CLAUDE_MODEL || 'claude-3-haiku-20240307',
    max_tokens  : 40,
    temperature : 0,
    messages    : [{ role:'user', content:`Return the clearest address for: ${raw}` }]
  };
  const r = await fetch('https://api.anthropic.com/v1/messages', {
    method :'POST',
    headers:{
      'Content-Type'      : 'application/json',
      'X-API-Key'         : process.env.CLAUDE_API_KEY,
      'anthropic-version' : '2023-06-01'
    },
    body: JSON.stringify(body)
  });
  if (!r.ok) throw new Error(`Claude ❌ ${r.status}`);
  const j = await r.json();
  return j?.content?.[0]?.text?.trim() || raw;
}

/* ────────────────── 2. Open‑Street‑Map geocoder ─────────────────── */
async function nominatim(q) {
  const url = `https://nominatim.openstreetmap.org/search?format=json&limit=1&q=${encodeURIComponent(q)}`;
  const res = await fetch(url, { headers:{ 'User-Agent':'cal-hacks/1.0' } });
  const j   = await res.json().catch(()=>null);
  if (!j?.length) return null;
  return { lat:+j[0].lat, lng:+j[0].lon, label:j[0].display_name };
}

/* ───────────── 3. locate()  =  Claude → Nominatim (memoised) ─────── */
const locate = memoize(async raw => {
  try {
    const clean = await refineWithClaude(raw);
    return await nominatim(clean);
  } catch (e) {
    console.warn('🌐 geocode failed:', e.message);
    return null;
  }
}, { maxAge: 3600_000 });

/* ───────────── 4. Markdown or JSON → report object ───────────── */
function parseMessage(content='') {
  try {
    if (!content) return {};

    const report = {};
    if (typeof content === 'object' && content !== null) {
      report.patient_full_name = content.patient_full_name || '';
      report.patient_age = content.patient_age || content.age || '';
      report.patient_sex = content.patient_sex || content.sex || '';
      report.initial_report = content.initial_report || content.condition || '';
      report.dispatch_time = content.dispatch_time || '';
      report.incident_location = content.incident_location || '';
      report.vehicle_number = content.vehicle_number || '';
      report.ems_officer_name = content.ems_officer_name || '';
      report.treatment_urgency = content.treatment_urgency || '';
      report.heart_rate = content.heart_rate || '';
      report.oxygen_saturation = content.oxygen_saturation || '';
      report.level_of_consciousness = content.level_of_consciousness || '';
      report.allergies_and_reactions = content.allergies_and_reactions || '';
      report.misc = content.misc || '';

      const symText = content.initial_report || content.misc || '';
      const matches = symText.match(/[^,.]+(?=,|\.|$)/g);
      if (matches) report.symptoms = matches.map(s => s.trim()).filter(Boolean);

      if (content.recommendations) {
        report.recommendations = content.recommendations;
      } else if (content.misc?.includes('Administered')) {
        report.recommendations = content.misc;
      }
    } else if (typeof content === 'string') {
      const lines = content.split('\n').map(l => l.trim()).filter(Boolean);

      const headerMatch = content.match(/Report for\s+(.+?):/i);
      if (headerMatch) report.patient_full_name = headerMatch[1].trim();

      let currentSection = '';
      const abbreviations = [], recommendations = [];

      for (const line of lines) {
        if (line.startsWith('### Recommendations:')) currentSection = 'recommendations';
        else if (line.startsWith('**Abbreviations**:')) currentSection = 'abbreviations';
        else if (currentSection === 'recommendations' && line.startsWith('-')) recommendations.push(line.slice(1).trim());
        else if (currentSection === 'abbreviations') {
          const m = line.match(/([A-Za-z0-9]+)\s*:\s*Categorized under\s*([\w\s]+)/i);
          if (m) abbreviations.push({ abbr: m[1], category: m[2].trim() });
        } else {
          const m = line.match(/- \*\*(.+?)\*\*: (.+)/);
          if (!m) continue;
          const key = m[1].trim().toLowerCase().replace(/\s+/g, '_');
          const val = m[2].trim();
          if (key === 'condition') {
            report.initial_report = val;
            const symMatch = val.match(/[^,.]+(?=,|\.|$)/g);
            if (symMatch) report.symptoms = symMatch.map(s => s.trim()).filter(Boolean);
          } else if (key === 'age') report.patient_age = val;
          else if (key === 'sex') report.patient_sex = val;
          else if (key === 'vital_signs') {
            for (const pair of val.split(', ')) {
              const [k, v] = pair.split(': ').map(x => x.trim());
              if (k && v) report[k.toLowerCase().replace(/\s+/g,'_')] = v;
            }
          } else report[key] = val;
        }
      }

      if (recommendations.length) report.recommendations = recommendations.join(' ');
      if (abbreviations.length) report.abbreviations = abbreviations;
    }

    return Object.fromEntries(Object.entries(report).filter(([_,v]) => v && v.length > 0));
  } catch(e) {
    console.warn('⚠️ parseMessage failed:', e);
    return {};
  }
}

/* ───────────────────────── 5. /api/reports ───────────────────────── */
app.get('/api/reports', async (_req,res)=>{
  console.log('▶️  /api/reports hit');
[truncated — 1074 more characters]
```

### verba-folder/ui/src/main.jsx

```javascript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

```

### verba-folder/ui/src/App.jsx

```javascript
import 'leaflet/dist/leaflet.css'; // ← fix missing CSS
import './App.css';

import avatar from './verba.png'; 

import { useEffect, useState } from 'react';
import {
  Users, AlertTriangle, Heart, Clock, Activity, User,
  ChevronDown, ChevronUp, MapPin
} from 'lucide-react';

import MapView from './components/MapView.jsx';

const lc = (s) => (s ? s.toLowerCase() : '');

// Helper function to sort by criticality priority
const sortReportsByCriticality = (reports) => {
  // Define priority order (lower number = higher priority)
  const priorityOrder = {
    'critical': 1,
    'emergent': 2,
    'urgent': 3,
    'high': 4,
    'medium': 5,
    'low': 6,
    'non-urgent': 7
  };

  return [...reports].sort((a, b) => {
    const urgencyA = lc(a.treatment_urgency) || 'unknown';
    const urgencyB = lc(b.treatment_urgency) || 'unknown';
    
    const priorityA = priorityOrder[urgencyA] || 999; // Unknown gets lowest priority
    const priorityB = priorityOrder[urgencyB] || 999;
    
    // Primary sort: by urgency priority
    if (priorityA !== priorityB) {
      return priorityA - priorityB;
    }
    
    // Secondary sort: by dispatch time (most recent first) for same urgency level
    if (a.dispatch_time && b.dispatch_time) {
      const timeA = new Date(a.dispatch_time);
      const timeB = new Date(b.dispatch_time);
      return timeB - timeA;
    }
    
    // If one has dispatch time and other doesn't, prioritize the one with time
    if (a.dispatch_time && !b.dispatch_time) return -1;
    if (!a.dispatch_time && b.dispatch_time) return 1;
    
    return 0;
  });
};

function Stat({ icon, num, label, css }) {
  return (
    <div className={`stat-card ${css}`}>
      <div className="stat-icon">{icon}</div>
      <div className="stat-content">
        <div className="stat-number">{num}</div>
        <div className="stat-label">{label}</div>
      </div>
    </div>
  );
}

function Detail({ title, icon, children }) {
  return (
    <section className="detail-section">
      <h4>{icon} {title}</h4>
      {children}
    </section>
  );
}

function Row({ lbl, val }) {
  return (
    <div className="vital-item">
      <span className="vital-label">{lbl}</span>
      <span className="vital-value">{val || '—'}</span>
    </div>
  );
}

export default function App() {
  const [reports, setReports] = useState([]);
  const [loading, setLoading] = useState(true);
  const [openSet, setOpenSet] = useState(new Set());

  /* fetch once */
  useEffect(() => {
    fetch('http://localhost:3001/api/reports')
      .then(r => r.json())
      .then(data => {
        console.log('Fetched reports:', data); // Debug fetched data
        const reportsArray = Array.isArray(data) ? data : [];
        // Sort by criticality immediately after fetching
        const sortedReports = sortReportsByCriticality(reportsArray);
        setReports(sortedReports);
      })
      .catch(error => {
        console.error('Fetch error:', error);
        setReports([]);
      })
      .finally(() => setLoading(false));
  }, []);

  /* stats */
  const stats = {
    total: reports.length,
    critical: reports.filter(r => lc(r.treatment_urgency) === 'critical').length,
    high: reports.filter(r => lc(r.treatment_urgency) === 'high').length,
    medium: reports.filter(r => lc(r.treatment_urgency) === 'medium').length,
    low: reports.filter(r => lc(r.treatment_urgency) === 'low').length,
    avgAge: reports.length
      ? Math.round(
          reports.reduce((s, r) => s + (parseInt(r.patient_age, 10) || 0), 0) /
          reports.filter(r => r.patient_age).length
        )
      : 0
  };

  if (loading) return (
    <div className="loading-container">
      <div className="loading-spinner" />
      <p>Loading emergency reports…</p>
    </div>
  );

  if (!reports.length) return (
    <div className="empty-state">
      <AlertTriangle size={48} />
      <h2>No Active Reports</h2>
      <p>All clear – no emergency reports at this time.</p>
    </div>
  );

  const toggle = (i) => {
    const s = new Set(openSet);
    s.has(i) ? s.delete(i) : s.add(i);
    setOpenSet(s);
  };

  return (
    <div className="dashboard">
 <header className="dashboard-header">
  <div className="header-content">
    <div className="header-left">
      <Activity className="header-icon" />
      <h1>VERBA Dashboard</h1>
      <img src={avatar} alt="User profile" className="avatar" />
    </div>
    <span className="last-updated">Last updated {new Date().toLocaleTimeString()}</span>
  </div>
</header>


      {/* interactive map */}
      <MapView reports={reports} />

      {/* stats */}
      <div className="stats-grid">
        <Stat icon={<Users />} num={stats.total} label="Total Cases" css="total" />
        <Stat icon={<AlertTriangle />} num={stats.critical} label="Critical" css="critical" />
        <Stat icon={<User />} num={stats.avgAge} label="Avg Age" css="avg-age" />
      </div>

      {/* patient cards */}
      <div className="cards-container">
        {reports.map((r, i) => {
          const open = openSet.has(i);
          return (
            <article
              key={i}
              className={`patient-card ${lc(r.treatment_urgency) || 'unknown'} ${open ? 'expanded' : ''}`}
            >
              {/* compact header */}
              <div className="card-header" onClick={() => toggle(i)}>
                <div className="patient-info">
                  <h3>{r.patient_full_name || 'Unnamed'}</h3>
                  <div className="patient-meta">
                    {r.patient_age && <span>{r.patient_age}y</span>}
                    {r.patient_sex && <span>• {r.patient_sex}</span>}
                    {r.incident_location && (
                      <span className="location"><MapPin size={14} />{r.incident_location}</span>
                    )}
                  </div>
                </div>

                <div className="card-summary">
                  <span className={`urgency-badge ${lc(r.treatment_urgency) || 'unknown'}`}>

[truncated — 3037 more characters]
```

### first_responder_env/lib/python3.12/site-packages/rsa/cli.py

```python
#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

"""Commandline scripts.

These scripts are called by the executables defined in setup.py.
"""

import abc
import sys
import typing
import optparse

import rsa
import rsa.key
import rsa.pkcs1

HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys())
Indexable = typing.Union[typing.Tuple, typing.List[str]]


def keygen() -> None:
    """Key generator."""

    # Parse the CLI options
    parser = optparse.OptionParser(
        usage="usage: %prog [options] keysize",
        description='Generates a new RSA key pair of "keysize" bits.',
    )

    parser.add_option(
        "--pubout",
        type="string",
        help="Output filename for the public key. The public key is "
        "not saved if this option is not present. You can use "
        "pyrsa-priv2pub to create the public key file later.",
    )

    parser.add_option(
        "-o",
        "--out",
        type="string",
        help="Output filename for the private key. The key is "
        "written to stdout if this option is not present.",
    )

    parser.add_option(
        "--form",
        help="key format of the private and public keys - default PEM",
        choices=("PEM", "DER"),
        default="PEM",
    )

    (cli, cli_args) = parser.parse_args(sys.argv[1:])

    if len(cli_args) != 1:
        parser.print_help()
        raise SystemExit(1)

    try:
        keysize = int(cli_args[0])
    except ValueError as ex:
        parser.print_help()
        print("Not a valid number: %s" % cli_args[0], file=sys.stderr)
        raise SystemExit(1) from ex

    print("Generating %i-bit key" % keysize, file=sys.stderr)
    (pub_key, priv_key) = rsa.newkeys(keysize)

    # Save public key
    if cli.pubout:
        print("Writing public key to %s" % cli.pubout, file=sys.stderr)
        data = pub_key.save_pkcs1(format=cli.form)
        with open(cli.pubout, "wb") as outfile:
            outfile.write(data)

    # Save private key
    data = priv_key.save_pkcs1(format=cli.form)

    if cli.out:
        print("Writing private key to %s" % cli.out, file=sys.stderr)
        with open(cli.out, "wb") as outfile:
            outfile.write(data)
    else:
        print("Writing private key to stdout", file=sys.stderr)
        sys.stdout.buffer.write(data)


class CryptoOperation(metaclass=abc.ABCMeta):
    """CLI callable that operates with input, output, and a key."""

    keyname = "public"  # or 'private'
    usage = "usage: %%prog [options] %(keyname)s_key"
    description = ""
    operation = "decrypt"
    operation_past = "decrypted"
    operation_progressive = "decrypting"
    input_help = "Name of the file to %(operation)s. Reads from stdin if " "not specified."
    output_help = (
        "Name of the file to write the %(operation_past)s file "
        "to. Written to stdout if this option is not present."
    )
    expected_cli_args = 1
    has_output = True

    key_class = rsa.PublicKey  # type: typing.Type[rsa.key.AbstractKey]

    def __init__(self) -> None:
        self.usage = self.usage % self.__class__.__dict__
        self.input_help = self.input_help % self.__class__.__dict__
        self.output_help = self.output_help % self.__class__.__dict__

    @abc.abstractmethod
    def perform_operation(
        self, indata: bytes, key: rsa.key.AbstractKey, cli_args: Indexable
    ) -> typing.Any:
        """Performs the program's operation.

        Implement in a subclass.

        :returns: the data to write to the output.
        """

    def __call__(self) -> None:
        """Runs the program."""

        (cli, cli_args) = self.parse_cli()

        key = self.read_key(cli_args[0], cli.keyform)

        indata = self.read_infile(cli.input)

        print(self.operation_progressive.title(), file=sys.stderr)
        outdata = self.perform_operation(indata, key, cli_args)

        if self.has_output:
            self.write_outfile(outdata, cli.output)

    def parse_cli(self) -> typing.Tuple[optparse.Values, typing.List[str]]:
        """Parse the CLI options

        :returns: (cli_opts, cli_args)
        """

        parser = optparse.OptionParser(usage=self.usage, description=self.description)

        parser.add_option("-i", "--input", type="string", help=self.input_help)

        if self.has_output:
            parser.add_option("-o", "--output", type="string", help=self.output_help)

        parser.add_option(
            "--keyform",
            help="Key format of the %s key - default PEM" % self.keyname,
            choices=("PEM", "DER"),
            default="PEM",
        )

        (cli, cli_args) = parser.parse_args(sys.argv[1:])

        if len(cli_args) != self.expected_cli_args:
            parser.print_help()
            raise SystemExit(1)

        return cli, cli_args

    def read_key(self, filename: str, keyform: str) -> rsa.key.AbstractKey:
        """Reads a public or private key."""

        print("Reading %s key from %s" % (self.keyname, filename), file=sys.stderr)
        with open(filename, "rb") as keyfile:
            keydata = keyfile.read()

        return self.key_class.load_pkcs1(keydata, keyform)

    def read_infile(self, inname: str) -> bytes:
        """Read the input file"""

        if inname:
            print("Reading input from %s" % inname, file=sys.stderr)
            with open(inname, "rb") as infile:
                return infile.read()

        print("Reading input from stdin", f
[truncated — 3861 more characters]
```

### first_responder_env/lib/python3.12/site-packages/tqdm/cli.py

```python
"""
Module version for monitoring CLI pipes (`... | python -m tqdm | ...`).
"""
import logging
import re
import sys
from ast import literal_eval as numeric
from textwrap import indent

from .std import TqdmKeyError, TqdmTypeError, tqdm
from .version import __version__

__all__ = ["main"]
log = logging.getLogger(__name__)


def cast(val, typ):
    log.debug((val, typ))
    if " or " in typ:
        for t in typ.split(" or "):
            try:
                return cast(val, t)
            except TqdmTypeError:
                pass
        raise TqdmTypeError(f"{val} : {typ}")

    # sys.stderr.write('\ndebug | `val:type`: `' + val + ':' + typ + '`.\n')
    if typ == 'bool':
        if (val == 'True') or (val == ''):
            return True
        if val == 'False':
            return False
        raise TqdmTypeError(val + ' : ' + typ)
    if typ == 'chr':
        if len(val) == 1:
            return val.encode()
        if re.match(r"^\\\w+$", val):
            return eval(f'"{val}"').encode()
        raise TqdmTypeError(f"{val} : {typ}")
    if typ == 'str':
        return val
    if typ == 'int':
        try:
            return int(val)
        except ValueError as exc:
            raise TqdmTypeError(f"{val} : {typ}") from exc
    if typ == 'float':
        try:
            return float(val)
        except ValueError as exc:
            raise TqdmTypeError(f"{val} : {typ}") from exc
    raise TqdmTypeError(f"{val} : {typ}")


def posix_pipe(fin, fout, delim=b'\\n', buf_size=256,
               callback=lambda float: None, callback_len=True):
    """
    Params
    ------
    fin  : binary file with `read(buf_size : int)` method
    fout  : binary file with `write` (and optionally `flush`) methods.
    callback  : function(float), e.g.: `tqdm.update`
    callback_len  : If (default: True) do `callback(len(buffer))`.
      Otherwise, do `callback(data) for data in buffer.split(delim)`.
    """
    fp_write = fout.write

    if not delim:
        while True:
            tmp = fin.read(buf_size)

            # flush at EOF
            if not tmp:
                getattr(fout, 'flush', lambda: None)()
                return

            fp_write(tmp)
            callback(len(tmp))
        # return

    buf = b''
    len_delim = len(delim)
    # n = 0
    while True:
        tmp = fin.read(buf_size)

        # flush at EOF
        if not tmp:
            if buf:
                fp_write(buf)
                if callback_len:
                    # n += 1 + buf.count(delim)
                    callback(1 + buf.count(delim))
                else:
                    for i in buf.split(delim):
                        callback(i)
            getattr(fout, 'flush', lambda: None)()
            return  # n

        while True:
            i = tmp.find(delim)
            if i < 0:
                buf += tmp
                break
            fp_write(buf + tmp[:i + len(delim)])
            # n += 1
            callback(1 if callback_len else (buf + tmp[:i]))
            buf = b''
            tmp = tmp[i + len_delim:]


# ((opt, type), ... )
RE_OPTS = re.compile(r'\n {4}(\S+)\s{2,}:\s*([^,]+)')
# better split method assuming no positional args
RE_SHLEX = re.compile(r'\s*(?<!\S)--?([^\s=]+)(\s+|=|$)')

# TODO: add custom support for some of the following?
UNSUPPORTED_OPTS = ('iterable', 'gui', 'out', 'file')

# The 8 leading spaces are required for consistency
CLI_EXTRA_DOC = r"""
    Extra CLI Options
    -----------------
    name  : type, optional
        TODO: find out why this is needed.
    delim  : chr, optional
        Delimiting character [default: '\n']. Use '\0' for null.
        N.B.: on Windows systems, Python converts '\n' to '\r\n'.
    buf_size  : int, optional
        String buffer size in bytes [default: 256]
        used when `delim` is specified.
    bytes  : bool, optional
        If true, will count bytes, ignore `delim`, and default
        `unit_scale` to True, `unit_divisor` to 1024, and `unit` to 'B'.
    tee  : bool, optional
        If true, passes `stdin` to both `stderr` and `stdout`.
    update  : bool, optional
        If true, will treat input as newly elapsed iterations,
        i.e. numbers to pass to `update()`. Note that this is slow
        (~2e5 it/s) since every input must be decoded as a number.
    update_to  : bool, optional
        If true, will treat input as total elapsed iterations,
        i.e. numbers to assign to `self.n`. Note that this is slow
        (~2e5 it/s) since every input must be decoded as a number.
    null  : bool, optional
        If true, will discard input (no stdout).
    manpath  : str, optional
        Directory in which to install tqdm man pages.
    comppath  : str, optional
        Directory in which to place tqdm completion.
    log  : str, optional
        CRITICAL|FATAL|ERROR|WARN(ING)|[default: 'INFO']|DEBUG|NOTSET.
"""


def main(fp=sys.stderr, argv=None):
    """
    Parameters (internal use only)
    ---------
    fp  : file-like object for tqdm
    argv  : list (default: sys.argv[1:])
    """
    if argv is None:
        argv = sys.argv[1:]
    try:
        log_idx = argv.index('--log')
    except ValueError:
        for i in argv:
            if i.startswith('--log='):
                logLevel = i[len('--log='):]
                break
        else:
            logLevel = 'INFO'
    else:
        # argv.pop(log_idx)
        # logLevel = argv.pop(log_idx)
        logLevel = argv[log_idx + 1]
    logging.basicConfig(level=getattr(logging, logLevel),
                        format="%(levelname)s:%(module)s:%(lineno)d:%(message)s")

    # py<3.13 doesn't dedent docstrings
    d = (tqdm.__doc__ if sys.version_info < (3, 13)
         else indent(tqdm.__doc__, "    ")) + CLI_EXTRA_DOC

    opt_types = dict(RE_OPTS.findall(d))
    # opt_types['delim'] = 'chr'

    for o in UNSUPPORTED_OPTS:
        opt_types.pop(o)

    log.debug(sorted(opt_types.items()))

    # d = RE_OPTS.sub(r'  --\1=<\1>  : \2', d)
    split = RE_OPTS.split(
[truncated — 5010 more characters]
```

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