# Project export: Agent 00Vision

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: TreeHacks 2026
- Tagline: Give your video local, multimodal intelligence powered by Edge AI. A flexible multi-agent VLM pipeline builder for detection systems with structured rule sets and auditable reference tracking.
- Devpost: https://devpost.com/software/agent-00vision
- GitHub: https://github.com/kuzeykantarcioglu/compliance_vision_cloud/
- Demo: https://drive.google.com/file/d/1T9T5li9Tg49KvprofNU-6hseeNkSz8N9/view
- Video: https://www.youtube.com/embed/ihFzpgckDaM?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — Dorukhan User (11 commits), Kuzey Kantarcioglu (6 commits), Kenan Erol (5 commits), Rick Huang (2 commits)

## Devpost submission (written by the team)

### Overview

The name's Bond. James Bond...

### Inspiration

Physical security and compliance monitoring is a $130B+ market running on human eyeballs. Security guards watch camera feeds for 8+ hours, with attention dropping drastically within a few hours. Compliance audits are manual, infrequent, and retrospective. Every new rule requires retraining staff and hoping humans remember. We saw an opportunity to put AI-powered compliance monitoring directly in the user's hands, with the option to keep all data local. With increasingly centralized compute, especially for AI applications, consumers face risks exposing sensitive personal information to data breaches and marketing. Companies and other entities require even stricter enforcement and legal requirements around security and data storage. However, as Edge AI hardware and open-source AI becomes more capable, we saw potential to personalize to tailor local AI solutions to specific technological needs, in areas like security and privacy.

### What it does

Agent 00Vision is an AI-powered video compliance monitoring platform that lets users define any compliance policy in plain English, point it at any camera (live webcam or uploaded video), and get structured, audit-ready compliance reports automatically. A Vision Language Model (VLM) performs inference-time, user-directed identification of people, objects, and actions in video frames A Large Language Model (LLM) takes agentic action based on user-defined compliance rulesets, evaluating observations against policies and generating structured verdicts A dual-mode compliance system (Incidents vs. Checklists) prevents alert fatigue while maintaining safety standards An optional on-premise deployment mode using NVIDIA DGX Spark ensures video data never leaves the building Email notification capability to notify stakeholders remotely and log violations. No model training. No computer vision expertise. Write the rule, point the camera, get the report.

### How we built it

Backend: Python 3.11 with FastAPI, serving REST and WebSocket endpoints for real-time monitoring Frontend: React 19 + TypeScript + Vite + Tailwind CSS for a responsive policy-builder and live monitoring dashboard Cloud AI pipeline: OpenAI GPT-4o Vision for scene understanding, GPT-4o-mini for policy evaluation, and Whisper for audio transcription Local AI pipeline on NVIDIA DGX Spark: Deployed Cosmos-Reason2 8B with vLLM as the Vision Language Model Deployed Nemotron-3-Nano 30B with Ollama as the compliance evaluation LLM Split VRAM between the two models for concurrent inference Deployed Cosmos-Reason2 8B with vLLM as the Vision Language Model Deployed Nemotron-3-Nano 30B with Ollama as the compliance evaluation LLM Split VRAM between the two models for concurrent inference Smart frame sampling: Built a dual-metric change detection engine (histogram correlation + structural similarity) with a threaded pipeline, reducing frames sent to cloud API calls Async processing: Celery + Redis for background video analysis with real-time progress updates via WebSocket

### Challenges we ran into

Hallucinations in both the VLM and LLM led to inaccurate compliance verdicts. We mitigated this with structured JSON output schemas, Pydantic validation, and retry logic with stricter prompts on parse failure Time constraints limited our ability to pretrain or fine-tune models on compliance-specific data, so we relied on prompt engineering and few-shot examples Splitting VRAM between Cosmos and Nemotron on the DGX Spark required careful configuration to keep both models loaded simultaneously Rate limiting from cloud API providers required exponential backoff with jitter and usage tracking to stay within quotas during demo-heavy periods Video seeking performance in compressed formats (H.264/H.265) was initially very slow. We switched from cap.set(POS_FRAMES) to sequential cap.read() with frame counting for a 5-10x speedup

### Accomplishments we're proud of

Building a complete end-to-end pipeline, from raw video to structured compliance reports, in a single hackathon Achieving reduction in API calls through our intelligent frame sampling, making the product economically viable at scale Implementing dual-mode compliance (Incidents vs. Checklists) with temporal memory so the system remembers what it has already verified Successfully running inference on the NVIDIA DGX Spark with two models sharing VRAM, proving that local deployment is feasible today

### What we learned

Local inference is the future for light consumer workloads. Privacy-sensitive applications don't need to send data to the cloud when edge hardware can handle it NVIDIA has a great variety of open-source models suitable for deployment. Cosmos and Nemotron worked well out of the box with minimal prompt tuning Transformer-based models give great flexibility by not limiting you to an ultra-specific use case. Traditional CV-based CNNs are hyper-specialized at the cost of generalization. VLMs can handle "any rule you can describe in English" without retraining Dual-mode compliance prevents alert fatigue. Continuously re-alerting on the same compliant hard hat every 6 seconds creates noise. Checklist mode with validity periods solves this

### What's next

