# Project export: ADEX - Adaptive Data Extraction System

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: Adex leverages temporal redundancy in video streams to mitigate stochastic noise. By analyzing multi-frame sequences, we achieve high-fidelity text extraction where static OCR fails. .
- Devpost: https://devpost.com/software/adex-adaptive-data-extraction-system
- GitHub: https://github.com/kanakapalli/adex
- Video: https://www.youtube.com/embed/ZS7hfa1IW0c?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Overview

Solving the "Static Image Failure" with Temporal RAG

### Inspiration

The idea for ADEX was born from a recurring dead roadblock I encountered while contributing to Truthin, a crowdsourced platform for packaged food ratings. Users frequently submitted unusable images—blurry, out-of-focus, or obscured by glare. In many cases, reflections from shiny plastic or folds in packaging made ingredient lists unreadable, even to the human eye. This problem extends far beyond Truthin. Industry-leading apps such as Yuka, Think Dirty, EWG Healthy Living, Open Food Facts, MyFitnessPal, Lifesum, and Calorie Mama still rely on static image capture. When users have shaky hands, poor lighting, or low-end devices, OCR fails—forcing repeated retakes or tedious manual entry. The core limitations of single-image approaches: The breakthrough came during a Serverpod 3.0 demo by Vik, showcasing its Industrial RAG (Retrieval-Augmented Generation) capabilities. I realized that by applying RAG to video frames instead of single images, we could triangulate accurate data from imperfect visual input over time. What It Does ADEX replaces unreliable one-shot photography with temporal video-based data extraction. Instead of asking users to take a perfect photo, the system captures a short video sweep of a product. By analyzing dozens of frames, ADEX reconstructs missing or obscured information—recovering text hidden by glare, motion blur, or physical distortion. The result is fast, accurate extraction of ingredients, nutrition facts, allergens, and any other product data with minimal user effort. The key insight: a single image is a gamble; a video is a guarantee. Text hidden by glare in frame 12 is visible in frame 37. A fold that obscures ingredients at one angle is flat at another. Motion blur in one frame is sharp in the next. ADEX doesn't need any single frame to be perfect — it reconstructs complete data from the collective information across all frames. Video Text Extraction Flow User-Configurable Parameters The extraction pipeline is fully configurable from the app's settings: How We Built It Backend — Serverpod 3 (Dart) Built on Serverpod 3.2, leveraging its native vector embedding support via pgvector and typed endpoint generation. The server handles video processing, AI orchestration, S3 storage, and JWT authentication — all in Dart. Video Processing — FFmpeg Integrated FFmpeg invoked at the OS level using Dart's Process API to extract frames at 2 FPS. Each video produces dozens of frames that form the input to the RAG pipeline. Multimodal Temporal RAG The core innovation. Each frame is converted to a 1408-dimensional vector embedding using Vertex AI multimodalembedding@001. Frame type descriptions are converted to text embeddings in the same vector space. PostgreSQL with pgvector performs cosine similarity search to find the frames that best match each category — this is the retrieval step of RAG. Gemini 2.0 Flash then performs the generation step, extracting structured text from the retrieved frames. Cloud Storage — AWS S3 Videos and extracted frames are stored in AWS S3 (eu-north-1). The pipeline saves video bytes locally for processing and uploads to S3 asynchronously in the background to avoid round-trip latency. Frontend — Flutter A cross-platform Flutter app with platform-adaptive UI: Mobile: Full-screen camera with record button, settings sheet for prompts and performance tuning, processing progress view, and tabbed results display Desktop/Web: File picker for video upload, responsive two-pane layout, model browsing, and JSON data viewer Authentication — JWT with Email Verification Serverpod Auth IDP provides JWT-based authentication with email identity provider, email verification codes, and password reset flow via Gmail SMTP. Challenges We Ran Into Optical Distortions: Packaging is often curved, glossy, or crinkled. Ensuring the AI recognized distorted text across multiple angles as the same semantic entity required careful embedding tuning. The multimodal embedding model handles this by encoding visual semantics rather than raw pixels. Optical Distortions: Packaging is often curved, glossy, or crinkled. Ensuring the AI recognized distorted text across multiple angles as the same semantic entity required careful embedding tuning. The multimodal embedding model handles this by encoding visual semantics rather than raw pixels. Rate Limiting at Scale: Processing a single video generates dozens of frames, each requiring an embedding API call. We implemented configurable batch concurrency (default 5 parallel calls), delays between batches (200ms), and exponential backoff with jitter — capped at 60 seconds — to handle 429 and RESOURCE_EXHAUSTED errors gracefully. Rate Limiting at Scale: Processing a single video generates dozens of frames, each requiring an embedding API call. We implemented configurable batch concurrency (default 5 parallel calls), delays between batches (200ms), and exponential backoff with jitter — capped at 60 seconds — to handle 429 and RESOURCE_EXHAUSTED errors gracefully. Single-Call Text Extraction: Initially we called Gemini per frame type, but this hit rate limits fast and lost cross-frame context. We refactored to send ALL extracted frames to Gemini in a single multimodal API call, which is both faster and more accurate since Gemini can cross-reference information across images. Single-Call Text Extraction: Initially we called Gemini per frame type, but this hit rate limits fast and lost cross-frame context. We refactored to send ALL extracted frames to Gemini in a single multimodal API call, which is both faster and more accurate since Gemini can cross-reference information across images. FFmpeg & Dart Ecosystem Gaps: FFmpeg lacks a robust native Dart package for frame extraction. As a workaround, we invoked FFmpeg directly at the OS level using Dart's Process API, enabling reliable frame slicing while preserving performance. FFmpeg & Dart Ecosystem Gaps: FFmpeg lacks a robust native Dart package for frame extraction. As a workaround, we invoked FFmpeg directly at the OS level using Dart's Process API, enabling reliable frame slicing while preserving performance. Upload Latency: High-resolution video uploads are expensive. We eliminated the S3 round-trip by saving video bytes locally for immediate processing and uploading to S3 asynchronously in the background using Dart's unawaited(). Upload Latency: High-resolution video uploads are expensive. We eliminated the S3 round-trip by saving video bytes locally for immediate processing and uploading to S3 asynchronously in the background using Dart's unawaited(). Accomplishments That We're Proud Of Successfully replaced fragile static OCR with a video-first temporal extraction pipeline. Achieved high accuracy even on low-end devices and poor lighting conditions. Built a production-grade multimodal RAG system in Dart — vector embeddings, cosine similarity search, and AI-powered text extraction. Single API call text extraction across all frame types, avoiding rate limits and improving accuracy. Reduced user friction by shifting accuracy responsibility from the user to the system — no more retaking photos. Cross-platform support from a single Flutter codebase — mobile camera capture and desktop file upload. What We Learned The biggest insight was the power of temporal redundancy. A single image is a gamble; a video is a guarantee. We learned how to use software intelligence to overcome hardware constraints — proving that you don't need a $1,200 phone camera when you can reason over imperfect frames from a $100 device. The project also deepened our expertise in: Real-time AI pipelines and multimodal embeddings Vector databases (pgvector) and similarity search Rate limit management with exponential backoff at scale Asynchronous cloud storage patterns Scalable RAG systems within the Serverpod ecosystem What's Next for ADEX Phase 1: Edge-Side Intelligence Integrate YOLO-based on-device models to perform real-time frame quality assessment, discarding junk frames locally before upload — reducing bandwidth and processing cost. Phase 1: Edge-Side Intelligence Integrate YOLO-based on-device models to perform real-time frame quality assessment, discarding junk frames locally before upload — reducing bandwidth and processing cost. Phase 2: Multi-Surface Reconstruction Extend temporal RAG to handle 3D object scans, allowing full-package capture (front, back, and sides) in a single video sweep with spatial awareness. Phase 2: Multi-Surface Reconstruction Extend temporal RAG to handle 3D object scans, allowing full-package capture (front, back, and sides) in a single video sweep with spatial awareness. Phase 3: Human-in-the-Loop Feedback Introduce community verification for low-confidence results, creating a feedback loop that continuously improves embedding quality and extraction accuracy. Phase 3: Human-in-the-Loop Feedback Introduce community verification for low-confidence results, creating a feedback loop that continuously improves embedding quality and extraction accuracy. Phase 4: Developer API Package ADEX as a plug-and-play API, enabling third-party apps like Yuka, Truthin, or Open Food Facts to replace static OCR with temporal video extraction. Phase 4: Developer API Package ADEX as a plug-and-play API, enabling third-party apps like Yuka, Truthin, or Open Food Facts to replace static OCR with temporal video extraction.

