FinPilot AI — Team DistributedMind
- Prerequisites
- Quick Start with CLI
- Manual Setup - Common Steps
- Path A: Full Docker (16GB+ RAM)
- Path B: Partial Docker (8GB RAM)
- End-to-End Verification
- Configuration Reference
- Demo Profiles
- Troubleshooting
- Project Map
| Tool | Version | Required For |
|---|---|---|
| Python | 3.11+ | ML service, data generation |
| Node.js | 18+ | Frontend |
| Docker Desktop | latest | PostgreSQL, Redis, optional containers |
| Java | 21 (JDK) | Backend (only if running outside Docker) |
| Git | any | Version control |
The fastest way to get the full system running:
# 1. Clone and configure
git clone <repo-url> DistributedMind-IDBI
cd DistributedMind-IDBI
cp .env.example .env
# 2. One-command startup
python cli.py devcp .env.example .env
docker compose -f docker/docker-compose.yml up -d
# Frontend at http://localhost:3000
# Score a demo customer:
curl -s -X POST http://localhost:8080/api/v1/score/CUST00042 | jq .export GHCR_NAMESPACE=your-org
docker compose -f docker/docker-compose.prod.yml up -dEach service on Railway has its own environment variables. Set these per-service in the Railway dashboard.
Backend (Railway → backend → Variables):
| Variable | Value | Notes |
|---|---|---|
SPRING_PROFILES_ACTIVE |
docker |
|
SPRING_DATASOURCE_URL |
jdbc:postgresql://${{finpilot-db.HOST}}:${{finpilot-db.PORT}}/fhss |
PostgreSQL plugin |
SPRING_DATASOURCE_USERNAME |
${{finpilot-db.USERNAME}} |
|
DB_PASSWORD |
${{finpilot-db.PASSWORD}} |
|
APP_JWT_SECRET |
(generate a secure 32+ char secret) | |
ML_SERVICE_URL |
http://${{ml-service.RAILWAY_PRIVATE_DOMAIN}} |
|
CORS_ALLOWED_ORIGINS |
https://${{frontend.RAILWAY_PUBLIC_DOMAIN}} |
|
REDIS_ENABLED |
false |
Set to true after adding Redis plugin |
ML Service (Railway → ml-service → Variables):
| Variable | Value | Notes |
|---|---|---|
MODEL_PATH |
/app/models/model_latest.joblib |
|
REDIS_ENABLED |
false |
Set to true after adding Redis plugin |
Frontend (Railway → frontend → Variables):
| Variable | Value | Notes |
|---|---|---|
VITE_API_BASE_URL |
/api/v1 |
|
BACKEND_HOST |
${{backend.RAILWAY_PRIVATE_DOMAIN}} |
|
BACKEND_PORT |
8080 |
Adding Redis later: Once a Redis plugin (
finpilot-redis) is provisioned, removeREDIS_ENABLED=falsefrom backend and ml-service, then add:
- Backend:
REDIS_HOST=${{finpilot-redis.HOST}},REDIS_PORT=${{finpilot-redis.PORT}}- ML Service:
REDIS_HOST=${{finpilot-redis.HOST}},REDIS_PORT=${{finpilot-redis.PORT}}The old
SPRING_DATA_REDIS_*vars are no longer read.
The CLI handles the full 7-step startup:
- Environment check (Python, Docker, Node)
- Start infrastructure (PostgreSQL + Redis via Docker)
- Start backend (Spring Boot with Flyway migrations)
- Seed database (350 customer profiles)
- Train ML model
- Start ML service (FastAPI)
- Start frontend (Vite dev server)
To skip slow steps when iterating:
python cli.py dev --skip-seed --skip-trainOther CLI commands:
python cli.py status # Health report (git, Docker, APIs)
python cli.py commit # Quality-gated commit with conventional commit promptThe CLI writes its own log to logs/cli.log.
For Docker containers started by the CLI, follow logs in real time:
docker compose -f docker/docker-compose.yml logs -f # all containers
docker compose -f docker/docker-compose.yml logs -f backend # single serviceThe ML service and frontend are launched as background processes with their output suppressed. To see their live output, run them manually in separate terminals instead:
# Terminal 2 — ML Service
cd ml-service
set MODEL_PATH=models\model_latest.joblib
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
# Terminal 3 — Frontend
cd frontend
npx vite --port 5173docker compose -f docker/docker-compose.yml up -d postgres redisWait 10 seconds, then verify:
docker compose -f docker/docker-compose.yml psBoth fhss-postgres and fhss-redis should show Up (healthy).
The backend runs Flyway migrations automatically on startup:
docker compose -f docker/docker-compose.yml up -d backendWait ~60 seconds for Spring Boot to start, then verify:
curl http://localhost:8080/api/v1/score/healthExpected: {"status":"UP","service":"fhss-gateway","timestamp":"..."}
cd synthetic-data
pip install psycopg2-binary
python seed.py
cd ..Expected: Loaded 350 labeled profiles ... Inserted: 350, Skipped: 0
cd ml-service
pip install -r requirements.txt
python -m app.training.train_model ..\synthetic-data\output\profiles_labeled.csvExpected: Classification report output, model saved to models/model_latest.joblib.
Everything runs in Docker containers:
# Start remaining services
docker compose -f docker/docker-compose.yml up -d ml-service frontendWait ~20 seconds for ML service to load the model, then verify:
curl http://localhost:8000/healthExpected: {"status":"UP","model_version":"2.0.x","model_loaded":true,...}
Access: http://localhost:3000
Run ML service and frontend locally for faster iteration.
Open a new terminal:
cd ml-service
set MODEL_PATH=models\model_latest.joblib
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reloadWait for Application startup complete, then verify:
curl http://localhost:8000/healthOpen a new terminal:
cd frontend
npm install
npm run devAccess: http://localhost:5173 (Vite proxy forwards /api to backend on :8080)
Run these from any terminal:
# Health check
curl http://localhost:8080/api/v1/score/health
# Score a customer
curl -s -X POST http://localhost:8080/api/v1/score/CUST00001 | jq .
# Get customer profile
curl -s http://localhost:8080/api/v1/customers/CUST00001/profile | jq .
# Submit a decision
curl -s -X POST http://localhost:8080/api/v1/decisions ^
-H "Content-Type: application/json" ^
-d "{\"customer_id\":\"CUST00001\",\"decision\":\"APPROVE\",\"remarks\":\"Good profile\"}"
# Get pending reviews
curl -s http://localhost:8080/api/v1/decisions/pending | jq .All configuration is driven by environment variables in .env.
| Variable | Default | Notes |
|---|---|---|
ML_SERVICE_URL |
http://ml-service:8000 |
Change to http://host.docker.internal:8000 for Path B |
SPRING_DATASOURCE_URL |
jdbc:postgresql://postgres:5432/fhss |
|
SPRING_DATASOURCE_USERNAME |
fhss |
|
SPRING_DATASOURCE_PASSWORD |
change_me_in_production |
|
CORS_ALLOWED_ORIGINS |
http://localhost:5173,http://localhost:3000 |
|
REDIS_ENABLED |
true |
Set false to skip Redis entirely |
REDIS_HOST |
localhost |
Only read when REDIS_ENABLED=true |
REDIS_PORT |
6379 |
Only read when REDIS_ENABLED=true |
Note:
SPRING_DATA_REDIS_HOST/SPRING_DATA_REDIS_PORTare no longer used. UseREDIS_HOST/REDIS_PORTinstead.
| Variable | Default | Notes |
|---|---|---|
MODEL_PATH |
/app/models/model_latest.joblib |
Use models\model_latest.joblib on Windows |
UVICORN_WORKERS |
2 |
|
ML_SERVICE_PORT |
8000 |
|
REDIS_ENABLED |
true |
Set false to skip Redis entirely |
REDIS_HOST |
localhost |
Only read when REDIS_ENABLED=true |
REDIS_PORT |
6379 |
Only read when REDIS_ENABLED=true |
| Variable | Default | Notes |
|---|---|---|
POSTGRES_DB |
fhss |
|
POSTGRES_USER |
fhss |
|
POSTGRES_PASSWORD |
change_me_in_production |
Override any variable by setting it in .env (gitignored). See .env.example for the full list.
The system comes pre-seeded with 350 synthetic profiles (CUST00001-CUST00350). Four curated demo profiles are hardcoded in the SearchBar dropdown:
| ID | Business Name | Type | Bucket | Notes |
|---|---|---|---|---|
| CUST00042 | Ramesh Traders | Manufacturing | yes-to-go | Blank-slate - approved via alt-data |
| CUST00011 | Shakti Manufacturing | Manufacturing | disciplined | Full data - high performer |
| CUST00087 | Kaveri Logistics | Logistics | non-disciplined | Fuel volatility risk |
| CUST00134 | Anand Cold Chain | Trading | no-to-go | High risk - rejected |
Cause: Backend cannot reach ML service, or model not loaded.
Check:
# Is ML running?
curl http://localhost:8000/health
# Is backend healthy?
curl http://localhost:8080/api/v1/score/healthIf ML returns DEGRADED, retrain the model (Step D).
Cause: Database not seeded.
Fix: Run python seed.py from synthetic-data/ directory.
Cause: ML service was down during scoring; response from Redis/audit cache.
Fix: Start ML service and re-score.
netstat -ano | findstr :8080
taskkill /PID <PID> /Fdocker logs fhss-backend --tail 50Old data exists - clear and restart:
docker compose -f docker/docker-compose.yml down -v
docker compose -f docker/docker-compose.yml up -d postgres redisEnsure Python scripts use the correct path separator:
set MODEL_PATH=models\model_latest.joblibAnd pip install with quotes for paths with spaces:
python -m pip install -r requirements.txtDistributedMind-IDBI/
cli.py Unified CLI tool (dev, status, commit)
synthetic-data/
generate_profiles.py 350-customer profile generator
label_profiles.py Composite score -> bucket assigner
seed.py Idempotent DB seeding via psycopg2
output/profiles_labeled.csv Seeded labeled profiles
ml-service/
app/
main.py FastAPI entry point with lifespan
router.py POST /predict, GET /health
feature_engineering.py 6-feature computation (THE CORE)
model_loader.py Model loading at startup
explain.py SHAP explanation computation
business_weights.py Sector-specific signal weights
epfo_checks.py EPFO plausibility flag
capacity_flag.py Loan-to-capacity assessment
seasonality.py Fuel/electricity volatility flags
training/
train_model.py GBM training script
models/ .joblib model artifacts
backend/
scoring/
controller/ScoreController.java REST endpoints
service/ScoringClientService.java Core orchestration + resilience
service/DecisionService.java Underwriter workflow
common/
resources/db/migration/ Flyway SQL migrations (V1-V10)
frontend/
src/
App.tsx 7-state orchestrator
components/ Layout, SearchBar, ScoreOverview,
ScoreDetail, AuditTrail
docs/
architecture.md Full system architecture
developer-guide.md This document
scoring-logic.md Complete scoring logic with formulas
stack-decisions.md Technology stack and design decisions
For detailed architecture, scoring logic, and tech decisions, refer to the other documents in docs/.