An AI-driven energy supply chain resilience platform that transforms real-time geopolitical signals into probability-weighted disruption risk assessments, scenario simulations, optimal procurement re-allocations, and strategic reserve drawdown decisions — scoped specifically to India's crude oil import dependency.
Energy Supply Chain Resilience Platform is a full-stack, 5-component system that provides end-to-end decision support for India's crude oil supply chain under geopolitical disruption. It ingests multi-source signals (GDELT, NewsAPI, sanctions data, crude price feeds), processes them through a dual-LLM causal reasoning pipeline, simulates disruption scenarios with Monte Carlo uncertainty bands, re-optimizes procurement allocations using CP-SAT constraint programming, and computes optimal Strategic Petroleum Reserve (SPR) drawdown decisions using a Teisberg-derived cost optimizer.
5 Integrated Components:
| # | Component | Purpose |
|---|---|---|
| 1 | Risk Intelligence Agent | Ingest signals → extract events → causal reasoning → probability-weighted risk assessments |
| 2 | Disruption Scenario Modeller | Template-driven cascade simulations with Monte Carlo p10/p50/p90 bands |
| 3 | Adaptive Procurement Orchestrator | CP-SAT optimizer for cost-minimized crude allocation under disrupted capacity |
| 4 | Strategic Reserve Optimizer | Teisberg-derived SPR drawdown with scenario-tree probability bridge |
| 5 | Operations Dashboard | Real-time visualization, explainability drill-down, and interactive map |
The platform is grounded in India's actual crude oil import structure — real corridors (Strait of Hormuz, Red Sea/Bab-el-Mandeb, Cape of Good Hope, Suez Canal), real suppliers (Iraq, Saudi Arabia, Russia, UAE, USA, Nigeria), real refineries (Reliance Jamnagar, Nayara Vadinar, BPCL Kochi, IOCL Panipat, HPCL Vizag), and real SPR sites (Visakhapatnam, Mangalore, Padur).
Submission: ET AI Hackathon 2026 — Problem Statement 2: AI-Driven Energy Supply Chain Resilience for Import-Dependent Economies
| Criterion (Weight) | How We Address It | README Section |
|---|---|---|
| Innovation (25%) | Dual-LLM causal reasoning pipeline (not sentiment classification), Teisberg-derived reserve optimizer with scenario-tree probability bridge, CP-SAT procurement solver with piecewise demand penalties, Monte Carlo uncertainty bands on all scenario outputs | Key Features, Design Principles |
| Business Impact (25%) | End-to-end signal→recommendation in <35s (closes McKinsey's 47-day stabilization gap), executable procurement re-allocations with demand fulfillment % and cost scoring, SPR drawdown recommendations with physical constraint enforcement | Problem & Solution, Evaluation Focus |
| Technical Excellence (20%) | Schema-validated LLM outputs via instructor+Pydantic v2, Neo4j knowledge graph with India-specific entity relationships, Qdrant vector store for policy corpus grounding, Langfuse end-to-end LLM observability, immutable snapshot architecture (branch, never mutate) |
Technology Stack, System Architecture |
| Scalability (15%) | Docker Compose 4-service orchestration, stateless API design, unlimited snapshot history via append-only tables, extensible knowledge graph (24 nodes → national scale) | Scalability |
| User Experience (15%) | 5-page operations dashboard with keep-alive state preservation, interactive Leaflet map with live risk overlays across 4 corridors, full explainability drill-down (raw signal → extracted event → reasoning → scenarios), one-click scenario simulation with Monte Carlo visualization | Key Features — Dashboard |
Context & Problem: India sources approximately 88% of its crude oil from imports, with 40–45% of that volume transiting through the Strait of Hormuz — a structural vulnerability that geopolitical events repeatedly stress-test. The 2025 US-Iran standoff sent Brent crude up over 8% in a single session and forced Indian refiners onto spot markets at steep premiums; through early 2026, renewed US sanctions pressure on Iranian oil exports, Persian Gulf maritime incidents, and escalating Houthi attacks on Red Sea shipping lanes have kept the supply threat live. India's Strategic Petroleum Reserves provide roughly 9.5 days of national consumption cover — a buffer that would be exhausted quickly in any sustained disruption. A McKinsey analysis of past energy supply shocks found that economies without automated rerouting and demand-management capability took an average of 47 days longer to stabilise supply than those with integrated response intelligence. Today, analysts manually monitor news feeds, run ad-hoc spreadsheet models, and make reactive procurement decisions with incomplete information. Traditional supply chain planning tools were designed for predictable environments — they have no ability to model geopolitical scenario impacts in real time, dynamically evaluate alternative procurement corridors, or orchestrate coordinated response across refiners, logistics providers, and strategic reserves. That intelligence layer is what this platform builds.
Goal of the System: Provide an integrated, AI-powered decision support platform that:
- Continuously ingests and reasons about geopolitical signals with full causal traceability
- Simulates "what-if" disruption scenarios with transparent, formula-based cascade models
- Automatically re-optimizes procurement allocations under disrupted conditions
- Recommends optimal strategic reserve drawdown with stochastic cost minimization
Step 1 — Signal Ingestion & Risk Assessment (Component 1)
- Trigger: Geopolitical event occurs (sanctions announcement, military action, price shock)
- Action: Multi-source ingestion (GDELT, NewsAPI, sanctions snapshots, price feeds) → LLM-powered event extraction → causal reasoning chain
- System Response: Probability-weighted disruption risk per corridor with full explainability trace (raw signal → extracted event → reasoning → scenarios)
Step 2 — Scenario Simulation (Component 2)
- Trigger: Analyst selects a disruption template (contained, partial disruption, or full closure)
- Action: Deterministic cascade formula computes refinery run-rate impact, price delta, and SPR drawdown — Monte Carlo wrapper adds p10/p50/p90 uncertainty bands
- System Response: Side-by-side scenario comparison with explicit, inspectable assumptions
Step 3 — Procurement Re-Optimization (Component 3)
- Trigger: Scenario reveals capacity reduction on a corridor
- Action: CP-SAT optimizer re-allocates crude flows across suppliers, corridors, and refineries — minimizing cost while respecting disrupted capacity constraints
- System Response: Optimal allocation plan with demand fulfillment %, cost score, and per-refinery breakdown
Step 4 — Reserve Drawdown Decision (Component 4)
- Trigger: Procurement solve reveals unmet demand (shortfall)
- Action: Teisberg-derived optimizer balances immediate shortfall coverage against future stockout risk using scenario-tree probabilities
- System Response: Optimal drawdown percentage, barrels released, days of cover remaining, and physical constraint binding status
An analyst monitoring the Strait of Hormuz sees a new Iranian sanctions signal ingested at 65% disruption probability. They run a "full_closure" scenario simulation showing a 48.8% capacity reduction, $24.16/bbl price spike, and 2.55 SPR days remaining. The procurement solver re-allocates 1.2M bpd away from Hormuz-dependent suppliers toward Russia and Nigeria via Cape of Good Hope, achieving 94% demand fulfillment at $11.28/bbl average cost. The reserve optimizer recommends a 3.2% SPR drawdown (1.22M barrels) to cover the remaining shortfall while preserving 8.1 days of strategic cover.
The five evaluation metrics specified for Problem Statement 2, mapped to our system's demonstrated capabilities:
| Evaluation Metric | Our Evidence | Source |
|---|---|---|
| Disruption signal detection lead time and accuracy | Full pipeline (ingest → extract → reason) completes in <30s; dual-LLM chain with instructor + Pydantic v2 ensures schema-validated extraction accuracy; Langfuse traces provide per-signal audit trail |
Performance Specs |
| Quality and executability of procurement alternatives | CP-SAT optimizer produces allocation[supplier][corridor][refinery] with demand_fulfillment_pct, cost_score, and weighted_score; piecewise demand penalties guarantee a complete, gracefully-degraded plan is always returned — never a bare "no solution" failure |
C3 Features |
| Scenario model fidelity (assumptions explicit and testable) | Every cascade parameter (refinery_runrate_sensitivity, price_passthrough_rate, spr_drawdown_rate) is named, sourced (PPAC FY24, MSCI benchmarks), and exposed in assumptions_json; Monte Carlo adds p10/p50/p90 uncertainty bands with triangular distribution over ±15% perturbation |
C2 Features |
| Geospatial evidence depth | Leaflet map with 4 corridor routes, 6 supplier terminals, 5 refineries, 3 SPR sites, color-coded risk overlays with live disruption probability labels; Neo4j knowledge graph encodes Supplier→Corridor→Port→Refinery→SPR entity relationships with real PPAC import-share data |
C5 Features |
| End-to-end response time from signal to recommendation | Signal ingestion <30s + scenario simulation <200ms + procurement solve <2s + reserve optimization <50ms = under 35 seconds from raw signal to actionable, executable recommendation | Performance Specs |
| # | Deliverable | Status |
|---|---|---|
| 1 | Working Prototype | Run locally via docker compose up + npm run dev — see Quick Start |
| 2 | Architecture Diagram | System Architecture (in this README) |
| 3 | Presentation Deck | Included as et.pptx in the repository root |
| 4 | Demo Video | [ADD LINK] |
| Component | Technology | Purpose |
|---|---|---|
| Frontend | React 19 + Vite 8 + TypeScript | Interactive operations dashboard |
| Mapping | Leaflet + React-Leaflet | Geospatial corridor & infrastructure visualization |
| Charts | Recharts | Monte Carlo bands, scenario comparison, allocation charts |
| Icons | Lucide React | Consistent iconography across all views |
| Backend | Python FastAPI | REST API, pipeline orchestration, all 4 backend components |
| LLM | Google Gemini 2.5 Flash | Event extraction (Call #1) + causal reasoning (Call #2) |
| Structured Output | instructor + Pydantic v2 |
Schema-validated LLM responses, no manual JSON parsing |
| Knowledge Graph | Neo4j Community Edition | India-specific supply chain entity graph |
| Vector Store | Qdrant | Semantic retrieval for policy corpus grounding |
| Relational DB | PostgreSQL 16 | Signals, events, assessments, scenarios, solves, decisions |
| Constraint Solver | Google OR-Tools CP-SAT | Procurement allocation optimization |
| Reserve Optimizer | Grid search (Teisberg model) | SPR drawdown cost minimization |
| Observability | Langfuse Cloud | End-to-end LLM trace observability |
| Containerization | Docker Compose | 4-service orchestration (Neo4j, Qdrant, Postgres, API) |
| Component | Backend Module | Dashboard Page | Core Algorithm |
|---|---|---|---|
| C1 — Risk Intelligence | ingestion/, extraction/, reasoning/ |
Risk Map, Explainability | Dual-LLM causal chain |
| C2 — Scenario Modeller | scenarios/ |
Scenario Explorer | Cascade formula + Monte Carlo |
| C3 — Procurement | procurement/ |
Procurement Allocation | CP-SAT flow optimization |
| C4 — Reserve | reserve/ |
Reserve Optimizer | Teisberg expected-cost grid search |
| C5 — Dashboard | — | All 5 pages | React + Leaflet + Recharts |
┌──────────────────────────────────────────────────────────────────────────────────┐
│ COMPONENT 1: Risk Intelligence Agent │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
│ │ GDELT │ │ NewsAPI │ │ Price │ │Sanctions │ Multi-Source Ingestion │
│ │ API │ │ / RSS │ │ Feed │ │ Snapshot │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬─────┘ │
│ └────────────┼───────────┼────────────┘ │
│ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ LLM Call #1 │ │ Context Assembly │ │
│ │ Event Extraction │ │ Neo4j + Qdrant │ │
│ │ (Gemini 2.5) │ │ + Extracted Event │ │
│ └────────┬─────────┘ └────────┬──────────┘ │
│ └──────────┬──────────────┘ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ LLM Call #2 │──→ Langfuse Traces │
│ │ Causal Reasoning │ │
│ │ (Gemini 2.5) │ │
│ └────────┬─────────┘ │
│ ▼ │
│ Risk Assessments (per corridor) │
│ • disruption_probability │
│ • capacity_reduction_pct │
│ • 2-4 named scenarios │
└──────────────────────────┬───────────────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────────────┐
│ COMPONENT 2: Disruption Scenario Modeller │
│ │
│ Template Selection → Cascade Formula → Monte Carlo (2000 iterations) │
│ │
│ Outputs per scenario run: │
│ • Refinery run-rate impact (%) — p10 / p50 / p90 │
│ • Price delta (USD/bbl) — p10 / p50 / p90 │
│ • SPR days remaining — p10 / p50 / p90 │
│ • Optional LLM narrative │
└──────────────────────────┬───────────────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────────────┐
│ COMPONENT 3: Adaptive Procurement Orchestrator │
│ │
│ CP-SAT Optimizer: minimize(cost + shortfall_penalty + stability_penalty) │
│ │
│ Decision variables: allocation[supplier][corridor][refinery] = quantity_bpd │
│ Subject to: disrupted corridor capacity, refinery demand, grade compatibility │
│ │
│ Outputs: optimal allocation, demand_fulfillment_pct, cost_score, solve_score │
└──────────────────────────┬───────────────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────────────────────┐
│ COMPONENT 4: Strategic Reserve Optimization Agent │
│ │
│ Teisberg Cost Model: min[ shortfall_cost(d) + expected_stockout_cost(d) ] │
│ │
│ Grid search at 0.5% increments over scenario-tree probability branches │
│ Physical constraint: max 150,000 bpd extraction rate │
│ │
│ Outputs: optimal_drawdown_pct, drawdown_bbl, reserve_after_drawdown, │
│ cost_breakdown, scenario_branches with drain_fractions │
└──────────────────────────────────────────────────────────────────────────────────┘
(Supplier) ──[EXPORTS_VIA]──→ (Corridor) ──[ROUTES_TO]──→ (Port) ──[SUPPLIES]──→ (Refinery) ──[FEEDS_RESERVE]──→ (SPRSite)
Seeded entities:
- Corridors (4): Strait of Hormuz, Red Sea/Bab-el-Mandeb, Cape of Good Hope, Suez Canal
- Suppliers (6): Iraq (22.5%), Saudi Arabia (17.8%), UAE (7.2%), Russia (35%), USA (5.5%), Nigeria (3.8%)
- Ports (6): Vadinar, Sikka, Paradip, JNPT Mumbai, Chennai, Kochi
- Refineries (5): Reliance Jamnagar (1.24M bpd), Nayara Vadinar (400K), BPCL Kochi (310K), IOCL Panipat (300K), HPCL Vizag (166K)
- SPR Sites (3): Visakhapatnam (10M bbl), Mangalore (11M bbl), Padur (17M bbl) — Total: 38M bbl, ~9.5 days cover
- GDELT API: Real-time geopolitical event monitoring with relevance filtering
- NewsAPI + RSS: Energy-sector news aggregation from curated feeds
- Price Data: Brent crude spot price via Alpha Vantage
- Sanctions Snapshots: OFAC-style sanctions event loader for embargo/sanctions scenarios
- LLM Call #1 (Event Extraction): Gemini 2.5 Flash +
instructorextracts structured event objects (actor, event type, severity, confidence, affected suppliers) from raw signals - LLM Call #2 (Causal Reasoning): Assembles context from Neo4j knowledge graph + Qdrant policy corpus + extracted event → produces probability-weighted corridor risk assessment with 2–4 named scenarios
- Full Explainability: Every output traceable from raw source → extracted event → reasoning chain → final assessment
- All entities, prompts, and corpora scoped to India's actual import structure
- Policy corpus includes synthetic-representative documents mirroring OFAC notices, PPAC statistics, and IEA supply analyses
- Knowledge graph encodes real supplier–corridor–port–refinery–SPR relationships
- No black-box ML — every number traceable to an explicit formula with named assumptions
- 3 downstream metrics: refinery run-rate impact (%), price delta (USD/bbl), SPR days remaining
- 12 seed templates: 3 severity levels (contained, partial_disruption, full_closure) × 4 corridors
- Assumption parameters: sourced from PPAC FY24 data, MSCI corridor benchmarks, and documented illustrative estimates
- 2000 iterations per run (~50ms) with triangular distribution
- ±15% perturbation on assumption parameters
- p10/p50/p90 output for each metric (optimistic/central/pessimistic)
- Immutable snapshot architecture: every run creates a new row — rows are never updated (branch, never mutate)
- Gemini-generated plain-English summary of scenario results
- Key driver identification and caveat disclosure
- Decision variables:
allocation[supplier][corridor][refinery] = quantity_bpd - Objective: minimize total cost + piecewise demand shortfall penalties + stability deviation
- Piecewise demand: Tier 1 (0–30% shortfall, moderate penalty) + Tier 2 (>30%, 5× escalation) + miss penalty (extreme)
- Always returns a plan: gracefully-degraded allocation, never a bare "no solution" failure
- When
base_plan_solve_idprovided, solver penalizes large deviations from prior allocation - Supports meaningful scenario-to-scenario comparison with minimal, targeted changes
- demand_fulfillment_pct: volume-weighted supply coverage
- cost_score: average delivered cost vs. worst-case unit cost
- miss_penalty_ratio: fraction of refineries receiving zero supply
- weighted_score: 50% fulfillment + 30% cost + 20% miss avoidance
- Cost function:
total_cost(d) = immediate_shortfall_cost + expected_stockout_cost - Quadratic stockout penalty: creates convex cost curve with interior optimum — the key advantage over naive threshold rules
- Grid search: 0.5% increments, ~200 evaluations, sub-millisecond solve time
- Component 1's LLM-generated scenarios matched to Component 2's fixed severity templates via severity-range matching (not name-string matching)
- Stochastic scenario-tree with probability-weighted branches feeds the expected stockout cost
- Max SPR extraction rate: 150,000 bpd (US SPR benchmark × 60% rock-cavern discount)
- Reserve state: stateless by default (assumes full 38M bbl), explicit override supported for chained depletion analysis
- Leaflet-based geospatial view with corridor routes, chokepoint markers, supplier terminals, refineries, and SPR sites
- Real-time risk overlay: color-coded corridors (green/amber/red) with disruption probability labels
- Corridor cards: live disruption %, capacity cut %, baseline capacity, supplier badges
- Run disruption simulations with template selection (contained, partial_disruption, full_closure)
- View point estimates and Monte Carlo p10/p50/p90 bands
- Full run history with side-by-side comparison
- Optional narrative generation toggle
- Select any scenario run → solve optimal procurement allocation
- Per-refinery demand fulfillment breakdown with cost metrics
- Solve scoring with transparent formula display
- SPR status display (total capacity, fill level, extraction rates)
- Optimal drawdown recommendation with cost breakdown
- Scenario-tree probability branches visualization
- 4-step pipeline view: Raw Signal → Extracted Event → Causal Reasoning → Risk Scenarios
- Full causal chain for each corridor with Langfuse trace ID
- Expandable raw signal content with source attribution
- Docker & Docker Compose
- Node.js 18+ and npm
- Google AI Studio API key (for Gemini LLM)
- Langfuse Cloud account (free tier: https://cloud.langfuse.com)
- (Optional) NewsAPI key, Alpha Vantage key
git clone https://github.com/joshuakarthik2005/et.git
cd etcd risk-agent
cp .env.example .env
# Edit .env with your API keys (GOOGLE_API_KEY, LANGFUSE keys)docker compose up -dThis starts 4 services:
- Neo4j — Knowledge graph (ports 7474, 7687)
- Qdrant — Vector store (port 6333)
- PostgreSQL — Relational storage (port 5432)
- Risk Agent API — FastAPI application (port 8000)
docker compose exec risk-agent python -m seed_data.load_graph
docker compose exec risk-agent python -m seed_data.seed_vectorscd ../dashboard
npm install
npm run dev# Backend health check
curl http://localhost:8000/health
# View corridor risks
curl http://localhost:8000/risk/corridors
# Open dashboard
open http://localhost:3000cd risk-agent
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000cd dashboard
npm install
npm run devcd risk-agent
pytest tests/ -v- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
| Component | Endpoint | Method | Purpose |
|---|---|---|---|
| C1 | /ingest/run |
POST | Trigger multi-source signal ingestion |
| C1 | /process/{signal_id} |
POST | Run extraction + reasoning pipeline for a signal |
| C1 | /risk/corridors |
GET | Current risk state for all corridors |
| C1 | /risk/corridors/{id}/trace |
GET | Full explainability trace (raw → event → reasoning → scenarios) |
| C2 | /scenarios/run |
POST | Run a disruption scenario simulation |
| C2 | /scenarios/runs/{corridor_id} |
GET | Historical scenario runs for a corridor |
| C2 | /scenarios/runs/{corridor_id}/compare |
GET | Side-by-side diff of scenario runs |
| C3 | /procurement/solve |
POST | Run CP-SAT procurement optimizer |
| C3 | /procurement/solves/{scenario_run_id} |
GET | Historical solves for a scenario |
| C3 | /procurement/solves/compare |
GET | Side-by-side diff of procurement solves |
| C4 | /reserve/optimize |
POST | Run SPR drawdown optimizer |
| C4 | /reserve/spr-status |
GET | Current SPR fill levels and constraints |
| C4 | /reserve/decisions/latest/{corridor_id} |
GET | Most recent reserve decision |
| — | /health |
GET | Service health check (Neo4j, Qdrant, Postgres) |
et/
├── risk-agent/ # Backend — FastAPI (Components 1–4)
│ ├── app/
│ │ ├── main.py # FastAPI application (all endpoints)
│ │ ├── config.py # Environment settings (Pydantic)
│ │ ├── pipeline.py # Pipeline orchestrator (C1)
│ │ ├── models/
│ │ │ └── schemas.py # Pydantic v2 schemas (C1)
│ │ ├── ingestion/ # C1 — Multi-source signal ingestion
│ │ │ ├── gdelt_client.py # GDELT API connector
│ │ │ ├── news_client.py # NewsAPI + RSS connector
│ │ │ ├── price_client.py # Brent crude price connector
│ │ │ └── sanctions_client.py # Sanctions snapshot loader
│ │ ├── extraction/
│ │ │ └── extractor.py # LLM Call #1 (event extraction)
│ │ ├── reasoning/
│ │ │ └── reasoner.py # LLM Call #2 (causal reasoning)
│ │ ├── graph/
│ │ │ └── queries.py # Neo4j client + queries
│ │ ├── vectorstore/
│ │ │ └── retrieve.py # Qdrant semantic retrieval
│ │ ├── storage/
│ │ │ ├── models.py # SQLAlchemy ORM models (C1)
│ │ │ └── repository.py # Postgres CRUD (C1)
│ │ ├── scenarios/ # C2 — Disruption Scenario Modeller
│ │ │ ├── cascade.py # Deterministic cascade formula
│ │ │ ├── monte_carlo.py # Monte Carlo uncertainty wrapper
│ │ │ ├── narrative.py # LLM narrative generation
│ │ │ ├── models.py # SQLAlchemy ORM (scenario tables)
│ │ │ ├── repository.py # Postgres CRUD (scenarios)
│ │ │ └── schemas.py # Pydantic request/response
│ │ ├── procurement/ # C3 — Procurement Orchestrator
│ │ │ ├── solver.py # CP-SAT optimizer
│ │ │ ├── data.py # Cost parameters & supplier data
│ │ │ ├── models.py # SQLAlchemy ORM (procurement)
│ │ │ ├── repository.py # Postgres CRUD (solves)
│ │ │ └── schemas.py # Pydantic request/response
│ │ ├── reserve/ # C4 — Reserve Optimizer
│ │ │ ├── optimizer.py # Teisberg grid-search optimizer
│ │ │ ├── data.py # SPR parameters & physical constraints
│ │ │ ├── narrative.py # LLM narrative for decisions
│ │ │ ├── models.py # SQLAlchemy ORM (decisions)
│ │ │ ├── repository.py # Postgres CRUD (decisions)
│ │ │ └── schemas.py # Pydantic request/response
│ │ └── tracing/
│ │ └── tracer.py # Langfuse wrapper
│ ├── seed_data/
│ │ ├── corridors.json # 4 shipping corridors
│ │ ├── suppliers.json # 6 crude oil suppliers
│ │ ├── refineries.json # 5 Indian refineries
│ │ ├── ports.json # 6 Indian ports
│ │ ├── spr_sites.json # 3 SPR sites (38M bbl total)
│ │ ├── sanctions_events.json # Sanctions event data
│ │ ├── grade_compatibility.json # Refinery-supplier compatibility matrix
│ │ ├── schema.sql # PostgreSQL init schema
│ │ ├── scenario_schema.sql # Scenario DDL
│ │ ├── load_graph.py # Neo4j seed script
│ │ ├── seed_vectors.py # Qdrant seed script
│ │ └── policy_corpus/
│ │ └── documents.json # Synthetic-representative policy corpus
│ ├── tests/
│ │ ├── test_scenarios.py # Scenario modeller tests
│ │ ├── test_procurement.py # Procurement solver tests
│ │ └── test_reserve.py # Reserve optimizer tests
│ ├── docker-compose.yml # 4-service orchestration
│ ├── Dockerfile # API container
│ ├── requirements.txt # Python dependencies
│ └── .env.example # Environment template
│
├── dashboard/ # Frontend — React + Vite (Component 5)
│ ├── src/
│ │ ├── App.tsx # Router and page registry
│ │ ├── main.tsx # React entry point
│ │ ├── index.css # Design system (vanilla CSS)
│ │ ├── api/
│ │ │ └── client.ts # API client (all backend endpoints)
│ │ ├── context/
│ │ │ └── CorridorContext.tsx # Global corridor state provider
│ │ ├── components/
│ │ │ ├── Layout.tsx # App shell (header, nav, footer)
│ │ │ └── ui.tsx # Shared UI components
│ │ ├── pages/
│ │ │ ├── CorridorMap.tsx # Risk Map with Leaflet
│ │ │ ├── ScenarioExplorer.tsx # Scenario simulation & history
│ │ │ ├── ProcurementView.tsx # Procurement allocation solver
│ │ │ ├── ReserveView.tsx # SPR drawdown optimizer
│ │ │ └── ExplainabilityView.tsx # 4-step causal chain drill-down
│ │ └── data/
│ │ └── geodata.ts # Geospatial coordinates & risk utils
│ ├── package.json # Node.js dependencies
│ ├── vite.config.ts # Vite configuration
│ └── index.html # HTML entry point
│
└── README.md # This file
| Variable | Description | Required |
|---|---|---|
GOOGLE_API_KEY |
Google AI Studio API key for Gemini 2.5 Flash | ✅ |
GEMINI_PROVIDER |
google_ai or vertex_ai |
✅ |
NEO4J_URI |
Neo4j Bolt URI (default: bolt://neo4j:7687) |
✅ |
NEO4J_USER |
Neo4j username (default: neo4j) |
✅ |
NEO4J_PASSWORD |
Neo4j password (default: riskagent2024) |
✅ |
QDRANT_HOST |
Qdrant hostname (default: qdrant) |
✅ |
QDRANT_PORT |
Qdrant port (default: 6333) |
✅ |
POSTGRES_USER |
PostgreSQL username (default: riskagent) |
✅ |
POSTGRES_PASSWORD |
PostgreSQL password (default: riskagent2024) |
✅ |
POSTGRES_DB |
PostgreSQL database (default: riskagent) |
✅ |
POSTGRES_HOST |
PostgreSQL hostname (default: postgres) |
✅ |
LANGFUSE_PUBLIC_KEY |
Langfuse Cloud public key | ✅ |
LANGFUSE_SECRET_KEY |
Langfuse Cloud secret key | ✅ |
LANGFUSE_HOST |
Langfuse Cloud host URL | ✅ |
NEWSAPI_KEY |
NewsAPI key for news ingestion | Optional |
ALPHA_VANTAGE_KEY |
Alpha Vantage key for price data | Optional |
VITE_API_URL |
Backend API URL for dashboard (default: http://localhost:8000) |
Optional |
| Operation | Target | Notes |
|---|---|---|
| Corridor risk fetch | < 200ms | Neo4j graph read |
| Explainability trace | < 500ms | Single corridor trace assembly |
| Scenario run (point estimate) | < 100ms | Deterministic cascade formula |
| Scenario run (Monte Carlo) | < 200ms | 2000 triangular iterations |
| Scenario run (with narrative) | < 10s | Includes Gemini LLM call |
| Procurement solve | < 2s | CP-SAT with soft demand constraints |
| Reserve optimization | < 50ms | Grid search, ~200 evaluations |
| Signal ingestion (full pipeline) | < 30s | Multi-source fetch + LLM extraction + reasoning |
| Resource | Specification |
|---|---|
| Memory | 4GB minimum (8GB recommended for Neo4j + Qdrant) |
| CPU | 2 vCPUs minimum |
| Storage | 2GB for Docker images + database volumes |
| Network | Outbound access for Gemini API, Langfuse, GDELT, NewsAPI |
| Metric | Specification |
|---|---|
| Concurrent dashboard users | 10+ per API instance |
| Historical scenario runs | Unlimited (immutable snapshot architecture) |
| Procurement solves | Unlimited (snapshot-per-solve pattern) |
| Knowledge graph entities | 24 seeded nodes, extensible |
| Policy corpus documents | Vector-indexed, unlimited capacity |
The current prototype models 4 corridors, 6 suppliers, 5 refineries, 6 ports, and 3 SPR sites — India's core import infrastructure. National-scale deployment would expand to:
- 12+ corridors (including pipeline routes from Central Asia and Myanmar)
- 20+ suppliers (full PPAC importer registry)
- 23 refineries (India's complete refining capacity of ~5.4M bpd)
- Real-time AIS vessel tracking integration for live corridor utilization
- Horizontal API scaling via container orchestration (Kubernetes) — the stateless, snapshot-per-decision architecture already supports this without schema changes
- Multi-tenant isolation for simultaneous use by MoPNG, PPAC, and individual refinery procurement teams
All database tables use immutable append-only patterns (branch, never mutate), so scaling historical depth adds zero contention to live operations.
- Causal reasoning, not sentiment classification — Multi-step chain: event → transmission mechanism → India-specific corridor/supplier/refinery impact → probability-weighted scenarios
- Scenario distributions, not single scores — 2–4 named scenarios per corridor with probabilities summing to ~1.0
- Full explainability — Every output traceable from raw source → extracted event → reasoning chain → final assessment
- No black-box ML — Transparent, formula-based cascade models with named, inspectable assumptions (matching Resilinc/Everstream/Interos approach)
- Branch, never mutate — Every scenario run, procurement solve, and reserve decision creates a new immutable row (Kinaxis-inspired architecture)
- India-specific grounding — All entities, prompts, and corpora scoped to India's actual crude oil import structure
- Schema-validated structured outputs — All LLM outputs via
instructor+ Pydantic v2, no manual JSON parsing
| Category | What's Real | What's Illustrative/Synthetic |
|---|---|---|
| Corridors | 4 real shipping corridors (Hormuz, Red Sea/Bab-el-Mandeb, COGH, Suez) with real geographic coordinates | — |
| Suppliers | 6 real supplier nations with real PPAC FY24 import share percentages | — |
| Refineries | 5 real Indian refineries with real nameplate capacities (PPAC data) | — |
| SPR Sites | 3 real SPR sites with real storage capacities (ISPRL data); 9.5-day cover validated against national import rate | — |
| Sanctions events | — | seed_data/sanctions_events.json — structured synthetic events mirroring real OFAC/EU sanctions patterns |
| Policy corpus | — | seed_data/policy_corpus/documents.json — synthetic-representative documents labeled "source_type": "synthetic_representative", mirroring OFAC notices, PPAC statistics, IEA analyses |
| Scenario assumptions | refinery_runrate_sensitivity sourced from PPAC FY24 import-share data |
price_passthrough_rate, spr_drawdown_rate — documented illustrative estimates (see scenario_templates.json source_note fields) |
| Grade compatibility | All 30 refinery-supplier cells confirmed true via Phase 1 research (web-sourced, with citations) |
processing_cost_multiplier (1.0–1.05 range) — illustrative, not sourced from refinery margin data |
| SPR extraction rate | — | 150,000 bpd — illustrative assumption derived from US SPR benchmark × 60% rock-cavern discount (see reserve/data.py) |
For detailed component documentation:




