| 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). MiniLM ~80 MB, CPU-friendly. |
| 64 | semsim = [ |
| 65 | "sentence-transformers>=3.0", |
| 66 | ] |
| 67 | # Optional .dlm integration. Only imported inside dlm_sway.integrations.dlm. |
| 68 | dlm = [ |
| 69 | "dlm>=0.9", |
| 70 | ] |
| 71 | # Visualization (P9). |
| 72 | viz = [ |
| 73 | "matplotlib>=3.8", |
| 74 | ] |
| 75 | all = [ |
| 76 | "torch>=2.4", |
| 77 | "transformers>=4.45", |
| 78 | "peft>=0.13", |
| 79 | "safetensors>=0.4", |
| 80 | "mlx>=0.18; sys_platform == 'darwin' and platform_machine == 'arm64'", |
| 81 | "mlx-lm>=0.19; sys_platform == 'darwin' and platform_machine == 'arm64'", |
| 82 | "spacy>=3.7", |
| 83 | "textstat>=0.7", |
| 84 | "nlpaug>=1.1", |
| 85 | "sentence-transformers>=3.0", |
| 86 | "matplotlib>=3.8", |
| 87 | ] |
| 88 | |
| 89 | [project.scripts] |
| 90 | dlm-sway = "dlm_sway.cli.app:main" |
| 91 | |
| 92 | [project.urls] |
| 93 | Homepage = "https://github.com/tenseleyFlow/DocumentLanguageModel" |
| 94 | Issues = "https://github.com/tenseleyFlow/DocumentLanguageModel/issues" |
| 95 | |
| 96 | [dependency-groups] |
| 97 | dev = [ |
| 98 | "pytest>=8.0", |
| 99 | "pytest-cov>=5.0", |
| 100 | "mypy>=1.11", |
| 101 | "ruff>=0.6", |
| 102 | "types-pyyaml>=6.0", |
| 103 | "hypothesis>=6.152.1", |
| 104 | ] |
| 105 | |
| 106 | [build-system] |
| 107 | requires = ["hatchling"] |
| 108 | build-backend = "hatchling.build" |
| 109 | |
| 110 | [tool.hatch.build.targets.wheel] |
| 111 | packages = ["src/dlm_sway"] |
| 112 | |
| 113 | # -------- ruff -------- |
| 114 | [tool.ruff] |
| 115 | line-length = 100 |
| 116 | target-version = "py311" |
| 117 | src = ["src", "tests"] |
| 118 | |
| 119 | [tool.ruff.lint] |
| 120 | select = [ |
| 121 | "E", # pycodestyle errors |
| 122 | "F", # pyflakes |
| 123 | "W", # pycodestyle warnings |
| 124 | "I", # isort |
| 125 | "UP", # pyupgrade |
| 126 | "B", # bugbear |
| 127 | "N", # pep8-naming |
| 128 | "C4", # comprehensions |
| 129 | "SIM", # simplify |
| 130 | "PT", # pytest |
| 131 | "RET", # return |
| 132 | "ARG", # unused args |
| 133 | "PTH", # use pathlib |
| 134 | "TID", # tidy imports |
| 135 | ] |
| 136 | ignore = [ |
| 137 | "E501", # handled by formatter |
| 138 | ] |
| 139 | |
| 140 | [tool.ruff.lint.per-file-ignores] |
| 141 | "tests/**/*.py" = ["ARG", "PT011", "SIM117"] |
| 142 | |
| 143 | [tool.ruff.lint.flake8-tidy-imports.banned-api] |
| 144 | # Hard architectural boundary: the `dlm` package is only importable |
| 145 | # from inside the optional integration shim. This keeps dlm-sway |
| 146 | # usable for anyone with just a HuggingFace base + PEFT adapter. |
| 147 | "dlm".msg = "Import `dlm` only from dlm_sway.integrations.dlm (the optional extra)." |
| 148 | |
| 149 | [tool.ruff.format] |
| 150 | quote-style = "double" |
| 151 | indent-style = "space" |
| 152 | |
| 153 | # -------- mypy -------- |
| 154 | [tool.mypy] |
| 155 | strict = true |
| 156 | python_version = "3.11" |
| 157 | packages = ["dlm_sway"] |
| 158 | mypy_path = "src" |
| 159 | warn_return_any = true |
| 160 | warn_unused_ignores = true |
| 161 | warn_redundant_casts = true |
| 162 | no_implicit_optional = true |
| 163 | disallow_untyped_decorators = true |
| 164 | plugins = ["pydantic.mypy"] |
| 165 | |
| 166 | [tool.pydantic-mypy] |
| 167 | init_forbid_extra = true |
| 168 | init_typed = true |
| 169 | warn_required_dynamic_aliases = true |
| 170 | |
| 171 | # Stubless ML ecosystem packages. Narrow boundaries in backends/* import |
| 172 | # them explicitly; the rest of the codebase stays strict. |
| 173 | [[tool.mypy.overrides]] |
| 174 | module = [ |
| 175 | "torch", |
| 176 | "torch.*", |
| 177 | "transformers.*", |
| 178 | "peft.*", |
| 179 | "safetensors.*", |
| 180 | "mlx.*", |
| 181 | "mlx_lm.*", |
| 182 | "sentence_transformers.*", |
| 183 | "spacy.*", |
| 184 | "textstat.*", |
| 185 | "nlpaug.*", |
| 186 | "huggingface_hub.*", |
| 187 | "dlm.*", |
| 188 | ] |
| 189 | ignore_missing_imports = true |
| 190 | disable_error_code = ["no-untyped-call"] |
| 191 | |
| 192 | # -------- pytest -------- |
| 193 | [tool.pytest.ini_options] |
| 194 | testpaths = ["tests"] |
| 195 | addopts = [ |
| 196 | "-ra", |
| 197 | "-m", "not slow and not gpu and not online", |
| 198 | ] |
| 199 | markers = [ |
| 200 | "slow: expensive; deselected by default", |
| 201 | "gpu: requires CUDA; skipped on CPU/MPS runners", |
| 202 | "online: touches the network; skipped in offline CI", |
| 203 | ] |