GridGPT

Back to Dashboard

About GridGPT

TypeScriptNext.js 14ClickHouseRedpandaClaude APIBlueprint.js

Overview

GridGPT is an open-source, real-time monitoring dashboard for Ontario's electricity grid. It ingests public data from the Independent Electricity System Operator (IESO) and presents it in a dense, analyst-grade interface across nine pricing zones. The dashboard covers zonal pricing, provincial demand, generation by fuel type, intertie flows to neighbouring jurisdictions, weather overlays, and a text-to-SQL AI chatbot that can query any of the underlying data using natural language.

The project is a full-stack engineering portfolio piece built to demonstrate real-time data pipeline design, time-series analytics, and AI-augmented data exploration. Every layer — from the Python producer that parses IESO XML feeds, to the ClickHouse OLAP storage, to the SSE-streamed chat interface — is designed for low-latency observability of a complex, always-on system.

Why Blueprint.js?

Blueprint.js was chosen as the component library because it was purpose-built for data-dense enterprise applications — exactly the kind of interface an electricity grid dashboard needs.

  • Data-dense layouts — Blueprint is designed for complex dashboards with tables, trees, and multi-panel views, not consumer landing pages. It doesn't waste space on oversized padding or rounded cards.
  • Dark-first design — A native dark theme ships out of the box. No CSS override hacks, no theme provider workarounds. The dark theme is a first-class citizen.
  • TypeScript native — Full type definitions for every component and prop, matching the strict TypeScript configuration used throughout this project.
  • 500+ icons — A comprehensive icon system for status indicators, navigation, fuel types, and controls without pulling in a separate icon library.

System Architecture

Data flows through a streaming pipeline from IESO's public report servers into the dashboard. Each stage is designed for reliability and low latency.

Data Pipeline

IESO Report ServersPublic XML & CSV feeds for 9 report types covering prices, demand, generation, interties, adequacy, and day-ahead marketsreports-public.ieso.ca
5-min polling
Python ProducerAsync parsers fetch, validate, and structure IESO reports into typed records. Runs on a cron schedule on the backend server.aiohttp + confluent-kafka + Pydantic
publishes to 10 topics
DOCKER COMPOSE
RedpandaKafka-compatible event streaming. 10 topics carry structured records from the producer to ClickHouse.redpandadata/redpanda + console
Kafka Engine MVs
ClickHouseColumnar OLAP database. Materialized views consume Kafka topics into MergeTree tables. 8 dedup views eliminate duplicate rows from producer backfills.v_zonal_prices, v_zonal_demand, v_generator_output, v_fuel_mix, v_intertie_flow, v_adequacy, v_da_ozp, v_weather
RedisCache layer
init-kafkaTopic provisioning
HTTP SQL queries
Next.js API Routes19 routes across 5 categories: Core, Historical, Day-Ahead, Time-Scrub, and AI. All routes use force-dynamic for real-time data.Deployed on Vercel (serverless)
REST + SSE
Fuel MixDonut / Table / Radar
GenerationArea charts per plant
Zone MapLeaflet + WMS weather
MarketDual-axis chart + DA
Grid AIText-to-SQL chatbot
IntertiesFlow table + carbon

A separate WMS connection to Environment and Climate Change Canada (ECCC) feeds weather overlays (temperature, cloud cover, precipitation) directly into the map component via GDPS forecast layers.

Dashboard Panels

The dashboard is built around six panels, each showing a different facet of Ontario's grid in real time.

Fuel Mix

v_generator_output aggregated by fuel type

Three views of Ontario's current generation: a donut chart showing the percentage split between fuel types (nuclear ~60%, hydro ~25%, gas, wind, solar, biofuel), a detailed table with per-fuel output and capability numbers, and a radar chart for comparing fuel contributions. The inner ring shows the energy flow direction.

Generation by Resource

v_generator_output per plant

Area charts for each major generating station (Bruce, Darlington, Pickering, and more), grouped by fuel type with color coding. Each section — Renewables, Nuclear, Hydro, Gas — shows individual plant contributions. This table can lag ~2 hours behind other data sources.

Ontario Zone Map

v_zonal_prices + v_intertie_flow + v_generator_output + ECCC WMS

An interactive Leaflet map with a pricing zone GeoJSON overlay using a 12-tier color scale, generation site markers sized by output, animated intertie chevrons showing flow direction, and ECCC weather WMS layers for temperature, cloud cover, and precipitation. A time scrubber allows historical replay of all map layers simultaneously.

Market Overview

v_da_ozp + v_zonal_prices + v_zonal_demand + v_adequacy

A dual-axis Recharts chart with demand, supply, and grid load on the left MW axis and price on the right $/MWh axis. Includes a day-ahead overlay for both the current day (D) and tomorrow (D+1), plus a peak demand display. Supports 1-hour and full-day time ranges.

Grid AI

All ClickHouse tables via /api/chat

A text-to-SQL chatbot powered by the Claude API. Users ask natural language questions like “What was the average price in Toronto this morning?” and the system generates validated SQL, executes it against ClickHouse, and streams back the answer with a strategy explanation. Chat history persists in localStorage.

Interties

v_intertie_flow

Shows flows across Ontario's five intertie connections: Quebec, Michigan, Minnesota, New York, and Manitoba. Displays actual flow in MW, direction (positive = export, negative = import), and net flow summary.

Grid AI: Text-to-SQL Architecture

Grid AI is a context-grounded query assistant that translates natural language into validated ClickHouse SQL. Rather than relying on the LLM's general knowledge, every query is anchored in domain expertise assembled from four context layers.

Context Layers

Schema Definitions8 table structures with column names, types, and relationships. The LLM knows exactly what columns exist and how tables relate.
Domain KnowledgeIESO market rules, timezone handling (EST storage vs UTC queries), zone mappings, fuel type categories, and pricing model details.
Query Patterns11 validated SQL templates for common questions: current prices, demand trends, fuel mix breakdowns, intertie flows, and historical comparisons.
Temporal ContextEST timestamp storage rules, UTC conversion formulas, delivery hour calculations, and common pitfalls around timezone boundaries.

Tool Loop

The chat API uses Claude's tool-use capability with a query_clickhouse tool. On each turn, the model generates SQL plus a plain-English strategy explanation. The SQL passes through a safety validator (SELECT-only, LIMIT required, no system table access) before executing against ClickHouse. If the query returns an error or empty results, the model can retry with corrected SQL for up to 5 iterations.

SSE Streaming

Results stream to the browser via Server-Sent Events. The frontend receives four event types: tool_use (SQL + strategy), tool_result (row count + duration), text_delta (chunked natural language response), and done. The Grid AI component renders tool badges, strategy text, markdown-formatted answers, and persists conversations in localStorage.

Grid AI Pipeline

User QuestionNatural language input via GridChat component
Context AssemblySchema + Domain Knowledge + Query Patterns + Temporal Rules combined into a single system prompt
Claude APIclaude-sonnet-4 with query_clickhouse tool definitionmax_tokens: 2048, tool loop up to 5 iterations
SQL + strategy
Safety Validation + ExecutionSELECT-only filter, LIMIT enforcement, system table blocklist. Validated SQL executes against ClickHouse via HTTP.
SSE stream
GridChat ComponentMarkdown rendering, tool badges with strategy text, localStorage persistence, suggested questions

API Reference

The Next.js API layer exposes 19 routes organized into five categories. All routes query ClickHouse directly and use force-dynamic for real-time data.

Core (6)

  • /api/prices Latest zonal prices
  • /api/demand Current demand
  • /api/fuel-mix Fuel mix breakdown
  • /api/generators Generator output
  • /api/interties Intertie flows
  • /api/weather Weather by zone

