flashspec 0.1.2__tar.gz → 0.1.3__tar.gz
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.
- {flashspec-0.1.2 → flashspec-0.1.3}/.github/workflows/benchmark.yml +1 -1
- {flashspec-0.1.2 → flashspec-0.1.3}/.github/workflows/ci.yml +5 -5
- {flashspec-0.1.2 → flashspec-0.1.3}/.github/workflows/gpu_tests.yml +2 -2
- {flashspec-0.1.2 → flashspec-0.1.3}/CHANGELOG.md +41 -1
- {flashspec-0.1.2 → flashspec-0.1.3}/CITATION.cff +1 -1
- {flashspec-0.1.2 → flashspec-0.1.3}/PKG-INFO +3 -2
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/__init__.py +4 -1
- flashspec-0.1.3/flashspec/kernels/__init__.py +72 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/pyproject.toml +7 -2
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/unit/test_verify_kernel.py +100 -0
- flashspec-0.1.2/flashspec/kernels/__init__.py +0 -18
- {flashspec-0.1.2 → flashspec-0.1.3}/.gitignore +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/.readthedocs.yaml +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/.zenodo.json +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/AGENTS.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/CONTRIBUTING.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/LICENSE +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/Makefile +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/PUBLISHING.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/README.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/README.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/baselines.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/benchmark_kernels.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/compare_baselines.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/configs/llama3_70b.yaml +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/configs/llama3_8b.yaml +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/configs/mistral_7b.yaml +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/results/.gitkeep +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/results/baseline.json +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/run_all.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/sweep_draft_sizes.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/benchmarks/sweep_gamma.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/deploy/Dockerfile +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/deploy/docker-compose.yml +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/deploy/k8s/flashspec-deployment.yaml +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/docs/api/index.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/docs/architecture.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/docs/bandit.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/docs/benchmarks.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/docs/index.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/docs/kernels.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/docs/mkdocs.yml +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/docs/requirements.txt +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/bandit/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/bandit/base.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/bandit/oracle.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/bandit/thompson.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/bandit/ucb.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/engine/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/engine/drafter.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/engine/speculative.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/engine/verifier.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/export/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/export/onnx.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/kernels/_reference.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/kernels/gather_kernel.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/kernels/verify_kernel.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/metrics/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/metrics/acceptance.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/metrics/latency.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/metrics/throughput.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/py.typed +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/sampling/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/sampling/rejection.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/sampling/typical.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/utils/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/utils/config.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/utils/device.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/flashspec/utils/logging.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/mkdocs.yml +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/notebooks/01_quickstart.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/paper/Makefile +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/paper/figures/.gitkeep +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/paper/flashspec.bib +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/paper/flashspec.tex +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/paper/joss/README.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/paper/joss/paper.bib +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/paper/joss/paper.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/scripts/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/scripts/check_regression.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/scripts/download_models.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/scripts/export_draft.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/scripts/profile_kernel.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/scripts/update_benchmark_results.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/social/linkedin_post.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/social/x_thread.md +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/chaos/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/chaos/test_bandit_adversarial.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/conftest.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/integration/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/integration/test_e2e_greedy.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/integration/test_e2e_sampling.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/integration/test_onnx_parity.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/unit/__init__.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/unit/test_acceptance.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/unit/test_bandit.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/unit/test_config.py +0 -0
- {flashspec-0.1.2 → flashspec-0.1.3}/tests/unit/test_sampling.py +0 -0
|
@@ -22,7 +22,7 @@ jobs:
|
|
|
22
22
|
python-version: "3.11"
|
|
23
23
|
cache: pip
|
|
24
24
|
- name: Install dev dependencies
|
|
25
|
-
run: pip install -e ".[dev]" --extra-index-url https://download.pytorch.org/whl/cpu
|
|
25
|
+
run: pip install -e ".[dev,gpu]" --extra-index-url https://download.pytorch.org/whl/cpu
|
|
26
26
|
- name: make lint
|
|
27
27
|
run: make lint
|
|
28
28
|
- name: pip-audit (fail on CRITICAL CVE — §15)
|
|
@@ -46,7 +46,7 @@ jobs:
|
|
|
46
46
|
python-version: "3.11"
|
|
47
47
|
cache: pip
|
|
48
48
|
- name: Install
|
|
49
|
-
run: pip install -e ".[dev]" --extra-index-url https://download.pytorch.org/whl/cpu
|
|
49
|
+
run: pip install -e ".[dev,gpu]" --extra-index-url https://download.pytorch.org/whl/cpu
|
|
50
50
|
- name: make test
|
|
51
51
|
run: make test
|
|
52
52
|
- name: Upload coverage XML
|
|
@@ -70,7 +70,7 @@ jobs:
|
|
|
70
70
|
python-version: "3.11"
|
|
71
71
|
cache: pip
|
|
72
72
|
- name: Install
|
|
73
|
-
run: pip install -e ".[dev]" --extra-index-url https://download.pytorch.org/whl/cpu
|
|
73
|
+
run: pip install -e ".[dev,gpu]" --extra-index-url https://download.pytorch.org/whl/cpu
|
|
74
74
|
- name: make test-chaos
|
|
75
75
|
run: make test-chaos
|
|
76
76
|
|
|
@@ -86,7 +86,7 @@ jobs:
|
|
|
86
86
|
python-version: "3.11"
|
|
87
87
|
cache: pip
|
|
88
88
|
- name: Install
|
|
89
|
-
run: pip install -e ".[dev]" --extra-index-url https://download.pytorch.org/whl/cpu
|
|
89
|
+
run: pip install -e ".[dev,gpu]" --extra-index-url https://download.pytorch.org/whl/cpu
|
|
90
90
|
- name: Download coverage artifact
|
|
91
91
|
uses: actions/download-artifact@v4
|
|
92
92
|
with:
|
|
@@ -117,7 +117,7 @@ jobs:
|
|
|
117
117
|
cache: pip
|
|
118
118
|
- name: Install with ONNX extras
|
|
119
119
|
run: |
|
|
120
|
-
pip install -e ".[dev,onnx]" \
|
|
120
|
+
pip install -e ".[dev,onnx,gpu]" \
|
|
121
121
|
--extra-index-url https://download.pytorch.org/whl/cpu
|
|
122
122
|
- name: Run ONNX parity tests
|
|
123
123
|
run: pytest tests/integration/test_onnx_parity.py -v
|
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
python-version: "3.11"
|
|
21
21
|
cache: pip
|
|
22
22
|
- name: Install
|
|
23
|
-
run: pip install -e ".[dev]"
|
|
23
|
+
run: pip install -e ".[dev,gpu]"
|
|
24
24
|
- name: make test-gpu
|
|
25
25
|
run: make test-gpu
|
|
26
26
|
- name: Run slow distribution tests (N=10,000 KS gate)
|
|
@@ -45,7 +45,7 @@ jobs:
|
|
|
45
45
|
python-version: "3.11"
|
|
46
46
|
cache: pip
|
|
47
47
|
- name: Install bench dependencies
|
|
48
|
-
run: pip install -e ".[bench]"
|
|
48
|
+
run: pip install -e ".[bench,gpu]"
|
|
49
49
|
- name: make bench-quick
|
|
50
50
|
run: make bench-quick
|
|
51
51
|
- name: Assert throughput ≥ 1.5× AR
|
|
@@ -10,7 +10,47 @@ Versioning follows [Semantic Versioning 2.0.0](https://semver.org/).
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
No unreleased changes. Next planned milestone: v0.2.0 (real GPU benchmark
|
|
14
|
+
results, completed paper figures, arXiv submission).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## [0.1.3] — Critical packaging fix (Windows/macOS install)
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Critical**: `pip install flashspec` failed completely on Windows and
|
|
22
|
+
macOS with `ERROR: No matching distribution found for triton>=3.0.0` —
|
|
23
|
+
Triton publishes official PyPI wheels for Linux only, but `triton>=3.0.0`
|
|
24
|
+
was a hard core dependency, making the package uninstallable on those
|
|
25
|
+
platforms. (Reported against v0.1.0–v0.1.2.)
|
|
26
|
+
- `pyproject.toml`: moved `triton` out of `dependencies` into a new
|
|
27
|
+
`gpu` extra with a `platform_system == 'Linux'` environment marker:
|
|
28
|
+
`pip install flashspec[gpu]`.
|
|
29
|
+
- `flashspec/kernels/__init__.py`: now imports `verify_kernel` and
|
|
30
|
+
`gather_kernel` inside a `try/except ImportError`. If Triton is
|
|
31
|
+
unavailable, `verify_tokens` and `gather_accepted` are bound to a
|
|
32
|
+
wrapper that raises an actionable `ImportError` only when *called*
|
|
33
|
+
(not at import time), explaining `pip install flashspec[gpu]`
|
|
34
|
+
(Linux-only) and pointing to the cross-platform
|
|
35
|
+
`flashspec.kernels._reference` module (identical numerics, verified in
|
|
36
|
+
`tests/unit/test_verify_kernel.py`). Adds module-level
|
|
37
|
+
`flashspec.kernels.TRITON_AVAILABLE: bool`.
|
|
38
|
+
- `tests/unit/test_verify_kernel.py`: added
|
|
39
|
+
`TestKernelsPackageWithoutTriton` (3 tests, CPU-only, no GPU/Triton
|
|
40
|
+
required) verifying the package imports cleanly and raises the
|
|
41
|
+
actionable error when Triton is absent.
|
|
42
|
+
- `.github/workflows/{ci,gpu_tests,benchmark}.yml`: updated `pip install`
|
|
43
|
+
steps to request the `gpu` extra wherever Triton-backed kernel tests
|
|
44
|
+
or benchmarks actually run (all on `ubuntu-22.04`/self-hosted Linux
|
|
45
|
+
runners, where the extra resolves normally).
|
|
46
|
+
- `README.md`: rewrote the Installation section — `pip install flashspec`
|
|
47
|
+
is now correctly described as cross-platform (CPU reference kernels),
|
|
48
|
+
with `pip install flashspec[gpu]` for Linux+CUDA accelerated kernels.
|
|
49
|
+
- Bumped version `0.1.0` → `0.1.3` in `pyproject.toml` and
|
|
50
|
+
`flashspec/__init__.py` (0.1.0–0.1.2 are broken on non-Linux and
|
|
51
|
+
should be marked yanked on PyPI after this release).
|
|
52
|
+
|
|
53
|
+
---
|
|
14
54
|
- Initial project scaffold following AGENTS.md specification.
|
|
15
55
|
- `flashspec.utils.config`: Pydantic v2 configuration models (`FlashSpecConfig`,
|
|
16
56
|
`BanditConfig`, `SamplingConfig`, `MetricsConfig`). All immutable (`frozen=True`).
|
|
@@ -27,7 +27,7 @@ abstract: >-
|
|
|
27
27
|
O(sqrt(K T log T)) regret bound. The output distribution is
|
|
28
28
|
provably identical to standard autoregressive sampling, verified
|
|
29
29
|
continuously via a Kolmogorov-Smirnov test in CI.
|
|
30
|
-
version: 0.1.
|
|
30
|
+
version: 0.1.3
|
|
31
31
|
date-released: "2026-06-12"
|
|
32
32
|
license: Apache-2.0
|
|
33
33
|
repository-code: "https://github.com/Mattral/FlashSpec"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flashspec
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Adaptive speculative-decoding inference engine with Triton-optimised verification and online bandit draft selection
|
|
5
5
|
Project-URL: Homepage, https://github.com/Mattral/FlashSpec
|
|
6
6
|
Project-URL: Repository, https://github.com/Mattral/FlashSpec
|
|
@@ -136,7 +136,6 @@ Requires-Dist: numpy>=1.26.0
|
|
|
136
136
|
Requires-Dist: pydantic>=2.0.0
|
|
137
137
|
Requires-Dist: torch>=2.2.0
|
|
138
138
|
Requires-Dist: transformers>=4.40.0
|
|
139
|
-
Requires-Dist: triton>=3.0.0
|
|
140
139
|
Provides-Extra: bench
|
|
141
140
|
Requires-Dist: lm-eval>=0.4; extra == 'bench'
|
|
142
141
|
Requires-Dist: matplotlib>=3.8; extra == 'bench'
|
|
@@ -152,6 +151,8 @@ Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
|
152
151
|
Provides-Extra: docs
|
|
153
152
|
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
154
153
|
Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
|
|
154
|
+
Provides-Extra: gpu
|
|
155
|
+
Requires-Dist: triton>=3.0.0; (platform_system == 'Linux') and extra == 'gpu'
|
|
155
156
|
Provides-Extra: onnx
|
|
156
157
|
Requires-Dist: onnx>=1.16; extra == 'onnx'
|
|
157
158
|
Requires-Dist: onnxruntime-gpu>=1.18; extra == 'onnx'
|
|
@@ -14,6 +14,7 @@ Public API surface (AGENTS.md §13.2 — do not modify without explicit approval
|
|
|
14
14
|
flashspec.register (draft model decorator)
|
|
15
15
|
flashspec.get_drafter
|
|
16
16
|
flashspec.list_drafters
|
|
17
|
+
flashspec.TRITON_AVAILABLE (bool — True only on Linux with triton installed)
|
|
17
18
|
|
|
18
19
|
References
|
|
19
20
|
----------
|
|
@@ -25,6 +26,7 @@ References
|
|
|
25
26
|
|
|
26
27
|
from flashspec.engine.drafter import get_drafter, list_drafters, register
|
|
27
28
|
from flashspec.engine.speculative import GenerationResult, SpeculativeEngine
|
|
29
|
+
from flashspec.kernels import TRITON_AVAILABLE
|
|
28
30
|
from flashspec.utils.config import BanditConfig, FlashSpecConfig, MetricsConfig, SamplingConfig
|
|
29
31
|
|
|
30
32
|
__all__ = [
|
|
@@ -34,10 +36,11 @@ __all__ = [
|
|
|
34
36
|
"MetricsConfig",
|
|
35
37
|
"SamplingConfig",
|
|
36
38
|
"SpeculativeEngine",
|
|
39
|
+
"TRITON_AVAILABLE",
|
|
37
40
|
"get_drafter",
|
|
38
41
|
"list_drafters",
|
|
39
42
|
"register",
|
|
40
43
|
]
|
|
41
44
|
|
|
42
|
-
__version__ = "0.1.
|
|
45
|
+
__version__ = "0.1.3"
|
|
43
46
|
__author__ = "Min Htet Myet (Mattral)"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Triton-optimised kernels sub-package.
|
|
2
|
+
|
|
3
|
+
All consumers of kernels must import from this module, not from the
|
|
4
|
+
individual kernel files directly. This is enforced by import-linter.
|
|
5
|
+
|
|
6
|
+
Private implementation modules:
|
|
7
|
+
- ``verify_kernel``: token acceptance kernel (requires Triton).
|
|
8
|
+
- ``gather_kernel``: accepted-token gather kernel (requires Triton).
|
|
9
|
+
- ``_reference``: pure-PyTorch reference implementations (cross-platform,
|
|
10
|
+
used by tests and as the runtime fallback when Triton is unavailable).
|
|
11
|
+
|
|
12
|
+
Notes
|
|
13
|
+
-----
|
|
14
|
+
Triton has no official PyPI wheels for Windows or macOS. On those
|
|
15
|
+
platforms (or any platform where ``triton`` is not installed),
|
|
16
|
+
``verify_tokens`` and ``gather_accepted`` are bound to wrappers that raise
|
|
17
|
+
a clear, actionable ``ImportError`` when called, rather than failing at
|
|
18
|
+
import time. Use ``flashspec.kernels._reference`` directly for a
|
|
19
|
+
cross-platform pure-PyTorch implementation with identical numerics
|
|
20
|
+
(verified in ``tests/unit/test_verify_kernel.py``).
|
|
21
|
+
|
|
22
|
+
To enable the accelerated kernels, install on Linux with a CUDA-capable
|
|
23
|
+
GPU::
|
|
24
|
+
|
|
25
|
+
pip install flashspec[gpu]
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from typing import TYPE_CHECKING, Any
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"gather_accepted",
|
|
34
|
+
"verify_tokens",
|
|
35
|
+
"TRITON_AVAILABLE",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
import torch
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
from flashspec.kernels.gather_kernel import gather_accepted
|
|
43
|
+
from flashspec.kernels.verify_kernel import verify_tokens
|
|
44
|
+
|
|
45
|
+
TRITON_AVAILABLE: bool = True
|
|
46
|
+
except ImportError as _exc: # pragma: no cover — exercised on non-Linux CI
|
|
47
|
+
_IMPORT_ERROR = _exc
|
|
48
|
+
TRITON_AVAILABLE = False
|
|
49
|
+
|
|
50
|
+
def _triton_unavailable(*_args: Any, **_kwargs: Any) -> "torch.Tensor":
|
|
51
|
+
"""Raise an actionable error explaining how to get Triton kernels.
|
|
52
|
+
|
|
53
|
+
Raises
|
|
54
|
+
------
|
|
55
|
+
ImportError
|
|
56
|
+
Always. Explains that Triton is not installed on this platform
|
|
57
|
+
and points to the pure-PyTorch reference implementation.
|
|
58
|
+
"""
|
|
59
|
+
raise ImportError(
|
|
60
|
+
"flashspec's accelerated Triton kernels are not available "
|
|
61
|
+
f"(original error: {_IMPORT_ERROR}).\n\n"
|
|
62
|
+
"Triton ships official PyPI wheels for Linux only. To use the "
|
|
63
|
+
"GPU-accelerated kernels, install on Linux with a CUDA-capable "
|
|
64
|
+
"GPU via:\n\n pip install flashspec[gpu]\n\n"
|
|
65
|
+
"For a cross-platform pure-PyTorch implementation with "
|
|
66
|
+
"identical numerics, use "
|
|
67
|
+
"flashspec.kernels._reference.verify_tokens_reference and "
|
|
68
|
+
"flashspec.kernels._reference.gather_accepted_reference instead."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
verify_tokens = _triton_unavailable
|
|
72
|
+
gather_accepted = _triton_unavailable
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flashspec"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.3"
|
|
8
8
|
description = "Adaptive speculative-decoding inference engine with Triton-optimised verification and online bandit draft selection"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -20,13 +20,18 @@ classifiers = [
|
|
|
20
20
|
]
|
|
21
21
|
dependencies = [
|
|
22
22
|
"torch>=2.2.0",
|
|
23
|
-
"triton>=3.0.0",
|
|
24
23
|
"transformers>=4.40.0",
|
|
25
24
|
"pydantic>=2.0.0",
|
|
26
25
|
"numpy>=1.26.0",
|
|
27
26
|
]
|
|
28
27
|
|
|
29
28
|
[project.optional-dependencies]
|
|
29
|
+
# Triton has no official PyPI wheels for Windows or macOS — it is Linux-only.
|
|
30
|
+
# Without this extra, flashspec runs on the pure-PyTorch reference kernels
|
|
31
|
+
# (flashspec.kernels._reference), which are numerically identical (see
|
|
32
|
+
# tests/unit/test_verify_kernel.py) but slower. Install this extra on Linux
|
|
33
|
+
# with a CUDA-capable GPU to enable the accelerated Triton kernels.
|
|
34
|
+
gpu = ["triton>=3.0.0; platform_system == 'Linux'"]
|
|
30
35
|
onnx = ["onnx>=1.16", "onnxruntime-gpu>=1.18"]
|
|
31
36
|
dev = [
|
|
32
37
|
"pytest>=8.0",
|
|
@@ -368,3 +368,103 @@ class TestVerifyTokensTritonParity:
|
|
|
368
368
|
assert (first_rej.cpu() == 0).all(), (
|
|
369
369
|
f"Expected all first_rej==0, got {first_rej.cpu().tolist()}"
|
|
370
370
|
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
# ── Triton-unavailable fallback (cross-platform, no GPU required) ───────────
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
class TestKernelsPackageWithoutTriton:
|
|
377
|
+
"""``flashspec.kernels`` must import successfully even if Triton is
|
|
378
|
+
absent (e.g. on Windows or macOS, where Triton ships no PyPI wheels).
|
|
379
|
+
|
|
380
|
+
Simulates the absence of Triton by removing ``triton`` and the kernel
|
|
381
|
+
submodules from ``sys.modules`` and blocking their import, then
|
|
382
|
+
re-importing ``flashspec.kernels`` fresh.
|
|
383
|
+
"""
|
|
384
|
+
|
|
385
|
+
def test_kernels_package_imports_without_triton(self) -> None:
|
|
386
|
+
"""``import flashspec.kernels`` succeeds even when Triton is missing."""
|
|
387
|
+
import builtins
|
|
388
|
+
import importlib
|
|
389
|
+
import sys
|
|
390
|
+
|
|
391
|
+
real_import = builtins.__import__
|
|
392
|
+
|
|
393
|
+
def fake_import(name, *args, **kwargs):
|
|
394
|
+
if name == "triton" or name.startswith("triton."):
|
|
395
|
+
raise ModuleNotFoundError(f"No module named '{name}'")
|
|
396
|
+
return real_import(name, *args, **kwargs)
|
|
397
|
+
|
|
398
|
+
# Drop any cached modules so re-import re-triggers the try/except.
|
|
399
|
+
for mod in list(sys.modules):
|
|
400
|
+
if mod.startswith("flashspec.kernels"):
|
|
401
|
+
del sys.modules[mod]
|
|
402
|
+
|
|
403
|
+
builtins.__import__ = fake_import
|
|
404
|
+
try:
|
|
405
|
+
kernels = importlib.import_module("flashspec.kernels")
|
|
406
|
+
finally:
|
|
407
|
+
builtins.__import__ = real_import
|
|
408
|
+
for mod in list(sys.modules):
|
|
409
|
+
if mod.startswith("flashspec.kernels"):
|
|
410
|
+
del sys.modules[mod]
|
|
411
|
+
|
|
412
|
+
assert kernels.TRITON_AVAILABLE is False, (
|
|
413
|
+
"TRITON_AVAILABLE should be False when triton import fails"
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
def test_verify_tokens_raises_actionable_import_error_without_triton(self) -> None:
|
|
417
|
+
"""Calling verify_tokens without Triton raises ImportError mentioning flashspec[gpu]."""
|
|
418
|
+
import builtins
|
|
419
|
+
import importlib
|
|
420
|
+
import sys
|
|
421
|
+
|
|
422
|
+
real_import = builtins.__import__
|
|
423
|
+
|
|
424
|
+
def fake_import(name, *args, **kwargs):
|
|
425
|
+
if name == "triton" or name.startswith("triton."):
|
|
426
|
+
raise ModuleNotFoundError(f"No module named '{name}'")
|
|
427
|
+
return real_import(name, *args, **kwargs)
|
|
428
|
+
|
|
429
|
+
for mod in list(sys.modules):
|
|
430
|
+
if mod.startswith("flashspec.kernels"):
|
|
431
|
+
del sys.modules[mod]
|
|
432
|
+
|
|
433
|
+
builtins.__import__ = fake_import
|
|
434
|
+
try:
|
|
435
|
+
kernels = importlib.import_module("flashspec.kernels")
|
|
436
|
+
with pytest.raises(ImportError, match=r"flashspec\[gpu\]"):
|
|
437
|
+
kernels.verify_tokens(None, None, None, None)
|
|
438
|
+
finally:
|
|
439
|
+
builtins.__import__ = real_import
|
|
440
|
+
for mod in list(sys.modules):
|
|
441
|
+
if mod.startswith("flashspec.kernels"):
|
|
442
|
+
del sys.modules[mod]
|
|
443
|
+
|
|
444
|
+
def test_gather_accepted_raises_actionable_import_error_without_triton(self) -> None:
|
|
445
|
+
"""Calling gather_accepted without Triton raises ImportError mentioning _reference."""
|
|
446
|
+
import builtins
|
|
447
|
+
import importlib
|
|
448
|
+
import sys
|
|
449
|
+
|
|
450
|
+
real_import = builtins.__import__
|
|
451
|
+
|
|
452
|
+
def fake_import(name, *args, **kwargs):
|
|
453
|
+
if name == "triton" or name.startswith("triton."):
|
|
454
|
+
raise ModuleNotFoundError(f"No module named '{name}'")
|
|
455
|
+
return real_import(name, *args, **kwargs)
|
|
456
|
+
|
|
457
|
+
for mod in list(sys.modules):
|
|
458
|
+
if mod.startswith("flashspec.kernels"):
|
|
459
|
+
del sys.modules[mod]
|
|
460
|
+
|
|
461
|
+
builtins.__import__ = fake_import
|
|
462
|
+
try:
|
|
463
|
+
kernels = importlib.import_module("flashspec.kernels")
|
|
464
|
+
with pytest.raises(ImportError, match="_reference"):
|
|
465
|
+
kernels.gather_accepted(None, None)
|
|
466
|
+
finally:
|
|
467
|
+
builtins.__import__ = real_import
|
|
468
|
+
for mod in list(sys.modules):
|
|
469
|
+
if mod.startswith("flashspec.kernels"):
|
|
470
|
+
del sys.modules[mod]
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"""Triton-optimised kernels sub-package.
|
|
2
|
-
|
|
3
|
-
All consumers of kernels must import from this module, not from the
|
|
4
|
-
individual kernel files directly. This is enforced by import-linter.
|
|
5
|
-
|
|
6
|
-
Private implementation modules:
|
|
7
|
-
- ``verify_kernel``: token acceptance kernel.
|
|
8
|
-
- ``gather_kernel``: accepted-token gather kernel.
|
|
9
|
-
- ``_reference``: pure-PyTorch reference implementations (tests only).
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
from flashspec.kernels.gather_kernel import gather_accepted
|
|
13
|
-
from flashspec.kernels.verify_kernel import verify_tokens
|
|
14
|
-
|
|
15
|
-
__all__ = [
|
|
16
|
-
"gather_accepted",
|
|
17
|
-
"verify_tokens",
|
|
18
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|