An ML-powered supply chain decision engine that transforms demand uncertainty into actionable intelligence.
Problem • Features • Architecture • Walkthrough • Stack • Setup • Deploy
Modern supply chains lose $1.1 trillion annually to inventory distortion — the gap between what's on the shelf and what should be. This breaks down into three core failures:
| Failure | Impact | Root Cause |
|---|---|---|
| Demand Blindness | Retailers order based on gut feeling, not data | No forecasting infrastructure |
| Stockout Cascades | One warehouse runs dry, triggering panic reorders across zones | No cross-warehouse visibility |
| Overstock Waste | Festival-period bulk orders rot when demand doesn't materialize | No seasonal intelligence |
Traditional ERP systems track what happened. They don't predict what will happen. Fluxo bridges this gap by embedding XGBoost-driven demand forecasting directly into the supply request workflow — every order is evaluated against a trained model before it touches inventory.
This project started from a simple observation: supply chain decisions are made in spreadsheets, not with machine learning.
The goal was to build a system where:
- A retailer types "Need 500 eggs for Zone A" and the platform automatically predicts demand, evaluates risk, and recommends an action — before any stock is moved.
- A warehouse manager sees projected shortages 7 days out, not after they've already happened.
- An admin gets a single-pane view of inventory health, demand trends, and risk signals across all zones.
Fluxo is not a CRUD inventory app. It's a Decision Support System — an AI copilot for logistics teams.
Triple XGBoost quantile regression models (median, lower bound, upper bound) generate confidence-interval predictions from a 22-feature vector constructed in real-time. The system reconstructs the exact training environment on every request.
Type "Ship 1200 units of rice to Zone B, festival period" — the Groq-powered LLM (Llama 3) extracts structured JSON (product, quantity, zone) and feeds it directly into the ML pipeline. No forms needed.
An AI analyst backed by over 4MB of synthetic supply chain data. Ask questions like "Which zone has the highest demand during festivals?" and get data-grounded answers with actual numbers.
Three roles with enforced permissions at both API and UI layers:
| Role | Can Do | Cannot Do |
|---|---|---|
| Admin | Full system access, company settings, all data | — |
| Retailer | Create requests, view own predictions | Access other retailers' data, inventory mgmt |
| Warehouse | View inventory, manage stock, view zone requests | Create demand requests |
Real-time metrics with Recharts visualizations: demand forecast area charts, seasonal pattern analysis, warehouse utilization gauges, risk distribution, and inventory health indicators — all driven by live API data.
Every supply request is classified against forecast confidence intervals:
requested_qty > upper_bound→ High Overstock Riskrequested_qty < lower_bound→ Understock Risk- Within bounds → Balanced
Aggregation-based reorder suggestions computed from historical demand averages with configurable safety factors and sensitivity thresholds.
Zone/warehouse filtering, stock adjustment workflows (inbound, outbound, threshold edits), transaction history tracking, and real-time stock projection bars.
1. LOGIN → JWT authentication, role assigned server-side
↓
2. DASHBOARD → Metrics, charts, system health, warehouse utilization
↓
3. CREATE REQUEST → Three input modes:
├── Structured Form (SKU, zone, qty, date, festival flag)
├── Bulk CSV Upload (multi-row validation)
└── Natural Language ("500 eggs to Zone A")
↓
4. ML INFERENCE → 22-feature vector → XGBoost → forecast + confidence interval
↓
5. RISK ANALYSIS → Overstock/understock classification
↓
6. INVENTORY UPDATE → Stock deducted, transaction logged
↓
7. AI INSIGHTS → Ask the RAG assistant about trends, patterns, anomalies
↓
8. ADMIN SETTINGS → Company config, operational defaults, user overview
graph TB
subgraph Client["Frontend — Vercel"]
UI["React SPA<br/>Vite + TypeScript + Tailwind"]
end
subgraph API["Backend — Render"]
GW["Express API Gateway<br/>JWT + RBAC + Helmet"]
FE["Feature Engineering"]
RISK["Risk Engine"]
end
subgraph ML["ML Service — Render"]
XGB["FastAPI<br/>XGBoost ×3 Models"]
end
subgraph RAG["RAG Service — Render"]
LLM["FastAPI<br/>Groq + CSV Context"]
end
subgraph DB["Database — Atlas"]
MONGO[(MongoDB<br/>6 Collections)]
end
UI -->|"HTTPS + Bearer JWT"| GW
GW --> FE
FE -->|"22-feature vector"| XGB
XGB -->|"forecast + bounds"| RISK
GW -->|"chat + context"| LLM
GW -->|"Mongoose"| MONGO
RISK -->|"persist prediction"| MONGO
sequenceDiagram
participant R as Retailer
participant BE as Backend
participant DB as MongoDB
participant ML as ML Service
R->>BE: POST /requests {sku, zone, qty, ...}
BE->>BE: JWT verify + RBAC check
BE->>DB: Fetch Product by SKU
BE->>DB: Fetch last 2 WeeklySales (lag features)
BE->>BE: Construct 22-feature vector
BE->>ML: POST /predict {features}
ML-->>BE: {forecast, lower_bound, upper_bound}
BE->>BE: Risk classification
BE->>DB: Save DemandRequest with prediction
BE->>DB: Deduct from Inventory
BE-->>R: {request + forecast + risk_level}
sequenceDiagram
participant U as User
participant FE as Frontend
participant BE as Backend
participant DB as MongoDB
U->>FE: Enter credentials
FE->>BE: POST /auth/login
BE->>DB: Find user, bcrypt compare
BE-->>FE: {token, user: {id, name, role}}
FE->>FE: Store in localStorage + AuthContext
FE->>BE: Subsequent requests with Bearer token
BE->>BE: JWT verify → attach user to req
BE->>BE: authorize() → check role
| Layer | Technology | Why This Choice |
|---|---|---|
| Frontend | React 18 + TypeScript | Type safety across 10 pages, 15+ components |
| Styling | Tailwind CSS + shadcn/ui | Design system consistency with Radix primitives |
| Charts | Recharts | Composable chart components for demand visualizations |
| Animation | Framer Motion | Micro-interactions: row expansion, page transitions, hover states |
| State | TanStack Query | Server-state caching with automatic refetch on mutations |
| Routing | React Router v6 | Protected routes with role-based guards |
| Backend | Express 5 (Node.js) | Lightweight API gateway with middleware composition |
| Database | MongoDB + Mongoose | Schema flexibility for evolving supply chain models |
| Auth | JWT + bcrypt | Stateless auth with role-encoded tokens |
| Security | Helmet + express-rate-limit | HTTP headers hardening + brute-force protection |
| ML Serving | FastAPI + XGBoost | Sub-100ms inference with joblib-serialized models |
| AI/NLP | Groq API (Llama 3) | Fast LLM inference for NL parsing and RAG chat |
| RAG | Pandas + CSV context | Lightweight retrieval over 4MB supply chain dataset |
The system constructs a 22-dimensional feature vector in real-time, matching the exact training schema:
| Group | Features | Count |
|---|---|---|
| Numerical | current_price, base_price, discount_percent, year_growth, month, is_festival, product_id, lag_1, lag_2 |
9 |
| Zone (one-hot) | South, West, East (North = baseline) | 3 |
| Warehouse (one-hot) | B, C (A = baseline) | 2 |
| Category (one-hot) | dairy, poultry, grains, vegetables, fruits, electronics, raw_materials, furniture | 8 |
The lag_1 and lag_2 features are fetched from the WeeklySales collection — the two most recent sales records for the product/zone/warehouse combination.
1. RBAC That Actually Works
The initial authorize() middleware was a no-op — it accepted role parameters but always called next(). This meant any authenticated user could access any endpoint. The fix required implementing proper role checking at both the API layer (middleware) and the UI layer (route guards), plus removing the frontend role dropdown that let users self-assign admin.
2. The Double-Unwrap Bug
The frontend's handleResponse() function auto-extracted data.data from API responses. But loginUser() then tried data.data.token — which was undefined because the data was already unwrapped. This caused silent auth failures where tokens were never stored. Fixed by making handleResponse() return raw JSON and having each API function explicitly access .data.
3. Microservice Port Conflicts
Both the ML service and RAG service defaulted to port 8000. They couldn't run simultaneously during local development. Resolved by making all ports env-driven (process.env.PORT) with distinct defaults (ML: 8001, RAG: 8000) and introducing ML_SERVICE_URL / RAG_SERVICE_URL env vars for production.
4. Production Build CSS Ordering
Vite's production build failed with @import must precede all other statements because Google Fonts was imported after @tailwind directives. CSS spec requires @import first — a rule that dev mode tolerates but production builds enforce.
5. Express 5 Error Handling
Express 5 changed error handling behavior from v4. The error middleware needed to properly handle both sync throws and async rejections while hiding internal error details in production (NODE_ENV=production).
| Layer | Implementation |
|---|---|
| Authentication | JWT with bcrypt password hashing (10 salt rounds) |
| Authorization | protect middleware (JWT verify) + authorize middleware (role check) |
| Rate Limiting | 20 requests / 15 minutes on /auth/* routes |
| Headers | Helmet.js (CSP, HSTS, X-Frame-Options, etc.) |
| Input Validation | Email regex, password ≥ 6 chars, payload schema checks |
| Error Masking | Internal errors hidden in production responses |
| CORS | Env-driven allowlist (CORS_ORIGINS) |
| Secrets | .env files gitignored, .env.example templates provided |
fluxo/
├── frontend/ # React SPA
│ ├── src/
│ │ ├── pages/ # Dashboard, Requests, Inventory, Insights, Admin, Profile
│ │ ├── components/ # AppLayout, Sidebar, Topbar, RequestIntakeDrawer
│ │ ├── context/ # AuthContext, UISettings, CompanyContext
│ │ ├── services/ # API client (fetch + JWT headers)
│ │ ├── types/ # TypeScript interfaces
│ │ └── hooks/ # Custom hooks (toast, mobile detection)
│ ├── .env # VITE_API_URL
│ └── .env.production # Production API URL
│
├── backend/ # Express API Gateway
│ ├── src/
│ │ ├── config/ # DB connection with retry logic
│ │ ├── controllers/ # Auth, Request, AI, Demand, Decision, Inventory
│ │ ├── middlewares/ # JWT protect, RBAC authorize, error handler
│ │ ├── models/ # User, Product, Inventory, DemandRequest, WeeklySales, Demand
│ │ ├── routes/ # Route definitions with middleware chains
│ │ └── services/ # Business logic, ML communication, auth
│ ├── seed-demo.js # Database seeding script
│ └── server.js # Entry point (async startup)
│
├── ml_service/ # XGBoost Inference API
│ ├── models/ # xgb_median.pkl, xgb_lower.pkl, xgb_upper.pkl
│ ├── app.py # FastAPI with /predict and /health
│ └── requirements.txt # Python dependencies
│
├── fluxo-rag/ # RAG Chat Service
│ ├── data/ # synthetic_supplychain_data.csv (4MB)
│ ├── main.py # FastAPI with /chat, /health, /data-check
│ └── requirements.txt # Python dependencies
│
├── render.yaml # Render Blueprint (all 3 services)
├── dataset_generator.ipynb # Synthetic data generation
├── demand_forecasting_model.ipynb # Model training notebook
└── .gitignore
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/auth/register |
Public | Register (name, email, password) |
POST |
/auth/login |
Public | Login → JWT + user object |
GET |
/health |
Public | Backend health check |
GET |
/inventory |
warehouse, admin |
List inventory items |
POST |
/inventory |
warehouse |
Create/adjust inventory |
GET |
/requests |
retailer, warehouse, admin |
List requests (role-filtered) |
POST |
/requests |
retailer |
Create request → ML inference |
GET |
/demand |
Authenticated | Demand analytics |
POST |
/demand |
Authenticated | Log demand data |
GET |
/decision/reorder |
Authenticated | Reorder suggestions |
POST |
/api/ai/chat |
Authenticated | RAG chat with AI analyst |
POST |
/api/ai/parse |
Authenticated | NLP → structured JSON |
- Node.js 18+
- Python 3.10+
- MongoDB (local or Atlas)
git clone https://github.com/MithunSrinivas28/Fluxo_AI_Supplychain.git
cd Fluxo_AI_Supplychaincd backend
npm install
cp .env.example .env
# Edit .env: set MONGO_URI, JWT_SECRET, GROQ_API_KEY
npm run devcd ml_service
python -m venv venv
# Windows: venv\Scripts\activate | macOS/Linux: source venv/bin/activate
pip install -r requirements.txt
uvicorn app:app --port 8001cd fluxo-rag
python -m venv venv
# Windows: venv\Scripts\activate | macOS/Linux: source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Edit .env: set GROQ_API_KEY
uvicorn main:app --port 8000cd frontend
npm install
# .env already has VITE_API_URL=http://localhost:5000
npm run devcd backend
node seed-demo.jsDemo credentials:
mithunsrinivas28@gmail.com/123(admin role)
Vercel (Frontend) → Render (Backend) → MongoDB Atlas
├──→ Render (ML Service)
└──→ Render (RAG Service)
- MongoDB Atlas — Create cluster, get connection string
- ML Service → Render (root:
ml_service, start:uvicorn app:app --host 0.0.0.0 --port $PORT) - RAG Service → Render (root:
fluxo-rag, start:uvicorn main:app --host 0.0.0.0 --port $PORT) - Backend → Render (root:
backend, start:node server.js) - Frontend → Vercel (root:
frontend, build:npm run build, output:dist) - Seed database — Run
seed-demo.jswith productionMONGO_URI
Backend (Render)
| Variable | Description |
|---|---|
PORT |
Auto-set by Render |
NODE_ENV |
production |
MONGO_URI |
MongoDB Atlas connection string |
JWT_SECRET |
64-char random string |
JWT_EXPIRES_IN |
1d |
GROQ_API_KEY |
From console.groq.com |
ML_SERVICE_URL |
Render ML service URL |
RAG_SERVICE_URL |
Render RAG service URL |
CORS_ORIGINS |
https://fluxo.vercel.app |
RAG Service (Render)
| Variable | Description |
|---|---|
PORT |
Auto-set by Render |
GROQ_API_KEY |
Same Groq key |
GROQ_MODEL |
llama-3.3-70b-versatile |
Frontend (Vercel)
| Variable | Description |
|---|---|
VITE_API_URL |
Render backend URL |
A render.yaml blueprint is included for one-click Render deployment.
Screenshots of the running application will be added here.
| View | Description |
|---|---|
| Dashboard | Metrics, demand charts, warehouse utilization, system health |
| Requests | ML-enriched request queue with decision intelligence panels |
| Inventory | Zone/warehouse filtering, stock levels, transaction history |
| Insights | RAG-powered AI chat with supply chain data |
| Admin | Company settings, operational defaults, platform guide |
- Real-time pipeline — Kafka/Redis streams for live inventory events
- Anomaly detection — Isolation Forest on demand patterns
- Multi-warehouse optimization — Linear programming for cross-zone allocation
- Predictive procurement — Auto-generate POs from forecast signals
- Model retraining pipeline — Scheduled retraining with MLflow tracking
- WebSocket notifications — Push alerts for stockout predictions
- Container orchestration — Docker Compose → Kubernetes migration
- Supplier scoring — Reliability metrics from delivery data
On ML integration — The hardest part wasn't training the model. It was reconstructing the exact 22-feature vector at inference time, with the same one-hot encoding order, the same lag feature extraction, and the same baseline categories. Any mismatch silently produces wrong predictions.
On microservice communication — Two services defaulting to the same port taught me to make everything env-driven from day one. Hardcoded URLs work in development until they don't.
On auth — Writing authorize() as a no-op "to implement later" meant the entire RBAC system was a facade for weeks. Security middleware should be strict by default and relaxed explicitly.
On frontend-backend contracts — The double-unwrap bug (data.data.data) was caused by the frontend and backend having different assumptions about response shape. A shared API contract (or at least a consistent unwrapping strategy) would have prevented hours of debugging.
On production readiness — The gap between "works on localhost" and "deploys on Render" is larger than expected. Missing numpy in requirements.txt, CSS import ordering, CORS configuration, DB retry logic — these are invisible in development but fatal in production.
Mithun S