## README (from the GitHub repository)



# ADEX — Adaptive Data Extraction System


**Video-first data extraction that succeeds where single-image OCR fails.**


<p align="center">
  <img src="adex_flutter/assets/logo/banner_bg.png" alt="ADEX Arrow" width="500"/>
</p>

ADEX replaces the fragile "take one perfect photo" approach with temporal video analysis. Users record a short video sweep of a product, and ADEX extracts accurate text and structured data — even from blurry, glare-obscured, or folded surfaces.

---

## The Problem

Consumer apps that analyze packaged products — food scanners, ingredient checkers, health ratings — all rely on static image capture. This approach breaks constantly in real-world conditions:

| Issue | Why It Happens |
|-------|---------------|
| **Light reflection / glare** | Shiny plastic packaging reflects camera flash or ambient light, whiting out text |
| **Folds and creases** | Flexible packaging wrinkles, hiding portions of ingredient lists |
| **Curved surfaces** | Bottles and cans distort text at the edges, making OCR unreliable |
| **Motion blur** | Shaky hands or low-end cameras produce unusable captures |
| **Unstructured layouts** | Every product has a unique label layout — no single template works |

Apps like **Yuka**, **Truthin**, **Think Dirty**, **EWG Healthy Living**, and **Open Food Facts** all suffer from this. Users are forced to retake photos multiple times or resort to tedious manual entry when OCR fails.

