Skip to content

Commit 5abb8a7

Browse files
authored
chore: allow transformers>=5 (#360)
2 parents d9efbff + 2f51531 commit 5abb8a7

9 files changed

Lines changed: 316 additions & 295 deletions

File tree

pyproject.toml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ classifiers = [
5151
"Topic :: Software Development :: Libraries :: Python Modules",
5252
]
5353
# NOTE: dependency version notes
54-
# - huggingface-hub can't bump to v1+ until transformers bumps to v5
54+
# - transformers>=5 (CVE-2026-1839 fix) requires huggingface-hub>=1; the upper
55+
# bounds were widened together (transformers<6, huggingface-hub<2). Reaching
56+
# transformers 5 also requires tfc-t0>=0.2 (older tfc-t0 capped hub<1).
5557
# - lightning and pytorch-lightning are the same library
5658
# NOTE: dependencies pinned for faster pip installs
5759
# versions chosen from uv's dependency resolution
@@ -66,7 +68,7 @@ dependencies = [
6668
"fire",
6769
"fsspec>=2025.9.0",
6870
"gluonts[torch]",
69-
"huggingface-hub>=0.36.2,<1.0",
71+
"huggingface-hub>=0.36.2,<2.0",
7072
"hydra-core>=1.3.2",
7173
"lightgbm>=4.6.0",
7274
"lightning-utilities>=0.15.2",
@@ -91,18 +93,17 @@ dependencies = [
9193
"statsforecast>=2.0.2",
9294
"tabpfn-time-series==1.0.3 ; python_full_version < '3.13'",
9395
"tensorboard>=2.20.0",
94-
"tfc-t0>=0.1.2 ; python_full_version >= '3.11' and python_full_version < '3.14'",
95-
"timecopilot-chronos-forecasting>=0.2.1",
96-
"timecopilot-granite-tsfm>=0.1.2",
97-
"timecopilot-timesfm>=0.2.1",
98-
"timecopilot-tirex>=0.1.0 ; python_full_version >= '3.11'",
99-
"timecopilot-tirex>=0.1.1",
96+
"tfc-t0>=0.2.0 ; python_full_version >= '3.11' and python_full_version < '3.14'",
97+
"timecopilot-chronos-forecasting>=0.2.2",
98+
"timecopilot-granite-tsfm>=0.2.1 ; python_full_version >= '3.11' and python_full_version < '3.14'",
99+
"timecopilot-timesfm>=0.3.0",
100+
"timecopilot-tirex>=0.1.1 ; python_full_version >= '3.11'",
100101
"timecopilot-toto-2>=0.1.1",
101-
"timecopilot-toto>=0.1.6",
102+
"timecopilot-toto>=0.1.7",
102103
"timecopilot-uni2ts>=0.1.2 ; python_full_version < '3.14'",
103104
"torchmetrics>=1.8.2",
104-
"transformers>=4.41,<5 ; python_full_version < '3.13'",
105-
"transformers>=4.48,<5 ; python_full_version >= '3.13'",
105+
"transformers>=4.41,<6 ; python_full_version < '3.13'",
106+
"transformers>=4.48,<6 ; python_full_version >= '3.13'",
106107
"tsfeatures>=0.4.5",
107108
"utilsforecast[plotting]>=0.2.15",
108109
"xgboost>=3.2.0",

tests/models/conftest.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
from timecopilot.models.ensembles.median import MedianEnsemble
66
from timecopilot.models.foundation.chronos import Chronos
7-
from timecopilot.models.foundation.flowstate import FlowState
87
from timecopilot.models.foundation.moirai import Moirai
9-
from timecopilot.models.foundation.patchtst_fm import PatchTSTFM
108
from timecopilot.models.foundation.timesfm import TimesFM
119
from timecopilot.models.foundation.toto import Toto
1210
from timecopilot.models.ml import (
@@ -99,14 +97,6 @@ def disable_mps_session(monkeypatch):
9997
Chronos(repo_id="amazon/chronos-bolt-tiny", alias="Chronos-Bolt"),
10098
Chronos(repo_id="amazon/chronos-2", alias="Chronos-2"),
10199
Chronos(repo_id="amazon/chronos-2", alias="Chronos-2", batch_size=2),
102-
FlowState(repo_id="ibm-research/flowstate"),
103-
FlowState(
104-
repo_id="ibm-granite/granite-timeseries-flowstate-r1",
105-
alias="FlowState-Granite",
106-
),
107-
PatchTSTFM(
108-
context_length=2_048,
109-
),
110100
Toto(context_length=256, batch_size=2),
111101
Toto(
112102
repo_id="Datadog/Toto-2.0-4m",
@@ -150,6 +140,19 @@ def disable_mps_session(monkeypatch):
150140

151141
models.append(T0(context_length=256, batch_size=2))
152142

143+
if (3, 11) <= sys.version_info < (3, 14):
144+
from timecopilot.models.foundation.flowstate import FlowState
145+
from timecopilot.models.foundation.patchtst_fm import PatchTSTFM
146+
147+
models.append(FlowState(repo_id="ibm-research/flowstate"))
148+
models.append(
149+
FlowState(
150+
repo_id="ibm-granite/granite-timeseries-flowstate-r1",
151+
alias="FlowState-Granite",
152+
)
153+
)
154+
models.append(PatchTSTFM(context_length=2_048))
155+
153156
if sys.version_info < (3, 13):
154157
from tabpfn_time_series import TabPFNMode
155158

tests/models/foundation/test_flowstate.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
import sys
2+
13
import numpy as np
24
import pandas as pd
5+
import pytest
6+
7+
if sys.version_info < (3, 11) or sys.version_info >= (3, 14):
8+
pytest.skip(
9+
"FlowState requires Python >= 3.11 and < 3.14",
10+
allow_module_level=True,
11+
)
312

413
from timecopilot import TimeCopilotForecaster
514
from timecopilot.models.foundation.flowstate import FlowState

tests/models/foundation/test_timesfm.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,9 @@ def test_load_model_from_local_path(mocker, model_class, mock_paths):
4747
expected_path = os.path.join(local_path, "torch_model.ckpt")
4848
mock_loader[0].assert_called_once_with(path=expected_path)
4949
elif model_class is _TimesFMV2_p5:
50-
expected_predictor = mock_loader[
51-
0
52-
].return_value.model.load_checkpoint.return_value
53-
assert predictor is expected_predictor
54-
mock_loader[0].return_value.model.load_checkpoint.assert_called_once_with(
55-
os.path.join(local_path, "model.safetensors")
56-
)
50+
# `from_pretrained` handles both local directories and HF repos.
51+
assert predictor is mock_loader[0].from_pretrained.return_value
52+
mock_loader[0].from_pretrained.assert_called_once_with(local_path)
5753

5854

5955
@pytest.mark.parametrize("model_class, mock_paths", MODEL_PARAMS)

tests/models/test_models.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,26 @@ def test_t0_import_fails():
3939
assert "requires Python >= 3.11 and < 3.14" in str(excinfo.value)
4040

4141

42+
@pytest.mark.skipif(
43+
(3, 11) <= sys.version_info < (3, 14),
44+
reason="FlowState requires Python >= 3.11 and < 3.14",
45+
)
46+
def test_flowstate_import_fails():
47+
with pytest.raises(ImportError) as excinfo:
48+
from timecopilot.models.foundation.flowstate import FlowState # noqa: F401
49+
assert "requires Python >= 3.11 and < 3.14" in str(excinfo.value)
50+
51+
52+
@pytest.mark.skipif(
53+
(3, 11) <= sys.version_info < (3, 14),
54+
reason="PatchTSTFM requires Python >= 3.11 and < 3.14",
55+
)
56+
def test_patchtst_fm_import_fails():
57+
with pytest.raises(ImportError) as excinfo:
58+
from timecopilot.models.foundation.patchtst_fm import PatchTSTFM # noqa: F401
59+
assert "requires Python >= 3.11 and < 3.14" in str(excinfo.value)
60+
61+
4262
@pytest.mark.skipif(
4363
sys.version_info < (3, 13),
4464
reason="Sundial requires Python < 3.13",

timecopilot/models/foundation/flowstate.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import sys
12
from contextlib import contextmanager
23

4+
if sys.version_info < (3, 11) or sys.version_info >= (3, 14):
5+
raise ImportError("FlowState requires Python >= 3.11 and < 3.14")
6+
37
import numpy as np
48
import pandas as pd
59
import torch
@@ -121,7 +125,7 @@ def _predict_batch(
121125
prediction_length=h,
122126
scale_factor=scale_factor,
123127
batch_first=False,
124-
).prediction_outputs
128+
).quantile_outputs
125129
fcst = fcst.squeeze(-1).transpose(-1, -2) # now shape is (batch, h, quantiles)
126130
fcst_mean = fcst[..., supported_quantiles.index(0.5)]
127131
fcst_mean_np = fcst_mean.detach().numpy(force=True)

timecopilot/models/foundation/patchtst_fm.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import sys
12
from contextlib import contextmanager
23

4+
if sys.version_info < (3, 11) or sys.version_info >= (3, 14):
5+
raise ImportError("PatchTSTFM requires Python >= 3.11 and < 3.14")
6+
37
import numpy as np
48
import pandas as pd
59
import torch
@@ -128,7 +132,7 @@ def _predict_batch(
128132
quantile_levels=quantile_levels,
129133
# scale_factor=scale_factor,
130134
# batch_first=False,
131-
).quantile_predictions
135+
).quantile_outputs
132136
fcst = fcst.squeeze(-1).transpose(-1, -2) # now shape is (batch, h, quantiles)
133137

134138
# may not be the ideal solution, but this should be more adaptable

timecopilot/models/foundation/timesfm.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,10 @@ def _get_predictor(
142142
self,
143143
prediction_length: int,
144144
) -> TimesFM_2p5_200M_torch:
145-
# automatically detect the best device
146-
# https://github.com/TimeCopilot/timesfm/blob/b810bbdf9f8a1e66396e7bd5cdb3b005e9116d86/src/timesfm/timesfm_2p5/timesfm_2p5_torch.py#L71
147-
if os.path.exists(self.repo_id):
148-
path = os.path.join(self.repo_id, "model.safetensors")
149-
tfm = TimesFM_2p5_200M_torch().model.load_checkpoint(path)
150-
elif repo_exists(self.repo_id):
145+
# `from_pretrained` handles both a local directory containing
146+
# `model.safetensors` and a Hugging Face repo id, and the model picks
147+
# the best available device on load.
148+
if os.path.exists(self.repo_id) or repo_exists(self.repo_id):
151149
tfm = TimesFM_2p5_200M_torch.from_pretrained(self.repo_id)
152150
else:
153151
raise OSError(

0 commit comments

Comments
 (0)