# Project export: Margin

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: Prepared in. Informed out.
- Devpost: https://devpost.com/software/margin-6lcfs7
- GitHub: https://github.com/purva-b/Margin-Medical-Copilot.git
- Team: 1 GitHub contributor(s) — Purva Bansod (2 commits)

## Devpost submission (written by the team)

### Inspiration

Independent physicians running small practices carry a weight most people never see. Every patient visit generates hours of work before and after the appointment itself, work that pulls them away from the reason they went to medical school in the first place. There are tools today that listen to the appointment and write the clinical note automatically. That problem is largely solved. Nobody has built what happens before the patient walks in, and after they leave. Before: the physician scrambles through a chart trying to remember who this patient is, what has changed, and what is urgent. They walk in underprepared. After: they have to convert everything discussed into a document the patient can actually use. Most patients leave without one. They forget 80% of what was said. They miss follow-ups. They come back to the emergency room. We wanted to know: what if the AI read the chart for you, spoke the key points as you walked in the room, and drafted the care plan while you were still in the conversation? Thus, Margin was born.

### What it does

Margin is a real-time clinical AI copilot for independent physicians. For this demo it is configured for an endocrinologist managing diabetes patients. If Margin works here, it works anywhere. Before the visit, Margin reads the full patient chart and speaks a clinical brief aloud through Deepgram Aura 2 before the physician enters the room. Hands-free. Eyes-free. The physician can ask follow-up questions by voice. Deepgram nova-2-medical transcribes in real time with clinical keyword boosting and answers come back spoken in under two seconds. Three Fetch.ai uAgents coordinate the pipeline. The Orchestrator receives requests from ASI:One, the Knowledge Agent runs RAG, and the Brief Agent generates the response via Claude. After the appointment the physician types their note. Margin runs parallel RAG queries against 227 chunks of ADA 2025 guidelines, KDIGO protocols, and FDA DailyMed, then generates a five-section evidence-grounded care plan. A two-layer safety check verifies every medication against the patient's allergy list, checks CKD dose contraindications, and validates recommendations against clinical guidelines before the physician sees anything. Eight parallel Claude Haiku evaluators score every care plan and brief from 0 to 100 across medication accuracy, guideline adherence, completeness, billing support, and readability. CMS-validated billing codes appear automatically with reimbursement estimates. At end of day the physician sees a quality scorecard and tomorrow's schedule already prepared.

### How we built it

CMS-validated billing codes appear automatically with reimbursement estimates. At end of day the physician sees a quality scorecard and tomorrow's schedule already prepared. How we built it Three Fetch.ai uAgents all mailbox-enabled, communicating over the Agentverse ACP protocol with deterministic seed-phrase addresses that stay stable across restarts. Three Fetch.ai uAgents all mailbox-enabled, communicating over the Agentverse ACP protocol with deterministic seed-phrase addresses that stay stable across restarts. Claude Sonnet 4.6 for all clinical reasoning and Claude Haiku for all parallel tasks like billing and the eight evaluators, cutting evaluation latency roughly 70%. Claude Sonnet 4.6 for all clinical reasoning and Claude Haiku for all parallel tasks like billing and the eight evaluators, cutting evaluation latency roughly 70%. Redis Cloud as the sole database with no SQL and no object storage. The Redis 8.0 native vectorset powers ANN search at 1024 dimensions under 10ms, built entirely from scratch with no existing library. Redis Cloud as the sole database with no SQL and no object storage. The Redis 8.0 native vectorset powers ANN search at 1024 dimensions under 10ms, built entirely from scratch with no existing library. 227 medical guideline chunks embedded with Voyage AI voyage-3, retrieved via four parallel VSIM queries through an 8-worker thread pool in under 200ms. 227 medical guideline chunks embedded with Voyage AI voyage-3, retrieved via four parallel VSIM queries through an 8-worker thread pool in under 200ms. Deepgram nova-2-medical for WebSocket STT and Aura 2 luna-en for TTS. The full voice loop completes in under two seconds. Deepgram nova-2-medical for WebSocket STT and Aura 2 luna-en for TTS. The full voice loop completes in under two seconds. Arize Phoenix with AnthropicInstrumentor auto-tracing every Claude call, RAG spans, and eight evaluator spans logging PASS or FAIL per dimension. Arize Phoenix with AnthropicInstrumentor auto-tracing every Claude call, RAG spans, and eight evaluator spans logging PASS or FAIL per dimension. Sentry initialized before FastAPI mounts with a custom tunnel endpoint to bypass hospital firewalls, session replay, and care plan quality wired as a custom measurement with alerts firing below 60.

### Challenges we ran into

