A production-ready supply chain risk prediction system using AI-powered intelligence processing, graph neural networks for risk propagation, and multi-objective route optimization.
This system provides real-time supply chain risk assessment and optimal route recommendations for logistics companies. It processes data from 6 different sources, uses Claude AI for semantic analysis, propagates risks through a graph neural network, and finds optimal routes using A* pathfinding.
AI-Powered Intelligence Processing - Claude API analyzes unstructured text data when available, with local heuristic fallback when it is not
Graph Neural Network - Spatial risk propagation considering geographical proximity and trade relationships
Temporal Memory - Tracks risk history for trend analysis (last 10 updates)
Multi-Objective Optimization - Balances risk, distance, time, and trade volume
REST API - Easy integration with web/mobile frontends
Integrated Scraper Runtime - The backend can use the scrapers directly in-process or call separately hosted scraper services
Automated Updates - Background worker updates graph every 30 minutes
Production-Ready - Database persistence, error handling, logging
┌─────────────────────────────────────────────────────────────┐
│ SCRAPER LAYER (6 scrapers) │
│ gscpi │ news │ political │ reporter │ trade │ weather │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SCRIPT 1: Intelligence Processor │
│ • Normalizes numerical data (GSCPI, Trade) │
│ • Claude API analyzes text (News, Political, Weather) │
│ • Applies Reporter credibility weights │
│ • Outputs: 6D risk vectors per node │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SCRIPT 2: Graph Risk Engine │
│ • Graph Attention Network (GAT) for risk propagation │
│ • Considers neighbor influence & trade relationships │
│ • Temporal memory (stores last 10 snapshots) │
│ • Outputs: Updated risk scores for all nodes │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SCRIPT 3: Route Optimizer │
│ • A* pathfinding with multi-objective cost function │
│ • Returns top-K alternative routes │
│ • Detailed risk analysis per route │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ SCRIPT 4: Flask API Server │
│ • REST API endpoints for route analysis │
│ • Background worker (updates every 30 min) │
│ • Database for persistence │
└─────────────────────────────────────────────────────────────┘
- Python 3.8+
- pip (Python package manager)
- SQLite3 (comes with Python)
- SERPAPI_API_KEY - Recommended for live news/search enrichment
- OPENWEATHERMAP_API_KEY - Optional richer weather data
- ANTHROPIC_API_KEY - Optional Claude enrichment for semantic scoring
The backend will still start without ANTHROPIC_API_KEY; it falls back to deterministic local scoring until you add the key.
- Minimum: 4GB RAM, 2 CPU cores
- Recommended: 8GB RAM, 4 CPU cores
- Storage: 1GB free space
Clone the repo to your machine:
git clone <your-repo-url> lamda
cd lamda# Create virtual environment (recommended)
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install backend dependencies
pip install -r requirements.txt# Copy the sample env file
cp .env.example .env
# Edit the values you have available
# SERPAPI_API_KEY=...
# OPENWEATHERMAP_API_KEY=...
# ANTHROPIC_API_KEY=... # optionalpython api_server.pyOn first start the backend now:
- builds the graph structure automatically from the supported nodes
- fetches live data from the integrated scraper layer
- generates
graph_nodes.json,graph_edges.json,graph_state.json,risk_vectors_output.json, andsupply_chain_graph.db - starts the API on
http://localhost:5001
If you want the API server to call separately running scraper services instead of using the scrapers directly in-process, set:
SCRAPER_HTTP_ENABLED=truepython api_server.pyThe server will:
- Initialize all components
- Build graph files automatically if they do not exist yet
- Run the initial graph update
- Start the background worker (updates every 30 min)
- Start Flask API on http://localhost:5001
To use a different port:
PORT=8080 python api_server.py- Install system packages:
sudo apt update
sudo apt install -y python3 python3-venv python3-pip git- Clone the repo onto the laptop:
sudo mkdir -p /opt/lamda
sudo chown "$USER":"$USER" /opt/lamda
git clone <your-repo-url> /opt/lamda
cd /opt/lamda
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
cp .env.example .env- Edit
/opt/lamda/.envand set the keys you have. For the single-service setup, keepSCRAPER_HTTP_ENABLED=false. - Copy the example systemd unit:
sudo cp deploy/lamda-api.service.example /etc/systemd/system/lamda-api.service
sudo systemctl daemon-reload
sudo systemctl enable --now lamda-api- Monitor the service:
sudo systemctl status lamda-api
journalctl -u lamda-api -fIf you want the app reachable from other machines, open port 5001 in the firewall or place Nginx in front of it as a reverse proxy.
http://localhost:5001/api
(Use the same host/port if you set PORT when starting the server.)
Find optimal routes between two locations.
Endpoint: POST /api/analyze_route
Request Body:
{
"source": "Hong_Kong",
"destination": "Los_Angeles",
"num_routes": 3
}Response:
{
"source": "Hong_Kong",
"destination": "Los_Angeles",
"num_routes": 3,
"routes": [
{
"rank": 1,
"path": ["Hong_Kong", "Tokyo", "Los_Angeles"],
"total_distance_km": 11019.45,
"avg_risk": 0.43,
"estimated_time_hours": 297.82,
"estimated_time_days": 12.4,
"route_score": 5234.21,
"analysis": {
"route_summary": {...},
"node_risks": [...],
"high_risk_segments": [...],
"bottlenecks": [...]
}
}
],
"timestamp": "2024-01-01T12:00:00"
}cURL Example:
curl -X POST http://localhost:5001/api/analyze_route \
-H "Content-Type: application/json" \
-d '{"source": "Hong_Kong", "destination": "Los_Angeles", "num_routes": 3}'Get current risk status of a specific node.
Endpoint: GET /api/node_status/<node_id>
Response:
{
"node_id": "Hong_Kong",
"latitude": 22.3193,
"longitude": 114.1694,
"current_risk": {
"gscpi_risk": 0.45,
"news_risk": 0.67,
"political_risk": 0.54,
"trade_risk": 0.23,
"weather_risk": 0.78,
"reporter_confidence": 0.85,
"overall_risk": 0.53
},
"risk_level": "MODERATE",
"last_updated": "2024-01-01T12:00:00"
}cURL Example:
curl http://localhost:5001/api/node_status/Hong_KongGet current state of entire graph.
Endpoint: GET /api/graph_snapshot
Response:
{
"timestamp": "2024-01-01T12:00:00",
"num_nodes": 100,
"num_edges": 250,
"nodes": [...],
"edges": [...],
"statistics": {
"avg_risk": 0.45,
"high_risk_nodes": 12,
"moderate_risk_nodes": 43,
"low_risk_nodes": 45
}
}Get historical risk trends for a node.
Endpoint: GET /api/historical_trends/<node_id>?limit=10
Response:
{
"node_id": "Hong_Kong",
"num_records": 10,
"history": [
{
"timestamp": "2024-01-01T12:00:00",
"gscpi_risk": 0.45,
"news_risk": 0.67,
"political_risk": 0.54,
"trade_risk": 0.23,
"weather_risk": 0.78,
"overall_risk": 0.53
}
]
}Get list of all nodes in the graph.
Endpoint: GET /api/available_nodes
Response:
{
"num_nodes": 100,
"nodes": [
{
"node_id": "Hong_Kong",
"latitude": 22.3193,
"longitude": 114.1694,
"overall_risk": 0.53
}
]
}Manually trigger graph update (admin endpoint).
Endpoint: POST /api/update_graph
Response:
{
"status": "success",
"message": "Graph update completed",
"timestamp": "2024-01-01T12:00:00"
}Check system health.
Endpoint: GET /api/health
Response:
{
"status": "healthy",
"timestamp": "2024-01-01T12:00:00",
"services": {
"intelligence_processor": true,
"graph_engine": true,
"route_optimizer": true
}
}cURL Example:
curl http://localhost:5001/api/healthWith the server running (python api_server.py), use these commands in a new terminal. Replace localhost:5001 with your host if different (e.g. after setting PORT=8080).
1. Root / API info (no 404 in browser):
curl http://localhost:5001/2. Health check:
curl http://localhost:5001/api/health3. List available nodes:
curl http://localhost:5001/api/available_nodes4. Node status (use a node_id from step 3, e.g. Hong_Kong):
curl http://localhost:5001/api/node_status/Hong_Kong5. Graph snapshot:
curl http://localhost:5001/api/graph_snapshot6. Analyze route (POST):
curl -X POST http://localhost:5001/api/analyze_route \
-H "Content-Type: application/json" \
-d '{"source": "Hong_Kong", "destination": "Los_Angeles", "num_routes": 3}'7. Historical trends for a node:
curl "http://localhost:5001/api/historical_trends/Hong_Kong?limit=5"8. Trigger manual graph update (admin):
curl -X POST http://localhost:5001/api/update_graphEdit configuration in api_server.py:
CONFIG = {
"UPDATE_INTERVAL_MINUTES": 30, # How often to update graph
"GRAPH_STATE_FILE": "graph_state.json",
"RISK_VECTORS_FILE": "risk_vectors_output.json",
"DB_PATH": "supply_chain_graph.db",
"GRAPH_NODES_FILE": "graph_nodes.json",
"GRAPH_EDGES_FILE": "graph_edges.json"
}Replace the fetch_scraper_data() function in api_server.py:
def fetch_scraper_data() -> List[ScraperData]:
"""Fetch data from your actual scrapers."""
# Call your scraper APIs
gscpi_response = requests.get("http://your-gscpi-scraper/api/latest")
news_response = requests.get("http://your-news-scraper/api/latest")
# ... etc
# Parse and return ScraperData objects
nodes_data = []
for node_id in your_node_list:
nodes_data.append(ScraperData(
node_id=node_id,
gscpi=gscpi_response.json()[node_id],
trade=trade_response.json()[node_id],
news=news_response.json()[node_id],
political=political_response.json()[node_id],
weather=weather_response.json()[node_id],
reporter_credibility=reporter_response.json()[node_id]
))
return nodes_dataSQLite database (supply_chain_graph.db) contains:
node_id TEXT PRIMARY KEY,
latitude REAL,
longitude REAL,
current_risk REAL,
last_updated TEXTedge_id INTEGER PRIMARY KEY,
source TEXT,
target TEXT,
distance_km REAL,
trade_volume REALid INTEGER PRIMARY KEY,
node_id TEXT,
timestamp TEXT,
gscpi_risk REAL,
news_risk REAL,
political_risk REAL,
trade_risk REAL,
weather_risk REAL,
reporter_confidence REAL,
overall_risk REAL// React example
const analyzeRoute = async (source, destination) => {
const response = await fetch('http://localhost:5001/api/analyze_route', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
source: source,
destination: destination,
num_routes: 3
})
});
const data = await response.json();
// Display routes
data.routes.forEach(route => {
console.log(`Route ${route.rank}: ${route.path.join(' → ')}`);
console.log(`Risk: ${route.avg_risk}, Distance: ${route.total_distance_km} km`);
});
};# Install as systemd service
sudo nano /etc/systemd/system/supply-chain-api.service[Unit]
Description=Supply Chain Risk Prediction API
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/supply-chain-risk-system
Environment="ANTHROPIC_API_KEY=your-key"
ExecStart=/home/ubuntu/supply-chain-risk-system/venv/bin/python api_server.py
Restart=always
[Install]
WantedBy=multi-user.targetsudo systemctl enable supply-chain-api
sudo systemctl start supply-chain-apiCreate Dockerfile:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY *.py .
COPY *.json .
ENV ANTHROPIC_API_KEY=""
CMD ["python", "api_server.py"]Build and run:
docker build -t supply-chain-api .
docker run -d -p 5001:5001 -e ANTHROPIC_API_KEY=your-key supply-chain-apiSee DEPLOYMENT.md for detailed cloud deployment instructions.
- 100 nodes: ~2 seconds per update
- 1000 nodes: ~20 seconds per update
- 10,000 nodes: Consider distributed architecture
For large-scale deployment:
- Use Redis for caching
- Deploy multiple API instances with load balancer
- Use Neo4j instead of SQLite
- Move GNN inference to GPU servers
Solution: Set the environment variable before running:
export ANTHROPIC_API_KEY='your-key'Solution: Install PyTorch Geometric:
pip install torch-geometric
pip install torch-scatter torch-sparse -f https://data.pyg.org/whl/torch-2.0.0+cpu.htmlSolution: Only one process should write to SQLite at a time. Use PostgreSQL for production.
Solution: The system batches all nodes in one API call. If you have >100 nodes, split into multiple batches.
MIT License - Free for commercial use
For enterprise deployment support, contact your development team.
- GPU acceleration for GNN
- Real-time alerts system
- Dashboard UI
- Mobile app integration
- Multi-modal transport (air, rail, sea)
- Carbon footprint optimization