Historical (6)

  • /api/prices/history
  • /api/demand/history
  • /api/supply/history
  • /api/fuel-mix/history
  • /api/generators/history
  • /api/market/history

Day-Ahead (2)

  • /api/market/day-ahead DA forecasts
  • /api/peak-demand Peak forecasts

Time-Scrub (4)

  • /api/prices/at-time
  • /api/weather/at-time
  • /api/interties/at-time
  • /api/interties/prices

AI (1)

  • /api/chat Text-to-SQL via Claude

Weather Integration

The Ontario Zone Map displays weather overlays sourced from ECCC's Global Deterministic Prediction System (GDPS) via WMS tile layers. Three layers are available: surface temperature, cloud cover, and total precipitation.

Weather frames are snapped to 3-hour forecast boundaries (00Z, 03Z, 06Z, etc.) to match GDPS model runs. The map uses a double-buffering technique: the next forecast image loads in a hidden layer while the current one displays, so transitions between time steps are smooth instead of flickering.

Next.js Techniques

  • Dynamic imports for Leaflet — The map component uses next/dynamic with ssr: false to avoid window is not defined errors during server-side rendering. Leaflet requires a browser environment.
  • force-dynamic on all API routes — Every API route exports export const dynamic = 'force-dynamic' to ensure fresh ClickHouse queries on every request rather than serving stale cached responses.
  • Revalidation on ClickHouse fetch — The ClickHouse HTTP client uses next: { revalidate: 5 } to allow brief caching while keeping data near-real-time.
  • Client-side hydration for suggested questions — The Grid AI panel renders a deterministic set of suggested questions on the server, then randomizes them client-side after hydration to avoid mismatch errors.

Hosting & Infrastructure

The system is split across two hosting providers, optimized for cost and reliability:

  • Frontend on Vercel — The Next.js app runs as serverless functions for API routes and uses Vercel's edge CDN for static assets. This handles bursty user traffic with auto-scaling at near-zero idle cost.
  • Backend on Hetzner VPS — A single VPS runs Docker Compose with 5 services: Redpanda, Redpanda Console, ClickHouse, Redis, and init-kafka. The Python producer runs on a cron schedule on the same machine. This provides persistent, always-on infrastructure for 24/7 data ingestion at ~$5–10/month.

The split rationale: Vercel handles user-facing traffic cheaply with auto-scaling, while Hetzner provides the stable, long-running services (Kafka, ClickHouse, Redis) that need to be online continuously regardless of user traffic.

Built with Claude Code

GridGPT was developed using Claude Code as the primary development tool. The project leveraged several MCP (Model Context Protocol) integrations and custom Skills to accelerate development:

  • ClickHouse direct queries — A custom /query Skill for running ad-hoc SQL against the live database during development, validating schema designs and query patterns in real time.
  • Context7 for documentation — Used for looking up Blueprint.js component APIs, Recharts configuration, Leaflet plugin patterns, and Next.js App Router conventions.
  • Sequential Thinking for complex logic — Applied to multi-step problems like the text-to-SQL context assembly, timezone conversion strategy, and deduplication view design.
  • Custom Skills/dev (start all infrastructure), /query (run ClickHouse SQL), and /ingest (trigger a producer run) provided one-command workflows during development.

Tech Stack

LayerTechnology
FrontendNext.js 14 (App Router), Blueprint.js 5.x, Recharts, Nivo, Leaflet
LanguageTypeScript (strict), Python 3.11
APINext.js API routes, Server-Sent Events (SSE)
DatabaseClickHouse (columnar OLAP)
StreamingRedpanda (Kafka-compatible)
CacheRedis
AIClaude API (claude-sonnet-4, tool use)
WeatherECCC GDPS via WMS
MapsLeaflet + react-leaflet, GeoJSON, WMS tiles
ProducerPython 3.11, aiohttp, confluent-kafka, Pydantic v2
InfrastructureDocker Compose, Vercel, Hetzner VPS
Dev ToolsClaude Code, Context7, Sequential Thinking