Sentry initialized before FastAPI mounts with a custom tunnel endpoint to bypass hospital firewalls, session replay, and care plan quality wired as a custom measurement with alerts firing below 60. Challenges we ran into Redis 8.0 native vectorset had no Python library and no documentation at build time. We reverse-engineered the command syntax and discovered RESP3 protocol was required for correct type handling. Redis 8.0 native vectorset had no Python library and no documentation at build time. We reverse-engineered the command syntax and discovered RESP3 protocol was required for correct type handling. Making Claude clinically safe was harder than expected. A patient with an Aspirin allergy prescribed Aspirin by her cardiologist post-heart attack broke simple allergy checking entirely. Real medical safety needs explicit exception logic. Making Claude clinically safe was harder than expected. A patient with an Aspirin allergy prescribed Aspirin by her cardiologist post-heart attack broke simple allergy checking entirely. Real medical safety needs explicit exception logic. Layer 2 verification had to be specific to be useful. Generic guideline checks meant nothing. Precise rules like checking statin dose against LDL and CV risk actually caught real problems. Layer 2 verification had to be specific to be useful. Generic guideline checks meant nothing. Precise rules like checking statin dose against LDL and CV risk actually caught real problems. Sentry is blocked by hospital IT and ad blockers. We built a tunnel endpoint that validates the DSN and proxies envelopes through our own domain. Sentry is blocked by hospital IT and ad blockers. We built a tunnel endpoint that validates the DSN and proxies envelopes through our own domain. Coordinating three mailbox-enabled uAgents without deadlock required preserving the original sender address across two agent hops and isolating timeouts per agent. Coordinating three mailbox-enabled uAgents without deadlock required preserving the original sender address across two agent hops and isolating timeouts per agent. Running eight evaluators in parallel while keeping the HTTP response unblocked required per-evaluator exception isolation so one timeout never kills the whole response.

### Accomplishments we're proud of

Running eight evaluators in parallel while keeping the HTTP response unblocked required per-evaluator exception isolation so one timeout never kills the whole response. Accomplishments that we're proud of Zero drug-allergy false negatives across all three demo patients and more than fifteen test scenarios including physician-approved exceptions correctly bypassed. Zero drug-allergy false negatives across all three demo patients and more than fifteen test scenarios including physician-approved exceptions correctly bypassed. Redis 8.0 vectorset RAG built entirely from scratch with ANN search under 10ms at 1024 dimensions and no separate vector database needed. Redis 8.0 vectorset RAG built entirely from scratch with ANN search under 10ms at 1024 dimensions and no separate vector database needed. Full voice loop in under two seconds from Deepgram STT through Claude to Deepgram TTS, fast enough for real clinical conversation. Full voice loop in under two seconds from Deepgram STT through Claude to Deepgram TTS, fast enough for real clinical conversation. Quality scores consistently above 80 out of 100 on ADA 2025-grounded care plans with a self-correction loop logging both versions to a Redis fine-tuning dataset. Quality scores consistently above 80 out of 100 on ADA 2025-grounded care plans with a self-correction loop logging both versions to a Redis fine-tuning dataset. All three Fetch.ai agents mailbox-enabled with the full ACP ChatMessage pipeline working end-to-end from ASI:One to Brief Agent and back. All three Fetch.ai agents mailbox-enabled with the full ACP ChatMessage pipeline working end-to-end from ASI:One to Brief Agent and back. Production-quality observability with Sentry traces, Arize Phoenix LLM traces, and a care plan quality SLO treating AI output with the same rigour as API latency.

### What we learned

Production-quality observability with Sentry traces, Arize Phoenix LLM traces, and a care plan quality SLO treating AI output with the same rigour as API latency. What we learned Redis 8.0 is production-ready as a vector database. VSIM at 1024 dimensions under 10ms eliminates the need for any dedicated vector infrastructure. Redis 8.0 is production-ready as a vector database. VSIM at 1024 dimensions under 10ms eliminates the need for any dedicated vector infrastructure. Parallel evaluation is nearly free. Eight evaluators via ThreadPoolExecutor costs almost no additional wall-clock time. Build quality signals in from day one. Parallel evaluation is nearly free. Eight evaluators via ThreadPoolExecutor costs almost no additional wall-clock time. Build quality signals in from day one. Medical voice is a different domain. General-purpose ASR misses HbA1c, eGFR, semaglutide, and tirzepatide. Deepgram nova-2-medical was better from the very first test. Medical voice is a different domain. General-purpose ASR misses HbA1c, eGFR, semaglutide, and tirzepatide. Deepgram nova-2-medical was better from the very first test. Clinical AI is a verification problem not a language problem. Claude writes fluent prose immediately. Grounding, checking, and scoring the output is 80% of the work. Clinical AI is a verification problem not a language problem. Claude writes fluent prose immediately. Grounding, checking, and scoring the output is 80% of the work. The physician's workflow is sacred. Every prototype that added an AI step got cut. The product inserts into the existing workflow without changing it.

### What's next

The physician's workflow is sacred. Every prototype that added an AI step got cut. The product inserts into the existing workflow without changing it. What's next for Margin The next step is a pilot with three independent physician practices, live within 48 hours of onboarding. After that a cardiology configuration with ACC/AHA guidelines and cardiac medication formulary. On the infrastructure side HIPAA-compliant deployment with AWS HIPAA-eligible regions, encryption at rest, and Business Associate Agreements with all vendors. Real FHIR EHR integration and live Dexcom and LibreView CGM APIs are on the roadmap. The simulator already matches their output format exactly so swapping in the real APIs is a single function call. Finally multi-specialty tenant support with Redis key namespacing will let Margin scale to any independent practice. Links Margin Orchestrator Agent The entry-point Fetch.ai uAgent for the Margin Clinical AI system. Accepts ACP ChatMessage requests from ASI:One / Agentverse, routes them through the Knowledge and Brief agents, and returns a spoken clinical brief. Runs as a mailbox agent connected to Agentverse. -Margin Brief Agent A Fetch.ai uAgent that generates AI-powered pre-visit clinical briefs using Claude. Runs as a mailbox agent connected to Agentverse. Margin Knowledge Agent A Fetch.ai uAgent that performs RAG (Retrieval-Augmented Generation) over clinical guidelines using LangChain + FAISS + HuggingFace embeddings. Runs as a mailbox agent connected to Agentverse. ASI:One CHAT Link

