flash-mamba 0.1.0__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.
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Add `create_mamba()` for official Mamba and Mamba2 instances.
6
+ - Expose lazy `Mamba` / `Mamba2` imports and actionable backend import errors.
7
+ - Add read-only environment diagnostics through Python and `flash-mamba doctor`.
8
+ - Package a pure Python wheel and sdist; keep GPU backend installation separate.
9
+ - Add wrapper tests, opt-in upstream GPU comparisons, CI, and release instructions.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Flash Mamba contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ include CHANGELOG.md
2
+ include VALIDATION.md
3
+ recursive-include docs *.md
4
+ recursive-include scripts *.py
5
+ recursive-include tests *.py
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: flash-mamba
3
+ Version: 0.1.0
4
+ Summary: A lightweight convenience wrapper for the official mamba-ssm package
5
+ Author: Jellyfish042
6
+ License-Expression: MIT
7
+ Keywords: mamba,mamba-ssm,pytorch,state-space-model
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Provides-Extra: ssm
17
+ Requires-Dist: mamba-ssm<3,>=2.3.2.post1; extra == "ssm"
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=8; extra == "dev"
20
+ Requires-Dist: build>=1.2; extra == "dev"
21
+ Requires-Dist: twine>=6; extra == "dev"
22
+ Requires-Dist: ruff>=0.11; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # flash-mamba
26
+
27
+ `flash-mamba` 是官方 [mamba-ssm](https://github.com/state-spaces/mamba) 的轻量包装库。
28
+ 统一创建 Mamba / Mamba2,提供懒加载和环境诊断,返回官方模型实例。
29
+ 训练、推理缓存、参数默认值、`state_dict` 和内核行为均由官方实现决定。
30
+
31
+ 这是独立项目,与官方团队没有隶属关系。项目名称不代表新增加速内核或性能提升。
32
+ v0.1.0 的封装目标是 PyPI `mamba-ssm==2.3.2.post1`;Mamba3 不在本次范围内。
33
+ 当前本机只验证了包装层和发行包,真实 GPU 验证需在 Linux 环境完成。
34
+
35
+ ## 安装
36
+
37
+ 包装层需要 Python 3.10+,自身没有运行时依赖,可在 Windows 导入和运行诊断。
38
+ 发行包发布前,从本地安装:
39
+
40
+ ```bash
41
+ python -m pip install .
42
+ ```
43
+
44
+ 发布到 PyPI 后才可使用:
45
+
46
+ ```bash
47
+ python -m pip install flash-mamba
48
+ ```
49
+
50
+ 实际模型运行需要先在 **Linux(Windows 用户可使用 WSL2)** 安装适配设备的
51
+ PyTorch,再安装官方 GPU 后端。包装层不提供 CPU 替代实现或原生 Windows 内核。
52
+ CUDA/ROCm、编译工具和模型的具体要求以
53
+ [官方安装文档](https://github.com/state-spaces/mamba#installation) 为准。
54
+
55
+ ```bash
56
+ # 先按 https://pytorch.org/get-started/locally/ 安装对应的 GPU 版 PyTorch
57
+ python -m pip install packaging ninja wheel setuptools
58
+ python -m pip install "mamba-ssm[causal-conv1d]==2.3.2.post1" --no-build-isolation
59
+ python -m pip install .
60
+ flash-mamba doctor
61
+ ```
62
+
63
+ `ssm` extra 声明 `mamba-ssm>=2.3.2.post1,<3`。安装前仍需先配置 GPU 版 PyTorch
64
+ 和官方构建依赖:
65
+
66
+ ```bash
67
+ python -m pip install ".[ssm]" --no-build-isolation
68
+ ```
69
+
70
+ 显式两步安装更方便排查官方编译或二进制兼容问题。包装层不会自动安装、下载或
71
+ 替换 PyTorch。官方 `main` 与 PyPI 发行版的安装默认值可能不同;当前 `main`
72
+ 默认不构建 `selective_scan_cuda`,Mamba-1 使用该扩展时需要显式开启:
73
+
74
+ ```bash
75
+ # 仅当选择官方最新源码时使用;并非 v0.1.0 已验证的发行版组合
76
+ MAMBA_KEEP_CUDA_BUILD=TRUE python -m pip install \
77
+ "mamba-ssm[causal-conv1d] @ git+https://github.com/state-spaces/mamba.git" \
78
+ --no-build-isolation
79
+ ```
80
+
81
+ ## 使用
82
+
83
+ ```python
84
+ import torch
85
+ from flash_mamba import create_mamba
86
+
87
+ x = torch.randn(2, 64, 128, device="cuda")
88
+ model = create_mamba(
89
+ d_model=128,
90
+ variant="mamba2", # 默认;Mamba-1 使用 "mamba"
91
+ d_state=64,
92
+ d_conv=4,
93
+ expand=2,
94
+ headdim=64,
95
+ device="cuda",
96
+ )
97
+ y = model(x)
98
+ assert y.shape == x.shape
99
+ y.square().mean().backward()
100
+ ```
101
+
102
+ `create_mamba` 只校验正整数 `d_model` 和 `variant`;其余关键字直接传给官方构造函数。
103
+ 不额外添加残差、归一化、转置或模型包装。Mamba2 的 `d_ssm`(默认为
104
+ `expand * d_model`,分布式时还受 `world_size` 影响)必须满足官方 `headdim`
105
+ 约束;例如 `d_model=16, expand=2, headdim=64` 不合法。
106
+
107
+ 也可以直接导入官方类,第一次访问时才加载 `mamba_ssm`:
108
+
109
+ ```python
110
+ from flash_mamba import Mamba, Mamba2
111
+
112
+ model = Mamba(d_model=128, device="cuda")
113
+ ```
114
+
115
+ 返回的实例可直接调用 `.to()`、`.parameters()`、`.state_dict()`、
116
+ `.allocate_inference_cache()` 和官方支持的 forward 关键字。
117
+ `import flash_mamba` 本身不导入 PyTorch 或 `mamba_ssm`。
118
+ 后端导入失败会抛出 `BackendUnavailableError`,保留原异常链;构造函数参数错误
119
+ 和模型执行错误直接沿用官方异常。
120
+
121
+ ## 环境诊断
122
+
123
+ ```bash
124
+ flash-mamba doctor
125
+ flash-mamba doctor --json
126
+ python -m flash_mamba doctor --json
127
+ ```
128
+
129
+ 检查已安装版本、PyTorch/官方后端能否导入、CUDA/ROCm 是否可访问。
130
+ 退出码 0 表示可以尝试 GPU smoke test;退出码 1 表示检查未通过。
131
+ 成功导入不等于内核可运行,真实前向/反向需执行下面的测试。
132
+ `flash_mamba.diagnose()` 返回相同的字典结果,不修改环境。
133
+
134
+ ## 开发与验证
135
+
136
+ ```bash
137
+ python -m pip install -e ".[dev]"
138
+ python -m ruff check .
139
+ python -m ruff format --check .
140
+ python -m pytest
141
+ python -m build
142
+ python -m twine check --strict dist/*
143
+ python scripts/verify_dist.py
144
+ ```
145
+
146
+ 仓库约定:没有其他环境要求时,以上 Python 命令在 conda `torch2` 中运行。
147
+ 默认测试不依赖官方后端,检查懒加载、参数传递、异常处理、诊断和 CLI。
148
+ 发行包验证在临时虚拟环境中安装 wheel 以及从 sdist 构建的 wheel。
149
+
150
+ 在已配置官方库的 Linux GPU 环境中,必须执行真实测试再确认模型可运行:
151
+
152
+ ```bash
153
+ FLASH_MAMBA_RUN_GPU_TESTS=1 python -m pytest tests/test_gpu.py -v
154
+ ```
155
+
156
+ 显式开启后,环境缺失或模型运行失败会导致测试失败,不会静默跳过。
157
+ 测试比较工厂实例与官方实例的前向结果、参数/输入梯度,并检查 checkpoint
158
+ 可直接互载。未开启时,GPU 测试明确标记为 skipped。
159
+
160
+ ## 发布
161
+
162
+ 发布准备、TestPyPI/PyPI 命令和 GitHub Trusted Publishing 配置见
163
+ [发布说明](docs/RELEASING.md)。CI 检查包装层并构建发行包,不能替代真实 GPU 测试。
164
+
165
+ ## 许可与来源
166
+
167
+ 本项目的新包装代码使用 MIT 许可证;不复制、不重新分发官方源码或 CUDA 内核。
168
+ `mamba-ssm` 由 Albert Gu、Tri Dao 等作者开发,使用 Apache-2.0 许可证。
169
+ 使用该模型进行研究时,请按[官方说明](https://github.com/state-spaces/mamba#citation)
170
+ 引用相关论文。
@@ -0,0 +1,146 @@
1
+ # flash-mamba
2
+
3
+ `flash-mamba` 是官方 [mamba-ssm](https://github.com/state-spaces/mamba) 的轻量包装库。
4
+ 统一创建 Mamba / Mamba2,提供懒加载和环境诊断,返回官方模型实例。
5
+ 训练、推理缓存、参数默认值、`state_dict` 和内核行为均由官方实现决定。
6
+
7
+ 这是独立项目,与官方团队没有隶属关系。项目名称不代表新增加速内核或性能提升。
8
+ v0.1.0 的封装目标是 PyPI `mamba-ssm==2.3.2.post1`;Mamba3 不在本次范围内。
9
+ 当前本机只验证了包装层和发行包,真实 GPU 验证需在 Linux 环境完成。
10
+
11
+ ## 安装
12
+
13
+ 包装层需要 Python 3.10+,自身没有运行时依赖,可在 Windows 导入和运行诊断。
14
+ 发行包发布前,从本地安装:
15
+
16
+ ```bash
17
+ python -m pip install .
18
+ ```
19
+
20
+ 发布到 PyPI 后才可使用:
21
+
22
+ ```bash
23
+ python -m pip install flash-mamba
24
+ ```
25
+
26
+ 实际模型运行需要先在 **Linux(Windows 用户可使用 WSL2)** 安装适配设备的
27
+ PyTorch,再安装官方 GPU 后端。包装层不提供 CPU 替代实现或原生 Windows 内核。
28
+ CUDA/ROCm、编译工具和模型的具体要求以
29
+ [官方安装文档](https://github.com/state-spaces/mamba#installation) 为准。
30
+
31
+ ```bash
32
+ # 先按 https://pytorch.org/get-started/locally/ 安装对应的 GPU 版 PyTorch
33
+ python -m pip install packaging ninja wheel setuptools
34
+ python -m pip install "mamba-ssm[causal-conv1d]==2.3.2.post1" --no-build-isolation
35
+ python -m pip install .
36
+ flash-mamba doctor
37
+ ```
38
+
39
+ `ssm` extra 声明 `mamba-ssm>=2.3.2.post1,<3`。安装前仍需先配置 GPU 版 PyTorch
40
+ 和官方构建依赖:
41
+
42
+ ```bash
43
+ python -m pip install ".[ssm]" --no-build-isolation
44
+ ```
45
+
46
+ 显式两步安装更方便排查官方编译或二进制兼容问题。包装层不会自动安装、下载或
47
+ 替换 PyTorch。官方 `main` 与 PyPI 发行版的安装默认值可能不同;当前 `main`
48
+ 默认不构建 `selective_scan_cuda`,Mamba-1 使用该扩展时需要显式开启:
49
+
50
+ ```bash
51
+ # 仅当选择官方最新源码时使用;并非 v0.1.0 已验证的发行版组合
52
+ MAMBA_KEEP_CUDA_BUILD=TRUE python -m pip install \
53
+ "mamba-ssm[causal-conv1d] @ git+https://github.com/state-spaces/mamba.git" \
54
+ --no-build-isolation
55
+ ```
56
+
57
+ ## 使用
58
+
59
+ ```python
60
+ import torch
61
+ from flash_mamba import create_mamba
62
+
63
+ x = torch.randn(2, 64, 128, device="cuda")
64
+ model = create_mamba(
65
+ d_model=128,
66
+ variant="mamba2", # 默认;Mamba-1 使用 "mamba"
67
+ d_state=64,
68
+ d_conv=4,
69
+ expand=2,
70
+ headdim=64,
71
+ device="cuda",
72
+ )
73
+ y = model(x)
74
+ assert y.shape == x.shape
75
+ y.square().mean().backward()
76
+ ```
77
+
78
+ `create_mamba` 只校验正整数 `d_model` 和 `variant`;其余关键字直接传给官方构造函数。
79
+ 不额外添加残差、归一化、转置或模型包装。Mamba2 的 `d_ssm`(默认为
80
+ `expand * d_model`,分布式时还受 `world_size` 影响)必须满足官方 `headdim`
81
+ 约束;例如 `d_model=16, expand=2, headdim=64` 不合法。
82
+
83
+ 也可以直接导入官方类,第一次访问时才加载 `mamba_ssm`:
84
+
85
+ ```python
86
+ from flash_mamba import Mamba, Mamba2
87
+
88
+ model = Mamba(d_model=128, device="cuda")
89
+ ```
90
+
91
+ 返回的实例可直接调用 `.to()`、`.parameters()`、`.state_dict()`、
92
+ `.allocate_inference_cache()` 和官方支持的 forward 关键字。
93
+ `import flash_mamba` 本身不导入 PyTorch 或 `mamba_ssm`。
94
+ 后端导入失败会抛出 `BackendUnavailableError`,保留原异常链;构造函数参数错误
95
+ 和模型执行错误直接沿用官方异常。
96
+
97
+ ## 环境诊断
98
+
99
+ ```bash
100
+ flash-mamba doctor
101
+ flash-mamba doctor --json
102
+ python -m flash_mamba doctor --json
103
+ ```
104
+
105
+ 检查已安装版本、PyTorch/官方后端能否导入、CUDA/ROCm 是否可访问。
106
+ 退出码 0 表示可以尝试 GPU smoke test;退出码 1 表示检查未通过。
107
+ 成功导入不等于内核可运行,真实前向/反向需执行下面的测试。
108
+ `flash_mamba.diagnose()` 返回相同的字典结果,不修改环境。
109
+
110
+ ## 开发与验证
111
+
112
+ ```bash
113
+ python -m pip install -e ".[dev]"
114
+ python -m ruff check .
115
+ python -m ruff format --check .
116
+ python -m pytest
117
+ python -m build
118
+ python -m twine check --strict dist/*
119
+ python scripts/verify_dist.py
120
+ ```
121
+
122
+ 仓库约定:没有其他环境要求时,以上 Python 命令在 conda `torch2` 中运行。
123
+ 默认测试不依赖官方后端,检查懒加载、参数传递、异常处理、诊断和 CLI。
124
+ 发行包验证在临时虚拟环境中安装 wheel 以及从 sdist 构建的 wheel。
125
+
126
+ 在已配置官方库的 Linux GPU 环境中,必须执行真实测试再确认模型可运行:
127
+
128
+ ```bash
129
+ FLASH_MAMBA_RUN_GPU_TESTS=1 python -m pytest tests/test_gpu.py -v
130
+ ```
131
+
132
+ 显式开启后,环境缺失或模型运行失败会导致测试失败,不会静默跳过。
133
+ 测试比较工厂实例与官方实例的前向结果、参数/输入梯度,并检查 checkpoint
134
+ 可直接互载。未开启时,GPU 测试明确标记为 skipped。
135
+
136
+ ## 发布
137
+
138
+ 发布准备、TestPyPI/PyPI 命令和 GitHub Trusted Publishing 配置见
139
+ [发布说明](docs/RELEASING.md)。CI 检查包装层并构建发行包,不能替代真实 GPU 测试。
140
+
141
+ ## 许可与来源
142
+
143
+ 本项目的新包装代码使用 MIT 许可证;不复制、不重新分发官方源码或 CUDA 内核。
144
+ `mamba-ssm` 由 Albert Gu、Tri Dao 等作者开发,使用 Apache-2.0 许可证。
145
+ 使用该模型进行研究时,请按[官方说明](https://github.com/state-spaces/mamba#citation)
146
+ 引用相关论文。
@@ -0,0 +1,45 @@
1
+ # v0.1.0 验证记录
2
+
3
+ 日期:2026-10-12(Asia/Shanghai)。以下是本机执行结果,不代表 GitHub CI 已运行。
4
+
5
+ ## 环境
6
+
7
+ - Windows,conda `torch2`,Python 3.12.0。
8
+ - PyTorch `2.13.0+cu130`,CUDA 13.0,NVIDIA GeForce RTX 2060。
9
+ - 未安装 `mamba-ssm`、`causal-conv1d` 或 Triton。
10
+ - 最初一次 PyTorch 导入遇到 WinError 4551;后续导入和 CUDA 设备查询成功。
11
+ - 已安装 WSL2 Ubuntu-24.04,能识别 GPU;其 PATH 中没有 conda,未检测到常规
12
+ 位置的 `torch2` 环境。没有为测试额外安装 Linux GPU 软件栈。
13
+
14
+ ## 已通过
15
+
16
+ - `python -m ruff check .`。
17
+ - `python -m ruff format --check .`。
18
+ - `python -m pytest`:32 passed,2 skipped(显式 opt-in 的 GPU 测试)。
19
+ - `python -m build`:生成 `flash_mamba-0.1.0-py3-none-any.whl` 和
20
+ `flash_mamba-0.1.0.tar.gz`;隔离构建环境不导入 PyTorch。
21
+ - `python -m twine check --strict dist/*`:wheel / sdist 均通过。
22
+ - `python scripts/verify_dist.py`:分别在全新虚拟环境安装 wheel,以及从 sdist
23
+ 重建的 wheel;导入、版本一致性、console script、模块入口、缺少后端时的
24
+ JSON 诊断和退出码均通过。
25
+ - `python -m flash_mamba doctor --json`:能够检查本机环境,按预期返回退出码 1,
26
+ 报告官方库缺失和原生 Windows 平台限制。
27
+
28
+ ## 尚未验证
29
+
30
+ 真实 `mamba-ssm` 的前向/反向、官方 checkpoint 兼容性以及具体 GPU 内核兼容性。
31
+ 包装层单元测试使用替身验证接口边界,不能代替真实模型测试。
32
+
33
+ 在已配置官方后端的 Linux GPU 环境执行:
34
+
35
+ ```bash
36
+ cd /mnt/e/codebase/flash_mamba
37
+ python -m pip install -e ".[dev]"
38
+ FLASH_MAMBA_RUN_GPU_TESTS=1 python -m pytest tests/test_gpu.py -v
39
+ ```
40
+
41
+ 运行前按 README 安装对应的 GPU 版 PyTorch 和官方后端,激活该 Linux 环境。
42
+ 若环境有 conda `torch2`,优先使用该环境。GPU 测试明确开启后不自动跳过依赖失败。
43
+
44
+ 尚未执行 TestPyPI/PyPI 上传、GitHub Actions、远程 Git push。发布说明在
45
+ [docs/RELEASING.md](docs/RELEASING.md)。
@@ -0,0 +1,83 @@
1
+ # 发布 flash-mamba 0.1.0
2
+
3
+ 源码和本地发行包已准备;上传 PyPI、创建远程仓库和 GPU 验证是独立步骤。
4
+ 没有配置远程仓库地址,所以元数据没有虚构的 Repository URL。
5
+ 创建仓库后可在 `pyproject.toml` 的 `[project.urls]` 中添加实际地址,并重新构建。
6
+
7
+ ## 本地检查
8
+
9
+ 在项目默认 conda 环境 `torch2` 中运行;Linux GPU 集成测试应使用安装了官方
10
+ GPU 后端的对应环境。PowerShell 示例:
11
+
12
+ ```powershell
13
+ conda run -n torch2 python -m pip install -e ".[dev]"
14
+ conda run -n torch2 python -m ruff check .
15
+ conda run -n torch2 python -m ruff format --check .
16
+ conda run -n torch2 python -m pytest
17
+ conda run -n torch2 python -m build
18
+ conda run -n torch2 python -m twine check --strict dist/*
19
+ conda run -n torch2 python scripts/verify_dist.py
20
+ git diff --cached --check
21
+ ```
22
+
23
+ 真实模型兼容性尚需在 Linux GPU 上运行(Bash):
24
+
25
+ ```bash
26
+ FLASH_MAMBA_RUN_GPU_TESTS=1 python -m pytest tests/test_gpu.py -v
27
+ ```
28
+
29
+ 本地 Windows 的发行包验证通过不能代替此步骤。发布前应记录 GPU、驱动、
30
+ PyTorch/CUDA、`mamba-ssm` 和 `causal-conv1d` 版本及真实测试结果。
31
+
32
+ ## Git 提交
33
+
34
+ 本地仓库准备好后可执行:
35
+
36
+ ```bash
37
+ git add .
38
+ git diff --cached --stat
39
+ git commit -m "feat: add lightweight mamba-ssm wrapper and release packaging"
40
+ ```
41
+
42
+ 实际远程地址由维护者指定,之后再设置 `origin` 并 push。不要把 `dist/`、
43
+ 虚拟环境、`.pypirc` 或 PyPI token 提交到 Git。
44
+
45
+ ## TestPyPI / PyPI
46
+
47
+ 查询不到项目不代表名称一定可注册,发布前确认账户有 `flash-mamba` 名称权限。
48
+ TestPyPI 和 PyPI 是独立账户/项目。先上传到 TestPyPI:
49
+
50
+ ```bash
51
+ python -m twine upload --repository testpypi dist/flash_mamba-0.1.0*
52
+ ```
53
+
54
+ 在新的临时虚拟环境验证 TestPyPI 上的纯包装包,避免同时从 TestPyPI 解析依赖:
55
+
56
+ ```bash
57
+ python -m pip install --index-url https://test.pypi.org/simple/ --no-deps flash-mamba==0.1.0
58
+ python -c "import flash_mamba; print(flash_mamba.__version__)"
59
+ flash-mamba --version
60
+ ```
61
+
62
+ 确认发行物和 GPU 验证结果后上传同一批文件到 PyPI:
63
+
64
+ ```bash
65
+ python -m twine upload dist/flash_mamba-0.1.0*
66
+ ```
67
+
68
+ 使用 Trusted Publishing 或在本机认证提示中输入 token;不要写入仓库或命令示例。
69
+ PyPI 版本不能覆盖。后续发布同时修改 `pyproject.toml` 与
70
+ `src/flash_mamba/__init__.py` 的版本,更新 CHANGELOG,清理旧发行物再构建。
71
+
72
+ ## GitHub Trusted Publishing
73
+
74
+ 仓库提供 `.github/workflows/publish.yml`,只能手动触发。
75
+ 在目标 TestPyPI/PyPI 项目中配置 Trusted Publisher:填写真实 GitHub 用户/组织、
76
+ 仓库名、workflow 文件名 `publish.yml`,environment 分别为 `testpypi` / `pypi`。
77
+ 在 GitHub 同名 environment 设置保护规则;生产发布应要求人工审核。
78
+
79
+ 从已审查且完成 GPU 验证的提交运行 workflow:首次选 `testpypi` 验证,之后选
80
+ `pypi`。workflow 自己执行 lint、包装层测试、构建、元数据检查及临时环境安装;
81
+ 它不会安装和测试 GPU 后端,不应把其成功当作模型兼容性的证据。
82
+
83
+ GitHub 发布和本地上传二选一,以避免重复上传同一版本。
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "flash-mamba"
7
+ version = "0.1.0"
8
+ description = "A lightweight convenience wrapper for the official mamba-ssm package"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Jellyfish042" }]
14
+ keywords = ["mamba", "mamba-ssm", "pytorch", "state-space-model"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3 :: Only",
19
+ "Operating System :: OS Independent",
20
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
21
+ ]
22
+ dependencies = []
23
+
24
+ [project.optional-dependencies]
25
+ ssm = ["mamba-ssm>=2.3.2.post1,<3"]
26
+ dev = ["pytest>=8", "build>=1.2", "twine>=6", "ruff>=0.11"]
27
+
28
+ [project.scripts]
29
+ flash-mamba = "flash_mamba.cli:main"
30
+
31
+ [tool.setuptools.packages.find]
32
+ where = ["src"]
33
+
34
+ [tool.pytest.ini_options]
35
+ testpaths = ["tests"]
36
+ addopts = "-ra --strict-markers"
37
+ markers = ["gpu: real upstream CUDA forward/backward tests (explicit opt-in)"]
38
+
39
+ [tool.ruff]
40
+ target-version = "py310"
41
+ line-length = 100
42
+
43
+ [tool.ruff.lint]
44
+ select = ["E", "F", "I", "UP", "B"]
@@ -0,0 +1,94 @@
1
+ """Install both distribution forms into fresh environments, without the GPU backend."""
2
+
3
+ import json
4
+ import os
5
+ import subprocess
6
+ import sys
7
+ import tempfile
8
+ import venv
9
+ from pathlib import Path
10
+
11
+ CHECK_IMPORT = """
12
+ import importlib.abc
13
+ import importlib.metadata
14
+ import pathlib
15
+ import sys
16
+ class RejectBackend(importlib.abc.MetaPathFinder):
17
+ def find_spec(self, fullname, path=None, target=None):
18
+ if fullname.split('.')[0] in {'torch', 'mamba_ssm'}:
19
+ raise AssertionError('Unexpected backend import: ' + fullname)
20
+ sys.meta_path.insert(0, RejectBackend())
21
+ import flash_mamba
22
+ assert flash_mamba.__version__ == importlib.metadata.version('flash-mamba')
23
+ assert pathlib.Path(flash_mamba.__file__).is_relative_to(pathlib.Path(sys.prefix))
24
+ assert callable(flash_mamba.create_mamba)
25
+ print('Installed package import OK:', flash_mamba.__version__)
26
+ """
27
+
28
+
29
+ def run(command: list[str], cwd: Path) -> None:
30
+ subprocess.run(command, cwd=cwd, check=True)
31
+
32
+
33
+ def verify_wheel(wheel: Path, temporary: Path, label: str) -> None:
34
+ environment = temporary / label
35
+ venv.EnvBuilder(with_pip=True).create(environment)
36
+ executable = environment / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
37
+ python = [str(executable), "-I"]
38
+ run([*python, "-m", "pip", "install", "--no-deps", "--no-index", str(wheel)], temporary)
39
+ run([*python, "-c", CHECK_IMPORT], temporary)
40
+ run([*python, "-m", "flash_mamba", "--version"], temporary)
41
+ console = environment / ("Scripts/flash-mamba.exe" if os.name == "nt" else "bin/flash-mamba")
42
+ run([str(console), "--version"], temporary)
43
+ doctor = subprocess.run(
44
+ [*python, "-m", "flash_mamba", "doctor", "--json"],
45
+ cwd=temporary,
46
+ text=True,
47
+ capture_output=True,
48
+ check=False,
49
+ )
50
+ if doctor.returncode != 1:
51
+ raise RuntimeError(
52
+ f"Expected missing-backend exit status 1: {doctor.stdout}\n{doctor.stderr}"
53
+ )
54
+ report = json.loads(doctor.stdout)
55
+ if report["torch_importable"] or report["backend_importable"]:
56
+ raise RuntimeError("Fresh environment unexpectedly has backend dependencies")
57
+ print(
58
+ f"{label}: wheel install, lazy import, CLI and missing-backend diagnostics OK", flush=True
59
+ )
60
+
61
+
62
+ def main() -> None:
63
+ root = Path(__file__).resolve().parents[1]
64
+ wheels = list((root / "dist").glob("*.whl"))
65
+ sources = list((root / "dist").glob("*.tar.gz"))
66
+ if len(wheels) != 1 or len(sources) != 1:
67
+ raise RuntimeError("Build exactly one wheel and one sdist in dist/ before verification")
68
+ with tempfile.TemporaryDirectory(prefix="flash-mamba-dist-") as directory:
69
+ temporary = Path(directory)
70
+ verify_wheel(wheels[0], temporary, "wheel")
71
+ rebuilt = temporary / "rebuilt"
72
+ # Use the caller's build tools; no runtime backend dependency is resolved.
73
+ run(
74
+ [
75
+ sys.executable,
76
+ "-m",
77
+ "pip",
78
+ "wheel",
79
+ "--no-deps",
80
+ "--no-build-isolation",
81
+ "--wheel-dir",
82
+ str(rebuilt),
83
+ str(sources[0]),
84
+ ],
85
+ temporary,
86
+ )
87
+ built_wheels = list(rebuilt.glob("*.whl"))
88
+ if len(built_wheels) != 1:
89
+ raise RuntimeError("Expected exactly one wheel rebuilt from sdist")
90
+ verify_wheel(built_wheels[0], temporary, "sdist")
91
+
92
+
93
+ if __name__ == "__main__":
94
+ main()
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,20 @@
1
+ """Convenience APIs for official Mamba blocks; importing this package is lightweight."""
2
+
3
+ from typing import Any
4
+
5
+ from ._backend import BackendUnavailableError, load_class
6
+ from .diagnostics import diagnose
7
+ from .factory import create_mamba
8
+
9
+ __version__ = "0.1.0"
10
+ __all__ = ["BackendUnavailableError", "Mamba", "Mamba2", "create_mamba", "diagnose"]
11
+
12
+
13
+ def __getattr__(name: str) -> Any:
14
+ if name in {"Mamba", "Mamba2"}:
15
+ return load_class(name)
16
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
17
+
18
+
19
+ def __dir__() -> list[str]:
20
+ return sorted(set(globals()) | set(__all__))
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,32 @@
1
+ """Load upstream only when a model is requested."""
2
+
3
+ import importlib
4
+ from typing import Any
5
+
6
+ INSTALL_HINT = (
7
+ "On Linux, install a CUDA-enabled PyTorch first, then run "
8
+ '`python -m pip install "mamba-ssm[causal-conv1d]==2.3.2.post1" '
9
+ "--no-build-isolation`. For current upstream source builds, Mamba-1 also needs "
10
+ "MAMBA_KEEP_CUDA_BUILD=TRUE to enable selective_scan_cuda. "
11
+ "Use `flash-mamba doctor` to inspect the environment. "
12
+ "Native Windows/CPU execution is not provided by this wrapper."
13
+ )
14
+
15
+
16
+ class BackendUnavailableError(RuntimeError):
17
+ """The official backend or one of its native dependencies could not be loaded."""
18
+
19
+
20
+ def load_class(name: str) -> Any:
21
+ try:
22
+ backend = importlib.import_module("mamba_ssm")
23
+ except (ImportError, OSError, RuntimeError) as exc:
24
+ raise BackendUnavailableError(
25
+ f"Cannot import official mamba_ssm ({type(exc).__name__}: {exc}). {INSTALL_HINT}"
26
+ ) from exc
27
+ try:
28
+ return getattr(backend, name)
29
+ except AttributeError as exc:
30
+ raise BackendUnavailableError(
31
+ f"Installed mamba_ssm does not expose {name}. {INSTALL_HINT}"
32
+ ) from exc
@@ -0,0 +1,36 @@
1
+ """Command-line environment checks."""
2
+
3
+ import argparse
4
+ import json
5
+
6
+ from . import __version__
7
+ from .diagnostics import diagnose
8
+
9
+
10
+ def main(argv: list[str] | None = None) -> int:
11
+ parser = argparse.ArgumentParser(prog="flash-mamba")
12
+ parser.add_argument("--version", action="version", version=__version__)
13
+ commands = parser.add_subparsers(dest="command", required=True)
14
+ doctor = commands.add_parser(
15
+ "doctor", help="Inspect upstream dependencies and GPU availability"
16
+ )
17
+ doctor.add_argument("--json", action="store_true", help="Print machine-readable JSON")
18
+ args = parser.parse_args(argv)
19
+ report = diagnose()
20
+ if args.json:
21
+ print(json.dumps(report, ensure_ascii=True, indent=2))
22
+ else:
23
+ print(f"Python {report['python']} on {report['platform']}")
24
+ for package, version in report["packages"].items():
25
+ print(f" {package}: {version or 'not installed'}")
26
+ print(f"CUDA/ROCm device available: {report['cuda_available']}")
27
+ print(f"Official backend importable: {report['backend_importable']}")
28
+ print(f"Ready for GPU smoke test: {report['ready_for_smoke_test']}")
29
+ for message in report["errors"]:
30
+ print(f"ERROR: {message}")
31
+ for message in report["warnings"]:
32
+ print(f"WARNING: {message}")
33
+ print(report["note"])
34
+ if not report["ready_for_smoke_test"]:
35
+ print(report["install_hint"])
36
+ return 0 if report["ready_for_smoke_test"] else 1
@@ -0,0 +1,65 @@
1
+ """Read-only diagnostics; these checks do not run kernels or install packages."""
2
+
3
+ import importlib
4
+ import platform
5
+ from importlib import metadata
6
+ from typing import Any
7
+
8
+ from ._backend import INSTALL_HINT
9
+
10
+
11
+ def diagnose() -> dict[str, Any]:
12
+ """Report imports and CUDA availability, without claiming kernel compatibility."""
13
+ report: dict[str, Any] = {
14
+ "python": platform.python_version(),
15
+ "platform": platform.system(),
16
+ "packages": {},
17
+ "torch_importable": False,
18
+ "backend_importable": False,
19
+ "cuda_available": False,
20
+ "cuda_version": None,
21
+ "hip_version": None,
22
+ "models": [],
23
+ "errors": [],
24
+ "warnings": [],
25
+ "ready_for_smoke_test": False,
26
+ }
27
+ for package in ("flash-mamba", "torch", "mamba-ssm", "causal-conv1d", "triton"):
28
+ try:
29
+ report["packages"][package] = metadata.version(package)
30
+ except metadata.PackageNotFoundError:
31
+ report["packages"][package] = None
32
+ if report["platform"] != "Linux":
33
+ report["warnings"].append("Use Linux/WSL2 for the upstream GPU backend.")
34
+ try:
35
+ torch = importlib.import_module("torch")
36
+ report["torch_importable"] = True
37
+ report["cuda_version"] = torch.version.cuda
38
+ report["hip_version"] = getattr(torch.version, "hip", None)
39
+ report["cuda_available"] = bool(torch.cuda.is_available())
40
+ except Exception as exc:
41
+ report["errors"].append(f"torch: {type(exc).__name__}: {exc}")
42
+ # Avoid importing a dependent library again if PyTorch already failed.
43
+ if report["torch_importable"]:
44
+ try:
45
+ backend = importlib.import_module("mamba_ssm")
46
+ report["models"] = [name for name in ("Mamba", "Mamba2") if hasattr(backend, name)]
47
+ missing = sorted({"Mamba", "Mamba2"} - set(report["models"]))
48
+ if missing:
49
+ report["errors"].append(f"mamba_ssm: missing exports: {', '.join(missing)}")
50
+ else:
51
+ report["backend_importable"] = True
52
+ except Exception as exc:
53
+ report["errors"].append(f"mamba_ssm: {type(exc).__name__}: {exc}")
54
+ if not report["cuda_available"]:
55
+ report["warnings"].append("PyTorch cannot access a CUDA/ROCm device.")
56
+ report["ready_for_smoke_test"] = (
57
+ report["platform"] == "Linux"
58
+ and report["torch_importable"]
59
+ and report["backend_importable"]
60
+ and report["cuda_available"]
61
+ and not report["errors"]
62
+ )
63
+ report["note"] = "Imports and device availability do not verify forward/backward kernels."
64
+ report["install_hint"] = INSTALL_HINT
65
+ return report
@@ -0,0 +1,25 @@
1
+ """A small factory that preserves upstream model behavior and checkpoint keys."""
2
+
3
+ from typing import Any, Literal
4
+
5
+ from ._backend import load_class
6
+
7
+
8
+ def create_mamba(
9
+ d_model: int,
10
+ *,
11
+ variant: Literal["mamba", "mamba2"] = "mamba2",
12
+ **kwargs: Any,
13
+ ) -> Any:
14
+ """Return an official Mamba/Mamba2 instance with kwargs passed through unchanged.
15
+
16
+ The default is Mamba2. Model execution follows upstream platform requirements.
17
+ Upstream controls defaults, device/dtype, dimension constraints, and validation
18
+ of all arguments other than d_model and variant.
19
+ """
20
+ if isinstance(d_model, bool) or not isinstance(d_model, int) or d_model <= 0:
21
+ raise ValueError("d_model must be a positive integer")
22
+ if variant not in ("mamba", "mamba2"):
23
+ raise ValueError("variant must be 'mamba' or 'mamba2'")
24
+ model_class = load_class("Mamba" if variant == "mamba" else "Mamba2")
25
+ return model_class(d_model=d_model, **kwargs)
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: flash-mamba
3
+ Version: 0.1.0
4
+ Summary: A lightweight convenience wrapper for the official mamba-ssm package
5
+ Author: Jellyfish042
6
+ License-Expression: MIT
7
+ Keywords: mamba,mamba-ssm,pytorch,state-space-model
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Provides-Extra: ssm
17
+ Requires-Dist: mamba-ssm<3,>=2.3.2.post1; extra == "ssm"
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=8; extra == "dev"
20
+ Requires-Dist: build>=1.2; extra == "dev"
21
+ Requires-Dist: twine>=6; extra == "dev"
22
+ Requires-Dist: ruff>=0.11; extra == "dev"
23
+ Dynamic: license-file
24
+
25
+ # flash-mamba
26
+
27
+ `flash-mamba` 是官方 [mamba-ssm](https://github.com/state-spaces/mamba) 的轻量包装库。
28
+ 统一创建 Mamba / Mamba2,提供懒加载和环境诊断,返回官方模型实例。
29
+ 训练、推理缓存、参数默认值、`state_dict` 和内核行为均由官方实现决定。
30
+
31
+ 这是独立项目,与官方团队没有隶属关系。项目名称不代表新增加速内核或性能提升。
32
+ v0.1.0 的封装目标是 PyPI `mamba-ssm==2.3.2.post1`;Mamba3 不在本次范围内。
33
+ 当前本机只验证了包装层和发行包,真实 GPU 验证需在 Linux 环境完成。
34
+
35
+ ## 安装
36
+
37
+ 包装层需要 Python 3.10+,自身没有运行时依赖,可在 Windows 导入和运行诊断。
38
+ 发行包发布前,从本地安装:
39
+
40
+ ```bash
41
+ python -m pip install .
42
+ ```
43
+
44
+ 发布到 PyPI 后才可使用:
45
+
46
+ ```bash
47
+ python -m pip install flash-mamba
48
+ ```
49
+
50
+ 实际模型运行需要先在 **Linux(Windows 用户可使用 WSL2)** 安装适配设备的
51
+ PyTorch,再安装官方 GPU 后端。包装层不提供 CPU 替代实现或原生 Windows 内核。
52
+ CUDA/ROCm、编译工具和模型的具体要求以
53
+ [官方安装文档](https://github.com/state-spaces/mamba#installation) 为准。
54
+
55
+ ```bash
56
+ # 先按 https://pytorch.org/get-started/locally/ 安装对应的 GPU 版 PyTorch
57
+ python -m pip install packaging ninja wheel setuptools
58
+ python -m pip install "mamba-ssm[causal-conv1d]==2.3.2.post1" --no-build-isolation
59
+ python -m pip install .
60
+ flash-mamba doctor
61
+ ```
62
+
63
+ `ssm` extra 声明 `mamba-ssm>=2.3.2.post1,<3`。安装前仍需先配置 GPU 版 PyTorch
64
+ 和官方构建依赖:
65
+
66
+ ```bash
67
+ python -m pip install ".[ssm]" --no-build-isolation
68
+ ```
69
+
70
+ 显式两步安装更方便排查官方编译或二进制兼容问题。包装层不会自动安装、下载或
71
+ 替换 PyTorch。官方 `main` 与 PyPI 发行版的安装默认值可能不同;当前 `main`
72
+ 默认不构建 `selective_scan_cuda`,Mamba-1 使用该扩展时需要显式开启:
73
+
74
+ ```bash
75
+ # 仅当选择官方最新源码时使用;并非 v0.1.0 已验证的发行版组合
76
+ MAMBA_KEEP_CUDA_BUILD=TRUE python -m pip install \
77
+ "mamba-ssm[causal-conv1d] @ git+https://github.com/state-spaces/mamba.git" \
78
+ --no-build-isolation
79
+ ```
80
+
81
+ ## 使用
82
+
83
+ ```python
84
+ import torch
85
+ from flash_mamba import create_mamba
86
+
87
+ x = torch.randn(2, 64, 128, device="cuda")
88
+ model = create_mamba(
89
+ d_model=128,
90
+ variant="mamba2", # 默认;Mamba-1 使用 "mamba"
91
+ d_state=64,
92
+ d_conv=4,
93
+ expand=2,
94
+ headdim=64,
95
+ device="cuda",
96
+ )
97
+ y = model(x)
98
+ assert y.shape == x.shape
99
+ y.square().mean().backward()
100
+ ```
101
+
102
+ `create_mamba` 只校验正整数 `d_model` 和 `variant`;其余关键字直接传给官方构造函数。
103
+ 不额外添加残差、归一化、转置或模型包装。Mamba2 的 `d_ssm`(默认为
104
+ `expand * d_model`,分布式时还受 `world_size` 影响)必须满足官方 `headdim`
105
+ 约束;例如 `d_model=16, expand=2, headdim=64` 不合法。
106
+
107
+ 也可以直接导入官方类,第一次访问时才加载 `mamba_ssm`:
108
+
109
+ ```python
110
+ from flash_mamba import Mamba, Mamba2
111
+
112
+ model = Mamba(d_model=128, device="cuda")
113
+ ```
114
+
115
+ 返回的实例可直接调用 `.to()`、`.parameters()`、`.state_dict()`、
116
+ `.allocate_inference_cache()` 和官方支持的 forward 关键字。
117
+ `import flash_mamba` 本身不导入 PyTorch 或 `mamba_ssm`。
118
+ 后端导入失败会抛出 `BackendUnavailableError`,保留原异常链;构造函数参数错误
119
+ 和模型执行错误直接沿用官方异常。
120
+
121
+ ## 环境诊断
122
+
123
+ ```bash
124
+ flash-mamba doctor
125
+ flash-mamba doctor --json
126
+ python -m flash_mamba doctor --json
127
+ ```
128
+
129
+ 检查已安装版本、PyTorch/官方后端能否导入、CUDA/ROCm 是否可访问。
130
+ 退出码 0 表示可以尝试 GPU smoke test;退出码 1 表示检查未通过。
131
+ 成功导入不等于内核可运行,真实前向/反向需执行下面的测试。
132
+ `flash_mamba.diagnose()` 返回相同的字典结果,不修改环境。
133
+
134
+ ## 开发与验证
135
+
136
+ ```bash
137
+ python -m pip install -e ".[dev]"
138
+ python -m ruff check .
139
+ python -m ruff format --check .
140
+ python -m pytest
141
+ python -m build
142
+ python -m twine check --strict dist/*
143
+ python scripts/verify_dist.py
144
+ ```
145
+
146
+ 仓库约定:没有其他环境要求时,以上 Python 命令在 conda `torch2` 中运行。
147
+ 默认测试不依赖官方后端,检查懒加载、参数传递、异常处理、诊断和 CLI。
148
+ 发行包验证在临时虚拟环境中安装 wheel 以及从 sdist 构建的 wheel。
149
+
150
+ 在已配置官方库的 Linux GPU 环境中,必须执行真实测试再确认模型可运行:
151
+
152
+ ```bash
153
+ FLASH_MAMBA_RUN_GPU_TESTS=1 python -m pytest tests/test_gpu.py -v
154
+ ```
155
+
156
+ 显式开启后,环境缺失或模型运行失败会导致测试失败,不会静默跳过。
157
+ 测试比较工厂实例与官方实例的前向结果、参数/输入梯度,并检查 checkpoint
158
+ 可直接互载。未开启时,GPU 测试明确标记为 skipped。
159
+
160
+ ## 发布
161
+
162
+ 发布准备、TestPyPI/PyPI 命令和 GitHub Trusted Publishing 配置见
163
+ [发布说明](docs/RELEASING.md)。CI 检查包装层并构建发行包,不能替代真实 GPU 测试。
164
+
165
+ ## 许可与来源
166
+
167
+ 本项目的新包装代码使用 MIT 许可证;不复制、不重新分发官方源码或 CUDA 内核。
168
+ `mamba-ssm` 由 Albert Gu、Tri Dao 等作者开发,使用 Apache-2.0 许可证。
169
+ 使用该模型进行研究时,请按[官方说明](https://github.com/state-spaces/mamba#citation)
170
+ 引用相关论文。
@@ -0,0 +1,23 @@
1
+ CHANGELOG.md
2
+ LICENSE
3
+ MANIFEST.in
4
+ README.md
5
+ VALIDATION.md
6
+ pyproject.toml
7
+ docs/RELEASING.md
8
+ scripts/verify_dist.py
9
+ src/flash_mamba/__init__.py
10
+ src/flash_mamba/__main__.py
11
+ src/flash_mamba/_backend.py
12
+ src/flash_mamba/cli.py
13
+ src/flash_mamba/diagnostics.py
14
+ src/flash_mamba/factory.py
15
+ src/flash_mamba.egg-info/PKG-INFO
16
+ src/flash_mamba.egg-info/SOURCES.txt
17
+ src/flash_mamba.egg-info/dependency_links.txt
18
+ src/flash_mamba.egg-info/entry_points.txt
19
+ src/flash_mamba.egg-info/requires.txt
20
+ src/flash_mamba.egg-info/top_level.txt
21
+ tests/test_diagnostics.py
22
+ tests/test_factory.py
23
+ tests/test_gpu.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ flash-mamba = flash_mamba.cli:main
@@ -0,0 +1,9 @@
1
+
2
+ [dev]
3
+ pytest>=8
4
+ build>=1.2
5
+ twine>=6
6
+ ruff>=0.11
7
+
8
+ [ssm]
9
+ mamba-ssm<3,>=2.3.2.post1
@@ -0,0 +1 @@
1
+ flash_mamba
@@ -0,0 +1,110 @@
1
+ import importlib
2
+ import json
3
+ from importlib import metadata
4
+ from types import SimpleNamespace
5
+
6
+ import pytest
7
+
8
+ from flash_mamba import cli, diagnose
9
+ from flash_mamba import diagnostics as module
10
+
11
+
12
+ @pytest.fixture
13
+ def healthy_environment(monkeypatch):
14
+ torch = SimpleNamespace(
15
+ version=SimpleNamespace(cuda="12.8", hip=None),
16
+ cuda=SimpleNamespace(is_available=lambda: True),
17
+ )
18
+ backend = SimpleNamespace(Mamba=object(), Mamba2=object())
19
+ imports = {"torch": torch, "mamba_ssm": backend}
20
+ monkeypatch.setattr(importlib, "import_module", lambda name: imports[name])
21
+ monkeypatch.setattr(module.platform, "system", lambda: "Linux")
22
+ monkeypatch.setattr(metadata, "version", lambda name: "1.0")
23
+ return imports
24
+
25
+
26
+ def test_healthy_imports_and_cuda_only_qualify_for_smoke_test(healthy_environment):
27
+ report = diagnose()
28
+ assert report["ready_for_smoke_test"] is True
29
+ assert report["errors"] == []
30
+ assert report["models"] == ["Mamba", "Mamba2"]
31
+ assert report["cuda_version"] == "12.8"
32
+ assert "do not verify" in report["note"]
33
+ json.dumps(report)
34
+
35
+
36
+ def test_missing_packages_and_blocked_torch_are_reported(monkeypatch):
37
+ imports = []
38
+
39
+ def missing_version(name):
40
+ raise metadata.PackageNotFoundError(name)
41
+
42
+ def blocked_import(name):
43
+ imports.append(name)
44
+ raise OSError("WinError 4551: c10.dll blocked")
45
+
46
+ monkeypatch.setattr(metadata, "version", missing_version)
47
+ monkeypatch.setattr(importlib, "import_module", blocked_import)
48
+ monkeypatch.setattr(module.platform, "system", lambda: "Windows")
49
+ report = diagnose()
50
+ assert imports == ["torch"]
51
+ assert report["ready_for_smoke_test"] is False
52
+ assert report["torch_importable"] is False
53
+ assert all(version is None for version in report["packages"].values())
54
+ assert "c10.dll blocked" in report["errors"][0]
55
+ assert any("WSL2" in warning for warning in report["warnings"])
56
+
57
+
58
+ def test_backend_import_failure_is_reported(healthy_environment):
59
+ del healthy_environment["mamba_ssm"]
60
+ report = diagnose()
61
+ assert report["torch_importable"] is True
62
+ assert report["backend_importable"] is False
63
+ assert report["ready_for_smoke_test"] is False
64
+ assert "mamba_ssm" in report["errors"][0]
65
+
66
+
67
+ def test_missing_class_is_not_considered_ready(healthy_environment):
68
+ del healthy_environment["mamba_ssm"].Mamba2
69
+ report = diagnose()
70
+ assert report["backend_importable"] is False
71
+ assert "missing exports: Mamba2" in report["errors"][0]
72
+
73
+
74
+ def test_no_gpu_is_not_considered_ready(healthy_environment):
75
+ healthy_environment["torch"].cuda.is_available = lambda: False
76
+ report = diagnose()
77
+ assert report["backend_importable"] is True
78
+ assert report["ready_for_smoke_test"] is False
79
+
80
+
81
+ def test_rocm_is_reported(healthy_environment):
82
+ healthy_environment["torch"].version.cuda = None
83
+ healthy_environment["torch"].version.hip = "6.4"
84
+ assert diagnose()["hip_version"] == "6.4"
85
+
86
+
87
+ def test_cli_json_and_exit_status(healthy_environment, capsys):
88
+ assert cli.main(["doctor", "--json"]) == 0
89
+ report = json.loads(capsys.readouterr().out)
90
+ assert report["ready_for_smoke_test"] is True
91
+ healthy_environment["torch"].cuda.is_available = lambda: False
92
+ assert cli.main(["doctor", "--json"]) == 1
93
+ assert json.loads(capsys.readouterr().out)["ready_for_smoke_test"] is False
94
+
95
+
96
+ def test_cli_readable_output(healthy_environment, capsys):
97
+ healthy_environment["torch"].cuda.is_available = lambda: False
98
+ assert cli.main(["doctor"]) == 1
99
+ output = capsys.readouterr().out
100
+ assert "Ready for GPU smoke test: False" in output
101
+ assert "--no-build-isolation" in output
102
+
103
+
104
+ def test_cli_version(capsys):
105
+ with pytest.raises(SystemExit) as caught:
106
+ cli.main(["--version"])
107
+ assert caught.value.code == 0
108
+ from flash_mamba import __version__
109
+
110
+ assert capsys.readouterr().out.strip() == __version__
@@ -0,0 +1,122 @@
1
+ import importlib
2
+ import subprocess
3
+ import sys
4
+ from types import SimpleNamespace
5
+
6
+ import pytest
7
+
8
+ import flash_mamba
9
+ from flash_mamba import BackendUnavailableError, create_mamba
10
+
11
+
12
+ def test_import_does_not_load_backend():
13
+ code = """
14
+ import importlib.abc
15
+ import sys
16
+ class RejectBackend(importlib.abc.MetaPathFinder):
17
+ def find_spec(self, fullname, path=None, target=None):
18
+ if fullname.split('.')[0] in {'torch', 'mamba_ssm'}:
19
+ raise AssertionError('Unexpected backend import: ' + fullname)
20
+ sys.meta_path.insert(0, RejectBackend())
21
+ import flash_mamba
22
+ assert callable(flash_mamba.create_mamba)
23
+ assert 'Mamba2' in dir(flash_mamba)
24
+ """
25
+ subprocess.run([sys.executable, "-c", code], check=True)
26
+
27
+
28
+ @pytest.fixture
29
+ def backend(monkeypatch):
30
+ class Mamba:
31
+ def __init__(self, **kwargs):
32
+ self.kwargs = kwargs
33
+
34
+ class Mamba2(Mamba):
35
+ pass
36
+
37
+ module = SimpleNamespace(Mamba=Mamba, Mamba2=Mamba2)
38
+ monkeypatch.setitem(sys.modules, "mamba_ssm", module)
39
+ return module
40
+
41
+
42
+ @pytest.mark.parametrize("variant, export", [("mamba", "Mamba"), ("mamba2", "Mamba2")])
43
+ def test_returns_upstream_instance_and_preserves_kwargs(backend, variant, export):
44
+ dtype = object()
45
+ process_group = object()
46
+ kwargs = {"device": "cuda:1", "dtype": dtype, "process_group": process_group, "layer_idx": 3}
47
+ model = create_mamba(128, variant=variant, **kwargs)
48
+ assert type(model) is getattr(backend, export)
49
+ assert model.kwargs == {"d_model": 128, **kwargs}
50
+
51
+
52
+ def test_default_variant_keeps_upstream_defaults(backend):
53
+ model = create_mamba(128)
54
+ assert type(model) is backend.Mamba2
55
+ assert model.kwargs == {"d_model": 128}
56
+
57
+
58
+ def test_lazy_exports_are_original_classes(backend):
59
+ assert flash_mamba.Mamba is backend.Mamba
60
+ assert flash_mamba.Mamba2 is backend.Mamba2
61
+
62
+
63
+ @pytest.mark.parametrize("d_model", [0, -1, 3.5, True, None, "128"])
64
+ def test_invalid_dimension_fails_before_backend_import(monkeypatch, d_model):
65
+ def unexpected_import(name):
66
+ raise AssertionError(f"Imported {name} for invalid arguments")
67
+
68
+ monkeypatch.setattr(importlib, "import_module", unexpected_import)
69
+ with pytest.raises(ValueError, match="positive integer"):
70
+ create_mamba(d_model)
71
+
72
+
73
+ @pytest.mark.parametrize("variant", ["Mamba2", "mamba3", None, [], {}])
74
+ def test_invalid_variant_fails_before_backend_import(monkeypatch, variant):
75
+ def unexpected_import(name):
76
+ raise AssertionError(f"Imported {name} for invalid arguments")
77
+
78
+ monkeypatch.setattr(importlib, "import_module", unexpected_import)
79
+ with pytest.raises(ValueError, match="variant"):
80
+ create_mamba(128, variant=variant)
81
+
82
+
83
+ @pytest.mark.parametrize(
84
+ "error",
85
+ [
86
+ ModuleNotFoundError("mamba_ssm"),
87
+ ImportError("selective_scan_cuda"),
88
+ OSError("DLL blocked"),
89
+ RuntimeError("ABI mismatch"),
90
+ ],
91
+ )
92
+ def test_backend_errors_keep_original_cause(monkeypatch, error):
93
+ def broken_import(name):
94
+ raise error
95
+
96
+ monkeypatch.setattr(importlib, "import_module", broken_import)
97
+ with pytest.raises(BackendUnavailableError, match="flash-mamba doctor") as caught:
98
+ create_mamba(128)
99
+ assert caught.value.__cause__ is error
100
+
101
+
102
+ def test_missing_export_has_actionable_error(monkeypatch):
103
+ monkeypatch.setitem(sys.modules, "mamba_ssm", SimpleNamespace())
104
+ with pytest.raises(BackendUnavailableError, match="does not expose Mamba2"):
105
+ create_mamba(128)
106
+
107
+
108
+ def test_constructor_failure_is_not_misreported_as_install_problem(monkeypatch):
109
+ original = TypeError("unexpected keyword argument")
110
+
111
+ def broken_constructor(**kwargs):
112
+ raise original
113
+
114
+ monkeypatch.setitem(sys.modules, "mamba_ssm", SimpleNamespace(Mamba2=broken_constructor))
115
+ with pytest.raises(TypeError) as caught:
116
+ create_mamba(128, unknown=True)
117
+ assert caught.value is original
118
+
119
+
120
+ def test_unknown_attribute_uses_normal_python_error():
121
+ with pytest.raises(AttributeError, match="no attribute"):
122
+ _ = flash_mamba.unavailable_attribute
@@ -0,0 +1,50 @@
1
+ """Real upstream comparisons: opt-in must fail if the GPU environment is broken."""
2
+
3
+ import os
4
+
5
+ import pytest
6
+
7
+ pytestmark = [
8
+ pytest.mark.gpu,
9
+ pytest.mark.skipif(
10
+ os.environ.get("FLASH_MAMBA_RUN_GPU_TESTS") != "1",
11
+ reason="Set FLASH_MAMBA_RUN_GPU_TESTS=1 in a configured Linux GPU environment",
12
+ ),
13
+ ]
14
+
15
+
16
+ @pytest.mark.parametrize("variant, class_name", [("mamba", "Mamba"), ("mamba2", "Mamba2")])
17
+ def test_forward_backward_and_checkpoint_match_upstream(variant, class_name):
18
+ import mamba_ssm
19
+ import torch
20
+
21
+ from flash_mamba import create_mamba
22
+
23
+ assert torch.cuda.is_available(), "GPU test explicitly requested but CUDA/ROCm is unavailable"
24
+ kwargs = {"d_state": 64, "d_conv": 4, "expand": 2, "device": "cuda"}
25
+ if variant == "mamba2":
26
+ kwargs["headdim"] = 64
27
+ torch.manual_seed(42)
28
+ wrapped = create_mamba(128, variant=variant, **kwargs)
29
+ official = getattr(mamba_ssm, class_name)(d_model=128, **kwargs)
30
+ assert type(wrapped) is type(official)
31
+ assert wrapped.state_dict().keys() == official.state_dict().keys()
32
+ official.load_state_dict(wrapped.state_dict(), strict=True)
33
+ wrapped.load_state_dict(official.state_dict(), strict=True)
34
+ x1 = torch.randn(2, 64, 128, device="cuda", requires_grad=True)
35
+ x2 = x1.detach().clone().requires_grad_(True)
36
+ y1, y2 = wrapped(x1), official(x2)
37
+ assert y1.shape == x1.shape
38
+ assert torch.isfinite(y1).all()
39
+ torch.testing.assert_close(y1, y2, rtol=1e-4, atol=1e-5)
40
+ y1.square().mean().backward()
41
+ y2.square().mean().backward()
42
+ assert torch.isfinite(x1.grad).all()
43
+ torch.testing.assert_close(x1.grad, x2.grad, rtol=1e-4, atol=1e-5)
44
+ for (name1, param1), (name2, param2) in zip(
45
+ wrapped.named_parameters(), official.named_parameters(), strict=True
46
+ ):
47
+ assert name1 == name2
48
+ assert param1.grad is not None, f"Missing gradient: {name1}"
49
+ assert torch.isfinite(param1.grad).all(), f"Nonfinite gradient: {name1}"
50
+ torch.testing.assert_close(param1.grad, param2.grad, rtol=1e-4, atol=1e-5)