Skip to content

Developer Guide

This guide explains how to extend the GRAFT. It covers the three most common extension points:

GuideWhat you will build
Adding a New ModelA new ML forecasting pipeline that integrates with the existing run, simulation, and evaluation infrastructure
Adding a New TabA new page in the NiceGUI dashboard with its own route, navbar entry, UI components, and business logic
Adding a Data SourceA new backend data adapter that feeds sensor readings into the dashboard

Before extending the dashboard, it helps to understand the three-layer pattern every feature follows:

app/models/<model>.py ← ML logic: train, predict, run_pipeline
app/services/model_manager.py ← Orchestration: fetch data, call pipeline, schedule
app/ui/control_tab_ui.py ← UI widgets: buttons, forms, config panels
app/ui/components/control_tab.py ← Event wiring: connect buttons to service calls
app/main.py ← Page routes: register new pages at /route

For a new model you touch the first two layers. For a new tab you touch the last three. For a new data source you touch app/backend/ and app/services/.

Async everywhere — all model pipelines, data fetches, and database writes are async functions. Always call them with asyncio.create_task() from a UI click handler, never with await directly in a synchronous callback.

app_state dict — shared mutable state within a page session is passed as a plain dict. Keys follow the pattern selected_for_*, available_*, *_running. Do not use global variables.

Config-driven — every model reads its hyperparameters from a YAML file in app/configs/. UI controls in the Configuration tab write back to those files via save_model_config(). New models must follow this same pattern.

Database logging — every model must call log_metrics() and save_prediction() from app/backend/database.py after each training and prediction run. This is what populates the Performance Metrics page and the Evaluation tab. Models that skip this step will not appear in the UI.

Logger — create a module-level logger at the top of every new file:

from app.backend.logger_config import setup_logger
logger = setup_logger(__name__)