## README (from the GitHub repository)

<div align="center">

# Margin

### The AI Copilot Built for Endocrinologists

**Less charting. More care.**

Margin is a real-time clinical AI copilot that reads a patient's full record before you walk in the room, speaks the briefing aloud, drafts a guideline-grounded care plan while you type your note, and catches drug-allergy conflicts before you sign — so the only thing left on your plate is the medicine.

[Live Demo](https://your-demo-url.com) · [Backend API Docs](https://your-app.onrender.com/docs) · [Report a Bug](https://github.com/your-org/margin/issues)

</div>

---

## The Problem

Endocrinologists spend **45 of every 60 appointment minutes on administrative work** — reviewing charts, dictating notes, coding visits, and cross-referencing drug databases. Only 15 minutes reach the patient. For complex diabetes patients on 6+ medications with CKD staging, CGM data, and multiple comorbidities, that ratio is even worse.

### Why Existing Solutions Fail

| Tool | Why it fails |
|------|-------------|
| **Epic / Cerner** | Chart navigation is a research project per patient |
| **Generic AI note-takers** | No clinical reasoning, no RAG over guidelines, no safety checks |
| **GPT wrappers** | No patient-record grounding; hallucinate doses and diagnoses |
| **Dragon Medical** | Transcription only — no synthesis, no planning, no verification |
| **Ambient AI (Suki, Nuance DAX)** | Post-visit only; can't advise *during* the visit |

---

## Our Solution

Margin provides three things that no existing tool combines:

1. **Pre-visit intelligence** — A spoken patient brief (real-time TTS via Deepgram Aura 2) generated from the full patient record before the doctor enters the room.

2. **Real-time safety** — A two-layer verification system (patient-record matching + external guideline grounding via RAG) that flags allergy conflicts, dose violations, and guideline deviations before any output reaches the clinician.

3. **Structured care plans** — Five-section care plans generated with retrieved evidence from ADA 2025, KDIGO, and FDA DailyMed, evaluated by 5 parallel AI evaluators and scored on a 0–100 quality scale.

---

## Key Features

### Clinical Workflow
- **Pre-visit spoken brief** — Synthesizes HbA1c, CGM metrics, medications, lab trends, and pending items into a bulleted summary, read aloud via Deepgram Aura 2 (`aura-2-luna-en`)
- **Live voice Q&A** — Ask follow-up questions via Deepgram `nova-2-medical` STT; answers are grounded against the patient record and spoken back in real-time
- **Auto-save clinical notes** — Notes are saved to Redis every 4 seconds; never lose work
- **Guideline-grounded care plans** — RAG over ADA 2025 / KDIGO / FDA DailyMed, with 5-section structured output
- **ICD-10 + CPT billing suggestions** — CMS-validated codes with national average reimbursement
- **Walk-in patient registration** — Real-time registration with atomic MR number generation

### AI Safety (Verified by Arize)
- **Layer 1** — Patient-record verification: allergy conflicts, dosage cross-checking against patient medications, CGM interpretation bounds, CKD dose contraindications
- **Layer 2** — External-source verification: RAG-sourced guideline citations that must support every recommendation
- **5 clinical evaluators** — Medication accuracy (35%), completeness (20%), guideline adherence (25%), billing code support (10%), plain-language readability (10%)
- **Self-correction loop** — If Layer 1/2 flags exist, the care plan regenerates once; both versions are logged to a correction dataset for future fine-tuning

### Observability
- **Sentry** — Full distributed tracing across browser + backend; 5 tracked transactions with p95 latency alerts; 10% session replay (100% on errors); ad-blocker bypass via tunnel endpoint
- **Arize Phoenix** — OpenTelemetry instrumentation on all Claude calls via `AnthropicInstrumentor()`; RAG retrieval spans; quality scores logged as Arize metrics

### Data & Integrations
- **Redis Cloud** — Sole persistence layer (30+ key patterns, 8h–30d TTL tiers)
- **Redis 8.0 vectorset** — Native ANN vector search (no RediSearch module) at 1024 dimensions
- **Fetch.ai uAgents** — 3-agent multi-agent system (Orchestrator + Knowledge + Brief agents) for Agentverse ACP integration
- **APScheduler** — Background refresh: CGM every 5 min, labs every 2 hours, adherence every 4 hours, device metrics every hour
- **FDA DailyMed** — Live drug information fallback (free, no API key required)

---

## How It Works

```
Doctor opens Margin → selects patient from today's schedule
        ↓
[Orchestrator Agent] reads full patient record from Redis
        ↓
[Knowledge Agent] runs 4–5 parallel RAG queries against
 ADA 2025 + KDIGO + FDA DailyMed vector index (Redis 8.0 vectorset)
        ↓
[Brief Agent] calls Claude Sonnet 4.6 → generates spoken brief
        ↓
[Safety Layer 1] verifies against patient record (allergies, doses, CGM bounds)
        ↓
Deepgram Aura 2 reads brief aloud while doctor enters room
        ↓
Doctor speaks notes → Deepgram nova-2-medical transcribes in real-time
        ↓
[Care Plan Agent] retrieves 12 guideline chunks (4 parallel queries)
  + fetches drug info from FDA DailyMed for up to 5 medications
        ↓
Claude Sonnet 4.6 generates 5-section structured care plan
        ↓
[Safety Layer 2] verifies care plan against guidelines
  → if flags exist, regenerates once, logs correction to dataset
        ↓
5 parallel evaluators score: medication accuracy, completeness,
  guideline adherence, billing support, readability → quality score 0–100
        ↓
Claude Haiku suggests ICD-10 + CPT billing codes (cached 24h)
        ↓
Visit data archived to Redis (30-day TTL) for continuity
```

---

## Architecture Overview

```
┌─────────────────────────────────────────────────────┐
│                     BROWSER                          │
│   React 18 + TypeScript + Vite + TailwindCSS        │
│   Deepgram WebSocket (STT)  │  Sentry Replay        │
└───────────────┬─────────────────────────────────────┘
                │ HTTPS/REST + Sentry trace headers
┌───────────────▼─────────────────────────────────────┐
│              FASTAPI BACKEND (Python)                │
│   16 endpoints  │  Uvicorn ASGI  │  APScheduler     │
│   ThreadPoolExecutor (_rag_pool ×8, _verify_pool ×4)│
└──┬──────────┬──────────┬──────────┬─────────────────┘
   │          │          │          │
   ▼          ▼          ▼          ▼
Redis     Claude      Deepgram    Arize Phoenix
Cloud    Sonnet 4.6   Aura 2 TTS  (OTLP traces)
(vectorset  Haiku
 + JSON    (billing)
 + cache)
   │          │
   ▼          ▼
Voyage AI  FDA DailyMed        Fetch.ai Agentverse
embeddings  (drug info)         (3 uAgents)
```

---

## Technology Stack

### Frontend

| Technology | Version | Purpose |
|-----------|---------|---------|
| React | 18.2 | UI framework |
| TypeScript | 5.3 | Type safety |
| Vite | 5.0 | Build tool + dev proxy |
| TailwindCSS | 3.4 | Styling |
| Axios | — | HTTP client |
| Sentry Browser SDK | — | Error tracking + session replay |
| Deepgram JS SDK | — | WebSocket STT + TTS playback |

### Backend

| Technology | Version | Purpose |
|-----------|---------|---------|
| FastAPI | 0.111 | API framework |
| Uvicorn | 0.30 | ASGI server |
| Pydantic | 2.7 | Request/response models |
| APScheduler | 3.10.4 | Background job scheduler |
| uAgents | 0.12.0 | Fetch.ai multi-agent framework |
| python-dotenv | — | Environment management |

### AI / ML

| Technology | Purpose |
|-----------|---------|
| Claude Sonnet 4.6 (`claude-sonnet-4-6`) | Patient briefs, care plans, voice Q&A, end-of-day summaries |
| Claude Haiku (`claude-haiku-4-5`) | Billing code suggestion, 5× parallel evaluators |
| Deepgram `nova-2-medical` | STT — medical terminology + keyword boosting |
| Deepgram Aura 2 (`aura-2-luna-en`) | TTS — spoken patient briefs |
| Voyage AI `voyage-3` | 1024-dimensional embeddings for medical guidelines RAG |
| Arize Phoenix (OTLP) | LLM observability, distributed tra

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 55 recognized source files, 484 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- LangChain (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Redis (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (65 of 65)

```
.gitignore
backend/.env.example
backend/BRIEF_AGENT_README.md
backend/brief_agent.py
backend/data/cpt_codes.json
backend/data/drug_interactions.json
backend/data/icd10_codes.json
backend/data/mock_adherence_data.py
backend/data/mock_cgm_data.py
backend/data/mock_lab_data.py
backend/data/mock_patients.py
backend/data/mock_schedule.py
backend/KNOWLEDGE_AGENT_README.md
backend/knowledge_agent.py
backend/main.py
backend/models/__init__.py
backend/models/patient.py
backend/models/requests.py
backend/multi_agent.py
backend/ORCHESTRATOR_AGENT_README.md
backend/orchestrator_agent.py
backend/requirements.txt
backend/scheduler.py
backend/scripts/embed_medical_sources.py
backend/services/arize_service.py
backend/services/cgm_simulator.py
backend/services/claude_service.py
backend/services/external_apis_service.py
backend/services/langchain_rag_service.py
backend/services/medical_sources_service.py
backend/services/redis_service.py
backend/services/tts_service.py
backend/services/vector_service.py
backend/start.sh
backend/test_multi_agent.py
frontend/.env.example
frontend/index.html
frontend/package.json
frontend/postcss.config.js
frontend/src/api/marginApi.ts
frontend/src/App.tsx
frontend/src/components/ArizeAlert.tsx
frontend/src/components/BillingCodes.tsx
frontend/src/components/BriefChat.tsx
frontend/src/components/CarePlanDocument.tsx
frontend/src/components/CGMPanel.tsx
frontend/src/components/Dashboard.tsx
frontend/src/components/EndOfDaySummary.tsx
frontend/src/components/Header.tsx
frontend/src/components/LoginPage.tsx
frontend/src/components/LogoutModal.tsx
frontend/src/components/MarginLogo.tsx
frontend/src/components/NoteEditor.tsx
frontend/src/components/SignedOutPage.tsx
frontend/src/components/WalkInForm.tsx
frontend/src/hooks/useDeepgram.ts
frontend/src/hooks/useSession.ts
frontend/src/index.css
frontend/src/main.tsx
frontend/src/types/index.ts
frontend/tailwind.config.js
frontend/tsconfig.json
frontend/tsconfig.node.json
frontend/vite.config.ts
README.md
```

### Dependencies

- backend/requirements.txt: anthropic@>=0.40.0, apscheduler@==3.10.4, arize-phoenix[otel]@>=4.0,<7.0, deepgram-sdk@==3.4.0, eval-type-backport@==0.2.0, faiss-cpu@>=1.8.0, fastapi@==0.111.0, httpx@==0.27.0, langchain@>=0.2.0, langchain-community@>=0.2.0, langchain-core@>=0.2.0, langchain-huggingface@>=0.0.3, openinference-instrumentation-anthropic@==0.1.4, opentelemetry-api@==1.24.0, opentelemetry-sdk@==1.24.0, pydantic@==2.7.0, python-dotenv@==1.0.1, redis@==5.0.0, sentence-transformers@>=3.0.0, sentry-sdk[fastapi]@==2.5.1, uagents@==0.12.0, uvicorn@==0.30.0, voyageai@==0.3.3
- frontend/package.json: @deepgram/sdk@^3.4.0, @sentry/react@^8.0.0, @types/react@^18.2.0, @types/react-dom@^18.2.0, @vitejs/plugin-react@^4.2.0, autoprefixer@^10.4.0, axios@^1.7.0, postcss@^8.4.0, react@^18.2.0, react-dom@^18.2.0, tailwindcss@^3.4.0, typescript@^5.3.0, vite@^5.0.0

### Recent commits (newest first)

- Clean up README by removing unnecessary sections
- Initial commit

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

### backend/BRIEF_AGENT_README.md

```markdown
# Margin Brief Agent

A Fetch.ai uAgent that generates AI-powered pre-visit clinical briefs using Claude. Runs as a mailbox agent connected to Agentverse.

## What It Does

1. Receives a `BriefRequest` from the Orchestrator Agent containing a `patient_id` and guideline chunks
2. Loads full patient context from Redis: profile, labs, CGM data, active alerts
3. Builds a structured clinical prompt with all context + relevant guideline excerpts
4. Calls Claude (`claude-sonnet-4-6`) to generate a BULLETS + SPOKEN brief
5. Returns the SPOKEN section (with active alert prefixes) to the Orchestrator

## Brief Format

Claude is instructed to produce:

```
BULLETS:
- [5-7 word clinical fact, most urgent first]
- ...

SPOKEN:
[2-3 natural sentences, no markdown, specific numbers, present-tense]
```

The agent extracts the SPOKEN section and prepends any active alerts before sending back.

## Agent Identity

| Property | Value |
|----------|-------|
| Name | Margin Brief Agent |
| Address | `agent1qdj2yr0kma2l4vgxdug0r6l68k2clv2772nct40573zxgnvp0l3agv2272f` |
| Port | 8013 |
| Seed env var | `AGENT_SEED_BRIEF` |

## Message Protocol

**Receives** — `BriefRequest`
```
patient_id: str
original_sender: str
original_msg_id: str
chunks: list[GuidelineChunk]  # from Knowledge Agent
```

**Sends** — `BriefResponse`
```
patient_id: str
original_sender: str
original_msg_id: str
brief: str
```

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `AGENTVERSE_KEY` | Yes | Agentverse API key for mailbox connection |
| `AGENT_SEED_BRIEF` | Yes | Seed phrase — determines agent address |
| `ANTHROPIC_API_KEY` | Yes | Claude API key for brief generation |
| `REDIS_URL` | Yes | Redis connection string for patient/CGM/alert data |

## Run

```bash
cd backend
source venv/bin/activate
python brief_agent.py
```

Expected startup output:
```
[brief] Address: agent1qdj2yr0kma2l4vgxdug0r6l68k2clv2772nct40573zxgnvp0l3agv2272f
INFO: [brief] Ready — agent1qdj2yr0kma2l4vgxdug0r6l68k2clv2772nct40573zxgnvp0l3agv2272f
```

## Dependencies

```
uagents
anthropic
redis
```

```

### backend/KNOWLEDGE_AGENT_README.md

```markdown
# Margin Knowledge Agent

A Fetch.ai uAgent that performs RAG (Retrieval-Augmented Generation) over clinical guidelines using LangChain + FAISS + HuggingFace embeddings. Runs as a mailbox agent connected to Agentverse.

## What It Does

1. Receives a `KnowledgeRequest` from the Orchestrator Agent containing a `patient_id`
2. Loads the patient profile and labs from Redis
3. Builds targeted queries based on diabetes type, eGFR, TSH, and active medications
4. Runs similarity search against a FAISS index of 26 ADA/KDIGO/ACC/FDA guideline chunks
5. Returns up to 8 deduplicated, ranked `GuidelineChunk` objects to the Orchestrator

## Embeddings

| Property | Value |
|----------|-------|
| Model | `all-MiniLM-L6-v2` |
| Dimensions | 384 |
| Provider | HuggingFace (local, no API key) |
| Vector store | FAISS (in-memory, built at startup) |
| Sources | ADA 2025, KDIGO 2022, ACC/AHA, IWGDF 2023, FDA DailyMed |

## Agent Identity

| Property | Value |
|----------|-------|
| Name | Margin Knowledge Agent |
| Address | `agent1qdzp2frdeqqc538k2xvndzl4vmhxd09gavafphqj9cfj9v0gdc9n55gkxg7` |
| Port | 8011 |
| Seed env var | `AGENT_SEED_KNOWLEDGE` |

## Message Protocol

**Receives** — `KnowledgeRequest`
```
patient_id: str
original_sender: str
original_msg_id: str
```

**Sends** — `KnowledgeResponse`
```
patient_id: str
original_sender: str
original_msg_id: str
chunks: list[GuidelineChunk]  # text, source, score
```

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `AGENTVERSE_KEY` | Yes | Agentverse API key for mailbox connection |
| `AGENT_SEED_KNOWLEDGE` | Yes | Seed phrase — determines agent address |
| `REDIS_URL` | Yes | Redis connection string for patient/lab data |

## Run

```bash
cd backend
source venv/bin/activate
python knowledge_agent.py
```

Expected startup output:
```
[knowledge] Address: agent1qdzp2frdeqqc538k2xvndzl4vmhxd09gavafphqj9cfj9v0gdc9n55gkxg7
INFO: [knowledge] Ready — agent1qdzp2frdeqqc538k2xvndzl4vmhxd09gavafphqj9cfj9v0gdc9n55gkxg7
```

## Dependencies

```
uagents
langchain-core
langchain-community
langchain-huggingface
faiss-cpu
sentence-transformers
redis
```

```

### frontend/package.json

```
{
  "name": "margin-frontend",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "axios": "^1.7.0",
    "@deepgram/sdk": "^3.4.0",
    "@sentry/react": "^8.0.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.0",
    "@types/react-dom": "^18.2.0",
    "@vitejs/plugin-react": "^4.2.0",
    "typescript": "^5.3.0",
    "vite": "^5.0.0",
    "tailwindcss": "^3.4.0",
    "autoprefixer": "^10.4.0",
    "postcss": "^8.4.0"
  }
}

```

### backend/requirements.txt

```
# Core
fastapi==0.111.0
eval-type-backport==0.2.0
uvicorn==0.30.0
pydantic==2.7.0
python-dotenv==1.0.1
httpx==0.27.0

# Fetch.ai agents
uagents==0.12.0

# Anthropic
anthropic>=0.40.0

# Deepgram (STT + TTS)
deepgram-sdk==3.4.0

# Redis
redis==5.0.0

# Embeddings — Voyage AI (care-plan RAG) + LangChain/FAISS (agent RAG)
voyageai==0.3.3
langchain>=0.2.0
langchain-core>=0.2.0
langchain-community>=0.2.0
langchain-huggingface>=0.0.3
sentence-transformers>=3.0.0
faiss-cpu>=1.8.0

# Sentry
sentry-sdk[fastapi]==2.5.1

# Arize / Phoenix
arize-phoenix[otel]>=4.0,<7.0
opentelemetry-api==1.24.0
opentelemetry-sdk==1.24.0
openinference-instrumentation-anthropic==0.1.4

# Scheduling
apscheduler==3.10.4


```

### frontend/src/main.tsx

```typescript
import * as Sentry from "@sentry/react";

Sentry.init({
  dsn: import.meta.env.VITE_SENTRY_DSN || "",
  tunnel: "/api/sentry-tunnel",
  integrations: [
    Sentry.browserTracingIntegration(),
    Sentry.replayIntegration({
      maskAllText: false,
      blockAllMedia: false,
    }),
  ],
  tracesSampleRate: 1.0,
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
  environment: "demo",
  release: "margin@1.0.0",
  // Inject sentry-trace + baggage headers into every API call so the backend
  // continues the same trace — errors on both sides show up as one event chain.
  tracePropagationTargets: [
    "localhost",
    /^https?:\/\/localhost:\d+/,
  ],
});

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

```

### frontend/src/types/index.ts

```typescript
// Mirrors backend Pydantic models

export interface Medication {
  name: string;
  dose: string;
  frequency: string;
  prescribed_date?: string;
  is_new?: boolean;
}

export interface LastVisit {
  date: string;
  reason?: string;
  notes?: string;
  physician?: string;
  hba1c?: number;
  assessment?: string;
}

export interface HealthMetric {
  name: string;
  values: number[];
  dates: string[];
  unit: string;
  trend: "improving" | "stable" | "worsening";
  threshold_min: number | null;
  threshold_max: number | null;
}

export interface Insurance {
  name: string;
  provider?: string;
  policy_number?: string;
  group_number?: string;
  member_id?: string;
  plan_type?: string;
}

export type DiabetesType = "T1DM" | "T2DM" | "Pre-diabetes" | "Gestational" | "LADA" | "Unknown";

export interface Patient {
  id: string;
  name: string;
  age: number;
  gender: string;
  dob: string;
  mrn: string;
  patient_number?: string;
  diabetes_type: DiabetesType;
  diabetes_duration_years?: number;
  cgm_device_type?: string | null;
  cgm_device_id?: string | null;
  insulin_pump?: boolean;
  insulin_pump_model?: string | null;
  primary_language?: string;
  conditions: string[];
  medications: Medication[];
  last_visit?: LastVisit;
  metrics?: HealthMetric[];
  pending_items?: string[];
  insurance: Insurance;
  allergies?: string[];
  allergy_exceptions?: string[];
  is_walkin?: boolean;
}

export interface CGMReading {
  glucose: number;
  timestamp: string;
  trend: "rapidly_rising" | "rising" | "steady" | "falling" | "rapidly_falling";
  trend_arrow: "↑↑" | "↑" | "→" | "↓" | "↓↓";
}

export interface CGMData {
  current_glucose: number;
  current_trend: string;
  current_trend_arrow: string;
  avg_glucose_14d: number;
  time_in_range: number;
  time_below_range: number;
  time_above_range: number;
  time_below_54: number;
  coefficient_of_variation?: number;
  std_deviation?: number;
  gmi: number;
  readings_24h?: CGMReading[];
  lowest_24h?: number;
  highest_24h?: number;
  device_type: string;
  last_reading_at?: string;
}

export type AppointmentStatus = "done" | "now" | "progress" | "upcoming" | "cancelled" | "walkin";

export interface Appointment {
  id: string;
  time: string;
  patient_id: string;
  patient_name: string;
  patient_age: number;
  patient_number?: string;
  diabetes_type: DiabetesType;
  reason: string;
  status: AppointmentStatus;
  care_plan_generated: boolean;
  initials: string;
  is_walkin: boolean;
}

// Individual billing code row (used in BillingCodes component)
export interface BillingCodeSuggestion {
  code: string;
  type: "ICD-10" | "CPT";
  description: string;
  national_average?: number;
  recommended?: boolean;
  rationale?: string;
  cms_validated?: boolean;
}

// Shape sent to /api/confirm-billing
export interface BillingCode {
  code: string;
  description: string;
  type: "ICD-10" | "CPT";
}

export interface ConversationTurn {
  role: "doctor" | "margin";
  text: string;
  timestamp?: string;
}

export interface ArizeFlag {
  type?: string;
  field: string;
  message: string;
  severity?: string;
  level: number;
  verification_layer?: number;
  suggested_correction?: string | null;
  blocked: boolean;
}

export interface PatientAlert {
  patient_id: string;
  alert_type?: string;
  message: string;
  severity: "critical" | "high" | "medium" | "low" | "urgent";
  value?: string | null;
  triggered_at?: string;
  resolved?: boolean;
}

export interface OutstandingItem {
  patient_name: string;
  description: string;
  priority?: "high" | "normal" | "info";
  severity?: string;
  action_label?: string;
}

export interface WeeklyStats {
  patients_seen: number;
  total_billed: number;
  care_plan_rate: number;
  avg_visit_minutes: number;
  overdue_referrals: number;
}

export interface DaySummary {
  date?: string;
  patients_seen: number;
  care_plans_generated: number;
  care_plans_pending?: number;
  total_billed?: number;
  avg_visit_minutes?: number;
  outstanding_items?: OutstandingItem[];
  tomorrow_count?: number;
  tomorrow_highlight?: string;
  tomorrow_appointments?: Appointment[];
  weekly_stats?: WeeklyStats;
  ai_summary?: string;
}

// Care plan structured sections
export interface CarePlan {
  assessment?: string;
  plan?: string;
  medications?: string;
  referrals?: string;
  patient_education?: string;
  return_date?: string;
  return_instructions?: string;
}

export interface ChatMessage {
  id: string;
  role: "doctor" | "margin";
  text: string;
  bulletPoints?: string[];   // pre-parsed bullets for brief messages
  audioBase64?: string | null;
  timestamp: string;
  infoGaps?: string[];
  type?: "brief" | "chat";
}

export interface VisitData {
  brief?: string;
  brief_points?: string[];
  conversation?: Array<{ role: "doctor" | "margin"; text: string; type?: string }>;
  note?: string;
  care_plan?: CarePlan;
  billing_codes?: BillingCodeSuggestion[];
  date?: string;
}

export interface CarePlanResponse {
  care_plan: CarePlan;
  arize_flags: ArizeFlag[];
  agent_statuses?: Record<string, string>;
  was_regenerated?: boolean;
  blocked?: boolean;
  sources_used?: string[];
  quality_score?: number;
  eval_results?: Record<string, boolean>;
}

export interface VoiceResponse {
  text?: string;
  brief?: string;
  brief_points?: string[];
  audio_base64?: string | null;
  mode?: string;
  arize_flags?: ArizeFlag[];
  agent_statuses?: Record<string, string>;
  blocked?: boolean;
  patient?: Patient;
  conversation_turns?: ConversationTurn[];
  info_gaps?: string[];
}

export interface WalkInPatientForm {
  name: string;
  dob: string;
  diabetes_type: DiabetesType;
  reason: string;
  mrn?: string;
  insurance_id?: string;
  phone?: string;
  is_existing_patient: boolean;
  patient_number?: string;   // MR-XXXXX for existing patient lookup
  appointment_time?: string; // HH:MM pre-selected slot
}

export interface WalkInResponse {
  success: boolean;
  appointment?: Appointment;
  message?: string;
}

```

### frontend/src/App.tsx

```typescript
import React, { useState, useCallback, useEffect, useRef } from "react";
import type { Appointment, Patient, CarePlanResponse, BillingCodeSuggestion, CGMData, ChatMessage } from "./types";
import { useSession } from "./hooks/useSession";
import {
  sendVoice,
  generateCarePlan,
  getBillingCodes,
  getPatientCGM,
  getPatientAlerts,
  saveVisitData,
  getVisitData,
  updateAppointmentStatus,
} from "./api/marginApi";

import LoginPage from "./components/LoginPage";
import LogoutModal from "./components/LogoutModal";
import SignedOutPage from "./components/SignedOutPage";
import Header from "./components/Header";
import Dashboard from "./components/Dashboard";
import WalkInForm from "./components/WalkInForm";
import NoteEditor from "./components/NoteEditor";
import BillingCodes from "./components/BillingCodes";
import CarePlanDocument from "./components/CarePlanDocument";
import EndOfDaySummary from "./components/EndOfDaySummary";
import BriefChat from "./components/BriefChat";

type AppView = "login" | "dashboard" | "visit" | "end-of-day" | "signed-out";
type VisitPhase = "before" | "after";
type ActiveNav = "today" | "schedule";


export default function App() {
  const { sessionId, isReady, clearSession, refreshSession } = useSession();

  const [view, setView] = useState<AppView>("login");
  const [showLogoutModal, setShowLogoutModal] = useState(false);
  const [signOutTime, setSignOutTime] = useState<Date>(new Date());
  const [activeNav, setActiveNav] = useState<ActiveNav>("today");

  const [walkInAdded, setWalkInAdded] = useState(0);
  const [showWalkIn, setShowWalkIn] = useState(false);
  const [walkInSlotTime, setWalkInSlotTime] = useState<string | undefined>();

  // Visit
  const [activeAppointment, setActiveAppointment] = useState<Appointment | null>(null);
  const [activePatient, setActivePatient] = useState<Patient | null>(null);
  const [activeVisitDate, setActiveVisitDate] = useState<string>("");
  const [inProgressAppointmentId, setInProgressAppointmentId] = useState<string | null>(null);
  const [visitPhase, setVisitPhase] = useState<VisitPhase>("before");

  // Brief/chat
  const [briefMessages, setBriefMessages] = useState<ChatMessage[]>([]);
  const [infoGaps, setInfoGaps] = useState<string[]>([]);
  const [isGeneratingBrief, setIsGeneratingBrief] = useState(false);

  // After-visit
  const [noteText, setNoteText] = useState("");
  const [billingCodes, setBillingCodes] = useState<BillingCodeSuggestion[]>([]);
  const [loadingBilling, setLoadingBilling] = useState(false);
  const [carePlanResponse, setCarePlanResponse] = useState<CarePlanResponse | null>(null);
  const [generatingPlan, setGeneratingPlan] = useState(false);

  const autoSaveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Helper to build the save payload (used in multiple places)
  const buildSavePayload = useCallback((note: string) => ({
    brief: briefMessages.find((m) => m.type === "brief")?.text,
    brief_points: briefMessages.find((m) => m.type === "brief")?.bulletPoints,
    conversation: briefMessages.map((m) => ({ role: m.role, text: m.text, type: m.type })),
    note: note || undefined,
    care_plan: carePlanResponse?.care_plan,
    billing_codes: billingCodes.length > 0 ? billingCodes : undefined,
  }), [briefMessages, carePlanResponse, billingCodes]);

  // Auto-save notes 4 s after the doctor stops typing
  useEffect(() => {
    if (!activeAppointment || view !== "visit") return;
    if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current);
    autoSaveTimer.current = setTimeout(async () => {
      const today = new Date().toLocaleDateString("en-CA", { timeZone: "America/Los_Angeles" });
      try {
        await saveVisitData(activeAppointment.patient_id, {
          ...buildSavePayload(noteText),
          _visit_date: activeVisitDate || today,
        } as Parameters<typeof saveVisitData>[1] & { _visit_date?: string });
      } catch (e) {
        console.error("[auto-save]", e);
      }
    }, 4000);
    return () => { if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current); };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [noteText, activeAppointment]);

  const handleLogin = () => {
    setView("dashboard");
    if (!isReady) refreshSession();
  };

  const handleSignOut = async () => {
    setShowLogoutModal(false);
    setSignOutTime(new Date());

    if (activeAppointment) {
      try {
        const today = new Date().toLocaleDateString("en-CA", { timeZone: "America/Los_Angeles" });
        await saveVisitData(activeAppointment.patient_id, {
          ...buildSavePayload(noteText),
          _visit_date: activeVisitDate || today,
        } as Parameters<typeof saveVisitData>[1] & { _visit_date?: string });
      } catch (e) {
        console.error("[save-visit-on-logout]", e);
      }
    }

    clearSession();
    setActiveAppointment(null);
    setView("signed-out");
  };

  const handleGenerateBrief = useCallback(async (appt: Appointment) => {
    setIsGeneratingBrief(true);
    try {
      const res = await sendVoice({
        text: "",
        session_id: sessionId,
        patient_id: appt.patient_id,
        mode: "brief",
      });

      const msg: ChatMessage = {
        id: `mg-${Date.now()}`,
        role: "margin",
        text: res.text || res.brief || "",
        bulletPoints: res.brief_points && res.brief_points.length > 0 ? res.brief_points : undefined,
        audioBase64: res.audio_base64,
        timestamp: new Date().toISOString(),
        type: "brief",
      };
      setBriefMessages([msg]);
      if (res.patient) setActivePatient(res.patient as unknown as Patient);
      // BriefChat handles auto-play via its own useEffect
    } catch (e) {
      console.error("[brief]", e);
      setBriefMessages([{
        id: "err",
        role: "margin",
        text: "Patient record loaded. Brief unavailable — check backend connection.",
        timestamp: new Date().toISOString(),
      }]);
    } finally {
      se
[truncated — 20900 more characters]
```

### frontend/postcss.config.js

```javascript
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

```

### frontend/vite.config.ts

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    proxy: {
      "/api": {
        target: "http://localhost:8000",
        changeOrigin: true,
      },
    },
  },
});

```

### backend/start.sh

```shell
#!/usr/bin/env bash
# Kill any process already holding port 8000, then start uvicorn with --reload.
set -e

PORT=8000
PID=$(lsof -ti :"$PORT" 2>/dev/null || true)
if [ -n "$PID" ]; then
  echo "[start] Killing existing process on port $PORT (PID $PID)"
  kill -9 $PID
  sleep 0.5
fi

cd "$(dirname "$0")"
source venv/bin/activate

exec uvicorn main:app --reload --host 0.0.0.0 --port "$PORT"

```

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