feat: hgb-v3 — Open-Meteo rain features clear the 12h warning gate
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 13s
Documentation / Validate Documentation (push) Failing after 7s
Documentation / Generate API Documentation (push) Successful in 8s
Documentation / Build Sphinx Documentation (push) Successful in 17s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 0s
Documentation / Documentation Summary (push) Successful in 2s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 24s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped

The rolling-origin harness (models/eval_rain.json) showed catchment rain
halving flood-year Brier scores, cutting flood-regime MAE 20-40%, and
extending the hard 2024 leads (+6h -> +11h at P.1, +10h -> +19h at
P.103). Ported: train_all loads the catchment-mean series (use_rain /
--no-rain to opt out; without it bundles train as v2), predict fetches
live rain hourly and passes an empty series on failure so rain-trained
bundles serve with NaN features instead of tripping the feature guard,
and the leader worker persists hourly per-point + catchment-mean rows to
a new openmeteo_rain table.

Regenerated backtest: the 2024 record flood now gets a 13-HOUR WARNING
(alert 04:00 vs 17:00 crossing, river at 2.9m at alert time) — the >=12h
acceptance gate PASSES for the first time. Journey on that crossing:
v1 -18h, v2 +6h, v3 +13h. The marginal 2025 double-crest trades its
artifact +46h latch for a calibrated +2h with zero false alarms. P.1
MAE 4.9/7.2/8.7 cm at 6/12/24h. Docs updated throughout.
This commit is contained in:
2026-08-12 17:05:01 +07:00
parent cbb3bf7369
commit df0ae8cda3
12 changed files with 646 additions and 53 deletions
+25
View File
@@ -264,6 +264,27 @@ app.add_middleware(
)
async def _persist_rain():
"""Save the latest Open-Meteo rain frame into openmeteo_rain (leader only)."""
store = app_state.get("forecast_store") # reuse its SQL engine
if not store:
return
try:
from .ml import rain as rain_mod
def fetch_and_save():
frame = rain_mod.fetch_forecast()
if not store.engine and not store.connect():
return 0
return rain_mod.save_to_db(frame, store.engine, store.db_type)
saved = await asyncio.to_thread(fetch_and_save)
if saved:
logger.info(f"openmeteo_rain: {saved} hourly rows upserted")
except Exception as e:
logger.warning(f"rain persistence failed: {e}")
async def _precompute_forecasts():
"""Refresh the forecast cache and persist the issued forecasts (leader only)."""
try:
@@ -351,6 +372,10 @@ async def background_scraping_task():
except Exception as e:
logger.error(f"HII collection failed: {e}")
# Persist the Open-Meteo catchment rain (observed tail +
# 48h forecast) so the DB carries the weather context too
await _persist_rain()
# Precompute forecasts on fresh data: primes the response
# cache (user requests never pay for inference) and records
# what the model predicted for later predicted-vs-actual