---

## How ADEX Solves It

Instead of gambling on a single frame, ADEX captures a short video sweep and analyzes **dozens of frames across time**. This temporal redundancy means:

- Text hidden by glare in frame 12 is visible in frame 37
- A fold that obscures ingredients at one angle is flat at another
- Motion blur in one frame is sharp in the next

The system doesn't need any single frame to be perfect — it reconstructs complete, accurate data from the collective information across all frames.

---

## How It Works

```
┌─────────────┐     ┌──────────────────┐     ┌───────────────────┐
│  User records│     │  Server extracts │     │  AI classifies &  │
│  video sweep │────>│  frames (FFmpeg) │────>│  generates         │
│  of product  │     │  at 2 FPS        │     │  embeddings        │
└─────────────┘     └──────────────────┘     └─────────┬─────────┘
                                                        │
┌─────────────┐     ┌──────────────────┐     ┌──────────▼─────────┐
│  Structured  │     │  Gemini extracts │     │  RAG identifies    │
│  JSON data   │<────│  text from best  │<────│  best frames per   │
│  returned    │     │  frames          │     │  category          │
└─────────────┘     └──────────────────┘     └────────────────────┘
```

### Step-by-step flow

1. **Capture** — On mobile, the user opens the camera and records a short video sweep around the product. On desktop, the user uploads a video file.

2. **Frame extraction** — The server uses FFmpeg to extract frames at 2 frames per second, capturing the start and middle of each second.

3. **Embedding generation** — Each frame is sent to Google Vertex AI (`multimodalembedding@001`) to generate 1408-dimensional vector embeddings, stored in PostgreSQL with pgvector.

4. **Frame classification** — Using timeline heuristics and RAG, the system identifies frame types:
   - **Product front** (brand, name, claims)
   - **Nutrition facts** panel
   - **Ingredients list**
   - **Product back** (additional info)
   - **Barcode**

5. **Text extraction** — Google Gemini analyzes the best frames for each category and extracts structured text, handling distortions that would break traditional OCR.

6. **Result delivery** — The app displays extracted frames organized by type, structured JSON data (nutrition facts, ingredients, allergens), and the original video with timeline markers.

---

## Comparison with Existing Solutions

