scale-forecasting 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scale_forecasting/__init__.py +275 -0
- scale_forecasting/_entry.py +110 -0
- scale_forecasting/_infra_args.py +91 -0
- scale_forecasting/agent_surfaces.py +1571 -0
- scale_forecasting/airflow_emit.py +325 -0
- scale_forecasting/airflow_tasks.py +414 -0
- scale_forecasting/automl_submit.py +383 -0
- scale_forecasting/backtest.py +1153 -0
- scale_forecasting/batch_infra.py +322 -0
- scale_forecasting/batch_telemetry.py +241 -0
- scale_forecasting/calibration.py +900 -0
- scale_forecasting/capacity.py +659 -0
- scale_forecasting/cluster_deps.py +113 -0
- scale_forecasting/cluster_submit.py +383 -0
- scale_forecasting/cluster_telemetry.py +108 -0
- scale_forecasting/code_delivery.py +196 -0
- scale_forecasting/commands.py +459 -0
- scale_forecasting/compute_fallback.py +190 -0
- scale_forecasting/config.py +2530 -0
- scale_forecasting/dag.py +551 -0
- scale_forecasting/data_gen/__init__.py +3 -0
- scale_forecasting/data_gen/generator.py +415 -0
- scale_forecasting/data_gen/seed_spark.py +505 -0
- scale_forecasting/dataproc_cluster.py +869 -0
- scale_forecasting/device_audit.py +219 -0
- scale_forecasting/diagnostics.py +1510 -0
- scale_forecasting/engines/__init__.py +6 -0
- scale_forecasting/engines/automl_engine.py +2162 -0
- scale_forecasting/engines/bigquery_engine.py +623 -0
- scale_forecasting/engines/bigquery_names.py +101 -0
- scale_forecasting/engines/bigquery_sql.py +783 -0
- scale_forecasting/engines/ray_engine.py +809 -0
- scale_forecasting/engines/ray_io.py +1007 -0
- scale_forecasting/engines/spark_explode.py +358 -0
- scale_forecasting/engines/spark_io.py +763 -0
- scale_forecasting/engines/vertex_engine.py +1171 -0
- scale_forecasting/ensemble_run.py +1102 -0
- scale_forecasting/ensembler.py +1279 -0
- scale_forecasting/errors.py +188 -0
- scale_forecasting/features.py +1352 -0
- scale_forecasting/gce_submit.py +1028 -0
- scale_forecasting/gke_submit.py +1587 -0
- scale_forecasting/hardware.py +265 -0
- scale_forecasting/hpo.py +424 -0
- scale_forecasting/identity.py +94 -0
- scale_forecasting/job_launch.py +768 -0
- scale_forecasting/job_outcome.py +241 -0
- scale_forecasting/job_wait.py +111 -0
- scale_forecasting/launch_plan.py +1107 -0
- scale_forecasting/main.py +799 -0
- scale_forecasting/mcp.py +1385 -0
- scale_forecasting/metrics/__init__.py +253 -0
- scale_forecasting/metrics/_intervals.py +19 -0
- scale_forecasting/metrics/base_metric.py +217 -0
- scale_forecasting/metrics/bias.py +34 -0
- scale_forecasting/metrics/coverage.py +40 -0
- scale_forecasting/metrics/cv.py +24 -0
- scale_forecasting/metrics/interval_score.py +42 -0
- scale_forecasting/metrics/interval_width.py +38 -0
- scale_forecasting/metrics/maape.py +38 -0
- scale_forecasting/metrics/mae.py +30 -0
- scale_forecasting/metrics/mape.py +33 -0
- scale_forecasting/metrics/mase.py +45 -0
- scale_forecasting/metrics/mase_seasonal.py +49 -0
- scale_forecasting/metrics/mse.py +32 -0
- scale_forecasting/metrics/msis.py +30 -0
- scale_forecasting/metrics/msse.py +28 -0
- scale_forecasting/metrics/ope.py +24 -0
- scale_forecasting/metrics/pinball.py +49 -0
- scale_forecasting/metrics/r2.py +27 -0
- scale_forecasting/metrics/rmse.py +30 -0
- scale_forecasting/metrics/rmsle.py +25 -0
- scale_forecasting/metrics/rmsse.py +41 -0
- scale_forecasting/metrics/smape.py +33 -0
- scale_forecasting/metrics/wape.py +34 -0
- scale_forecasting/models/__init__.py +112 -0
- scale_forecasting/models/_lag_forecaster.py +407 -0
- scale_forecasting/models/_neuralforecast_base.py +605 -0
- scale_forecasting/models/_vertex_automl_base.py +268 -0
- scale_forecasting/models/auto_arima.py +142 -0
- scale_forecasting/models/auto_ces.py +103 -0
- scale_forecasting/models/auto_theta.py +113 -0
- scale_forecasting/models/autoets.py +106 -0
- scale_forecasting/models/base_model.py +600 -0
- scale_forecasting/models/bigquery_native.py +177 -0
- scale_forecasting/models/catboost_model.py +159 -0
- scale_forecasting/models/croston.py +137 -0
- scale_forecasting/models/fft.py +108 -0
- scale_forecasting/models/holtwinters.py +85 -0
- scale_forecasting/models/kalman.py +95 -0
- scale_forecasting/models/lightgbm_model.py +160 -0
- scale_forecasting/models/naive_drift.py +69 -0
- scale_forecasting/models/naive_mean.py +60 -0
- scale_forecasting/models/naive_moving_average.py +93 -0
- scale_forecasting/models/naive_seasonal.py +72 -0
- scale_forecasting/models/neuralprophet_model.py +421 -0
- scale_forecasting/models/patchtst.py +51 -0
- scale_forecasting/models/prophet_model.py +121 -0
- scale_forecasting/models/random_forest.py +153 -0
- scale_forecasting/models/regression_lags.py +171 -0
- scale_forecasting/models/sarimax.py +92 -0
- scale_forecasting/models/stl_bagging.py +109 -0
- scale_forecasting/models/tbats_model.py +102 -0
- scale_forecasting/models/tft.py +46 -0
- scale_forecasting/models/theta.py +89 -0
- scale_forecasting/models/tide.py +47 -0
- scale_forecasting/models/tsmixer.py +48 -0
- scale_forecasting/models/ucm.py +91 -0
- scale_forecasting/models/vertex_l2l.py +31 -0
- scale_forecasting/models/vertex_seq2seq.py +30 -0
- scale_forecasting/models/vertex_tft.py +32 -0
- scale_forecasting/models/vertex_tide.py +32 -0
- scale_forecasting/models/xgboost_model.py +164 -0
- scale_forecasting/notebook_acceptance.py +755 -0
- scale_forecasting/playground.py +793 -0
- scale_forecasting/probes/__init__.py +3 -0
- scale_forecasting/probes/cancel.py +408 -0
- scale_forecasting/probes/reconcile.py +577 -0
- scale_forecasting/probes/runtimes.py +941 -0
- scale_forecasting/probes/settle.py +466 -0
- scale_forecasting/probes/vocabulary.py +245 -0
- scale_forecasting/profiling/__init__.py +3 -0
- scale_forecasting/profiling/baseline.py +240 -0
- scale_forecasting/profiling/cost.py +705 -0
- scale_forecasting/profiling/measure.py +440 -0
- scale_forecasting/profiling/numeric.py +73 -0
- scale_forecasting/profiling/sampling.py +290 -0
- scale_forecasting/profiling/signature.py +136 -0
- scale_forecasting/profiling/source.py +473 -0
- scale_forecasting/profiling/stats.py +221 -0
- scale_forecasting/py.typed +0 -0
- scale_forecasting/quota.py +1767 -0
- scale_forecasting/ray_cluster.py +767 -0
- scale_forecasting/ray_entry.py +46 -0
- scale_forecasting/ray_infra.py +121 -0
- scale_forecasting/ray_jobs.py +462 -0
- scale_forecasting/ray_reaper.py +392 -0
- scale_forecasting/ray_submit.py +323 -0
- scale_forecasting/ray_telemetry.py +106 -0
- scale_forecasting/reconciliation.py +929 -0
- scale_forecasting/registry/__init__.py +3 -0
- scale_forecasting/registry/artifacts.py +223 -0
- scale_forecasting/registry/cells.py +115 -0
- scale_forecasting/registry/ddl.py +408 -0
- scale_forecasting/registry/harvest.py +252 -0
- scale_forecasting/registry/header.py +368 -0
- scale_forecasting/registry/ids.py +556 -0
- scale_forecasting/registry/jobs.py +191 -0
- scale_forecasting/registry/lifecycle.py +284 -0
- scale_forecasting/registry/ops.py +1666 -0
- scale_forecasting/registry/params.py +150 -0
- scale_forecasting/registry/reads.py +971 -0
- scale_forecasting/registry/rows.py +556 -0
- scale_forecasting/registry/tables.py +150 -0
- scale_forecasting/registry/views.py +399 -0
- scale_forecasting/registry/write_api.py +386 -0
- scale_forecasting/report.py +932 -0
- scale_forecasting/resources/__init__.py +3 -0
- scale_forecasting/resources/audit.py +64 -0
- scale_forecasting/resources/catalog.py +263 -0
- scale_forecasting/resources/cluster.py +352 -0
- scale_forecasting/resources/fleet.py +613 -0
- scale_forecasting/resources/serverless.py +428 -0
- scale_forecasting/resources/slot.py +295 -0
- scale_forecasting/retry_policy.py +378 -0
- scale_forecasting/retry_run.py +535 -0
- scale_forecasting/review.py +2689 -0
- scale_forecasting/router.py +41 -0
- scale_forecasting/scenarios.py +564 -0
- scale_forecasting/sdk.py +2073 -0
- scale_forecasting/seasonality.py +60 -0
- scale_forecasting/settings.py +171 -0
- scale_forecasting/shared_clusters.py +348 -0
- scale_forecasting/smoke_run.py +83 -0
- scale_forecasting/spark_entry.py +47 -0
- scale_forecasting/staging.py +121 -0
- scale_forecasting/submit.py +667 -0
- scale_forecasting/submitters.py +515 -0
- scale_forecasting/validation.py +201 -0
- scale_forecasting/vertex_entry.py +81 -0
- scale_forecasting/vertex_submit.py +680 -0
- scale_forecasting/worker.py +1432 -0
- scale_forecasting-1.0.0.dist-info/METADATA +361 -0
- scale_forecasting-1.0.0.dist-info/RECORD +186 -0
- scale_forecasting-1.0.0.dist-info/WHEEL +4 -0
- scale_forecasting-1.0.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""scale-forecasting — massively-parallel time-series forecasting on Google Cloud.
|
|
2
|
+
|
|
3
|
+
The package is deliberately flat and readable: one capability per file. Start with the
|
|
4
|
+
architecture overview in the README, then read ``config.py`` (the run contract),
|
|
5
|
+
``worker.py`` (the unit of work), and one file under ``models/`` to see the whole shape.
|
|
6
|
+
|
|
7
|
+
Public surface — two ways in, one engine underneath:
|
|
8
|
+
|
|
9
|
+
* **The easy path.** `Forecaster` — point it at a config and call
|
|
10
|
+
`dry_run` / `run`
|
|
11
|
+
/ `review`. Thin wrapper over `run`; no logic of its
|
|
12
|
+
own.
|
|
13
|
+
* **The manage path.** `Registry` — the operator surface over the registry a deployment writes
|
|
14
|
+
to: `doctor` (read-only health), `drop_run`, `sweep_orphans`, `snapshot`, `export`, `init`.
|
|
15
|
+
Same verbs as ``python -m scale_forecasting.registry.ops``.
|
|
16
|
+
* **The direct path.** Drive Spark or Ray yourself and reuse the *same* model machinery:
|
|
17
|
+
`run_group` (pure, per-bucket), `make_group_runner` / `make_chunk_runner` (the
|
|
18
|
+
writer-attached ``applyInPandas`` / Ray-task closures), `chunk_cells`, and the unit of work
|
|
19
|
+
`run_cell`. Both paths run byte-identical cell code. See ``docs/using_the_sdk.md``.
|
|
20
|
+
|
|
21
|
+
Import cost: ``import scale_forecasting`` is near-instant. Light names (config, settings, errors)
|
|
22
|
+
are eager; the heavy names above load the model modules and are therefore **lazy** — resolved on
|
|
23
|
+
first attribute access via PEP 562. Touching ``Forecaster``/``run``/``run_cell`` pays the model
|
|
24
|
+
import cost; touching ``RunConfig``/``Settings`` does not. ``test_sdk.py`` guards this contract.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from importlib.metadata import PackageNotFoundError
|
|
30
|
+
from importlib.metadata import version as _pkg_version
|
|
31
|
+
from typing import TYPE_CHECKING
|
|
32
|
+
|
|
33
|
+
# Eager (light): the config contract, infra identity, and the error hierarchy. None of these pull
|
|
34
|
+
# the model modules, so they keep ``import scale_forecasting`` cheap.
|
|
35
|
+
from .config import (
|
|
36
|
+
Fanout,
|
|
37
|
+
RunConfig,
|
|
38
|
+
Workload,
|
|
39
|
+
estimate_fanout,
|
|
40
|
+
estimate_workload,
|
|
41
|
+
load_config,
|
|
42
|
+
)
|
|
43
|
+
from .errors import (
|
|
44
|
+
ConfigError,
|
|
45
|
+
DataError,
|
|
46
|
+
EngineError,
|
|
47
|
+
ModelError,
|
|
48
|
+
RegistryError,
|
|
49
|
+
ScaleForecastError,
|
|
50
|
+
)
|
|
51
|
+
from .settings import Settings
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
__version__: str = _pkg_version("scale-forecasting")
|
|
55
|
+
except PackageNotFoundError: # pragma: no cover - zip-staged cloud worker without .dist-info
|
|
56
|
+
__version__ = "1.0.0"
|
|
57
|
+
|
|
58
|
+
# Lazy (heavy): name -> (module, attribute). Resolved on first access by ``__getattr__`` (PEP 562)
|
|
59
|
+
# so the model modules (statsmodels et al., ~5s) load only when a caller actually reaches for the
|
|
60
|
+
# run/model surface — never merely to ``import scale_forecasting``.
|
|
61
|
+
_LAZY: dict[str, tuple[str, str]] = {
|
|
62
|
+
# Easy path.
|
|
63
|
+
"Forecaster": (".sdk", "Forecaster"),
|
|
64
|
+
"Registry": (".sdk", "Registry"),
|
|
65
|
+
"DryRunResult": (".sdk", "DryRunResult"),
|
|
66
|
+
"RunResult": (".sdk", "RunResult"),
|
|
67
|
+
"ModelResult": (".sdk", "ModelResult"),
|
|
68
|
+
"JobTrace": (".sdk", "JobTrace"),
|
|
69
|
+
"build_explain_frame": (".sdk", "build_explain_frame"),
|
|
70
|
+
"build_best_params_frame": (".sdk", "build_best_params_frame"),
|
|
71
|
+
"build_trace_frame": (".sdk", "build_trace_frame"),
|
|
72
|
+
"plot_trace": (".sdk", "plot_trace"),
|
|
73
|
+
# Run inspection — monitor a running run, review a finished one.
|
|
74
|
+
"monitor_run": (".review", "monitor_run"),
|
|
75
|
+
"review_run": (".review", "review_run"),
|
|
76
|
+
"RunProgress": (".review", "RunProgress"),
|
|
77
|
+
"RunReview": (".review", "RunReview"),
|
|
78
|
+
"calibration_report": (".review", "calibration_report"),
|
|
79
|
+
"CalibrationReport": (".review", "CalibrationReport"),
|
|
80
|
+
"build_leaderboard_frame": (".review", "build_leaderboard_frame"),
|
|
81
|
+
"build_predictions_frame": (".review", "build_predictions_frame"),
|
|
82
|
+
"build_hierarchy_frame": (".review", "build_hierarchy_frame"),
|
|
83
|
+
"build_cohorts_frame": (".review", "build_cohorts_frame"),
|
|
84
|
+
"build_calibration_frames": (".review", "build_calibration_frames"),
|
|
85
|
+
"build_ensemble_weights_frame": (".review", "build_ensemble_weights_frame"),
|
|
86
|
+
"build_attributions_frame": (".review", "build_attributions_frame"),
|
|
87
|
+
"explain_forecast_frame": (".review", "explain_forecast_frame"),
|
|
88
|
+
"plot_progress": (".review", "plot_progress"),
|
|
89
|
+
"plot_leaderboard": (".review", "plot_leaderboard"),
|
|
90
|
+
"plot_metric_distribution": (".review", "plot_metric_distribution"),
|
|
91
|
+
"plot_forecasts_frame": (".review", "plot_forecasts_frame"),
|
|
92
|
+
"plot_hierarchy_frame": (".review", "plot_hierarchy_frame"),
|
|
93
|
+
"plot_calibration": (".review", "plot_calibration"),
|
|
94
|
+
"plot_ensemble_weights": (".review", "plot_ensemble_weights"),
|
|
95
|
+
"plot_attributions": (".review", "plot_attributions"),
|
|
96
|
+
"plot_forecast_explanation": (".review", "plot_forecast_explanation"),
|
|
97
|
+
"DagNode": (".dag", "DagNode"),
|
|
98
|
+
# Cross-run ensembling.
|
|
99
|
+
"merge_configs_for_ensemble": (".ensemble_run", "merge_configs_for_ensemble"),
|
|
100
|
+
"ensemble_cross_runs": (".ensemble_run", "ensemble_cross_runs"),
|
|
101
|
+
# Orchestration.
|
|
102
|
+
"run": (".main", "run"),
|
|
103
|
+
# Direct path — pure core + writer-attached runners + unit of work.
|
|
104
|
+
"run_group": (".engines.spark_io", "run_group"),
|
|
105
|
+
"make_group_runner": (".engines.spark_io", "make_group_runner"),
|
|
106
|
+
"RunOutcome": (".engines.spark_io", "RunOutcome"),
|
|
107
|
+
"aggregate_status": (".engines.spark_io", "aggregate_status"),
|
|
108
|
+
"make_chunk_runner": (".engines.ray_io", "make_chunk_runner"),
|
|
109
|
+
"chunk_cells": (".engines.ray_io", "chunk_cells"),
|
|
110
|
+
"run_cell": (".worker", "run_cell"),
|
|
111
|
+
"CellResult": (".worker", "CellResult"),
|
|
112
|
+
# Model registry + runtime routing + catalogs.
|
|
113
|
+
"get_model": (".models", "get_model"),
|
|
114
|
+
"list_models": (".models", "list_models"),
|
|
115
|
+
"model_catalog": (".playground", "model_catalog"),
|
|
116
|
+
"metric_catalog": (".playground", "metric_catalog"),
|
|
117
|
+
"split_by_runtime": (".router", "split_by_runtime"),
|
|
118
|
+
# Hierarchical aggregation + coherent reconciliation.
|
|
119
|
+
"HierarchySpec": (".reconciliation", "HierarchySpec"),
|
|
120
|
+
"build_hierarchy": (".reconciliation", "build_hierarchy"),
|
|
121
|
+
"reconcile_matrix": (".reconciliation", "reconcile_matrix"),
|
|
122
|
+
"reconcile_forecasts": (".reconciliation", "reconcile_forecasts"),
|
|
123
|
+
"reconcile_oof": (".reconciliation", "reconcile_oof"),
|
|
124
|
+
"reconcile_cells": (".reconciliation", "reconcile_cells"),
|
|
125
|
+
"verify_coherence": (".reconciliation", "verify_coherence"),
|
|
126
|
+
# AI agent surfaces & MCP server.
|
|
127
|
+
"probe_environment": (".agent_surfaces", "probe_environment"),
|
|
128
|
+
"McpServer": (".mcp", "McpServer"),
|
|
129
|
+
# Forecaster Workbench diagnostics, scenario simulation & self-contained reports.
|
|
130
|
+
"compute_demand_profile": (".features", "compute_demand_profile"),
|
|
131
|
+
"compute_fva": (".diagnostics", "compute_fva"),
|
|
132
|
+
"diebold_mariano_test": (".diagnostics", "diebold_mariano_test"),
|
|
133
|
+
"wilcoxon_pvalue": (".diagnostics", "wilcoxon_pvalue"),
|
|
134
|
+
"compute_horizon_decay": (".diagnostics", "compute_horizon_decay"),
|
|
135
|
+
"compute_bias_report": (".diagnostics", "compute_bias_report"),
|
|
136
|
+
"compute_realized_accuracy": (".diagnostics", "compute_realized_accuracy"),
|
|
137
|
+
"compute_revision_stability": (".diagnostics", "compute_revision_stability"),
|
|
138
|
+
"compute_exceptions_queue": (".diagnostics", "compute_exceptions_queue"),
|
|
139
|
+
"rollup_time": (".diagnostics", "rollup_time"),
|
|
140
|
+
"plot_fva": (".diagnostics", "plot_fva"),
|
|
141
|
+
"plot_horizon_decay": (".diagnostics", "plot_horizon_decay"),
|
|
142
|
+
"apply_scenario_shocks": (".scenarios", "apply_scenario_shocks"),
|
|
143
|
+
"simulate_scenarios": (".scenarios", "simulate_scenarios"),
|
|
144
|
+
"summarize_scenarios": (".scenarios", "summarize_scenarios"),
|
|
145
|
+
"plot_scenarios": (".scenarios", "plot_scenarios"),
|
|
146
|
+
"build_report_payload": (".report", "build_report_payload"),
|
|
147
|
+
"render_html_report": (".report", "render_html_report"),
|
|
148
|
+
"export_report": (".report", "export_report"),
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
__all__ = [
|
|
152
|
+
"__version__",
|
|
153
|
+
# Eager.
|
|
154
|
+
"RunConfig",
|
|
155
|
+
"load_config",
|
|
156
|
+
"estimate_workload",
|
|
157
|
+
"Workload",
|
|
158
|
+
"estimate_fanout",
|
|
159
|
+
"Fanout",
|
|
160
|
+
"Settings",
|
|
161
|
+
"ScaleForecastError",
|
|
162
|
+
"ConfigError",
|
|
163
|
+
"DataError",
|
|
164
|
+
"ModelError",
|
|
165
|
+
"RegistryError",
|
|
166
|
+
"EngineError",
|
|
167
|
+
# Lazy.
|
|
168
|
+
*_LAZY,
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
if TYPE_CHECKING: # so IDEs / type-checkers see the lazy names as real imports (re-exported below)
|
|
172
|
+
from .agent_surfaces import probe_environment # noqa: F401
|
|
173
|
+
from .dag import DagNode # noqa: F401
|
|
174
|
+
from .diagnostics import ( # noqa: F401
|
|
175
|
+
compute_bias_report,
|
|
176
|
+
compute_exceptions_queue,
|
|
177
|
+
compute_fva,
|
|
178
|
+
compute_horizon_decay,
|
|
179
|
+
compute_realized_accuracy,
|
|
180
|
+
compute_revision_stability,
|
|
181
|
+
diebold_mariano_test,
|
|
182
|
+
plot_fva,
|
|
183
|
+
plot_horizon_decay,
|
|
184
|
+
rollup_time,
|
|
185
|
+
wilcoxon_pvalue,
|
|
186
|
+
)
|
|
187
|
+
from .engines.ray_io import chunk_cells, make_chunk_runner # noqa: F401
|
|
188
|
+
from .engines.spark_io import ( # noqa: F401
|
|
189
|
+
RunOutcome,
|
|
190
|
+
aggregate_status,
|
|
191
|
+
make_group_runner,
|
|
192
|
+
run_group,
|
|
193
|
+
)
|
|
194
|
+
from .ensemble_run import ensemble_cross_runs, merge_configs_for_ensemble # noqa: F401
|
|
195
|
+
from .features import compute_demand_profile # noqa: F401
|
|
196
|
+
from .main import run # noqa: F401
|
|
197
|
+
from .mcp import McpServer # noqa: F401
|
|
198
|
+
from .models import get_model, list_models # noqa: F401
|
|
199
|
+
from .playground import metric_catalog, model_catalog # noqa: F401
|
|
200
|
+
from .reconciliation import ( # noqa: F401
|
|
201
|
+
HierarchySpec,
|
|
202
|
+
build_hierarchy,
|
|
203
|
+
reconcile_cells,
|
|
204
|
+
reconcile_forecasts,
|
|
205
|
+
reconcile_matrix,
|
|
206
|
+
reconcile_oof,
|
|
207
|
+
verify_coherence,
|
|
208
|
+
)
|
|
209
|
+
from .report import ( # noqa: F401
|
|
210
|
+
build_report_payload,
|
|
211
|
+
export_report,
|
|
212
|
+
render_html_report,
|
|
213
|
+
)
|
|
214
|
+
from .review import ( # noqa: F401
|
|
215
|
+
CalibrationReport,
|
|
216
|
+
RunProgress,
|
|
217
|
+
RunReview,
|
|
218
|
+
build_attributions_frame,
|
|
219
|
+
build_calibration_frames,
|
|
220
|
+
build_cohorts_frame,
|
|
221
|
+
build_ensemble_weights_frame,
|
|
222
|
+
build_hierarchy_frame,
|
|
223
|
+
build_leaderboard_frame,
|
|
224
|
+
build_predictions_frame,
|
|
225
|
+
calibration_report,
|
|
226
|
+
explain_forecast_frame,
|
|
227
|
+
monitor_run,
|
|
228
|
+
plot_attributions,
|
|
229
|
+
plot_calibration,
|
|
230
|
+
plot_ensemble_weights,
|
|
231
|
+
plot_forecast_explanation,
|
|
232
|
+
plot_forecasts_frame,
|
|
233
|
+
plot_hierarchy_frame,
|
|
234
|
+
plot_leaderboard,
|
|
235
|
+
plot_metric_distribution,
|
|
236
|
+
plot_progress,
|
|
237
|
+
review_run,
|
|
238
|
+
)
|
|
239
|
+
from .router import split_by_runtime # noqa: F401
|
|
240
|
+
from .scenarios import ( # noqa: F401
|
|
241
|
+
apply_scenario_shocks,
|
|
242
|
+
plot_scenarios,
|
|
243
|
+
simulate_scenarios,
|
|
244
|
+
summarize_scenarios,
|
|
245
|
+
)
|
|
246
|
+
from .sdk import ( # noqa: F401
|
|
247
|
+
DryRunResult,
|
|
248
|
+
Forecaster,
|
|
249
|
+
JobTrace,
|
|
250
|
+
ModelResult,
|
|
251
|
+
Registry,
|
|
252
|
+
RunResult,
|
|
253
|
+
build_best_params_frame,
|
|
254
|
+
build_explain_frame,
|
|
255
|
+
build_trace_frame,
|
|
256
|
+
plot_trace,
|
|
257
|
+
)
|
|
258
|
+
from .worker import CellResult, run_cell # noqa: F401
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def __getattr__(name: str) -> object:
|
|
262
|
+
"""PEP 562 lazy loader: resolve a heavy public name on first access, then cache it."""
|
|
263
|
+
target = _LAZY.get(name)
|
|
264
|
+
if target is None:
|
|
265
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
266
|
+
from importlib import import_module
|
|
267
|
+
|
|
268
|
+
module_name, attr = target
|
|
269
|
+
value = getattr(import_module(module_name, __name__), attr)
|
|
270
|
+
globals()[name] = value # cache so subsequent lookups skip __getattr__
|
|
271
|
+
return value
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def __dir__() -> list[str]:
|
|
275
|
+
return sorted(__all__)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Shared on-cluster entrypoint core for the Dataproc and Ray forecast launchers.
|
|
2
|
+
|
|
3
|
+
Both `spark_entry` and `ray_entry` parse the same on-cluster contract — ``--config-uri`` (the run
|
|
4
|
+
config, staged to GCS as JSON — the JSON *is* the reproducibility record) + the executed
|
|
5
|
+
``--models`` subset + ``--manage-header`` + the ``--sf-*`` infra args, which `export_infra_env`
|
|
6
|
+
promotes to ``os.environ`` before anything resolves `Settings` — then load the staged config and
|
|
7
|
+
run the runtime's engine ``run(cfg, models=, manage_header=)``. This module is that shared skeleton,
|
|
8
|
+
parametrized by a resolver so each launcher differs only in which engine callable it selects (Spark
|
|
9
|
+
and Ray each have exactly one — there is no method switch).
|
|
10
|
+
|
|
11
|
+
``--models m1,m2`` restricts the executed subset (the staged config's ``run_id`` is unchanged, so
|
|
12
|
+
both runtimes share it) and ``--manage-header false`` puts the engine in contributor mode (``main``
|
|
13
|
+
owns the single shared header). Both are optional — absent, the engine runs its standalone
|
|
14
|
+
lifecycle over ``cfg.models``.
|
|
15
|
+
|
|
16
|
+
``--provisioned-hardware gpu`` states what the submitter actually bought, so a model can select a
|
|
17
|
+
device explicitly rather than asking its library to guess (see `hardware`). Emitted only by GPU
|
|
18
|
+
jobs; absent everywhere else, which leaves device selection at ``auto`` — today's behaviour.
|
|
19
|
+
|
|
20
|
+
The GCP/engine imports stay lazy so the launchers import cleanly offline.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import argparse
|
|
26
|
+
from collections.abc import Callable
|
|
27
|
+
from typing import TYPE_CHECKING
|
|
28
|
+
|
|
29
|
+
from ._infra_args import add_infra_args, export_infra_env
|
|
30
|
+
from .errors import configure_cli_logging, get_logger
|
|
31
|
+
from .hardware import add_hardware_arg, export_hardware_env
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from .config import RunConfig
|
|
35
|
+
|
|
36
|
+
_log = get_logger(__name__)
|
|
37
|
+
|
|
38
|
+
# A resolved (engine_run, label) pair: the callable to run and a short name for logging. The
|
|
39
|
+
# callable takes ``(cfg, models=, manage_header=)`` — the shared engine contract.
|
|
40
|
+
EngineResolver = Callable[[argparse.Namespace], "tuple[Callable[..., object], str]"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def parse_models(raw: str | None) -> list[str] | None:
|
|
44
|
+
"""Parse the optional ``--models m1,m2`` CSV into a subset list (``None`` → run ``cfg.models``).
|
|
45
|
+
|
|
46
|
+
Empty/whitespace-only tokens are dropped so a trailing comma is harmless; an all-empty value
|
|
47
|
+
collapses to ``None`` (standalone) rather than an empty subset (which would run nothing).
|
|
48
|
+
"""
|
|
49
|
+
if raw is None:
|
|
50
|
+
return None
|
|
51
|
+
names = [tok.strip() for tok in raw.split(",") if tok.strip()]
|
|
52
|
+
return names or None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def build_parser(prog: str, description: str) -> argparse.ArgumentParser:
|
|
56
|
+
"""The shared launcher parser: ``--config-uri`` + ``--models`` + ``--manage-header`` + infra."""
|
|
57
|
+
p = argparse.ArgumentParser(prog=prog, description=description)
|
|
58
|
+
p.add_argument("--config-uri", required=True, help="gs:// (or local) path to run config JSON")
|
|
59
|
+
p.add_argument(
|
|
60
|
+
"--models",
|
|
61
|
+
default=None,
|
|
62
|
+
help="optional comma-separated executed subset; absent runs all cfg.models",
|
|
63
|
+
)
|
|
64
|
+
p.add_argument(
|
|
65
|
+
"--manage-header",
|
|
66
|
+
default="true",
|
|
67
|
+
choices=("true", "false"),
|
|
68
|
+
help="false = contributor mode; main.run owns the shared header",
|
|
69
|
+
)
|
|
70
|
+
add_infra_args(p)
|
|
71
|
+
add_hardware_arg(p)
|
|
72
|
+
return p
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def run_entry(
|
|
76
|
+
argv: list[str] | None,
|
|
77
|
+
*,
|
|
78
|
+
prog: str,
|
|
79
|
+
description: str,
|
|
80
|
+
resolve_engine: EngineResolver,
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Parse the shared launcher args, load the staged config, and run the resolved engine.
|
|
83
|
+
|
|
84
|
+
The one on-cluster driver skeleton for both runtimes: parse → export infra env → load the
|
|
85
|
+
staged config (`load_config_uri`, which reads a ``gs://`` URI directly or a local path) →
|
|
86
|
+
``resolve_engine(ns)`` picks the engine callable → run it with the parsed subset + header mode.
|
|
87
|
+
"""
|
|
88
|
+
from .config import load_config_uri
|
|
89
|
+
|
|
90
|
+
configure_cli_logging()
|
|
91
|
+
p = build_parser(prog, description)
|
|
92
|
+
ns = p.parse_args(argv)
|
|
93
|
+
export_infra_env(ns)
|
|
94
|
+
# The driver half of the device carrier. The executor half rides the job spec
|
|
95
|
+
# (`hardware.spark_executor_env` / `hardware.ray_env_vars`) because a driver's environment does
|
|
96
|
+
# not reach a Spark executor or a Ray task worker.
|
|
97
|
+
export_hardware_env(ns)
|
|
98
|
+
models = parse_models(ns.models)
|
|
99
|
+
manage_header = ns.manage_header == "true"
|
|
100
|
+
engine_run, label = resolve_engine(ns)
|
|
101
|
+
_log.info(
|
|
102
|
+
"%s: runtime=%s config_uri=%s models=%s manage_header=%s",
|
|
103
|
+
prog,
|
|
104
|
+
label,
|
|
105
|
+
ns.config_uri,
|
|
106
|
+
models,
|
|
107
|
+
manage_header,
|
|
108
|
+
)
|
|
109
|
+
cfg: RunConfig = load_config_uri(ns.config_uri)
|
|
110
|
+
engine_run(cfg, models=models, manage_header=manage_header)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Infra-identity CLI args ↔ ``SF_*`` environment — the one delivery mapping.
|
|
2
|
+
|
|
3
|
+
Dataproc Serverless allowlists Spark property prefixes and rejects driver-env
|
|
4
|
+
(``spark.kubernetes.driverEnv.*`` → "unsupported properties"), so a batch cannot hand the
|
|
5
|
+
infra identity to the driver as environment variables. Instead every entrypoint accepts the
|
|
6
|
+
identity as ``--sf-*`` job args and exports them into ``os.environ`` before
|
|
7
|
+
`resolve` runs — keeping env-based ``Settings`` the
|
|
8
|
+
single local/cloud seam rather than forking a "resolve from args" path.
|
|
9
|
+
|
|
10
|
+
This module owns that mapping in ONE place so the seed job, the Spark forecast launcher
|
|
11
|
+
(``spark_entry``), the local submit helper (``submit``), and the Terraform modules that build the
|
|
12
|
+
``--sf-*`` arg list all stay in agreement. Local runs pass no ``--sf-*`` and use the ambient
|
|
13
|
+
``SF_*`` environment untouched.
|
|
14
|
+
|
|
15
|
+
Public surface: ``INFRA_ARG_ENV``, ``add_infra_args``, ``export_infra_env``, ``infra_args_from``.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
from typing import TYPE_CHECKING
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
import argparse
|
|
25
|
+
|
|
26
|
+
from .settings import Settings
|
|
27
|
+
|
|
28
|
+
# The (argparse dest → SF_* env var) mapping. ``add_infra_args`` registers ``--sf-*`` flags whose
|
|
29
|
+
# argparse dests are these first elements; ``export_infra_env`` copies each provided value into the
|
|
30
|
+
# matching env var before Settings.resolve().
|
|
31
|
+
INFRA_ARG_ENV: tuple[tuple[str, str], ...] = (
|
|
32
|
+
("sf_project_id", "SF_PROJECT_ID"),
|
|
33
|
+
("sf_connection", "SF_CONNECTION"),
|
|
34
|
+
("sf_warehouse_uri", "SF_WAREHOUSE_URI"),
|
|
35
|
+
("sf_dataset_id", "SF_DATASET_ID"),
|
|
36
|
+
# Only emitted when the deployment actually splits the registry from the source panel (the
|
|
37
|
+
# value is "" otherwise and `infra_args_from` skips empty values), so an existing deployment's
|
|
38
|
+
# submitted command is byte-identical. It has to travel, though: without it a cluster driver
|
|
39
|
+
# would re-resolve to the *source* dataset and write the run's rows to the wrong registry.
|
|
40
|
+
("sf_registry_dataset_id", "SF_REGISTRY_DATASET_ID"),
|
|
41
|
+
("sf_region", "SF_REGION"),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def add_infra_args(parser: argparse.ArgumentParser) -> None:
|
|
46
|
+
"""Register the ``--sf-*`` infra-identity flags on a parser (all default ``None``).
|
|
47
|
+
|
|
48
|
+
When unset (local runs) the ambient ``SF_*`` environment is used as-is; when set (a cluster
|
|
49
|
+
batch) `export_infra_env` promotes them to ``os.environ`` before resolution.
|
|
50
|
+
"""
|
|
51
|
+
for dest, env_name in INFRA_ARG_ENV:
|
|
52
|
+
flag = "--" + dest.replace("_", "-")
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
flag, type=str, default=None, help=f"sets {env_name} in the environment"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def export_infra_env(ns: argparse.Namespace) -> None:
|
|
59
|
+
"""Copy any provided ``--sf-*`` args from ``ns`` into ``os.environ`` (only when set).
|
|
60
|
+
|
|
61
|
+
Keeps env-based ``Settings.resolve()`` the single local/cloud seam without a parallel
|
|
62
|
+
"resolve from args" path. Values left unset are skipped, so a local run's ambient environment
|
|
63
|
+
is untouched.
|
|
64
|
+
"""
|
|
65
|
+
for dest, env_name in INFRA_ARG_ENV:
|
|
66
|
+
value = getattr(ns, dest, None)
|
|
67
|
+
if value:
|
|
68
|
+
os.environ[env_name] = value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def infra_args_from(settings: Settings) -> list[str]:
|
|
72
|
+
"""Build the ``--sf-*`` arg list carrying ``settings`` to a cluster batch (submit side).
|
|
73
|
+
|
|
74
|
+
The inverse of `add_infra_args` / `export_infra_env`: turns a resolved
|
|
75
|
+
`Settings` into the flat ``["--sf-project-id", ...]`` list a
|
|
76
|
+
Dataproc batch passes so the driver re-materializes the same identity via env.
|
|
77
|
+
"""
|
|
78
|
+
values = {
|
|
79
|
+
"sf_project_id": settings.project_id,
|
|
80
|
+
"sf_connection": settings.connection,
|
|
81
|
+
"sf_warehouse_uri": settings.warehouse_uri,
|
|
82
|
+
"sf_dataset_id": settings.dataset_id,
|
|
83
|
+
"sf_registry_dataset_id": settings.registry_dataset_id_override,
|
|
84
|
+
"sf_region": settings.region,
|
|
85
|
+
}
|
|
86
|
+
args: list[str] = []
|
|
87
|
+
for dest, _env_name in INFRA_ARG_ENV:
|
|
88
|
+
value = values[dest]
|
|
89
|
+
if value:
|
|
90
|
+
args += ["--" + dest.replace("_", "-"), value]
|
|
91
|
+
return args
|