Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,14 @@ jobs:
# 5.10.1 is the first installable release past CVE-2026-4372,
# CVE-2026-5241 and CVE-2026-9856 (5.10.0, which carries the last of
# them, was yanked); it requires huggingface_hub>=1.5 /
# tokenizers>=0.22, and ForgeLM itself needs torch>=2.4; trl 1.0.0
# tokenizers>=0.22 and imports torch.float8_e8m0fnu, which first
# exists in torch 2.7.0 (this job failed on torch 2.4.0 with
# AttributeError at `import transformers`); trl 1.0.0
# pulls the accelerate>=1.4 and datasets>=4.7 floor. Keep 1:1 with the
# pyproject [project].dependencies minimums (tests/test_packaging.py
# compares them).
pip install \
"torch==2.4.0" \
"torch==2.7.0" \
"transformers==5.10.1" \
"huggingface_hub==1.5.0" \
"peft==0.19.0" \
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
with:
python-version: ${{ matrix.python }}

- uses: actions/download-artifact@v8
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: dist
path: dist
Expand Down Expand Up @@ -124,7 +124,7 @@ jobs:
contents: read # download-artifact reads workflow run state
id-token: write # OIDC trusted publishing to PyPI
steps:
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: dist
path: dist/
Expand Down
23 changes: 20 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,26 @@ All notable changes to ForgeLM are documented here.
which no longer works: the documented `{type: linear, factor: 4}` raised `KeyError: 'rope_type'` while the model was
built, and `{rope_type: ..., factor: ...}` replaced the whole parameter dict so `rope_theta` was lost (`TypeError`).
The requested keys are now merged over the model's own `rope_parameters` (the legacy key `type` is read as
`rope_type`), applied to the text config of a multimodal checkpoint, and the finished config is loaded. The
`sliding_window_attention` override travels in the same config object. Verified on transformers 5.13.0 for
`linear`, `dynamic` and `yarn` on a tiny local Llama; not exercised on the 5.3-5.9 range the floor still admits (#103).
`rope_type`), applied to the text config of a multimodal checkpoint, and the finished config is loaded. A model that
keeps one entry per layer type (Gemma 3: `full_attention` and `sliding_attention`) gets the scaling on its
`full_attention` entry, and one with per-layer-type entries but no `full_attention` is refused with exit 1 instead of
being scaled nowhere. Verified on transformers 5.13.0 for `linear`, `dynamic` and `yarn` on a tiny local Llama, and
on real Gemma 3 configs (#103).
- `training.sliding_window_attention` reaches the text config and takes effect. The override was passed as a
`from_pretrained(sliding_window=...)` keyword, which transformers 5 forwards to the model constructor once `config=`
is set, and a multimodal model (Qwen2-VL) keeps `sliding_window` on its text config, not the outer one. It now travels in
the same config object as `rope_scaling`. For the Qwen2 family the config also derives a per-layer `layer_types`
list from `use_sliding_window`, `sliding_window` and `max_window_layers`, so setting the number alone left every layer
on full attention; the text config is rebuilt with the flag on so the list is derived again (a pattern the checkpoint
supplied itself is kept), and a warning is logged when the result still has no sliding-attention layer. The unsloth backend builds no transformers config, so it
applied neither `rope_scaling` nor `sliding_window_attention` and trained with the model's own settings; it now
refuses a config that sets either when the config loads (exit 1, `--dry-run` included) instead of ignoring it (#103).
- The `torch` floor is `>=2.7.0`, not `>=2.4.0`. transformers 5.10.1 imports `torch.float8_e8m0fnu` when
`transformers.modeling_utils` loads, and that dtype first exists in torch 2.7.0; on 2.6.0 the import raises
`AttributeError`. transformers lists torch only as an extra, so pip never raised the old floor. The Nightly
Compatibility job's minimum-versions leg caught it (it installs every floor with `--no-deps`); its torch pin, the
installation pages (English and Turkish) and the `forgelm doctor` samples in the guides, references and user manuals
now say 2.7.0. The Intel Mac note stands: there is no `torch>=2.7` wheel for x86_64 macOS.
- The `forgelm` CLI no longer sends Hugging Face's anonymous usage ping. TRL's trainers call `send_telemetry` when they are
built, and `huggingface_hub` sends it unless `HF_HUB_DISABLE_TELEMETRY` is true or offline mode is on, which contradicted the
privacy page ("does not send usage data anywhere"). `main()` now sets `HF_HUB_DISABLE_TELEMETRY=1` before anything imports
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/air_gap_deployment-tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ forgelm doctor - environment check

[+ pass] forgelm.install ForgeLM 0.9.0 running from /opt/airgap-venv/lib/python3.11/site-packages/forgelm (inside site-packages).
[+ pass] python.version Python 3.11.4 (CPython).
[+ pass] torch.cuda torch 2.4.0 with CUDA 12.4.
[+ pass] numpy.torch_abi torch 2.4.0 + numpy 1.26.4 are ABI-compatible.
[+ pass] torch.cuda torch 2.7.0 with CUDA 12.6.
[+ pass] numpy.torch_abi torch 2.7.0 + numpy 1.26.4 are ABI-compatible.
[+ pass] gpu.inventory 1 GPU(s) — GPU0: NVIDIA A100 (80.0 GiB).
[+ pass] extras.qlora Discoverable, not import-tested (module bitsandbytes, purpose: 4-bit / 8-bit QLoRA training).
[+ pass] extras.eval Discoverable, not import-tested (module lm_eval, purpose: lm-evaluation-harness benchmark scoring).
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/air_gap_deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ forgelm doctor - environment check

[+ pass] forgelm.install ForgeLM 0.9.0 running from /opt/airgap-venv/lib/python3.11/site-packages/forgelm (inside site-packages).
[+ pass] python.version Python 3.11.4 (CPython).
[+ pass] torch.cuda torch 2.4.0 with CUDA 12.4.
[+ pass] numpy.torch_abi torch 2.4.0 + numpy 1.26.4 are ABI-compatible.
[+ pass] torch.cuda torch 2.7.0 with CUDA 12.6.
[+ pass] numpy.torch_abi torch 2.7.0 + numpy 1.26.4 are ABI-compatible.
[+ pass] gpu.inventory 1 GPU(s) — GPU0: NVIDIA A100 (80.0 GiB).
[+ pass] extras.qlora Discoverable, not import-tested (module bitsandbytes, purpose: 4-bit / 8-bit QLoRA training).
[+ pass] extras.eval Discoverable, not import-tested (module lm_eval, purpose: lm-evaluation-harness benchmark scoring).
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/getting-started-tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ forgelm doctor - environment check

[+ pass] forgelm.install ForgeLM 0.9.0 running from /home/me/.venv/lib/python3.11/site-packages/forgelm (inside site-packages).
[+ pass] python.version Python 3.11.4 (CPython).
[+ pass] torch.cuda torch 2.4.0 with CUDA 12.4.
[+ pass] numpy.torch_abi torch 2.4.0 + numpy 1.26.4 are ABI-compatible.
[+ pass] torch.cuda torch 2.7.0 with CUDA 12.6.
[+ pass] numpy.torch_abi torch 2.7.0 + numpy 1.26.4 are ABI-compatible.
[+ pass] gpu.inventory 1 GPU(s) — GPU0: NVIDIA RTX 4090 (24.0 GiB).
[+ pass] extras.qlora Discoverable, not import-tested (module bitsandbytes, purpose: 4-bit / 8-bit QLoRA training).
[! warn] extras.tracking Optional extra missing — install with: pip install 'forgelm[tracking]' (purpose: Weights & Biases experiment tracking).
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ forgelm doctor - environment check

[+ pass] forgelm.install ForgeLM 0.9.0 running from /home/me/.venv/lib/python3.11/site-packages/forgelm (inside site-packages).
[+ pass] python.version Python 3.11.4 (CPython).
[+ pass] torch.cuda torch 2.4.0 with CUDA 12.4.
[+ pass] numpy.torch_abi torch 2.4.0 + numpy 1.26.4 are ABI-compatible.
[+ pass] torch.cuda torch 2.7.0 with CUDA 12.6.
[+ pass] numpy.torch_abi torch 2.7.0 + numpy 1.26.4 are ABI-compatible.
[+ pass] gpu.inventory 1 GPU(s) — GPU0: NVIDIA RTX 4090 (24.0 GiB).
[+ pass] extras.qlora Discoverable, not import-tested (module bitsandbytes, purpose: 4-bit / 8-bit QLoRA training).
[! warn] extras.tracking Optional extra missing — install with: pip install 'forgelm[tracking]' (purpose: Weights & Biases experiment tracking).
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/configuration-tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ training:

| Alan | Tip | Varsayılan | Açıklama |
|------|-----|-----------|----------|
| `rope_scaling` | `Optional[Dict[str, Any]]` | `null` | RoPE ölçekleme yöntemi sözlüğü (`{"type": "linear", "factor": 2.0}` vs.). Desteklenen tipler: `"linear"`, `"dynamic"`, `"yarn"`, `"longrope"`. |
| `rope_scaling` | `Optional[Dict[str, Any]]` | `null` | RoPE ölçekleme yöntemi sözlüğü (`{"type": "linear", "factor": 2.0}` vs.). Desteklenen tipler: `"linear"`, `"dynamic"`, `"yarn"`, `"longrope"`. `model.backend: unsloth` ile desteklenmez (reddedilir, exit 1). |
| `neftune_noise_alpha` | float | `null` | NEFTune gürültü enjeksiyonu alpha değeri (ör. `5.0`) |
| `sliding_window_attention` | int | `null` | Kayan pencere dikkat boyutu (token) |
| `sliding_window_attention` | int | `null` | Kayan pencere dikkat boyutu (token). `model.backend: unsloth` ile desteklenmez (reddedilir, exit 1). |
| `sample_packing` | bool | `false` | **Kullanımdan kaldırıldı** — `packing` için takma ad (TRL tek bir packing düğmesi sunar). `true` ayarlamak `DeprecationWarning` ile `packing: true`'ya yönlendirir; v1.0.0'da kaldırılacak. Bunun yerine `packing` kullanın. |

> Kaldırma, daha erken değil v1.0.0'da gerçekleşecek; çünkü bir YAML alanını
Expand Down
4 changes: 2 additions & 2 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ across retries. Each retry attempt is logged to the audit trail.

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `rope_scaling` | `Optional[Dict[str, Any]]` | `null` | RoPE scaling method dict (`{"type": "linear", "factor": 2.0}` etc.). Supported types: `"linear"`, `"dynamic"`, `"yarn"`, `"longrope"`. |
| `rope_scaling` | `Optional[Dict[str, Any]]` | `null` | RoPE scaling method dict (`{"type": "linear", "factor": 2.0}` etc.). Supported types: `"linear"`, `"dynamic"`, `"yarn"`, `"longrope"`. Not supported with `model.backend: unsloth` (rejected, exit 1). |
| `neftune_noise_alpha` | float | `null` | NEFTune noise injection alpha (e.g., `5.0`) |
| `sliding_window_attention` | int | `null` | Sliding window attention size in tokens |
| `sliding_window_attention` | int | `null` | Sliding window attention size in tokens. Not supported with `model.backend: unsloth` (rejected, exit 1). |
| `sample_packing` | bool | `false` | **Deprecated** alias for `packing` (TRL exposes a single packing knob). Setting `true` forwards to `packing: true` with a `DeprecationWarning`; will be removed in v1.0.0. Use `packing` instead. |

> Removal will land in v1.0.0, not sooner, because removing a YAML field is a
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/doctor_subcommand-tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ $ forgelm doctor
forgelm doctor - environment check

[+ pass] python.version Python 3.11.4 (CPython).
[+ pass] torch.cuda torch 2.4.0 with CUDA 12.4.
[+ pass] torch.cuda torch 2.7.0 with CUDA 12.6.
[+ pass] gpu.inventory 1 GPU(s) - GPU0: NVIDIA RTX 4090 (24.0 GiB).
[+ pass] extras.qlora Installed (module bitsandbytes, purpose: 4-bit / 8-bit QLoRA training).
[! warn] extras.tracking Optional extra missing - install with: pip install 'forgelm[tracking]' (purpose: Weights & Biases experiment tracking).
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/doctor_subcommand.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ $ forgelm doctor
forgelm doctor - environment check

[+ pass] python.version Python 3.11.4 (CPython).
[+ pass] torch.cuda torch 2.4.0 with CUDA 12.4.
[+ pass] torch.cuda torch 2.7.0 with CUDA 12.6.
[+ pass] gpu.inventory 1 GPU(s) - GPU0: NVIDIA RTX 4090 (24.0 GiB).
[+ pass] extras.qlora Installed (module bitsandbytes, purpose: 4-bit / 8-bit QLoRA training).
[! warn] extras.tracking Optional extra missing - install with: pip install 'forgelm[tracking]' (purpose: Weights & Biases experiment tracking).
Expand Down
30 changes: 29 additions & 1 deletion docs/usermanuals/en/compliance/gdpr.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,38 @@ The scan reports the lines where the identifier appears; zero matches is a valid
"Delete my data."

If a person's data was in your training set:
1. Find the rows with `forgelm reverse-pii`, then remove them with `forgelm purge --row-id <id> --corpus <file>`; every erasure is written to the audit log.
1. Find the rows with `forgelm reverse-pii`, then remove them with `forgelm purge --row-id <id> --corpus <file>`; every erasure is written to the audit log. `--row-id` is matched against the row's `id` or `row_id` field and never against a line number, and `forgelm ingest` does not write either field, so give every row a stable id **before you train** (see below). A corpus without ids cannot be purged row by row.
2. Source documents may still hold it — drop them from your raw store and re-ingest if necessary.
3. The model may have memorised some details — see "model-level erasure" below.

To make rows addressable, give each JSONL row a unique `id` once, when the corpus is created, and keep it when the file is later filtered or re-ordered. Rows with a missing, null or empty `id` get a new one; a row whose `id` (or `row_id`, which `purge` also matches) another row already uses stops the script, because `forgelm purge --row-id` would see both rows as one match (it refuses by default, and with `--row-matches all` it would erase every match) and you have to decide which row keeps the id. The output is written to a temporary file and moved into place only after every row passed, so a failed run leaves an existing output file untouched:

```python
import json, os, uuid

SRC, DST = "data/train.jsonl", "data/train.with-ids.jsonl"
tmp, seen = DST + ".tmp", set()
try:
with open(SRC, encoding="utf-8") as src, open(tmp, "w", encoding="utf-8") as dst:
for line_no, line in enumerate(src, start=1):
if not line.strip():
continue
row = json.loads(line)
if row.get("id") in (None, ""):
row["id"] = str(uuid.uuid4())
ids = {str(row[key]) for key in ("id", "row_id") if row.get(key) not in (None, "")}
if ids & seen:
raise SystemExit(f"line {line_no}: id {sorted(ids & seen)[0]!r} is already used by another row")
seen |= ids
dst.write(json.dumps(row, ensure_ascii=False) + "\n")
os.replace(tmp, DST) # reached only when every row passed
finally:
if os.path.exists(tmp):
os.remove(tmp)
```

Train from the file with ids. `forgelm reverse-pii` reports the file and line where the identifier occurs; read that row's `id` from the file and pass it to `forgelm purge --row-id`. Re-assigning ids after training breaks the link between the audit log and the corpus, so assign once.

### Model-level erasure

LLMs can memorise rare strings from training. Even with PII masking, removing all traces from a deployed model is hard. ForgeLM's defences:
Expand Down
8 changes: 5 additions & 3 deletions docs/usermanuals/en/getting-started/first-run.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ $ forgelm doctor --offline
forgelm doctor - environment check

[+ pass] python.version Python 3.11.4 (CPython).
[! warn] torch.cuda torch 2.4.0 installed but CUDA is unavailable. CPU-only runs are supported but training will be very slow; expect to use this only for tiny experiments or smoke tests.
[+ pass] numpy.torch_abi torch 2.4.0 + numpy 2.1.0 are ABI-compatible.
[! warn] torch.cuda torch 2.7.0 installed but CUDA is unavailable. CPU-only runs are supported but training will be very slow; expect to use this only for tiny experiments or smoke tests.
[+ pass] numpy.torch_abi torch 2.7.0 + numpy 2.1.0 are ABI-compatible.
[! warn] gpu.inventory No CUDA devices visible; CPU-only mode.
[! warn] extras.distributed Optional extra missing — install with: pip install 'forgelm[distributed]' (purpose: DeepSpeed ZeRO + offload distributed training (Linux + macOS only)).
[+ pass] disk.workspace Workspace /home/me/forgelm — 387.0 GiB free of 500.0 GiB.
Expand Down Expand Up @@ -145,12 +145,14 @@ The output is a stream of timestamped `[INFO]` lines from the `forgelm.*` logger
## 6. Try the model

```shell
$ forgelm chat ./checkpoints/customer-support
$ forgelm chat Qwen/Qwen2.5-7B-Instruct --adapter ./checkpoints/customer-support/final_model
forgelm> how do I cancel my subscription?
You can cancel from Settings → Billing → Cancel subscription. Your access
continues until the end of the current billing period…
```

The positional argument is the base model from the `Model` line of the run summary; `--adapter` points at the LoRA weights in `final_model/` and merges them into it for the session. If your run saved a merged checkpoint instead (`final_model/` holds full weights and no `adapter_model.safetensors`), pass that directory as the only argument: `forgelm chat ./checkpoints/customer-support/final_model`.

## What you got on disk

```text
Expand Down
2 changes: 1 addition & 1 deletion docs/usermanuals/en/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ ForgeLM ships as a single PyPI package with optional dependency groups (called *
:::

:::note
**Intel Macs (x86_64) are not supported.** ForgeLM requires `torch>=2.4.0` (the minimum that `transformers` 5 supports), and PyPI publishes no `torch>=2.4` wheel for x86_64 macOS. Use Apple Silicon, Linux, or Windows.
**Intel Macs (x86_64) are not supported.** ForgeLM requires `torch>=2.7.0` (the first release with a dtype `transformers` 5.10 imports), and PyPI publishes no `torch>=2.7` wheel for x86_64 macOS. Use Apple Silicon, Linux, or Windows.
:::

## Base install
Expand Down
2 changes: 1 addition & 1 deletion docs/usermanuals/en/operations/air-gap.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ $ forgelm doctor --offline
forgelm doctor — environment check

[✓ pass] python.version Python 3.11.4 (CPython).
[✓ pass] torch.cuda torch 2.4.0 with CUDA 12.4.
[✓ pass] torch.cuda torch 2.7.0 with CUDA 12.6.
[✓ pass] gpu.inventory 1 GPU(s) — GPU0: NVIDIA A100 (80.0 GiB).
[✓ pass] extras.qlora Installed (module bitsandbytes, purpose: 4-bit / 8-bit QLoRA training).
[✓ pass] extras.eval Installed (module lm_eval, purpose: lm-evaluation-harness benchmark scoring).
Expand Down
17 changes: 10 additions & 7 deletions docs/usermanuals/en/operations/supply-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,21 @@ ForgeLM emits a CycloneDX 1.5 SBOM per release tag (as a workflow-run artifact,
## Where to get the SBOM

```bash
# Find the successful publish run for a tag, then download its SBOMs
# (one directory per artifact). Run artifacts expire after 90 days.
# Find the successful publish run for a tag, then download one SBOM into the
# current directory. Run artifacts expire after 90 days. (--pattern 'sbom-*'
# would fetch every SBOM instead, one sub-directory per artifact.)
gh run list --repo HodeTech/ForgeLM --workflow publish.yml --branch v<X.Y.Z> --status success \
--json databaseId,createdAt
gh run download <databaseId> --repo HodeTech/ForgeLM --pattern 'sbom-*'
gh run download <databaseId> --repo HodeTech/ForgeLM --name sbom-ubuntu-latest-py3.11

# Pretty-print one.
# Pretty-print it.
jq . sbom-ubuntu-latest-py3.11.json | less

# Diff between two releases (lists added/removed dependencies).
diff <(jq -S '.components | sort_by(.purl)' sbom-prev.json) \
<(jq -S '.components | sort_by(.purl)' sbom.json)
# Diff between two releases (lists added/removed dependencies): download the
# previous release's SBOM into its own directory, then compare.
gh run download <previousDatabaseId> --repo HodeTech/ForgeLM --name sbom-ubuntu-latest-py3.11 --dir previous
diff <(jq -S '.components | sort_by(.purl)' previous/sbom-ubuntu-latest-py3.11.json) \
<(jq -S '.components | sort_by(.purl)' sbom-ubuntu-latest-py3.11.json)

# Ingest into Dependency-Track for CVE correlation.
curl -X POST -H "X-Api-Key: $DT_KEY" -H "Content-Type: application/octet-stream" \
Expand Down
Loading
Loading