| Feature | Yuka / Truthin / Think Dirty | Open Food Facts | **ADEX** |
|---------|------------------------------|-----------------|----------|
| Input method | Single photo | Single photo + barcode | Video sweep |
| Handles glare | No | No | **Yes** — temporal redundancy |
| Handles folds/creases | No | No | **Yes** — multi-angle coverage |
| Unstructured layouts | Limited templates | Community-curated | **Yes** — AI-driven extraction |
| Low-end device support | Poor (needs sharp photo) | Poor | **Good** — compensates across frames |
| User effort on failure | Retake photo repeatedly | Manual entry | **Minimal** — just re-sweep |
| Data extraction | Barcode lookup + basic OCR | Barcode database | **AI-powered** multimodal analysis |
| Works without barcode | Rarely | No | **Yes** — vision-based |

---

## Tech Stack

| Layer | Technology |
|-------|-----------|
| Frontend | Flutter (iOS, Android, Web, macOS, Windows, Linux) |
| Backend | Serverpod 3.2 (Dart) |
| Database | PostgreSQL with pgvector extension |
| Cache | Redis |
| File storage | AWS S3 |
| AI embeddings | Google Vertex AI (multimodal, 1408D) |
| Text extraction | Google Gemini 2.5 Flash |
| Video processing | FFmpeg (OS-level via Dart Process API) |
| Authentication | JWT with Serverpod Auth IDP |

---

## Project Structure

```
adex/
├── adex_server/       # Dart backend — endpoints, video processing, AI integration
├── adex_client/       # Auto-generated typed client library
├── adex_flutter/      # Cross-platform Flutter app
├── SETUP.md           # How to set up and run the project
└── README.md          # This file
```

---

## Getting Started

See **[SETUP.md](SETUP.md)** for full instructions on setting up and running the project locally.

---

## Key Features

- **Video-based capture** — Record a sweep instead of taking a single photo
- **Temporal RAG** — Retrieval-Augmented Generation across video frames
- **Multi-surface reconstruction** — Handles front, back, sides in one sweep
- **Configurable extraction** — Custom prompts for what data to extract
- **Processing history** — Browse and revisit past extractions
- **Cross-platform** — Mobile camera capture and desktop file upload
- **Structured output** — JSON data with nutrition facts, ingredients, allergens
- **Rate-limit resilient** — Exponential backoff with configurable concurrency

---

## Roadmap

- **Edge-side intelligence** — On-device frame quality assessment (YOLO-based) to discard junk frames before upload
- **3D object scanning** — Full-package capture (front, back, sides) via multi-surface reconstruction
- **Community verification** — Human-in-the-loop feedback for low-confidence results
- **Developer API** — Plug-and-play API for third-party apps to replace static OCR with temporal video extraction

---

## License

Proprietary. All rights reserved.


## Detected evidence (automated analysis)

