SmartChain-AI / app.py
Alireza Aminzadeh
Initial commit: SmartChain AI - Enterprise Supply Chain Optimization Platform
5656ebd
"""SmartChain AI - Streamlit Application for Hugging Face Space."""
import streamlit as st
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import plotly.graph_objects as go
import plotly.express as px
# Page configuration
st.set_page_config(
page_title="SmartChain AI",
page_icon="🚚",
layout="wide",
initial_sidebar_state="expanded",
)
# Custom CSS
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
.main {
font-family: 'Inter', sans-serif;
}
h1, h2, h3 {
font-family: 'Inter', sans-serif;
font-weight: 700;
}
.stButton > button {
background-color: #2563eb;
color: white;
border-radius: 8px;
padding: 0.5rem 1.5rem;
font-weight: 600;
border: none;
transition: all 0.3s ease;
}
.stButton > button:hover {
background-color: #1d4ed8;
box-shadow: 0 4px 6px rgba(37, 99, 235, 0.3);
}
</style>
""", unsafe_allow_html=True)
# Sidebar
st.sidebar.title("🚚 SmartChain AI")
st.sidebar.markdown("**Enterprise Supply Chain Optimization**")
page = st.sidebar.selectbox(
"Select Module",
["🏠 Home", "πŸ“ˆ Demand Forecasting", "πŸ“¦ Inventory Optimization", "🚚 Route Optimization"]
)
# Helper functions for demo data
def generate_forecast_data(days=30):
"""Generate demo forecast data."""
dates = pd.date_range(start=datetime.now(), periods=days, freq='D')
base_demand = 100
trend = np.linspace(0, 20, days)
seasonality = 30 * np.sin(np.linspace(0, 4*np.pi, days))
noise = np.random.normal(0, 10, days)
predictions = base_demand + trend + seasonality + noise
lower_bound = predictions - 20
upper_bound = predictions + 20
return pd.DataFrame({
'date': dates,
'prediction': predictions,
'lower_bound': lower_bound,
'upper_bound': upper_bound
})
def generate_inventory_data():
"""Generate demo inventory metrics."""
return {
'eoq': 450,
'reorder_point': 120,
'safety_stock': 45,
'holding_cost': 2400,
'ordering_cost': 800,
'total_cost': 3200,
'service_level': 0.95
}
def generate_route_data():
"""Generate demo route optimization data."""
np.random.seed(42)
num_locations = 8
depot = {'lat': 40.7128, 'lon': -74.0060, 'name': 'Distribution Center'}
locations = []
for i in range(num_locations):
locations.append({
'id': i+1,
'name': f'Customer {i+1}',
'lat': depot['lat'] + np.random.uniform(-0.5, 0.5),
'lon': depot['lon'] + np.random.uniform(-0.5, 0.5),
'demand': np.random.randint(10, 100)
})
return depot, locations
# Home Page
if page == "🏠 Home":
st.title("🚚 SmartChain AI")
st.subheader("Enterprise Supply Chain Optimization Platform")
st.markdown("""
Welcome to **SmartChain AI**, an AI-powered platform for optimizing end-to-end supply chain operations.
### 🎯 Key Features
- **πŸ“ˆ Demand Forecasting**: Predict future demand using advanced ML models (Prophet + XGBoost)
- **πŸ“¦ Inventory Optimization**: Optimize stock levels using EOQ, safety stock, and reorder points
- **🚚 Route Optimization**: Solve vehicle routing problems efficiently with Google OR-Tools
- **🎯 Scenario Analysis**: Perform what-if analysis for strategic planning
### πŸ› οΈ Technology Stack
- **ML/AI**: Prophet, XGBoost, OR-Tools, CVXPY
- **Backend**: FastAPI, PostgreSQL, Redis
- **Frontend**: Streamlit, Plotly
### πŸš€ Getting Started
Use the sidebar to explore different modules and see SmartChain AI in action!
---
""")
# Quick stats
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Products", "1,000", "+10%")
with col2:
st.metric("Warehouses", "5", "0%")
with col3:
st.metric("Daily Orders", "2,500", "+15%")
with col4:
st.metric("Delivery Locations", "500", "+5%")
st.markdown("---")
st.info("ℹ️ **Demo Mode**: This is a demonstration version with synthetic data. The full version includes backend API integration.")
# Demand Forecasting Page
elif page == "πŸ“ˆ Demand Forecasting":
st.title("πŸ“ˆ Demand Forecasting")
st.markdown("Generate AI-powered demand forecasts using Prophet, XGBoost, or Hybrid models.")
# Sidebar controls
st.sidebar.header("Forecast Configuration")
product_id = st.sidebar.number_input("Product ID", min_value=1, max_value=1000, value=1)
horizon_days = st.sidebar.slider("Forecast Horizon (days)", min_value=7, max_value=90, value=30)
model_type = st.sidebar.selectbox("Model Type", ["Hybrid", "Prophet", "XGBoost"])
col1, col2 = st.columns([2, 1])
with col1:
st.subheader("Generate Forecast")
if st.button("πŸš€ Generate Forecast", type="primary", use_container_width=True):
with st.spinner("Generating forecast..."):
# Generate demo data
forecast_df = generate_forecast_data(horizon_days)
st.success(f"βœ… Forecast generated for Product {product_id} using {model_type} model")
# Create forecast chart
fig = go.Figure()
# Confidence interval
fig.add_trace(go.Scatter(
x=forecast_df['date'],
y=forecast_df['upper_bound'],
fill=None,
mode='lines',
line_color='rgba(37, 99, 235, 0.2)',
showlegend=False,
))
fig.add_trace(go.Scatter(
x=forecast_df['date'],
y=forecast_df['lower_bound'],
fill='tonexty',
mode='lines',
line_color='rgba(37, 99, 235, 0.2)',
fillcolor='rgba(37, 99, 235, 0.1)',
name='95% Confidence Interval',
))
# Forecast line
fig.add_trace(go.Scatter(
x=forecast_df['date'],
y=forecast_df['prediction'],
mode='lines+markers',
name='Forecast',
line=dict(color='#2563eb', width=3),
marker=dict(size=6),
))
fig.update_layout(
title=f'Demand Forecast - Product {product_id}',
xaxis_title='Date',
yaxis_title='Demand (units)',
hovermode='x unified',
template='plotly_white',
height=500,
)
st.plotly_chart(fig, use_container_width=True)
# Forecast table
st.subheader("πŸ“‹ Forecast Data")
st.dataframe(
forecast_df.style.format({
'prediction': '{:.2f}',
'lower_bound': '{:.2f}',
'upper_bound': '{:.2f}'
}),
use_container_width=True,
height=300,
)
# Download button
csv = forecast_df.to_csv(index=False)
st.download_button(
"⬇️ Download Forecast CSV",
data=csv,
file_name=f"forecast_product_{product_id}.csv",
mime="text/csv",
)
with col2:
st.subheader("ℹ️ Model Information")
if model_type == "Hybrid":
st.info("""
**Hybrid Model**
Combines Prophet and XGBoost:
- Prophet: Captures seasonality
- XGBoost: Learns complex patterns
- Best accuracy for most cases
""")
elif model_type == "Prophet":
st.info("""
**Prophet Model**
Meta's time-series forecaster:
- Automatic seasonality detection
- Handles holidays
- Robust to missing data
""")
else:
st.info("""
**XGBoost Model**
Gradient boosting approach:
- Feature-rich predictions
- Captures non-linear patterns
- Fast training and inference
""")
st.metric("Forecast Accuracy (MAPE)", "8.5%", "-1.2%")
st.metric("RΒ² Score", "0.92", "+0.05")
# Inventory Optimization Page
elif page == "πŸ“¦ Inventory Optimization":
st.title("πŸ“¦ Inventory Optimization")
st.markdown("Optimize inventory policies using EOQ, safety stock, and reorder point calculations.")
# Sidebar controls
st.sidebar.header("Optimization Parameters")
product_id = st.sidebar.number_input("Product ID", min_value=1, max_value=1000, value=1)
service_level = st.sidebar.slider("Service Level", min_value=0.80, max_value=0.99, value=0.95, step=0.01)
holding_cost_rate = st.sidebar.number_input("Holding Cost Rate (%)", min_value=1.0, max_value=50.0, value=20.0)
ordering_cost = st.sidebar.number_input("Ordering Cost ($)", min_value=10, max_value=1000, value=100)
if st.button("🎯 Optimize Inventory Policy", type="primary", use_container_width=True):
with st.spinner("Optimizing inventory policy..."):
inventory_data = generate_inventory_data()
st.success(f"βœ… Inventory policy optimized for Product {product_id}")
# Metrics
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Economic Order Quantity (EOQ)", f"{inventory_data['eoq']} units")
st.metric("Reorder Point", f"{inventory_data['reorder_point']} units")
with col2:
st.metric("Safety Stock", f"{inventory_data['safety_stock']} units")
st.metric("Service Level", f"{inventory_data['service_level']*100:.1f}%")
with col3:
st.metric("Annual Holding Cost", f"${inventory_data['holding_cost']:,.0f}")
st.metric("Annual Ordering Cost", f"${inventory_data['ordering_cost']:,.0f}")
# Cost breakdown chart
st.subheader("πŸ“Š Cost Analysis")
fig = go.Figure(data=[
go.Bar(
x=['Holding Cost', 'Ordering Cost', 'Total Cost'],
y=[
inventory_data['holding_cost'],
inventory_data['ordering_cost'],
inventory_data['total_cost'],
],
marker_color=['#10b981', '#f59e0b', '#2563eb'],
text=[
f"${inventory_data['holding_cost']:,.0f}",
f"${inventory_data['ordering_cost']:,.0f}",
f"${inventory_data['total_cost']:,.0f}",
],
textposition='auto',
)
])
fig.update_layout(
title='Annual Inventory Costs',
yaxis_title='Cost ($)',
template='plotly_white',
height=400,
)
st.plotly_chart(fig, use_container_width=True)
# Recommendations
st.subheader("πŸ’‘ Recommendations")
st.markdown(f"""
- **Order Quantity**: Place orders of **{inventory_data['eoq']} units** each time
- **Reorder Trigger**: Reorder when inventory reaches **{inventory_data['reorder_point']} units**
- **Safety Buffer**: Maintain **{inventory_data['safety_stock']} units** as safety stock
- **Cost Savings**: Optimized policy can save up to **15-20%** on total inventory costs
""")
# Route Optimization Page
elif page == "🚚 Route Optimization":
st.title("🚚 Route Optimization")
st.markdown("Optimize delivery routes using Vehicle Routing Problem (VRP) algorithms.")
# Sidebar controls
st.sidebar.header("Route Configuration")
num_vehicles = st.sidebar.slider("Number of Vehicles", min_value=1, max_value=5, value=2)
vehicle_capacity = st.sidebar.number_input("Vehicle Capacity (kg)", min_value=100, max_value=2000, value=500)
if st.button("πŸ—ΊοΈ Optimize Routes", type="primary", use_container_width=True):
with st.spinner("Optimizing delivery routes..."):
depot, locations = generate_route_data()
st.success(f"βœ… Routes optimized for {num_vehicles} vehicles")
# Metrics
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Distance", "127.5 km")
with col2:
st.metric("Total Time", "3h 45m")
with col3:
st.metric("Cost", "$255")
with col4:
st.metric("Efficiency", "92%", "+8%")
# Map
st.subheader("πŸ—ΊοΈ Route Visualization")
# Create map figure
fig = go.Figure()
colors = px.colors.qualitative.Plotly
# Split locations into routes for visualization
locations_per_vehicle = len(locations) // num_vehicles
for v in range(num_vehicles):
start_idx = v * locations_per_vehicle
end_idx = start_idx + locations_per_vehicle if v < num_vehicles - 1 else len(locations)
route_locs = locations[start_idx:end_idx]
lats = [depot['lat']] + [loc['lat'] for loc in route_locs] + [depot['lat']]
lons = [depot['lon']] + [loc['lon'] for loc in route_locs] + [depot['lon']]
names = [depot['name']] + [loc['name'] for loc in route_locs] + [depot['name']]
fig.add_trace(go.Scattermapbox(
lat=lats,
lon=lons,
mode='lines+markers',
name=f'Vehicle {v+1}',
marker=dict(size=10, color=colors[v]),
line=dict(width=2, color=colors[v]),
text=names,
))
# Depot marker
fig.add_trace(go.Scattermapbox(
lat=[depot['lat']],
lon=[depot['lon']],
mode='markers',
name='Depot',
marker=dict(size=20, color='red', symbol='star'),
text=[depot['name']],
))
fig.update_layout(
mapbox=dict(
style='open-street-map',
center=dict(lat=depot['lat'], lon=depot['lon']),
zoom=10,
),
height=600,
showlegend=True,
)
st.plotly_chart(fig, use_container_width=True)
# Route details
st.subheader("πŸ“‹ Route Details")
for v in range(num_vehicles):
with st.expander(f"πŸš› Vehicle {v+1} Route"):
start_idx = v * locations_per_vehicle
end_idx = start_idx + locations_per_vehicle if v < num_vehicles - 1 else len(locations)
route_locs = locations[start_idx:end_idx]
route_df = pd.DataFrame(route_locs)
st.dataframe(route_df, use_container_width=True)
total_demand = sum(loc['demand'] for loc in route_locs)
st.metric("Total Demand", f"{total_demand} kg")
# Footer
st.sidebar.markdown("---")
st.sidebar.markdown("""
**Version**: 0.1.0
**Author**: Alireza Aminzadeh
**License**: Apache 2.0
[GitHub](https://github.com/syeedalireza) | [HuggingFace](https://huggingface.co/syeedalireza)
""")
st.markdown("---")
st.markdown("""
<div style='text-align: center'>
<p>Built with ❀️ using FastAPI + Streamlit + OR-Tools</p>
<p><em>SmartChain AI - Enterprise Supply Chain Optimization Platform</em></p>
</div>
""", unsafe_allow_html=True)