TensorRT optimization for DGX models to reduce inference latency Modal.com deployment for elastic cloud scaling across multiple cameras Notification channels: SMS via Twilio, Slack, and Microsoft Teams alerts for critical violations Multi-camera orchestration: Dashboard for managing dozens of feeds with independent policies As local inference gets cheaper, Agent 00Vision becomes a viable option for privacy-focused customers to explore wide-ranging use cases, from off-the-grid home security to endangered animal identification in the wild

## README (from the GitHub repository)

<div align="center">

# Agent 00Vision

### AI-Powered Video Compliance Monitoring

[![Python](https://img.shields.io/badge/Python-3.11%2B-blue)](https://www.python.org/)
[![FastAPI](https://img.shields.io/badge/FastAPI-0.115-green)](https://fastapi.tiangolo.com/)
[![React](https://img.shields.io/badge/React-19-61dafb)](https://react.dev/)
[![NVIDIA](https://img.shields.io/badge/NVIDIA-DGX%20Spark-76b900)](https://www.nvidia.com/en-us/data-center/dgx-spark/)

**Define any compliance policy in plain English. Point it at any camera. Get structured, audit-ready reports.**

</div>

---

## Quick Start

**Prerequisites:** Python 3.11+, Node.js 18+, an OpenAI API key

```bash
git clone https://github.com/kuzeykantarcioglu/treehacks2026.git
cd treehacks2026

# Add your API key
echo "OPENAI_API_KEY=sk-your-key-here" > .env

# Run everything
./run.sh
```

That's it. The script creates a virtualenv, installs dependencies, and starts both servers.

- **Frontend:** http://localhost:5173
- **Backend API:** http://localhost:8082
- **API Docs:** http://localhost:8082/docs

Press `Ctrl+C` to stop all services.

### Manual Setup

If you prefer to run things separately:

```bash
# Backend (terminal 1)
python3 -m venv venv && source venv/bin/activate
pip install -r backend/requirements.txt
PYTHONPATH=$(pwd) uvicorn backend.main:app --reload --host 0.0.0.0 --port 8082

# Frontend (terminal 2)
cd frontend && npm install && npm run dev
```

---

## What It Does

Agent 00Vision watches video feeds and enforces compliance rules you write in plain English.

```
"All personnel must wear a hard hat and yellow safety vest"
     |
     v
  AI watches the camera feed
     |
     v
  Structured report: 2 violations detected, timestamps, severity, recommendations
```

**Two modes of operation:**

| Mode | Input | Use Case |
|------|-------|----------|
| **Live monitoring** | Webcam feed | Real-time compliance with continuous alerts |
| **File analysis** | Uploaded video | Batch processing with full report |

**Two AI backends:**

| Provider | Models | Data Residency |
|----------|--------|----------------|
| **OpenAI (cloud)** | GPT-4o Vision + GPT-4o-mini + Whisper | Cloud |
| **NVIDIA DGX Spark (local)** | Cosmos-Reason2 8B + Nemotron-3-Nano 30B | On-premise |

---

## Features

- **Policy-as-prompt** — Write any compliance rule in English, no model training needed
- **Dual-mode compliance** — Incident mode (alert every violation) vs. Checklist mode (check once per validity period) to prevent alert fatigue
- **Smart frame sampling** — Change detection reduces frames sent to the VLM by 80-95%, making the product economically viable
- **Reference image matching** — Upload photos of authorized personnel or badges for identity verification
- **Audio compliance** — Whisper transcription for speech-based rules (safety briefings, verbal confirmations)
- **Structured reports** — Machine-readable JSON output with severity, timestamps, and recommendations
- **AI policy assistant** — Chatbot that helps you build compliance policies

---

## Architecture

```
Frontend (React + TypeScript + Vite + Tailwind)
  |
  | /api proxy (Vite -> :8082)
  v
Backend (FastAPI)
  |
  |-- POST /analyze/        Full video pipeline (sync)
  |-- POST /analyze/frame   Single frame analysis (webcam)
  |-- POST /polly/chat      AI policy assistant
  |-- GET  /health          System status
  |
  v
Processing Pipeline
  1. Frame Extraction + Change Detection (OpenCV)
  2. Visual Analysis (GPT-4o Vision or Cosmos-Reason2)
  3. Audio Transcription (Whisper) [optional]
  4. Policy Evaluation (GPT-4o-mini or Nemotron-3-Nano)
  5. Report Generation (structured JSON)
```

---

## Project Structure

```
treehacks2026/
├── backend/
│   ├── main.py                 # FastAPI entry point
│   ├── core/config.py          # Environment + OpenAI client config
│   ├── models/schemas.py       # Pydantic data models
│   ├── routers/
│   │   ├── analyze.py          # /analyze, /analyze/frame endpoints
│   │   ├── async_analyze.py    # /async/analyze (requires Redis)
│   │   ├── polly.py            # /polly/chat AI assistant
│   │   └── websocket.py        # WebSocket for task updates
│   └── services/
│       ├── video.py            # Frame extraction + keyframe sampling
│       ├── vlm.py              # GPT-4o Vision calls
│       ├── policy.py           # Compliance evaluation engine
│       ├── dgx.py              # NVIDIA DGX Spark integration
│       ├── whisper.py          # Audio transcription
│       └── api_utils.py        # Retry logic + rate limiting
├── frontend/
│   ├── src/
│   │   ├── App.tsx             # Main app component
│   │   ├── api.ts              # Backend API client
│   │   └── components/
│   │       ├── PolicyConfig.tsx      # Rule builder UI
│   │       ├── LiveReportView.tsx    # Real-time monitoring
│   │       ├── ReportView.tsx        # Analysis results
│   │       ├── VideoInput.tsx        # Webcam/file input
│   │       ├── ReferenceImages.tsx   # Reference photo management
│   │       ├── PollyChat.tsx         # AI policy assistant
│   │       └── DualModeReport.tsx    # Incident vs. Checklist display
│   └── vite.config.ts          # Vite config (proxies /api -> :8082)
├── scene_detection.py          # OpenCV change detection engine
├── run.sh                      # Single script to start everything
├── stop.sh                     # Stop all services
└── .env                        # OPENAI_API_KEY (not committed)
```

---

## Configuration

```bash
# .env
OPENAI_API_KEY=sk-your-key-here

# Optional — only needed for async features
REDIS_URL=redis://localhost:6379/0

# Optional — DGX Spark local inference
DGX_SPARK_IP=10.19.176.53
DGX_PROXY_PORT=8001
```

---

## Built With

**AI/ML:** OpenAI GPT-4o Vision, GPT-4o-mini, Whisper, NVIDIA Cosmos-Reason2 8B, NVIDIA Nemotron-3-Nano 30B

**Backend:** Python, FastAPI, OpenCV, Celery, Redis, WebSockets

**Frontend:** React 19, TypeScript, Vite, Tailwind CSS

**Infrastructure:** NVIDIA DGX Spark, vLLM, Ollama

---

<div align="center">

**Built at TreeHacks 2026**

</div>


## Detected evidence (automated analysis)

Indexed codebase: 60 recognized source files, 539 KB.
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- OpenAI (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
- Ollama (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (72 of 72)

```
.env.example
.gitignore
.pids
backend.log
backend/__init__.py
backend/core/__init__.py
backend/core/config.py
backend/main.py
backend/models/__init__.py
backend/models/schemas.py
backend/requirements.txt
backend/routers/__init__.py
backend/routers/analyze.py
backend/routers/async_analyze.py
backend/routers/polly.py
backend/routers/websocket.py
backend/services/__init__.py
backend/services/api_utils.py
backend/services/celery_app.py
backend/services/celery_tasks.py
backend/services/compliance_state.py
backend/services/dgx.py
backend/services/policy.py
backend/services/speech_policy.py
backend/services/video.py
backend/services/vlm.py
backend/services/whisper.py
compliance_state.json
frontend.log
frontend/.gitignore
frontend/index.html
frontend/package.json
frontend/src/api-async.ts
frontend/src/api.ts
frontend/src/App.tsx
frontend/src/components/AsyncAnalysis.tsx
frontend/src/components/DualModeReport.tsx
frontend/src/components/EmptyStateAnimation.tsx
frontend/src/components/Header.tsx
frontend/src/components/LiveReportView.tsx
frontend/src/components/PipelineStatus.tsx
frontend/src/components/PolicyConfig.tsx
frontend/src/components/PollyChat.tsx
frontend/src/components/ProviderToggle.tsx
frontend/src/components/ReferenceImages.tsx
frontend/src/components/ReferencesPanel.tsx
frontend/src/components/ReportView.tsx
frontend/src/components/StatusIndicator.tsx
frontend/src/components/ThemeToggle.tsx
frontend/src/components/VideoInput.tsx
frontend/src/index.css
frontend/src/main.tsx
frontend/src/security.py
frontend/src/types.ts
frontend/tailwind.config.js
frontend/tsconfig.json
frontend/vite.config.ts
local_ui/app.py
local_ui/README.md
local_ui/requirements.txt
local_ui/security.py
local_ui/static/css/styles.css
local_ui/static/index.html
local_ui/static/js/app.js
README.md
run.sh
scene_detection.py
start-services.sh
start.sh
stop.sh
test_dual_mode.py
text
```

### Dependencies

- backend/requirements.txt: celery[redis], fastapi, httpx, numpy, openai, opencv-python-headless, python-dotenv, python-multipart, redis@>=6.4.0, requests, setuptools, uvicorn[standard], websockets
- frontend/package.json: @tailwindcss/vite@^4.1.18, @types/react@^19.2.14, @types/react-dom@^19.2.3, @vitejs/plugin-react@^5.1.4, axios@^1.13.5, lucide-react@^0.564.0, react@^19.2.4, react-dom@^19.2.4, tailwindcss@^4.1.18, typescript@~5.9.3, vite@^7.3.1
- local_ui/requirements.txt: fastapi@>=0.104.0, opencv-python@>=4.8.0, pydantic@>=2.0.0, python-multipart@>=0.0.6, requests@>=2.31.0, uvicorn[standard]@>=0.24.0

### Recent commits (newest first)

- Delete ROADMAP.md
- Delete PITCH.md
- Delete IMPLEMENTATION_PLAN.md
- Delete DUAL_MODE_COMPLIANCE.md
- bruh
- Merge remote-tracking branch 'origin/changes'
- Added checklisting and whisper for cloud
- FINAL CHANGES
- FINAL VERSION BEFORE OPENAI)"
- rh: forgot this
- rh: fixed history tab duplicating, also improved prompt
- add submodule
- rh: pushing doruk's new gui
- Merge remote-tracking branch 'origin/changes'
- security
- Fix README with correct repository path and startup instructions
- Merge pull request #1 from kuzeykantarcioglu/changes
- Finished Project
- Updated with receive
- Added dual mode compliance for checklist and incident specification

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

### local_ui/requirements.txt

```
fastapi>=0.104.0
uvicorn[standard]>=0.24.0
python-multipart>=0.0.6
requests>=2.31.0
opencv-python>=4.8.0
pydantic>=2.0.0

```

### backend/requirements.txt

```
fastapi
uvicorn[standard]
python-dotenv
openai
opencv-python-headless
numpy
python-multipart
httpx
requests
celery[redis]
redis>=6.4.0
websockets
setuptools  # Required for Python 3.14+ (distutils removed)

```

### frontend/package.json

```
{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "devDependencies": {
    "typescript": "~5.9.3",
    "vite": "^7.3.1"
  },
  "dependencies": {
    "@tailwindcss/vite": "^4.1.18",
    "@types/react": "^19.2.14",
    "@types/react-dom": "^19.2.3",
    "@vitejs/plugin-react": "^5.1.4",
    "axios": "^1.13.5",
    "lucide-react": "^0.564.0",
    "react": "^19.2.4",
    "react-dom": "^19.2.4",
    "tailwindcss": "^4.1.18"
  }
}

```

### backend/main.py

```python
"""FastAPI application entrypoint."""

import logging
import sys

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

# Configure logging BEFORE any application imports.
# logging.basicConfig() is a no-op if the root logger already has handlers
# (e.g. when uvicorn configures logging before importing this module).
# Force our handler onto the root logger so all app modules get proper output.
_root = logging.getLogger()
if not any(isinstance(h, logging.StreamHandler) and h.stream == sys.stderr for h in _root.handlers):
    _handler = logging.StreamHandler(sys.stderr)
    _handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s"))
    _root.addHandler(_handler)
_root.setLevel(logging.INFO)

from backend.core.config import OPENAI_API_KEY
from backend.routers.analyze import router as analyze_router
from backend.routers.polly import router as polly_router

logger = logging.getLogger(__name__)

# Optional async features (require Redis/Celery)
try:
    from backend.routers.async_analyze import router as async_router
    from backend.routers.websocket import router as websocket_router
    ASYNC_ENABLED = True
except ImportError as e:
    logger.warning(f"Async features disabled (Redis/Celery not available): {e}")
    ASYNC_ENABLED = False

app = FastAPI(
    title="Agent 00Vision API",
    description="AI-powered video compliance monitoring",
    version="0.1.0",
)

# CORS — allow frontend dev server
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Tighten in production
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Routers
app.include_router(analyze_router)
app.include_router(polly_router)

# Include async routers if available
if ASYNC_ENABLED:
    app.include_router(async_router)
    app.include_router(websocket_router)
    logger.info("✅ Async features enabled (Celery + WebSocket)")
else:
    logger.info("⚠️ Running without async features (install Redis + run Celery for full functionality)")


@app.get("/health")
async def health_check():
    """Health check endpoint with service status."""
    health_status = {
        "status": "ok",
        "openai_key_set": bool(OPENAI_API_KEY),
    }

    # DGX Spark status (cached, never blocks)
    try:
        from backend.services.dgx import get_dgx_cached_status
        health_status["dgx"] = get_dgx_cached_status()
    except ImportError:
        health_status["dgx"] = {"status": "not available"}
    
    # Check Redis connection (if available)
    try:
        from backend.services.celery_app import redis_client
        redis_client.ping()
        health_status["redis"] = "connected"
    except ImportError:
        health_status["redis"] = "not installed"
    except Exception as e:
        health_status["redis"] = f"error: {e}"
        health_status["status"] = "degraded"
    
    # Check Celery workers (if available)
    try:
        from backend.services.celery_app import app as celery_app
        inspect = celery_app.control.inspect()
        stats = inspect.stats()
        health_status["celery_workers"] = len(stats) if stats else 0
    except ImportError:
        health_status["celery_workers"] = "not installed"
    except Exception as e:
        health_status["celery_workers"] = 0
        health_status["celery_error"] = str(e)
    
    # Get API usage stats
    try:
        from backend.services.api_utils import get_usage_stats
        health_status["api_usage"] = get_usage_stats()
    except:
        pass
    
    return health_status

```

### frontend/src/main.tsx

```typescript
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";

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

```

### local_ui/app.py

```python
#!/usr/bin/env python3
"""
Compliance Vision - Local UI Backend
FastAPI server that wraps the security badge detection logic
and exposes it through a modern web interface.
"""

import warnings
warnings.filterwarnings("ignore", category=UserWarning)

import os
import base64
import tempfile
import time
import json
import asyncio
import logging
from typing import Optional
from pathlib import Path

import cv2
import requests

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("compliance-vision")
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, UploadFile, File, HTTPException
from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse, FileResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel

app = FastAPI(title="Compliance Vision Local UI")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ─── Default Configuration ───────────────────────────────────────────────────

DEFAULT_CONFIG = {
    "spark_ip": "10.19.176.53",
    "proxy_port": 8001,
    "endpoint_path": "/v1/analyze_frame_sync",
    "model_id": "/home/asus/.cache/huggingface/hub/models--nvidia--Cosmos-Reason2-8B/snapshots/7d6a645088b550bbd45daaf782e2430bba9c82bb",
    "prompt": """You are a security camera AI for TreeHacks 2026 hackathon at Stanford University.

THE OFFICIAL TREEHACKS BADGE:
- Christmas tree / pine tree shaped 
- Has "TREE HACKS" and "2026" text in white
- Worn around neck or held in hand

JOB: For each person in the video, determine if they have a TreeHacks badge or not.

RESPOND IN THIS EXACT JSON FORMAT:
{
  "people_count": <number>,
  "people": [
    {
      "person": "Person 1",
      "facing_camera": true or false,
      "badge_visible": true or false,
      "description": "brief appearance description and text on badge"
    }
  ]
}

RULES:
- If a person is NOT facing the camera, set facing_camera to false and badge_visible to false.
- Only set badge_visible to true if you can clearly see the TreeHacks PCB badge.
- If no people are visible, return: {"people_count": 0, "people": []}
- Return ONLY the JSON, no other text.""",
    "max_tokens": 2048,
    "temperature": 0.6,
    "clip_duration": 3,
    "fps": 4,
    "auto_analyze": True,
    "ai_provider": "local",
    "openai_api_key": "",
    "openai_model": "gpt-4o",
    "alert_email": "userdorukhan@gmail.com",
    "email_alerts_enabled": True,
    "resend_api_key": "",
    "email_from": "Compliance Vision <onboarding@resend.dev>",
    "location": "Stanford University - TreeHacks 2026",
}

# Runtime state
config = {**DEFAULT_CONFIG}
session_stats = {"clips_analyzed": 0, "alerts": 0, "start_time": None}
analysis_history = []


# ─── Models ──────────────────────────────────────────────────────────────────

class ConfigUpdate(BaseModel):
    spark_ip: Optional[str] = None
    proxy_port: Optional[int] = None
    endpoint_path: Optional[str] = None
    model_id: Optional[str] = None
    prompt: Optional[str] = None
    max_tokens: Optional[int] = None
    temperature: Optional[float] = None
    clip_duration: Optional[int] = None
    fps: Optional[int] = None
    auto_analyze: Optional[bool] = None
    ai_provider: Optional[str] = None
    openai_api_key: Optional[str] = None
    openai_model: Optional[str] = None
    alert_email: Optional[str] = None
    email_alerts_enabled: Optional[bool] = None
    resend_api_key: Optional[str] = None
    email_from: Optional[str] = None
    location: Optional[str] = None


class AnalyzeRequest(BaseModel):
    video_base64: str
    prompt_override: Optional[str] = None
    max_tokens_override: Optional[int] = None
    temperature_override: Optional[float] = None


# ─── Helper Functions ────────────────────────────────────────────────────────

def get_proxy_url():
    return f"http://{config['spark_ip']}:{config['proxy_port']}{config['endpoint_path']}"


def send_alert_email(report: dict):
    """Send an email alert via Resend API when violations are detected."""
    if not config.get("email_alerts_enabled"):
        return
    recipient = config.get("alert_email", "").strip()
    api_key = config.get("resend_api_key", "").strip()
    email_from = config.get("email_from", "Compliance Vision <onboarding@resend.dev>").strip()
    if not recipient:
        logger.warning("Email alerts enabled but no recipient configured")
        return
    if not api_key:
        logger.warning("Email alerts enabled but no Resend API key configured")
        return

    location = config.get("location", "Unknown Location")
    violations = report.get("violations", [])
    people = report.get("people", [])
    timestamp = report.get("timestamp", time.strftime("%Y-%m-%d %H:%M:%S"))
    status = report.get("status", "UNKNOWN")
    v_count = report.get("violation_count", len(violations))

    subject = f"\U0001f6a8 Security Alert \u2014 {v_count} Violation(s) at {location}"

    # Build HTML email body
    violation_rows = ""
    for v in violations:
        violation_rows += f"""
        <tr>
            <td style="padding:8px 12px;border-bottom:1px solid #eee;font-weight:600;color:#dc2626;">{v.get('subject', 'Unknown')}</td>
            <td style="padding:8px 12px;border-bottom:1px solid #eee;">{v.get('rule', 'Violation')}</td>
            <td style="padding:8px 12px;border-bottom:1px solid #eee;color:#666;">{v.get('description', '')}</td>
        </tr>"""

    people_rows = ""
    for p in people:
        badge_color = "#16a34a" if p.get("compliant") else "#dc2626"
        badge_text = "Compliant" if p.get("compliant") else "Violation"
        if p.get("facing_camera") is False:
            badge_color = "#ca8a04"
            badge_text = "Not Facing"
        people_rows += f"""
        <tr>
            <td style="padding:6px 12px;border-bottom:1px solid #f0f0f0;">{p.get('person', 'Person')}</td>
            <td style="padding:6px 12px;border-bottom:1px soli
[truncated — 23022 more characters]
```

### frontend/src/App.tsx

```typescript
import { useState, useRef, useCallback, useEffect } from "react";
import { Scan, ScrollText, Image, Shield, Sparkles, Camera, ShieldCheck, Activity } from "lucide-react";
import type { ReferenceImage, AIProvider } from "./types";
import StatusBar from "./components/Header";
import StatusIndicator from "./components/StatusIndicator";
import ThemeToggle, { applyTheme } from "./components/ThemeToggle";
import EmptyStateAnimation from "./components/EmptyStateAnimation";
import VideoInput, { type InputMode } from "./components/VideoInput";
import PolicyConfig from "./components/PolicyConfig";
import ReferencesPanel from "./components/ReferencesPanel";
import PollyChat from "./components/PollyChat";
import PipelineStatus from "./components/PipelineStatus";
import ReportView from "./components/ReportView";
import LiveReportView from "./components/LiveReportView";
import ProviderToggle from "./components/ProviderToggle";
import { analyzeVideo, analyzeFrame, analyzeFrameBatch, analyzeFrameBatchParallel, transcribeAudio, resetComplianceState, healthCheck } from "./api";
import type { Policy, Report, PipelineStage } from "./types";

// Apply saved theme before first paint to avoid flash
applyTheme((localStorage.getItem("compliance_vision_theme") || "light") as "light" | "night" | "dark" | "high-contrast");

type LeftTab = "policy" | "references" | "polly";

const CHUNK_DURATION_MS = 6_000;  // 6s chunks — good balance of context vs speed
const FIRST_CHUNK_DURATION_MS = 2_000; // 2s first chunk for fast initial result
const REFS_STORAGE_KEY = "compliance_vision_references";

/** Load saved reference images from localStorage */
function loadSavedReferences(): ReferenceImage[] {
  try {
    const raw = localStorage.getItem(REFS_STORAGE_KEY);
    if (!raw) return [];
    const parsed = JSON.parse(raw);
    return Array.isArray(parsed) ? parsed : [];
  } catch {
    return [];
  }
}

/** Save reference images to localStorage */
function saveReferences(images: ReferenceImage[]) {
  try {
    localStorage.setItem(REFS_STORAGE_KEY, JSON.stringify(images));
  } catch (e) {
    console.warn("Failed to save references to localStorage (may exceed quota):", e);
  }
}

export default function App() {
  const [inputMode, setInputMode] = useState<InputMode>("file");
  const [leftTab, setLeftTab] = useState<LeftTab>("policy");
  const [provider, setProvider] = useState<AIProvider>(
    () => (localStorage.getItem("compliance_vision_provider") as AIProvider) || "openai"
  );

  const [videoFile, setVideoFile] = useState<File | null>(null);
  const [stage, setStage] = useState<PipelineStage>("idle");
  const [report, setReport] = useState<Report | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [uploadProgress, setUploadProgress] = useState<number>(0);

  const [isMonitoring, setIsMonitoring] = useState(false);
  const [liveReports, setLiveReports] = useState<Report[]>([]);
  const [sessionStart, setSessionStart] = useState<number | null>(null);
  const [liveStage, setLiveStage] = useState<PipelineStage>("idle");
  const [liveError, setLiveError] = useState<string | null>(null);
  const [chunksProcessed, setChunksProcessed] = useState(0);

  const recorderRef = useRef<MediaRecorder | null>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const monitoringRef = useRef(false);
  const sessionIdRef = useRef(0); // incremented each session to discard stale in-flight results
  const liveReportsRef = useRef<Report[]>([]);
  const accumulatedTranscriptRef = useRef<string>(""); // accumulated across audio chunks

  const [policy, setPolicy] = useState<Policy>(() => {
    const savedRefs = loadSavedReferences();
    return { rules: [], custom_prompt: "", include_audio: false, reference_images: savedRefs, enabled_reference_ids: [] };
  });
  const policyRef = useRef<Policy>(policy);
  const providerRef = useRef<AIProvider>(provider);

  // Persist provider selection
  useEffect(() => {
    localStorage.setItem("compliance_vision_provider", provider);
    providerRef.current = provider;
  }, [provider]);

  // Persist reference images to localStorage whenever they change
  useEffect(() => {
    saveReferences(policy.reference_images);
  }, [policy.reference_images]);

  // Keep liveReportsRef in sync for use in analyzeChunk (avoids stale closure)
  useEffect(() => {
    liveReportsRef.current = liveReports;
  }, [liveReports]);

  const handlePolicyChange = useCallback((p: Policy) => {
    setPolicy(p);
    policyRef.current = p;
  }, []);

  // Stable callback for reference image changes — avoids stale closure over `policy`
  const handleReferenceImagesChange = useCallback((imgs: ReferenceImage[]) => {
    setPolicy(prev => {
      // Ensure all references have ids
      const withIds = imgs.map(img => img.id ? img : { ...img, id: crypto.randomUUID() });
      const updated = { ...prev, reference_images: withIds };
      policyRef.current = updated;
      return updated;
    });
  }, []);

  const canAnalyze =
    inputMode === "file" &&
    videoFile !== null &&
    (policy.rules.length > 0 || policy.custom_prompt.trim().length > 0) &&
    policy.rules.every((r) => r.description.trim().length > 0) &&
    stage === "idle";

  const handleAnalyze = async () => {
    if (!videoFile) return;
    setStage("uploading");
    setReport(null);
    setError(null);
    setUploadProgress(0);
    
    const result = await analyzeVideo(
      videoFile, 
      policy, 
      (s) => setStage(s as PipelineStage),
      (progress) => setUploadProgress(progress)
    );
    
    if (result.status === "complete" && result.report) {
      setReport(result.report);
      setStage("complete");
    } else {
      setError(result.error || "Analysis failed.");
      setStage("error");
    }
  };

  const handleReset = () => { 
    setStage("idle"); 
    setReport(null); 
    setError(null);
    setUploadProgress(0);
  };

  // --- Pipelined monitoring: record chunk N+1 while analyzing chunk N ---

  /** Rec
[truncated — 29309 more characters]
```

### local_ui/static/js/app.js

```javascript
/* ═══════════════════════════════════════════════════════════
   Compliance Vision — Local UI v2
   Dashboard-first SPA with pipeline progress & live monitoring
   ═══════════════════════════════════════════════════════════ */
(function () {

"use strict";

/* ─── State ──────────────────────────────────────────────── */
const state = {
    cameraOn: false,
    monitoring: false,
    stream: null,
    recorder: null,
    ws: null,
    chunks: [],
    clipNum: 0,
    sessionStart: null,
    timerInterval: null,
    totalIncidents: 0,
    clipsAnalyzed: 0,
    lastStatus: "---",
    history: [],
    config: {},
    pipeline: "idle", // idle | recording | converting | uploading | analyzing | complete | error
    rawJsonVisible: false,
};

/* ─── DOM refs ───────────────────────────────────────────── */
const $ = (sel) => document.querySelector(sel);
const $$ = (sel) => document.querySelectorAll(sel);

const el = {
    // header
    connBadge:       $("#connectionBadge"),
    connDot:         $("#connectionBadge .conn-dot"),
    connText:        $("#connectionBadge .conn-text"),
    // stats
    complianceStatus:$("#complianceStatus"),
    totalIncidents:  $("#totalIncidents"),
    clipsAnalyzed:   $("#clipsAnalyzed"),
    sessionTime:     $("#sessionTime"),
    // video
    videoWrapper:    $("#videoWrapper"),
    webcamVideo:     $("#webcamVideo"),
    captureCanvas:   $("#captureCanvas"),
    videoOverlay:    $("#videoOverlay"),
    videoPlaceholder:$("#videoPlaceholder"),
    recBadge:        $("#recBadge"),
    clipBadge:       $("#clipBadge"),
    // controls
    btnCamera:       $("#btnCamera"),
    btnMonitor:      $("#btnMonitor"),
    btnCapture:      $("#btnCapture"),
    btnUploadVideo:  $("#btnUploadVideo"),
    fileInput:       $("#fileInput"),
    // pipeline
    pipelineSteps:   $("#pipelineSteps"),
    // result
    resultEmpty:     $("#resultEmpty"),
    resultContent:   $("#resultContent"),
    resultBadge:     $("#resultBadge"),
    resultPeople:    $("#resultPeople"),
    resultTs:        $("#resultTs"),
    resultDetails:   $("#resultDetails"),
    btnRawToggle:    $("#btnRawToggle"),
    rawJson:         $("#rawJson"),
    // history
    historyList:     $("#historyList"),
    btnClearHistory: $("#btnClearHistory"),
    // settings
    sparkIp:         $("#sparkIp"),
    proxyPort:       $("#proxyPort"),
    endpointPath:    $("#endpointPath"),
    modelId:         $("#modelId"),
    maxTokens:       $("#maxTokens"),
    temperature:     $("#temperature"),
    clipDuration:    $("#clipDuration"),
    fps:             $("#fps"),
    autoAnalyze:     $("#autoAnalyze"),
    promptText:      $("#promptText"),
    // ai provider
    dgxSettingsCard: $("#dgxSettingsCard"),
    openaiSettingsCard: $("#openaiSettingsCard"),
    openaiApiKey:    $("#openaiApiKey"),
    openaiModel:     $("#openaiModel"),
    // email
    emailAlertsEnabled: $("#emailAlertsEnabled"),
    alertEmail:      $("#alertEmail"),
    locationLabel:   $("#locationLabel"),
    resendApiKey:    $("#resendApiKey"),
    emailFrom:       $("#emailFrom"),
    btnTestEmail:    $("#btnTestEmail"),
    btnSaveAll:      $("#btnSaveAll"),
    btnResetAll:     $("#btnResetAll"),
    btnTestConn:     $("#btnTestConn"),
    // toast
    toastContainer:  $("#toastContainer"),
};

/* ─── Prompt Templates ───────────────────────────────────── */
const TEMPLATES = {
    badge: `You are a security camera AI for TreeHacks 2026 hackathon at Stanford University.

THE OFFICIAL TREEHACKS BADGE:
- A Christmas tree / pine tree shaped green PCB (printed circuit board)
- Has "TREE HACKS" and "2026" text in white
- Has a rocket ship, stars, and planet graphics
- Has a QR code, LEDs, and USB-C connectors
- Worn around neck or held in hand

JOB: For each person in the video, determine if they have a TreeHacks badge or not.

RESPOND IN THIS EXACT JSON FORMAT:
{
  "people_count": <number>,
  "people": [
    {
      "person": "Person 1",
      "facing_camera": true or false,
      "badge_visible": true or false,
      "description": "brief appearance description"
    }
  ]
}

RULES:
- If a person is NOT facing the camera, set facing_camera to false and badge_visible to false.
- Only set badge_visible to true if you can clearly see the TreeHacks PCB badge.
- If no people are visible, return: {"people_count": 0, "people": []}
- Return ONLY the JSON, no other text.`,

    safety: `You are a safety compliance AI monitoring a workplace. Analyze the video feed for PPE (Personal Protective Equipment) compliance.

Check for: hard hats, safety vests, safety glasses, gloves, steel-toe boots, ear protection.

RESPOND IN THIS EXACT JSON FORMAT:
{
  "people_count": <number>,
  "people": [
    {
      "person": "Person 1",
      "ppe_items": ["hard hat", "safety vest"],
      "missing_items": ["safety glasses"],
      "compliant": false,
      "description": "brief description"
    }
  ]
}
Return ONLY the JSON, no other text.`,

    crowd: `You are a crowd monitoring AI. Analyze the video for crowd density and behavior.

RESPOND IN THIS EXACT JSON FORMAT:
{
  "people_count": <number>,
  "density": "low" | "medium" | "high" | "overcrowded",
  "concerns": ["list any safety concerns"],
  "description": "brief scene description"
}
Return ONLY the JSON, no other text.`,

    general: `Analyze this video feed and describe what you see in detail.

RESPOND IN THIS EXACT JSON FORMAT:
{
  "scene_description": "detailed description of the scene",
  "objects": ["list of notable objects"],
  "people_count": <number>,
  "activity": "description of any activity",
  "concerns": ["list any concerns or notable observations"]
}
Return ONLY the JSON, no other text.`,
};

/* ─── Toast Notifications ────────────────────────────────── */
function toast(msg, type = "info", duration = 3500) {
    const icons = { success: "✓", error: "✕", info: "ℹ" };
    const t = document.createElement("div");
    t.className = `toast ${type}`;
    t.innerHTML = `<span class="toast-icon">${
[truncated — 24720 more characters]
```

### stop.sh

```shell
#!/bin/bash

echo "🛑 Stopping Agent 00Vision..."

# Kill processes from PID file
if [ -f .pids ]; then
    while read pid; do
        kill -9 $pid 2>/dev/null
    done < .pids
    rm .pids
fi

# Also kill any stragglers by port
lsof -ti:8000 | xargs kill -9 2>/dev/null
lsof -ti:5173 | xargs kill -9 2>/dev/null

# Kill any remaining python/node processes related to our app
pkill -f "uvicorn backend.main:app" 2>/dev/null
pkill -f "npm run dev" 2>/dev/null
pkill -f "celery.*backend.services" 2>/dev/null

echo "✅ All services stopped"
```

### run.sh

```shell
#!/bin/bash

# Agent 00Vision - Cloud Mode (no Redis/Celery required)

cd "$(dirname "$0")"

# --- .env setup ---
if [ ! -f .env ]; then
    echo "No .env file found. Enter your OpenAI API key:"
    read -r api_key
    echo "OPENAI_API_KEY=$api_key" > .env
fi

# --- Kill old processes on our ports ---
lsof -ti:8082 | xargs kill -9 2>/dev/null
lsof -ti:5173 | xargs kill -9 2>/dev/null

# --- Python venv + deps ---
if [ ! -d "venv" ]; then
    echo "Creating virtual environment..."
    python3 -m venv venv
fi
source venv/bin/activate
pip install -r backend/requirements.txt -q

# --- Frontend deps ---
if [ ! -d "frontend/node_modules" ]; then
    echo "Installing frontend dependencies..."
    (cd frontend && npm install)
fi

# --- Start backend on port 8082 (matches Vite proxy) ---
echo "Starting backend on :8082..."
PYTHONPATH=$(pwd) uvicorn backend.main:app --reload --host 0.0.0.0 --port 8082 > backend.log 2>&1 &
BACKEND_PID=$!

# --- Start frontend on port 5173 ---
echo "Starting frontend on :5173..."
(cd frontend && npm run dev) > frontend.log 2>&1 &
FRONTEND_PID=$!

# --- Wait for backend to be ready ---
echo "Waiting for backend..."
for i in $(seq 1 30); do
    if curl -s http://localhost:8082/health > /dev/null 2>&1; then
        echo ""
        echo "Agent 00Vision is running!"
        echo ""
        echo "  Frontend: http://localhost:5173"
        echo "  Backend:  http://localhost:8082"
        echo "  API Docs: http://localhost:8082/docs"
        echo ""
        break
    fi
    sleep 1
    printf "."
done

# --- Save PIDs for cleanup ---
echo "$BACKEND_PID" > .pids
echo "$FRONTEND_PID" >> .pids

echo "Press Ctrl+C to stop..."
trap "kill $BACKEND_PID $FRONTEND_PID 2>/dev/null; rm -f .pids; echo 'Stopped.'; exit" INT TERM

wait

```

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