TOML · 7369 bytes Raw Blame History
1 [project]
2 name = "dlm-sway"
3 version = "0.1.0.dev0"
4 description = "Differential testing for fine-tuned causal LMs: did LoRA/QLoRA training actually change behavior, or is the model defaulting to the pretrained base?"
5 readme = "README.md"
6 requires-python = ">=3.11"
7 license = { text = "MIT" }
8 authors = [{ name = "Matt Wolffe", email = "mfwolffe@outlook.com" }]
9 keywords = [
10 "lora",
11 "qlora",
12 "peft",
13 "fine-tuning",
14 "evaluation",
15 "llm",
16 "differential-testing",
17 ]
18 classifiers = [
19 "Development Status :: 3 - Alpha",
20 "Intended Audience :: Developers",
21 "Intended Audience :: Science/Research",
22 "License :: OSI Approved :: MIT License",
23 "Programming Language :: Python :: 3",
24 "Programming Language :: Python :: 3.11",
25 "Programming Language :: Python :: 3.12",
26 "Topic :: Scientific/Engineering :: Artificial Intelligence",
27 ]
28
29 # Core deps: spec loading, orchestration, reporting. No torch — a user
30 # who only defines specs or writes a custom backend shouldn't pull 3 GB
31 # of CUDA wheels.
32 dependencies = [
33 "pydantic>=2.9",
34 "pyyaml>=6.0",
35 "typer>=0.12",
36 "rich>=13.7",
37 "numpy>=1.26",
38 "packaging>=24.0",
39 ]
40
41 [project.optional-dependencies]
42 # HuggingFace + PEFT scoring backend. The canonical path.
43 hf = [
44 "torch>=2.4",
45 "transformers>=4.45",
46 "peft>=0.13",
47 "safetensors>=0.4",
48 ]
49 # Apple Silicon inference. Env markers keep `uv sync --extra mlx` a no-op
50 # on non-Apple hosts so Linux/CUDA contributors' wheel resolution stays
51 # sane.
52 mlx = [
53 "mlx>=0.18; sys_platform == 'darwin' and platform_machine == 'arm64'",
54 "mlx-lm>=0.19; sys_platform == 'darwin' and platform_machine == 'arm64'",
55 ]
56 # Stylistic fingerprinting (C1). spaCy models pull at runtime via
57 # `python -m spacy download`.
58 style = [
59 "spacy>=3.7",
60 "textstat>=0.7",
61 "nlpaug>=1.1",
62 ]
63 # Semantic similarity (A2) + cluster-coherent KL (S16 / F8). The
64 # SentenceTransformer and k-means clustering pair ride the same
65 # ~80 MB MiniLM load; putting scikit-learn in the same extra keeps
66 # users from hitting "wait, which extra?" friction.
67 semsim = [
68 "sentence-transformers>=3.0",
69 "scikit-learn>=1.4",
70 ]
71 # Optional .dlm integration. Only imported inside dlm_sway.integrations.dlm.
72 dlm = [
73 "dlm>=0.9",
74 ]
75 # OpenAI-compatible HTTP scoring backend (S13 / F7). Unlocks hosted
76 # fine-tunes (OpenAI platform, vLLM serve, Ollama) without pulling
77 # torch. httpx + tenacity together are a few hundred KB of deps vs
78 # the 3 GB the [hf] extra costs.
79 api = [
80 "httpx>=0.27",
81 "tenacity>=9.0",
82 ]
83 # pytest integration (S15 / F10). The plugin is discovered via the
84 # `pytest11` entry point below — ``pip install 'dlm-sway[pytest]'``
85 # adds pytest if the user doesn't have it, then pytest auto-loads
86 # the plugin on next invocation.
87 pytest = [
88 "pytest>=8.0",
89 ]
90 # Visualization (P9 + S12 HTML report).
91 viz = [
92 "matplotlib>=3.8",
93 "plotly>=5.20",
94 ]
95 all = [
96 "torch>=2.4",
97 "transformers>=4.45",
98 "peft>=0.13",
99 "safetensors>=0.4",
100 "mlx>=0.18; sys_platform == 'darwin' and platform_machine == 'arm64'",
101 "mlx-lm>=0.19; sys_platform == 'darwin' and platform_machine == 'arm64'",
102 "spacy>=3.7",
103 "textstat>=0.7",
104 "nlpaug>=1.1",
105 "sentence-transformers>=3.0",
106 "scikit-learn>=1.4",
107 "httpx>=0.27",
108 "tenacity>=9.0",
109 "pytest>=8.0",
110 "matplotlib>=3.8",
111 "plotly>=5.20",
112 ]
113
114 [project.scripts]
115 sway = "dlm_sway.cli.app:main"
116
117 # S15 / F10: pytest plugin discovered via the canonical pytest11
118 # entry-point. Gets auto-loaded the moment the wheel is installed,
119 # even without an ``@pytest.mark.sway`` import — consistent with how
120 # pytest-cov, pytest-xdist, etc. ship their plugins.
121 [project.entry-points.pytest11]
122 sway = "dlm_sway.pytest_plugin"
123
124 [project.urls]
125 Homepage = "https://github.com/tenseleyFlow/sway"
126 Issues = "https://github.com/tenseleyFlow/sway/issues"
127 "Related project" = "https://github.com/tenseleyFlow/DocumentLanguageModel"
128
129 [dependency-groups]
130 dev = [
131 "pytest>=8.0",
132 "pytest-cov>=5.0",
133 "mypy>=1.11",
134 "ruff>=0.6",
135 "types-pyyaml>=6.0",
136 "hypothesis>=6.152.1",
137 # Required by the tiny_model fixture (snapshot_download) used by every
138 # slow+online integration test. Not transitively guaranteed by hf
139 # optional-dep because contributors may want to run integration tests
140 # without the full [hf] extra installed.
141 "huggingface_hub>=0.25",
142 # S19: the pre-commit-hook integration test spawns ``pre-commit``
143 # as a subprocess. Keeps the tool out of the user's runtime deps.
144 "pre-commit>=3.8",
145 ]
146
147 [build-system]
148 requires = ["hatchling"]
149 build-backend = "hatchling.build"
150
151 [tool.hatch.build.targets.wheel]
152 packages = ["src/dlm_sway"]
153
154 # -------- ruff --------
155 [tool.ruff]
156 line-length = 100
157 target-version = "py311"
158 src = ["src", "tests"]
159
160 [tool.ruff.lint]
161 select = [
162 "E", # pycodestyle errors
163 "F", # pyflakes
164 "W", # pycodestyle warnings
165 "I", # isort
166 "UP", # pyupgrade
167 "B", # bugbear
168 "N", # pep8-naming
169 "C4", # comprehensions
170 "SIM", # simplify
171 "PT", # pytest
172 "RET", # return
173 "ARG", # unused args
174 "PTH", # use pathlib
175 "TID", # tidy imports
176 ]
177 ignore = [
178 "E501", # handled by formatter
179 ]
180
181 [tool.ruff.lint.per-file-ignores]
182 "tests/**/*.py" = ["ARG", "PT011", "SIM117"]
183 # PyTorch's canonical `import torch.nn.functional as F` is universally
184 # read, so we allow the naming exception in the HF backend only.
185 "src/dlm_sway/backends/hf.py" = ["N812"]
186 # The .dlm bridge is the one place allowed to import the ``dlm`` package.
187 "src/dlm_sway/integrations/dlm/*.py" = ["TID251"]
188
189 [tool.ruff.lint.flake8-tidy-imports.banned-api]
190 # Hard architectural boundary: the `dlm` package is only importable
191 # from inside the optional integration shim. This keeps dlm-sway
192 # usable for anyone with just a HuggingFace base + PEFT adapter.
193 "dlm".msg = "Import `dlm` only from dlm_sway.integrations.dlm (the optional extra)."
194
195 [tool.ruff.format]
196 quote-style = "double"
197 indent-style = "space"
198
199 # -------- mypy --------
200 [tool.mypy]
201 strict = true
202 python_version = "3.11"
203 packages = ["dlm_sway"]
204 mypy_path = "src"
205 warn_return_any = true
206 warn_unused_ignores = true
207 warn_redundant_casts = true
208 no_implicit_optional = true
209 disallow_untyped_decorators = true
210 plugins = ["pydantic.mypy"]
211
212 [tool.pydantic-mypy]
213 init_forbid_extra = true
214 init_typed = true
215 warn_required_dynamic_aliases = true
216
217 # Stubless ML ecosystem packages. Narrow boundaries in backends/* import
218 # them explicitly; the rest of the codebase stays strict.
219 [[tool.mypy.overrides]]
220 module = [
221 "torch",
222 "torch.*",
223 "transformers.*",
224 "peft.*",
225 "safetensors.*",
226 "mlx.*",
227 "mlx_lm.*",
228 "sentence_transformers.*",
229 "sklearn",
230 "sklearn.*",
231 "spacy.*",
232 "textstat.*",
233 "nlpaug.*",
234 "matplotlib",
235 "matplotlib.*",
236 "plotly",
237 "plotly.*",
238 "tenacity",
239 "tenacity.*",
240 "httpx",
241 "httpx.*",
242 "huggingface_hub.*",
243 "dlm.*",
244 ]
245 ignore_missing_imports = true
246 disable_error_code = ["no-untyped-call"]
247
248 # -------- pytest --------
249 [tool.pytest.ini_options]
250 testpaths = ["tests"]
251 addopts = [
252 "-ra",
253 "-m", "not slow and not gpu and not online",
254 ]
255 markers = [
256 "slow: expensive; deselected by default",
257 "gpu: requires CUDA; skipped on CPU/MPS runners",
258 "online: touches the network; skipped in offline CI",
259 ]