Indexed codebase: 116 recognized source files, 653 KB.
- C (language) — detected in the code
- C++ (language) — detected in the code
- CSS (language) — detected in the code
- Dart (language) — detected in the code
- HTML (language) — detected in the code
- Kotlin (language) — detected in the code
- SQL (language) — detected in the code
- Swift (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (120 of 195)

```
.DS_Store
.github/workflows/analyze.yml
.github/workflows/format.yml
.github/workflows/tests.yml
.gitignore
.vscode/settings.json
adex_client/.gitignore
adex_client/analysis_options.yaml
adex_client/CHANGELOG.md
adex_client/dartdoc_options.yaml
adex_client/doc/endpoint.md
adex_client/lib/adex_client.dart
adex_client/lib/src/protocol/adex_service/adex_model.dart
adex_client/lib/src/protocol/client.dart
adex_client/lib/src/protocol/greetings/greeting.dart
adex_client/lib/src/protocol/protocol.dart
adex_client/lib/src/protocol/videoExtractions/video_frame_embedding.dart
adex_client/pubspec.yaml
adex_client/README.md
adex_flutter/.firebase/hosting.YnVpbGQvd2Vi.cache
adex_flutter/.firebaserc
adex_flutter/.gitignore
adex_flutter/.metadata
adex_flutter/analysis_options.yaml
adex_flutter/android/.gitignore
adex_flutter/android/app/build.gradle.kts
adex_flutter/android/app/src/debug/AndroidManifest.xml
adex_flutter/android/app/src/main/AndroidManifest.xml
adex_flutter/android/app/src/main/kotlin/com/example/adex_flutter/MainActivity.kt
adex_flutter/android/app/src/main/res/drawable-v21/launch_background.xml
adex_flutter/android/app/src/main/res/drawable/launch_background.xml
adex_flutter/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
adex_flutter/android/app/src/main/res/values-night/styles.xml
adex_flutter/android/app/src/main/res/values/colors.xml
adex_flutter/android/app/src/main/res/values/styles.xml
adex_flutter/android/app/src/profile/AndroidManifest.xml
adex_flutter/android/build.gradle.kts
adex_flutter/android/gradle.properties
adex_flutter/android/gradle/wrapper/gradle-wrapper.properties
adex_flutter/android/settings.gradle.kts
adex_flutter/assets/config.json
adex_flutter/firebase.json
adex_flutter/ios/.gitignore
adex_flutter/ios/Flutter/AppFrameworkInfo.plist
adex_flutter/ios/Flutter/Debug.xcconfig
adex_flutter/ios/Flutter/Release.xcconfig
adex_flutter/ios/Podfile
adex_flutter/ios/Runner.xcodeproj/project.pbxproj
adex_flutter/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
adex_flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
adex_flutter/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
adex_flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
adex_flutter/ios/Runner.xcworkspace/contents.xcworkspacedata
adex_flutter/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
adex_flutter/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
adex_flutter/ios/Runner/AppDelegate.swift
adex_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
adex_flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
adex_flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
adex_flutter/ios/Runner/Base.lproj/LaunchScreen.storyboard
adex_flutter/ios/Runner/Base.lproj/Main.storyboard
adex_flutter/ios/Runner/Info.plist
adex_flutter/ios/Runner/Runner-Bridging-Header.h
adex_flutter/ios/RunnerTests/RunnerTests.swift
adex_flutter/lib/main.dart
adex_flutter/lib/screens/mobile/adex_service_screen.dart
adex_flutter/lib/screens/mobile/history_screen.dart
adex_flutter/lib/screens/mobile/processing_screen.dart
adex_flutter/lib/screens/mobile/result_screen.dart
adex_flutter/lib/screens/sign.dart
adex_flutter/lib/screens/web/_blob_url_stub.dart
adex_flutter/lib/screens/web/_blob_url_web.dart
adex_flutter/lib/screens/web/adex_models_screen.dart
adex_flutter/lib/screens/web/adex_result_view.dart
adex_flutter/lib/screens/web/adex_service_screen.dart
adex_flutter/lib/screens/web/main_navigation_screen.dart
adex_flutter/linux/.gitignore
adex_flutter/linux/CMakeLists.txt
adex_flutter/linux/flutter/CMakeLists.txt
adex_flutter/linux/flutter/generated_plugin_registrant.cc
adex_flutter/linux/flutter/generated_plugin_registrant.h
adex_flutter/linux/flutter/generated_plugins.cmake
adex_flutter/linux/runner/CMakeLists.txt
adex_flutter/linux/runner/main.cc
adex_flutter/linux/runner/my_application.cc
adex_flutter/linux/runner/my_application.h
adex_flutter/macos/.gitignore
adex_flutter/macos/Flutter/Flutter-Debug.xcconfig
adex_flutter/macos/Flutter/Flutter-Release.xcconfig
adex_flutter/macos/Flutter/GeneratedPluginRegistrant.swift
adex_flutter/macos/Podfile
adex_flutter/macos/Runner.xcodeproj/project.pbxproj
adex_flutter/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
adex_flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
adex_flutter/macos/Runner.xcworkspace/contents.xcworkspacedata
adex_flutter/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
adex_flutter/macos/Runner/AppDelegate.swift
adex_flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
adex_flutter/macos/Runner/Base.lproj/MainMenu.xib
adex_flutter/macos/Runner/Configs/AppInfo.xcconfig
adex_flutter/macos/Runner/Configs/Debug.xcconfig
adex_flutter/macos/Runner/Configs/Release.xcconfig
adex_flutter/macos/Runner/Configs/Warnings.xcconfig
adex_flutter/macos/Runner/DebugProfile.entitlements
adex_flutter/macos/Runner/Info.plist
adex_flutter/macos/Runner/MainFlutterWindow.swift
adex_flutter/macos/Runner/Release.entitlements
adex_flutter/macos/RunnerTests/RunnerTests.swift
adex_flutter/PROJECT.md
adex_flutter/pubspec.lock
adex_flutter/pubspec.yaml
adex_flutter/README.md
adex_flutter/test/widget_test.dart
adex_flutter/web/index.html
adex_flutter/web/manifest.json
adex_flutter/windows/.gitignore
adex_flutter/windows/CMakeLists.txt
adex_flutter/windows/flutter/CMakeLists.txt
adex_flutter/windows/flutter/generated_plugin_registrant.cc
adex_flutter/windows/flutter/generated_plugin_registrant.h
[75 more files omitted for size]
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- init
- fix: Update API URL in config.json for production environment
- refactor: Load service account JSON from passwords.yaml instead of hardcoding
- fix: Update logo paths in README and project story for consistency
- Add README, SETUP, and project story documentation for ADEX
- feat: Implement email verification and password reset functionality
- Add web support for blob URL creation and video result display
- Add MainNavigationScreen with responsive navigation for wide and narrow layouts
- Refactor code structure for improved readability and maintainability
- Add History, Processing, and Result screens with UI components and data handling
- mobile converted
- update CORS middleware to allow all sub-paths under /uploads and refine OPTIONS request handling
- update base URL to point to the production server
- update API endpoint to use Gemini 2.0 model for text extraction
- update API endpoints to use Gemini 2.0 model and configure .gitignore for uploads
- update API endpoint to use Gemini 3 Pro model for content generation
- update API endpoint to use latest Gemini model for content generation
- hosting + cross origin
- init

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

### SETUP.md

```markdown
# Setup and Run Guide

Instructions for setting up and running the ADEX project locally.

---

## Prerequisites

Install the following before starting:

| Tool | Version | Purpose |
|------|---------|---------|
| [Dart SDK](https://dart.dev/get-dart) | ^3.8.0 | Server runtime |
| [Flutter SDK](https://flutter.dev/docs/get-started/install) | ^3.32.0 | Frontend framework |
| [Docker](https://docs.docker.com/get-docker/) | Latest | PostgreSQL and Redis containers |
| [FFmpeg](https://ffmpeg.org/download.html) | Latest | Video frame extraction |
| [Serverpod CLI](https://docs.serverpod.dev/) | 3.2.x | Code generation |

### Install Serverpod CLI

```bash
dart pub global activate serverpod_cli
```

### Verify installations

```bash
dart --version
flutter --version
docker --version
ffmpeg -version
serverpod version
```

---

## Configuration

### 1. Server passwords

Create or update the passwords file at `adex_server/config/passwords.yaml` with your credentials:

```yaml
development:
  database: '<your-database-password>'
  redis: '<your-redis-password>'
  serviceSecret: '<your-service-secret>'
```

### 2. External service credentials

The server requires credentials for the following services. These are configured in `adex_server/config/passwords.yaml` and referenced by the server code:

- **AWS S3** — For video and frame file storage
- **Google Cloud (Vertex AI / Gemini)** — For embeddings and text extraction
- **Gmail SMTP** — For email verification (optional in development — verification codes are logged to console)

### 3. Flutter server URL

The Flutter app connects to the server via a URL configured in one of two ways:

**Option A — Build flag:**
```bash
flutter run --dart-define=SERVER_URL=http://localhost:8080
```

**Option B — Config file:**
Edit `adex_flutter/assets/config.json`:
```json
{
  "serverUrl": "http://localhost:8080"
}
```

---

## Running the Project

### Step 1: Start the database and cache

```bash
cd adex_server
docker compose up --build --detach
```

This starts:
- **PostgreSQL** on port `8090` (with pgvector extension)
- **Redis** on port `8091`

### Step 2: Run code generation (if needed)

Run this after modifying any `.spy.yaml` model files or endpoint signatures:

```bash
cd adex_server
serverpod generate
```

### Step 3: Start the server

```bash
cd adex_server
dart bin/main.dart --apply-migrations
```

The `--apply-migrations` flag ensures database schema is up to date. The server starts on `http://localhost:8080` by default.

### Step 4: Run the Flutter app

**Mobile (iOS/Android):**
```bash
cd adex_flutter
flutter run
```

**Web:**
```bash
cd adex_flutter
flutter run -d chrome
```

**macOS:**
```bash
cd adex_flutter
flutter run -d macos
```

---

## Building for Production

### Build Flutter web app and serve from Serverpod

```bash
cd adex_flutter
flutter build web --base-href /app/ --wasm
rm -rf ../adex_server/web/app && mv build/web/ ../adex_server/web/app
```

After this, the Flutter app is served at `http://<ser
[truncated — 1441 more characters]
```

### CLAUDE.md

```markdown
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is a Serverpod project with three packages:
- **adex_server**: Dart backend server with PostgreSQL and Redis
- **adex_client**: Auto-generated client library for server communication
- **adex_flutter**: Flutter mobile/web frontend

Serverpod version: 3.2.3 | Dart SDK: ^3.8.0 | Flutter: ^3.32.0

## Common Commands

### Start Development Environment
```bash
# Start PostgreSQL and Redis (required before running server)
cd adex_server && docker compose up --build --detach

# Run the server with migrations
cd adex_server && dart bin/main.dart --apply-migrations

# Stop services when done
cd adex_server && docker compose stop
```

### Code Generation
After modifying endpoints or model YAML files:
```bash
cd adex_server && serverpod generate
```

### Run Flutter App
```bash
cd adex_flutter && flutter run
```

### Build Flutter Web App for Server
```bash
cd adex_flutter && flutter build web --base-href /app/ --wasm
rm -rf ../adex_server/web/app && mv build/web/ ../adex_server/web/app
```

### Run Tests
```bash
# Server tests (requires test database running)
cd adex_server && dart test

# Run specific test file
cd adex_server && dart test test/integration/greeting_endpoint_test.dart
```

### Analyze Code
```bash
cd adex_server && dart analyze
cd adex_flutter && flutter analyze
```

### Database Migrations
```bash
# Create a new migration (use --force for destructive schema changes)
cd adex_server && serverpod create-migration
cd adex_server && serverpod create-migration --force

# Apply pending migrations
cd adex_server && dart bin/main.dart --apply-migrations
```

## Architecture

### Server Structure (adex_server)
- `lib/server.dart` - Server initialization, auth setup, and web route configuration
- `lib/src/<feature>/` - Feature directories containing endpoints and models
- `lib/src/<feature>/*.spy.yaml` - Model definitions (Serverpod protocol YAML)
- `lib/src/<feature>/*_endpoint.dart` - API endpoint classes
- `lib/src/generated/` - Auto-generated protocol and endpoint code (do not edit)
- `config/` - Environment configs: development.yaml, test.yaml, production.yaml, passwords.yaml

### Client Structure (adex_client)
- `lib/src/protocol/` - Auto-generated client code from server definitions
- The `Client` class provides typed access to all endpoints (e.g., `client.adexService.processVideoFromUrl(...)`)

### Flutter Structure (adex_flutter)
- Uses global `Client` instance from `main.dart` for server communication
- Server URL configured via `--dart-define=SERVER_URL=...` or `assets/config.json`

### Model Definition Pattern
Models are defined in `.spy.yaml` files (e.g., `adex_model.spy.yaml`):
```yaml
class: ModelName
table: table_name
fields:
  fieldName: Type
```
Run `serverpod generate` after changes to update generated code in both server and client.

### Endpoint Pattern
Endpoints extend `Endpoint` class. M
[truncated — 4372 more characters]
```

### adex_server/Dockerfile

```
# Build stage
FROM dart:3.8.0 AS build
WORKDIR /app
COPY . .

# Install dependencies and compile the server executable
RUN dart pub get
RUN dart compile exe bin/main.dart -o bin/server

# Final stage
FROM alpine:latest

# Environment variables
ENV runmode=production
ENV serverid=default
ENV logging=normal
ENV role=monolith

# Copy runtime dependencies
COPY --from=build /runtime/ /

# Copy compiled server executable
COPY --from=build /app/bin/server server

# Copy configuration files and resources
COPY --from=build /app/config/ config/
COPY --from=build /app/web/ web/
COPY --from=build /app/migrations/ migrations/

# This file is required to enable the endpoint log filter in Insights.
COPY --from=build /app/lib/src/generated/protocol.yaml lib/src/generated/protocol.yaml

# Expose ports
EXPOSE 8080
EXPOSE 8081
EXPOSE 8082

# Define the entrypoint command
ENTRYPOINT ./server --mode=$runmode --server-id=$serverid --logging=$logging --role=$role

```

### adex_server/docker-compose.yaml

```yaml
services:
  # Development services
  postgres:
    image: pgvector/pgvector:pg16
    ports:
      - "8090:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_DB: adex
      POSTGRES_PASSWORD: "tIi2x6pJLf6YoiCi5B9URzOrbzrmkuhY"
    volumes:
      - adex_data:/var/lib/postgresql/data

  redis:
    image: redis:6.2.6
    ports:
      - "8091:6379"
    command: redis-server --requirepass "hO1woqfQZuiu2ov1UHvzbIxICVd-IWiS"
    environment:
      - REDIS_REPLICATION_MODE=master

  # Test services
  postgres_test:
    image: pgvector/pgvector:pg16
    ports:
      - "9090:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_DB: adex_test
      POSTGRES_PASSWORD: "rv7F70ojhlcX4PojYImVasmSg33SJmTQ"
    volumes:
      - adex_test_data:/var/lib/postgresql/data

  redis_test:
    image: redis:6.2.6
    ports:
      - "9091:6379"
    command: redis-server --requirepass "yB-F5nfowapd03oVFsGY1Efygh4jZLCU"
    environment:
      - REDIS_REPLICATION_MODE=master

volumes:
  adex_data:
  adex_test_data:

```

### adex_server/dart_test.yaml

```yaml
tags:
  integration: {}

```

### adex_client/dartdoc_options.yaml

```yaml
dartdoc:
  categories: 
    "Endpoint":
      markdown: doc/endpoint.md
      name: Endpoint
```

### adex_client/pubspec.yaml

```yaml
name: adex_client
description: Starting point for a Serverpod client.

environment:
  sdk: '^3.8.0'

dependencies:
  serverpod_auth_idp_client: 3.2.3
  serverpod_client: 3.2.3


```

### adex_client/analysis_options.yaml

```yaml
# Defines a default set of lint rules enforced for
# projects at Google. For details and rationale,
# see https://github.com/dart-lang/pedantic#enabled-lints.

# For lint rules and documentation, see http://dart-lang.github.io/linter/lints.
# Uncomment to specify additional rules.
# linter:
#   rules:
#     - camel_case_types

analyzer:
  exclude:
    - lib/src/protocol/**

formatter:
  trailing_commas: preserve

```

### adex_server/pubspec.yaml

```yaml
name: adex_server
description: Starting point for a Serverpod server.
# version: 1.0.0
# homepage: https://www.example.com

environment:
  sdk: '^3.8.0'

dependencies:
  serverpod: 3.2.3
  serverpod_auth_idp_server: 3.2.3
  serverpod_cloud_storage_s3: 3.2.3
  amazon_cognito_identity_dart_2: ^3.8.1
  http: ^1.2.0
  http_parser: ^4.1.0
  path: ^1.9.0
  crypto: ^3.0.0
  mailer: ^6.2.0

dev_dependencies:
  lints: '>=3.0.0 <7.0.0'
  serverpod_test: 3.2.3
  test: ^1.25.5


serverpod:
  scripts:
    # Starts the server and applies migrations
    start: dart bin/main.dart --apply-migrations

    # Build the Flutter web app and move it to the server's web directory
    # 
    # Unfortunately, we can't use the `-o` flag directly because of an error
    # that happens on windows. Issue is tracked in the flutter
    # repository here: https://github.com/flutter/flutter/issues/157886
    flutter_build: cd ../adex_flutter && flutter build web --base-href /app/ --wasm && rm -rf ../adex_server/web/app && mv build/web/ ../adex_server/web/app

```

### adex_server/analysis_options.yaml

```yaml
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.

include: package:lints/recommended.yaml

# Uncomment the following section to specify additional rules.

linter:
  rules:
    unawaited_futures: true
    avoid_print: true

analyzer:
  exclude:
    - lib/src/generated/**
    - test/integration/test_tools/serverpod_test_tools.dart
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints

# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

formatter:
  trailing_commas: preserve

```

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