Spaces:
Sleeping
Sleeping
Aryanshh commited on
Commit ·
51cbaeb
1
Parent(s): eaea692
docs: Rewrite and finalize project README and fix ESG/production error feedbacks
Browse files- README.md +31 -15
- dashboard/app.js +5 -4
- netzero_nav/env.py +7 -1
README.md
CHANGED
|
@@ -7,24 +7,40 @@ sdk: docker
|
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
-
# NetZero Nav
|
| 11 |
|
| 12 |
**Autonomous RL logistics agent navigating global disruptions with a Net-Zero carbon mandate.**
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
## The Challenge
|
| 17 |
Every logistical decision involves a strategic trade-off:
|
| 18 |
-
- **Sea Freight**: Ultra-low carbon
|
| 19 |
-
- **Air Freight**: High speed, but massive CO2 emissions and 5x the cost.
|
| 20 |
-
- **Rail
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
+
# NetZero Nav
|
| 11 |
|
| 12 |
**Autonomous RL logistics agent navigating global disruptions with a Net-Zero carbon mandate.**
|
| 13 |
|
| 14 |
+
## Overview
|
| 15 |
+
**NetZero Nav** is an AI agentic simulation environment built for logistics optimization. In modern supply chains, decision-making is a multi-objective problem: balancing financial costs, delivery speed, and environmental sustainability. This project simulates a supply chain command center where an agent (or human operator) manages electronic product manufacturing while adhering strictly to "Net-Zero" carbon quotas.
|
| 16 |
+
|
| 17 |
+
This interface serves as the visual playground and telemetry dashboard representing what an AI Reinforcement Learning (RL) agent interacts with behind the scenes via a custom FastAPI `/step` loop.
|
| 18 |
|
| 19 |
## The Challenge
|
| 20 |
Every logistical decision involves a strategic trade-off:
|
| 21 |
+
- **Sea Freight ($10, 10 Days, 0.1kg CO2)**: Ultra-low carbon footprint and cheap, but extremely slow.
|
| 22 |
+
- **Air Freight ($50, 2 Days, 2.0kg CO2)**: High speed fulfillment, but massive CO2 emissions and 5x the cost.
|
| 23 |
+
- **Rail Freight ($25, 5 Days, 0.5kg CO2)**: The hybrid option for balanced regional resilience.
|
| 24 |
+
|
| 25 |
+
Operators must balance raw inventory against pending order fulfillment schedules to prevent bleeding capital, while ensuring they do not breach the 1,000kg Carbon Footprint limit.
|
| 26 |
+
|
| 27 |
+
## Core Features
|
| 28 |
+
1. **Real-time Telemetry Dashboard**: A high-fidelity, reactive command console providing live readouts on Capital Balance, Carbon Footprint, and Raw Inventory limitations.
|
| 29 |
+
2. **Dynamic Order Routing & Cart Aggregation**: The environment automatically merges overlapping delivery constraints and cleanly manages multi-batch shipments in the `Your Orders` logistics stream.
|
| 30 |
+
3. **Disruption Management ("The Suez Jam")**: Triggering real-world crises blocks optimal ocean routes (e.g., SEA mode) for 7 simulation days. The agent must rapidly recompute air and rail alternatives without bankrupting the system or violating carbon quotas.
|
| 31 |
+
4. **ESG Strategic Offsetting**: Allows excess capital to be dynamically diverted into Carbon Offset programs, driving the footprint back down toward sustainable parameters.
|
| 32 |
+
5. **Headless Python RL Engine**: Driven entirely by a custom `AtlasEcoEnv` state machine with a strict Pydantic ruleset, validating constraints prior to DOM interaction.
|
| 33 |
+
|
| 34 |
+
## Tech Stack
|
| 35 |
+
- **Simulation Engine**: Custom Python RL Simulator (`env.py`)
|
| 36 |
+
- **Backend Infrastructure**: FastAPI (Handling continuous Step/Reset states)
|
| 37 |
+
- **Frontend Command Center**: Vanilla HTML/CSS/JS (Glassmorphic Enterprise aesthetic)
|
| 38 |
+
- **Deployment**: Fully dockerized & hosted on Hugging Face Spaces
|
| 39 |
+
|
| 40 |
+
## Getting Started
|
| 41 |
+
To run locally:
|
| 42 |
+
```bash
|
| 43 |
+
pip install fastapi uvicorn pydantic
|
| 44 |
+
uvicorn netzero_nav.server:app --host 0.0.0.0 --port 7860
|
| 45 |
+
```
|
| 46 |
+
Navigate to `http://localhost:7860/` to launch the Command Console.
|
dashboard/app.js
CHANGED
|
@@ -153,7 +153,6 @@ window.execute = async function(type, event, shipment_id = null) {
|
|
| 153 |
if (type === 'order_parts') {
|
| 154 |
const part = document.getElementById('part-select').value;
|
| 155 |
const qty = parseInt(document.getElementById('qty-input').value) || 1;
|
| 156 |
-
spawnFeedback(`+${qty} ${part} ordered`, event.clientX, event.clientY);
|
| 157 |
actionObj.part_type = part;
|
| 158 |
actionObj.mode = document.getElementById('mode-select').value;
|
| 159 |
actionObj.quantity = qty;
|
|
@@ -161,11 +160,9 @@ window.execute = async function(type, event, shipment_id = null) {
|
|
| 161 |
actionObj.product = document.getElementById('product-select').value;
|
| 162 |
const qty = parseInt(document.getElementById('produce-qty-input').value) || 1;
|
| 163 |
actionObj.quantity = qty;
|
| 164 |
-
spawnFeedback(`Initiated x${qty} run`, event.clientX, event.clientY);
|
| 165 |
} else if (type === 'offset') {
|
| 166 |
const amt = parseFloat(document.getElementById('offset-qty-input').value) || 10;
|
| 167 |
actionObj.offset_amount = amt;
|
| 168 |
-
spawnFeedback(`Purchased ${amt} offsets`, event.clientX, event.clientY);
|
| 169 |
} else if (type === 'skip') {
|
| 170 |
actionObj.action_type = 'skip';
|
| 171 |
} else if (type === 'cancel') {
|
|
@@ -183,8 +180,12 @@ window.execute = async function(type, event, shipment_id = null) {
|
|
| 183 |
|
| 184 |
if (result.info && result.info.error) {
|
| 185 |
log(`ERROR: ${result.info.error}`, 'error');
|
|
|
|
| 186 |
} else {
|
| 187 |
-
if (type === '
|
|
|
|
|
|
|
|
|
|
| 188 |
else log(`SUCCESS: ${type === 'skip' ? 'Skipped Day' : 'Executed ' + type}`, 'action');
|
| 189 |
}
|
| 190 |
|
|
|
|
| 153 |
if (type === 'order_parts') {
|
| 154 |
const part = document.getElementById('part-select').value;
|
| 155 |
const qty = parseInt(document.getElementById('qty-input').value) || 1;
|
|
|
|
| 156 |
actionObj.part_type = part;
|
| 157 |
actionObj.mode = document.getElementById('mode-select').value;
|
| 158 |
actionObj.quantity = qty;
|
|
|
|
| 160 |
actionObj.product = document.getElementById('product-select').value;
|
| 161 |
const qty = parseInt(document.getElementById('produce-qty-input').value) || 1;
|
| 162 |
actionObj.quantity = qty;
|
|
|
|
| 163 |
} else if (type === 'offset') {
|
| 164 |
const amt = parseFloat(document.getElementById('offset-qty-input').value) || 10;
|
| 165 |
actionObj.offset_amount = amt;
|
|
|
|
| 166 |
} else if (type === 'skip') {
|
| 167 |
actionObj.action_type = 'skip';
|
| 168 |
} else if (type === 'cancel') {
|
|
|
|
| 180 |
|
| 181 |
if (result.info && result.info.error) {
|
| 182 |
log(`ERROR: ${result.info.error}`, 'error');
|
| 183 |
+
spawnFeedback(`❌ ${result.info.error}`, event.clientX, event.clientY);
|
| 184 |
} else {
|
| 185 |
+
if (type === 'order_parts') spawnFeedback(`+${actionObj.quantity} ordered`, event.clientX, event.clientY);
|
| 186 |
+
else if (type === 'produce') spawnFeedback(`Initiated x${actionObj.quantity} run`, event.clientX, event.clientY);
|
| 187 |
+
else if (type === 'offset') spawnFeedback(`Purchased ${actionObj.offset_amount} offsets`, event.clientX, event.clientY);
|
| 188 |
+
else if (type === 'cancel') log(`SUCCESS: Canceled Shipment`, 'action');
|
| 189 |
else log(`SUCCESS: ${type === 'skip' ? 'Skipped Day' : 'Executed ' + type}`, 'action');
|
| 190 |
}
|
| 191 |
|
netzero_nav/env.py
CHANGED
|
@@ -187,12 +187,18 @@ class AtlasEcoEnv:
|
|
| 187 |
|
| 188 |
def _handle_offset(self, action: Action, info: dict) -> float:
|
| 189 |
if not action.offset_amount: return -5.0
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
cost = action.offset_amount * 2.0
|
| 191 |
if self.cash_balance >= cost:
|
| 192 |
self.cash_balance -= cost
|
| 193 |
self.carbon_total = max(0.0, self.carbon_total - action.offset_amount)
|
| 194 |
return 5.0
|
| 195 |
-
|
|
|
|
|
|
|
| 196 |
|
| 197 |
def _handle_reroute(self, action: Action, info: dict) -> float:
|
| 198 |
return 0.0
|
|
|
|
| 187 |
|
| 188 |
def _handle_offset(self, action: Action, info: dict) -> float:
|
| 189 |
if not action.offset_amount: return -5.0
|
| 190 |
+
if self.carbon_total <= 0:
|
| 191 |
+
info["error"] = "No carbon footprint to offset"
|
| 192 |
+
return -5.0
|
| 193 |
+
|
| 194 |
cost = action.offset_amount * 2.0
|
| 195 |
if self.cash_balance >= cost:
|
| 196 |
self.cash_balance -= cost
|
| 197 |
self.carbon_total = max(0.0, self.carbon_total - action.offset_amount)
|
| 198 |
return 5.0
|
| 199 |
+
else:
|
| 200 |
+
info["error"] = "Insufficient funds for offset"
|
| 201 |
+
return -10.0
|
| 202 |
|
| 203 |
def _handle_reroute(self, action: Action, info: dict) -> float:
|
| 204 |
return 0.0
|