# Alemno Payments [![Python Version](https://img.shields.io/badge/python-3.14-blue?style=flat-square&logo=python)](file:///home/sortedcord/Projects/alemno_payments/pyproject.toml) [![FastAPI](https://img.shields.io/badge/FastAPI-0.139-009688?style=flat-square&logo=fastapi)](file:///home/sortedcord/Projects/alemno_payments/app/app.py) [![Test CI Status](https://github.com/sortedcord/alemno-payments/actions/workflows/tests.yml/badge.svg)](https://github.com/sortedcord/alemno-payments/actions/workflows/tests.yml) [![Lint CI Status](https://github.com/sortedcord/alemno-payments/actions/workflows/lint.yml/badge.svg)](https://github.com/sortedcord/alemno-payments/actions/workflows/lint.yml) [![Docker CI Status](https://github.com/sortedcord/alemno-payments/actions/workflows/docker.yml/badge.svg)](https://github.com/sortedcord/alemno-payments/actions/workflows/docker.yml) [![Security CI Status](https://github.com/sortedcord/alemno-payments/actions/workflows/security.yml/badge.svg)](https://github.com/sortedcord/alemno-payments/actions/workflows/security.yml) An asynchronous transactional data cleaning, validation, and LLM powered categorization pipeline. ## 🚀 Features - Reads multi format dates and handles missing transaction IDs by auto generating unique fallbacks, and parses messy amounts with currency signs and commas cleanly. - Offloads validation, spend conversion, and analysis to a Celery background worker backed by Redis. - Integrates with the Google GenAI SDK (`gemini-3.5-flash`) using structured output schemas to categorize transaction records into database-defined custom categories. - Uses Redis caching with a 24-hour TTL to store live exchange rates, falling back to a hardcoded rate of `93.0` if offline. - Statistical Anomaly Detection: - Flags transactions exceeding 3 times the account's median spend as statistical outliers. - Flags domestic transactions in USD currency. - Flags high value transactions and transactions with fraud indicative keywords. ## 🐳 Self Hosting & Deployment We publish production-ready images to **GitHub Container Registry (GHCR)**. You can pull them directly or run them via Docker Compose. ### Option A: Docker Compose (Using Pre-built Images) Create a `docker-compose.yml` file to self-host the entire stack: ```yaml version: "3.8" services: db: image: postgres:16-alpine container_name: alemno_db ports: - "5432:5432" environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: alemno_payments volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d alemno_payments"] interval: 5s timeout: 5s retries: 5 redis: image: redis:7-alpine container_name: alemno_redis ports: - "6379:6379" web: image: ghcr.io/sortedcord/alemno-payments/web:latest container_name: alemno_web ports: - "3000:3000" environment: - POSTGRES_HOST=db - POSTGRES_PORT=5432 - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres - POSTGRES_DB=alemno_payments - REDIS_HOST=redis - REDIS_PORT=6379 - REDIS_DB=0 - GEMINI_API_KEY=your-gemini-api-key depends_on: db: condition: service_healthy redis: condition: service_started worker: image: ghcr.io/sortedcord/alemno-payments/worker:latest container_name: alemno_worker environment: - POSTGRES_HOST=db - POSTGRES_PORT=5432 - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres - POSTGRES_DB=alemno_payments - REDIS_HOST=redis - REDIS_PORT=6379 - REDIS_DB=0 - GEMINI_API_KEY=your-gemini-api-key depends_on: db: condition: service_healthy redis: condition: service_started volumes: postgres_data: ``` Launch the stack: ```bash docker compose up -d ``` ### Option B: Direct Pull (Manual Run) You can pull and run individual containers manually from GHCR: 1. **Pull the images**: ```bash docker pull ghcr.io/sortedcord/alemno-payments/web:latest docker pull ghcr.io/sortedcord/alemno-payments/worker:latest ``` 2. **Run Web (FastAPI API Server)**: ```bash docker run -d \ --name alemno_web \ -p 3000:3000 \ -e POSTGRES_HOST=your-db-host \ -e REDIS_HOST=your-redis-host \ -e GEMINI_API_KEY=your-gemini-api-key \ ghcr.io/sortedcord/alemno-payments/web:latest ``` 3. **Run Worker (Celery Processing Worker)**: ```bash docker run -d \ --name alemno_worker \ -e POSTGRES_HOST=your-db-host \ -e REDIS_HOST=your-redis-host \ -e GEMINI_API_KEY=your-gemini-api-key \ ghcr.io/sortedcord/alemno-payments/worker:latest celery -A app.worker.celery_app worker --loglevel=info ```