Skip to content

Extending

This library deliberately stops at collecting and normalizing forecasts. Higher-level products — consensus/ensemble forecasts, forecast verification and provider accuracy scoring — are meant to live in separate packages built on the extension points below.

1. Response hooks

Register callables that receive every completed ForecastResponse. Hooks may be sync or async; failures are logged and never break the forecast.

from omni_weather_forecast_apis.client import create_omni_weather
from omni_weather_forecast_apis.types import ForecastResponse


async def record_for_verification(response: ForecastResponse) -> None:
    """e.g. ship normalized forecasts to a verification store."""
    for result in response.results:
        ...


client = await create_omni_weather(
    config,
    response_hooks=[record_for_verification],
)
try:
    ...
finally:
    await client.close()

This is the natural seam for an ensemble package (compute a consensus from response.results and publish it) or a verification package (persist each forecast now, compare against observations later).

2. Custom provider plugins

Any object satisfying the WeatherPlugin protocol can be used, including from outside this package. The preferred mechanism is per-client injection — it keeps plugin sets isolated between clients and avoids mutating global state:

from omni_weather_forecast_apis.client import create_omni_weather
from omni_weather_forecast_apis.plugins import open_meteo_plugin

client = await create_omni_weather(
    config,
    plugins=[my_plugin, open_meteo_plugin],
)

plugins accepts a sequence of plugins or a mapping keyed by ProviderId. When omitted, the client uses the process-global registry, which the CLI relies on and which remains available for global registration:

from omni_weather_forecast_apis.plugins import register_plugin

register_plugin(my_plugin)

Subclass BasePlugin/BasePluginInstance from omni_weather_forecast_apis.plugins._base to inherit config validation, HTTP error mapping, and the normalized point builders.

3. The SQLite feature matrix

The CLI's SQLite output has a documented relationship model, column reference, and lifecycle (see Database Design and CLI). The stacking_features view exposes one row per provider/model/hour with the forecast horizon and synthetic six-hour run-cycle bucket — exactly the shape an ensemble stacker or verification job needs:

SELECT valid_time, provider, model, horizon_hours, temperature
FROM stacking_features
WHERE valid_time_unix BETWEEN :start AND :end
ORDER BY valid_time, provider;

A verification project can join this view against observed weather by valid_time and location to score each provider's historical accuracy; an ensemble project can use it as a training feature matrix and weight providers accordingly.

Metrics hooks

metrics_hooks and log_hooks are a fourth extension point: they observe every request attempt, retry, HTTP-cache lookup, and quota consumption as typed events, and an OpenTelemetry bridge ships as the otel extra. See Observability.

Quota trackers

The daily-quota mechanism is also pluggable: implement the QuotaTracker protocol (get_usage / record_request / try_consume) to back quota accounting with your own store (Redis, Postgres, ...) and pass it to the client:

client = await create_omni_weather(config, quota_tracker=my_tracker)