A service where an AI analyzes two photos uploaded by a user, creates a "Moment Frame", and connects them with other moments worldwide based on semantic similarity and geographic distance.
| Category | Stack |
|---|---|
| Frontend | Next.js 16, React 19, TypeScript, MapLibre GL, h3-js |
| Backend | FastAPI, Motor (async MongoDB), Celery |
| AI | OpenAI / Google Gemini (Vision & Embeddings) |
| Storage | MongoDB Atlas Local ($vectorSearch), Redis (Celery broker) |
| Geo | H3 (Uber's Hexagonal Hierarchical Spatial Index) |
| Infra | Docker Compose (MongoDB + Redis), Makefile |
- Docker Desktop
- Python 3.11+
- Node.js 20+
make
# 1. Install dependencies (Python venv + npm)
make install
# 2. Configure backend/.env (refer to the Environment Variables section below)
# 3. Initialize DB + Vector Index (first time only)
make setup
# 4. Start all services (Backend + Worker + Frontend)
make devAfter starting:
- Frontend: http://localhost:3000
- Backend API: http://localhost:8000/docs
| Command | Description |
|---|---|
make / make help |
Print available Makefile target help center (default command) |
make install |
Create Python venv + Install packages + npm install |
make setup |
Start Docker → Wait for MongoDB boot (25s) → Create vector index |
make up |
Start only MongoDB + Redis containers |
make down |
Stop Docker containers |
make dev |
Run all services in background (Docker + Backend + Worker + Frontend) |
make backend / worker / frontend |
Run each service individually in the foreground |
make stop |
Terminate all processes + Docker containers |
make status |
Check Docker/process status |
make logs-backend / logs-worker / logs-frontend |
Tail logs |
make endpoints |
Print all available endpoints |
make reset |
Clear DB volumes/uploads/build caches completely |
Running
make devdirectly aftermake resetwithoutmake setupwill cause the vector search index to be missing, and matching will not work.
# First install or full DB reset
make reset # Clear DB volumes + uploads + build caches
make setup # Start Docker → Wait for MongoDB Search engine (25s) → Create 3072-dim index
make dev # Start all services
# Routine restart (keep DB)
make stop
make dev# Infrastructure
MONGO_URI=mongodb://localhost:27018/?directConnection=true
MONGO_DB_NAME=paraline
REDIS_URL=redis://localhost:6380/0
# AI Provider Selection (openai | google)
VISION_PROVIDER=google
EMBED_PROVIDER=google
# OpenAI Configuration
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_VISION_URI=/chat/completions
OPENAI_EMBEDDING_URI=/embeddings
OPENAI_VISION_MODEL=gpt-4o
OPENAI_EMBEDDING_MODEL=text-embedding-3-large
# Google Gemini Configuration
GOOGLE_API_KEY=AIzaSyA...
GOOGLE_BASE_URL=https://generativelanguage.googleapis.com/v1beta
GOOGLE_VISION_URI=/models/{model}:generateContent
GOOGLE_EMBEDDING_URI=/models/{model}:embedContent
GOOGLE_VISION_MODEL=gemini-2.5-flash
GOOGLE_EMBEDDING_MODEL=gemini-embedding-001
# Common AI Settings
EMBEDDING_DIMENSIONS=3072 # google: 3072, openai text-embedding-3-large: 3072, text-embedding-3-small: 1536
AI_TEMPERATURE=0.7
# Matching Engine
SIMILARITY_THRESHOLD=0.75
HORIZONTAL_MIN_DISTANCE_KM=200
MAX_CONNECTIONS_PER_MOMENT=10
# Server
BACKEND_PORT=8000
PHOTO_UPLOAD_DIR=uploads
LOG_LEVEL=INFOparaline2/
├── backend/
│ ├── api/ # FastAPI Routers (upload, moments, connections, ...)
│ ├── services/ # ai_service, matching_service, exif_service
│ ├── scripts/ # init_vector_index, rebuild_connections, etc.
│ ├── main.py # FastAPI Entry Point
│ ├── celery_app.py # Celery Instance
│ ├── models.py # Pydantic Models
│ └── requirements.txt
├── frontend/
│ ├── app/ # Next.js App Router Pages
│ ├── components/
│ └── lib/ # API Client, deviceId, etc.
├── docs/ # Plannings, Task Specs
├── docker-compose.yml # MongoDB + Redis
└── Makefile
[Upload 2 photos]
↓
Extract EXIF (GPS + H3 Index)
↓
Save to Mongo (status=processing)
↓
Celery: analyze_moment_task
├─ AI Vision API → Output JSON
├─ AI Embeddings → High-dimensional Vector
└─ Update Mongo (status=completed)
↓
Celery: match_moment_task
└─ Mongo $vectorSearch → Match Similar Moments → Save connections → Create Notification
- Vertical (Same Location): At least one H3 grid cell overlaps between two moments.
- Horizontal (Distant Location): No H3 overlap + central coordinate distance ≥
HORIZONTAL_MIN_DISTANCE_KM(default 200km). - Distances in between are intentionally excluded from connection.
- Only candidates with Cosine Similarity ≥
SIMILARITY_THRESHOLD(default 0.75) are evaluated.
Non-standard ports are used to avoid conflicts with other projects:
| Service | Host Port | Container Port |
|---|---|---|
| MongoDB | 27018 | 27017 |
| Redis | 6380 | 6379 |
| Backend | 8000 | - |
| Frontend | 3000 | - |