Quick Start

# Start infrastructure
docker compose up -d

# Start frontend dev server
cd frontend && npm run dev

# Start producer (separate terminal, activate venv)
cd producer && source venv/bin/activate && python main.py

# Query ClickHouse directly
docker exec -it clickhouse clickhouse-client \
  -q "SELECT zone, price FROM ieso.v_zonal_prices \
      ORDER BY timestamp DESC LIMIT 9"

IESO Data Sources

All data comes from IESO's public report server. These reports are freely available and require no authentication.

ReportFormatFrequencyURL
RealtimeZonalEnergyPricesXML5-minLink
RealtimeDemandZonalCSV5-minLink
GenOutputCapabilityXML5-minLink
GenOutputbyFuelHourlyXMLHourlyLink
IntertieScheduleFlowXMLHourlyLink
DayAheadOntarioZonalPriceXMLDailyLink
DayAheadIntertieLMPXMLDailyLink
RealtimeIntertieLMPXML5-minLink
AdequacyDayXMLDailyLink

Glossary

Plain-language definitions for terms used throughout this page.

Intertie
A power line that connects Ontario's electricity grid to another region like Quebec or Michigan. Power can flow in or out through these connections.
Pricing Zone
An area of Ontario where electricity costs the same amount. Ontario has 9 pricing zones with names like TORONTO, OTTAWA, and NORTHWEST.
IESO
Independent Electricity System Operator. The organization that runs Ontario's power grid and shares public data about it.
Megawatt (MW)
A unit of power. One megawatt can power about 1,000 homes at the same time.
$/MWh
Dollars per megawatt-hour. This is how electricity prices are measured — like a “price per gallon” for electricity.
Fuel Mix
The combination of energy sources used to make electricity right now — things like nuclear, water (hydro), natural gas, wind, and solar.
Day-Ahead Market
A market where electricity prices are set one day before the power is actually used. This helps the grid plan ahead.
DA-OZP
Day-Ahead Ontario Zonal Price. The official price for electricity, decided one day in advance. This is what consumers actually pay.
Settlement Price
The final, official price used for billing. In Ontario, this is the DA-OZP — not the real-time 5-minute price.
Real-Time Price
The live electricity price, updated every 5 minutes. Used for watching the grid, but not for billing.
Baseload
Power plants that run all the time, like nuclear plants. They provide steady, low-cost power around the clock.
Peaker Plant
A power plant that only turns on when lots of people need electricity at the same time. Usually gas-powered and more expensive.
ClickHouse
A very fast database built for analyzing large amounts of data over time. GridGPT stores all IESO data here.
Redpanda
A streaming platform that moves data in real-time — like a conveyor belt carrying information from the producer to the database.
Text-to-SQL
Turning a plain English question (like “What's the price in Toronto?”) into a database query that finds the answer.
Context-Grounded
An AI approach that uses specific domain knowledge (like IESO rules) instead of relying on general knowledge alone.
SSE
Server-Sent Events. A way for the server to push live updates to your browser without you needing to refresh the page.
OLAP
Online Analytical Processing. A type of database designed for fast, complex queries on historical data — perfect for time-series energy data.
Blueprint.js
A design system made for building data-heavy dashboards. It provides ready-made dark theme components like tables, icons, and buttons.
Materialized View
A saved database query that updates itself automatically when new data arrives. Used to move data from Kafka into ClickHouse tables.
Dedup View
A database view that removes duplicate rows. When the producer re-fetches data, these views make sure you only see one copy of each record.
WMS
Web Map Service. A standard way to serve map images over the internet. GridGPT uses this for weather overlays from Environment Canada.
ECCC
Environment and Climate Change Canada. The government agency that provides weather forecast data used on the map.
Double-Buffering
A technique where the next image loads in the background before being shown, so transitions look smooth instead of flickering.
Hydration
The process where a web page that was already drawn by the server becomes interactive once JavaScript loads in your browser.