Spaces:
Running on Zero
Running on Zero
File size: 8,374 Bytes
8028640 279522c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | """Enrollment, the no-lookahead guarantee at the runtime layer, and tiering.
These cover the parts of the acceptance list that live between the adapters and
the UI: that enrollment is idempotent and refuses anything it cannot load
safely, that `load_context` cannot hand a model a bar from the future, and that
a CPU-tier model either meets its budget or is recorded as demoted.
"""
from __future__ import annotations
import numpy as np
import pandas as pd
import pytest
from src import config, runtime
from src.adapters import LookaheadError
from tests.fixture import synth
@pytest.fixture
def seeded_store(store):
"""A store with prices and one enrolled baseline."""
bars = synth(600)
for year, chunk in bars.groupby(bars["ts"].dt.year):
store.write_parquet(config.prices_path("BTC-USD", "1h", int(year)), chunk)
outcome = runtime.enroll(store, "baseline", "baseline/random-walk",
enrolled_by="tests")
assert outcome.ok, outcome.message
return store
# --------------------------------------------------------------------------
# 5. Enrollment is idempotent, and refuses what it cannot vouch for
# --------------------------------------------------------------------------
def test_enrolling_the_same_revision_twice_is_a_no_op(store):
first = runtime.enroll(store, "baseline", "baseline/drift", enrolled_by="a")
registry = store.get_registry()
second = runtime.enroll(store, "baseline", "baseline/drift", enrolled_by="b",
registry=registry)
assert first.ok and second.ok
assert second.already is True
assert len(registry["models"]) == 1
# The second attempt must not have rewritten who enrolled it.
assert registry["models"]["drift"]["enrolled_by"] == "a"
def test_unsupported_family_is_refused_with_a_clear_message(store):
outcome = runtime.enroll(store, "llama", "meta-llama/Llama-3-8B")
assert not outcome.ok
assert "not a supported adapter family" in outcome.message
assert "baseline" in outcome.message # names what *is* supported
def test_a_malformed_model_id_is_refused(store):
for bad in ("not-a-model-id", "../../etc/passwd", "a/b/c", ""):
outcome = runtime.enroll(store, "baseline", bad)
assert not outcome.ok, f"{bad!r} was accepted"
def test_enrolling_under_the_wrong_family_is_refused(store):
"""A known Chronos model must not be loaded through the Kronos loader."""
outcome = runtime.enroll(store, "kronos", "amazon/chronos-bolt-tiny")
assert not outcome.ok
assert "chronos" in outcome.message
def test_enrollment_records_what_it_verified(store):
runtime.enroll(store, "baseline", "baseline/bootstrap", enrolled_by="tests")
entry = store.get_registry()["models"]["bootstrap"]
assert entry["revision"] # pinned
assert entry["smoke_test"]["ok"] is True # actually ran
assert entry["capabilities"]["output"] == "ohlcv_paths"
assert entry["enrolled_by"] == "tests"
assert entry["inference_version"]
# --------------------------------------------------------------------------
# 2. No lookahead, at the runtime layer
# --------------------------------------------------------------------------
def test_load_context_never_returns_a_bar_after_the_cut(seeded_store):
prices = seeded_store.get_prices("BTC-USD", "1h")
cut = pd.to_datetime(prices["ts"], utc=True).iloc[300]
context = runtime.load_context(seeded_store, "BTC-USD", "1h", as_of=cut)
assert pd.to_datetime(context["ts"], utc=True).max() <= cut
def test_an_as_of_forecast_is_issued_from_the_cut_not_the_clock(seeded_store):
prices = seeded_store.get_prices("BTC-USD", "1h")
cut = pd.to_datetime(prices["ts"], utc=True).iloc[400]
run = runtime.run_forecast(seeded_store, "random-walk", "BTC-USD", "1h",
horizon=6, seed=1, as_of=cut, archive=False)
assert run.issued_ts <= cut
assert run.target_ts[0] > cut
def test_a_context_reaching_past_the_issue_moment_raises(seeded_store):
"""The adapter re-checks, so a caller cannot smuggle one past it."""
from src.adapters import get_adapter
context = runtime.load_context(seeded_store, "BTC-USD", "1h")
stale = pd.to_datetime(context["ts"], utc=True).iloc[-10]
adapter = get_adapter("baseline", "baseline/random-walk").load()
with pytest.raises(LookaheadError):
adapter.predict(context, horizon=4, seed=0, issued_ts=stale)
def test_forecast_targets_start_after_the_context_ends(seeded_store):
run = runtime.run_forecast(seeded_store, "random-walk", "BTC-USD", "1h",
horizon=8, seed=3, archive=False)
assert run.target_ts[0] > run.issued_ts
assert len(run.target_ts) == 8
# --------------------------------------------------------------------------
# 7. Tiering
# --------------------------------------------------------------------------
def test_a_cpu_tier_model_either_meets_the_budget_or_is_recorded_as_demoted(store):
"""The rule the bootstrap script applies, checked directly.
The registry on the Hub is written by a measurement run; this asserts the
rule that run enforces, so a change to the thresholds cannot silently let a
model sit in the CPU tier while missing its budget.
"""
from scripts.bootstrap_registry import measure
context = synth(config.DEFAULT_CONTEXT_BARS)
latency = measure("baseline", "baseline/random-walk", context)
assert latency["cold_s"] <= config.CPU_BUDGET_COLD_S
assert latency["warm_s"] <= config.CPU_BUDGET_WARM_S
assert latency["hardware"] == "cpu"
assert latency["demoted"] is False
# The measurement is meaningless without the machine it was taken on.
assert latency["machine"]
def test_the_demotion_rule_fires_when_a_budget_is_missed(monkeypatch):
"""A CPU-declared model that is too slow must come back as gpu."""
from scripts import bootstrap_registry
context = synth(config.DEFAULT_CONTEXT_BARS)
monkeypatch.setattr(config, "CPU_BUDGET_WARM_S", 0.0)
latency = bootstrap_registry.measure("baseline", "baseline/drift", context)
assert latency["declared_hardware"] == "cpu"
assert latency["hardware"] == "gpu"
assert latency["demoted"] is True
# --------------------------------------------------------------------------
# Anonymous session cap
# --------------------------------------------------------------------------
def test_the_session_cap_is_a_real_number():
assert config.ANON_SESSION_CAP > 0
# --------------------------------------------------------------------------
# ZeroGPU: constructing an adapter must not touch CUDA
# --------------------------------------------------------------------------
def test_constructing_an_adapter_never_probes_the_device(monkeypatch):
"""Regression: on ZeroGPU an out-of-context CUDA probe is fatal.
`torch.cuda.is_available()` raises unless it is called inside a
`@spaces.GPU` function. Probing eagerly in `__init__` made merely building
an adapter fail -- including for CPU-tier models that never wanted a GPU,
which took the whole forecast endpoint down on ZeroGPU.
"""
from src.adapters import base, get_adapter
def explode():
raise AssertionError("device was probed at construction time")
monkeypatch.setattr(base, "default_device", explode)
for family, model_id in (("baseline", "baseline/random-walk"),
("chronos", "amazon/chronos-bolt-tiny"),
("kronos", "NeoQuasar/Kronos-mini"),
("timesfm", "google/timesfm-2.5-200m-pytorch")):
adapter = get_adapter(family, model_id)
# Capabilities must be readable without a device, because that is how
# `run_forecast` decides whether a GPU is even needed.
caps = adapter.capabilities()
assert caps.hardware in ("cpu", "gpu")
assert caps.output in ("quantile_line", "ohlcv_paths")
def test_capabilities_are_readable_without_loading_weights():
from src.adapters import get_adapter
adapter = get_adapter("kronos", "NeoQuasar/Kronos-base")
caps = adapter.capabilities()
assert adapter._model is None, "capabilities() loaded the model"
assert caps.output == "ohlcv_paths"
assert caps.hardware == "gpu"
|