-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpyproject.toml
More file actions
271 lines (243 loc) · 10.4 KB
/
Copy pathpyproject.toml
File metadata and controls
271 lines (243 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# NOSONAR text:S8565 — Sonar wants a deterministic uv.lock / poetry.lock.
# ForgeLM does not use uv or poetry; supply-chain determinism is provided
# instead by (a) bounded dependency ranges in [project.dependencies], (b)
# pip-audit nightly gating in .github/workflows/nightly.yml, (c) per-OS
# CycloneDX SBOM emission in publish.yml. A pip-tools workflow producing
# requirements.lock for development environments is tracked under
# F-PR29-A6-lock-file in docs/roadmap/risks-and-decisions.md (v0.6.x).
[build-system]
requires = ["setuptools>=69", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "forgelm"
version = "0.9.1rc1"
description = "Config-driven LLM fine-tuning with safety evaluation, EU AI Act compliance, 6 alignment methods, and one-command bundled quickstart templates."
readme = "README.md"
requires-python = ">=3.10"
license = { text = "Apache-2.0" }
authors = [{ name = "ForgeLM Contributors" }]
keywords = [
"llm", "fine-tuning", "lora", "qlora", "dpo", "rlhf",
"transformers", "peft", "trl", "safety", "eu-ai-act",
"deepspeed", "alignment", "machine-learning",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Typing :: Typed",
]
dependencies = [
"torch>=2.4.0,<3.0.0",
# transformers 5.x requires torch>=2.4. PyTorch Foundation never published
# >=2.3 wheels for x86_64 Darwin, so Intel Macs (x86_64) can no longer
# install ForgeLM's core dependency stack — that platform was dropped with
# the transformers-5 migration (v0.9.0). Apple Silicon, Linux and Windows
# are unaffected. The previous `numpy<2; darwin x86_64` ABI-guard marker is
# gone with the platform it protected (torch 2.4+ ships against NumPy 2.x).
"transformers>=5.3.0,<6.0.0",
"peft>=0.19.0,<1.0.0",
"datasets>=4.7.0,<6.0.0",
"accelerate>=1.4.0,<2.0.0",
"pyyaml>=6.0.1,<7.0.0",
"pydantic>=2.0.0,<3.0.0",
"trl>=1.0.0,<2.0.0",
"tensorboard>=2.15.0,<3.0.0",
"huggingface_hub>=1.3.0,<2.0.0",
"requests>=2.32.2,<3.0.0",
# requests-toolbelt is required by forgelm._http for the HostHeaderSSLAdapter
# used to close the DNS-rebinding TOCTOU window in safe_post / safe_get
# (issue #14). Small (~30KB) and from the same requests ecosystem, so it's
# a hard dep rather than an optional extra — SSRF hardening must always be
# active, not opt-in.
"requests-toolbelt>=1.0.0,<2.0.0",
]
[project.optional-dependencies]
# QLoRA via bitsandbytes 4-bit/8-bit quantization (Linux-only for most setups).
qlora = [
"bitsandbytes>=0.42.0,<1.0.0; sys_platform == 'linux'",
]
# Unsloth fused-kernel acceleration for SFT/DPO (Linux-only).
unsloth = [
"unsloth>=2024.3.0; sys_platform == 'linux'",
]
# Benchmarking — lm-eval-harness for OpenLLM-style tasks.
eval = [
"lm-eval>=0.4.4,<1.0.0",
]
# Experiment tracking — Weights & Biases run logging.
tracking = [
"wandb>=0.16.0",
]
# Multi-GPU/multi-node training via DeepSpeed (ZeRO-1/2/3).
distributed = [
"deepspeed>=0.14.0",
]
# Model merging (TIES / DARE / SLERP / linear) is implemented natively on
# state dicts via the core peft + torch deps — no extra packages are required
# (mergekit was declared here but never imported anywhere; it carried its own
# tight transformers/torch pins that could downgrade a training env for zero
# benefit). Kept as a no-op extra so `pip install forgelm[merging]` still resolves.
merging = []
# GGUF export via llama-cpp-python's bundled convert_hf_to_gguf.py.
# Produces f16 and q8_0 GGUF directly. K-quants (q4_k_m etc.) require a
# separate llama-quantize step; see docs/guides/export.md for the full flow.
# Linux and macOS only; Windows builds are available but less reliable.
export = [
"llama-cpp-python>=0.2.90; sys_platform != 'win32'",
]
# Document ingestion — PDF/DOCX/EPUB/TXT → SFT-ready JSONL.
# pypdf is pure-Python (cross-platform); ebooklib + beautifulsoup4 parse
# EPUB containers; langdetect powers the audit's top-3 language stat.
# xxhash is an optional non-cryptographic 64-bit hash that materially speeds
# up the simhash backend; we fall back to BLAKE2b when it is not present
# so a bare install still works.
# OCR is intentionally out of scope (handle externally with Tesseract).
ingestion = [
"pypdf>=4.0.0,<6.0.0",
"python-docx>=1.0.0,<2.0.0",
"ebooklib>=0.18,<1.0.0",
"beautifulsoup4>=4.12.0,<5.0.0",
"langdetect>=1.0.9,<2.0.0",
"xxhash>=3.0.0,<4.0.0",
]
# Optional MinHash LSH near-duplicate detector for >50K-row corpora.
# Default audit path stays simhash + LSH banding; this extra unlocks
# `forgelm audit --dedup-method minhash --jaccard-threshold 0.85` for
# operators auditing larger datasets where MinHash's recall/precision
# trade-off is the industry standard (NeMo Curator, Dolma, RedPajama).
ingestion-scale = [
"datasketch>=1.6.0,<2.0.0",
]
# Optional Presidio ML-NER PII detector. Adds the unstructured-identifier
# categories (person / organization / location) that the regex detector
# inherently misses. Heavyweight install (≈ 50 MB spaCy model on first
# analyzer build), so opt-in only — the regex set already covers the
# GDPR-mandated structured identifiers (email, phone, IBAN, credit card,
# national IDs).
ingestion-pii-ml = [
"presidio-analyzer>=2.2.0,<3.0.0",
]
# Rich terminal rendering for the `forgelm chat` REPL.
# Falls back to plain text output when not installed.
chat = [
"rich>=13.0.0",
]
# Developer tooling — test runner, coverage, linter/formatter.
dev = [
"pytest>=8.0.0",
"pytest-cov>=5.0.0",
# pytest-randomly shuffles test order per session so order-dependent
# bugs (e.g. the v0.5.7 round-3 sys.modules.pop pollution cascade)
# surface in CI instead of waiting for a Python micro-bump or runner
# change to flip collection order. Run locally via
# ``pytest --randomly-seed=<n>`` to reproduce a failure.
"pytest-randomly>=3.15.0,<4.0.0",
"ruff>=0.4.0",
# tomllib is stdlib only on 3.11+; the 3.10 CI runner needs the tomli
# backport so the dev-only ``tools/check_notebook_pins.py`` guard (and its
# test module) can parse pyproject.toml. Never imported by the runtime path.
"tomli>=2.0.0; python_version < '3.11'",
]
# ISO 27001 / SOC 2 alignment supply-chain security tooling.
# Operators install via `pip install forgelm[security]` to run the same
# pip-audit (CVE) + bandit (static-analysis) checks ForgeLM's nightly +
# CI workflows enforce. Both are dev/CI tooling, never imported by
# the runtime path.
security = [
"pip-audit>=2.7.0,<3.0.0",
"bandit[toml]>=1.7.0,<2.0.0",
]
[project.urls]
Homepage = "https://github.com/HodeTech/ForgeLM"
Documentation = "https://github.com/HodeTech/ForgeLM/tree/main/docs/guides"
Repository = "https://github.com/HodeTech/ForgeLM"
Issues = "https://github.com/HodeTech/ForgeLM/issues"
Changelog = "https://github.com/HodeTech/ForgeLM/blob/main/CHANGELOG.md"
[project.scripts]
forgelm = "forgelm.cli:main"
[tool.setuptools]
package-dir = {"" = "."}
[tool.setuptools.packages.find]
where = ["."]
include = ["forgelm*"]
# Bundle quickstart template assets (config.yaml, data.jsonl, README.md) into
# the wheel so `forgelm quickstart` works after a `pip install` rather than
# only from a source checkout.
[tool.setuptools.package-data]
"forgelm.templates" = [
"*.md",
"*/*.yaml",
"*/*.jsonl",
"*/*.md",
]
# Phase 19: PEP 561 type-hint distribution marker so downstream
# `mypy --strict` / pyright consumers see in-source type hints without
# a separate stubs package. Empty file at forgelm/py.typed.
"forgelm" = ["py.typed"]
# Phase 36: bundled default safety probes so `forgelm safety-eval
# --default-probes` works after a wheel install.
"forgelm.safety_prompts" = ["*.jsonl"]
# F1 (review-cycle 3): schema-derived wizard defaults shipped as
# package data so ``forgelm/wizard/_state.py`` reads them via
# ``importlib.resources`` from a wheel install (otherwise the file
# wouldn't survive ``pip install`` and the wizard would fall back
# to its hardcoded sentinels). Generated by
# ``tools/generate_wizard_defaults.py``; CI guard at
# ``tools/check_wizard_defaults_sync.py``.
"forgelm.wizard" = ["_defaults.json"]
[tool.pytest.ini_options]
# F-P8-C-20: --strict-markers turns any undefined/typo'd @pytest.mark into
# an error instead of a silent no-op. The marker roster is kept to what is
# actually applied (the four aspirational unit/integration/smoke/slow
# markers had zero usages and documented a selection that never existed).
addopts = "--cov=forgelm --cov-report=term-missing --cov-fail-under=40 --strict-markers"
testpaths = ["tests"]
filterwarnings = ["ignore::DeprecationWarning"]
markers = [
"fixture_drift: tests sensitive to upstream pricing/dataset drift; excluded from the release matrix via `pytest -m 'not fixture_drift'` in publish.yml",
]
[tool.ruff]
target-version = "py310"
line-length = 120
[tool.ruff.lint]
select = ["F", "E9", "W", "B", "I"]
ignore = ["B008", "B905"]
# Pylint config (consumed by Codacy's pylint plugin). Pylint's default
# max-line-length is 100, but the project formatter is ruff @ 120 (see
# [tool.ruff] above). Without this alignment, ruff format reflows long
# f-strings back to single lines that pylint then flags — a no-win.
# Pinning pylint to 120 keeps the two tools in agreement.
[tool.pylint.format]
max-line-length = 120
# Bandit static-analysis config (consumed by Codacy + standalone bandit).
# - tests/ is excluded from the broader scan; we still scan the package itself.
# - No `skips` entry: B101 (assert_used) is left enabled repo-wide so any
# stray `assert` in production source under `forgelm/` is flagged. Tests
# are already out of scope via `exclude_dirs`, so an explicit B101 skip
# would only mask real findings in product code. If a genuine in-product
# assert is ever needed (e.g. invariant guard the type checker depends on),
# tag the line with `# nosec B101 — <rationale>` rather than re-adding a
# global skip.
[tool.bandit]
exclude_dirs = ["tests", ".venv", "build", "dist"]
[tool.coverage.run]
source = ["forgelm"]
omit = ["forgelm/templates/__init__.py"]
[tool.coverage.report]
fail_under = 40
show_missing = true
exclude_lines = [
"pragma: no cover",
"if __name__ == .__main__.",
"raise ImportError",
"except ImportError",
]