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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ All notable changes to ForgeLM are documented here.

### Fixed

- Make the Document Ingestion quick-start example runnable in the English and Turkish user manuals: pair `--chunk-tokens`
with `--tokenizer` and show the real `summarize_result` output, with `--output` resolved to the absolute path the tool
prints, instead of a layout the tool never prints (#395).

- Make the GDPR, erasure, retention, PII-masking, GPU-cost and chat documentation say what ForgeLM does, so a deployer can copy a
documented command or YAML block, and quote a documented limit in a DPIA or an access reply, without it being untrue. The GDPR
/ KVKK manual page was rewritten earlier in this cycle; this completes it and the pages around it (#101):
Expand Down
16 changes: 12 additions & 4 deletions docs/usermanuals/en/data/ingestion.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,20 @@ $ forgelm ingest ./policies/ \
--recursive \
--strategy markdown \
--chunk-tokens 1024 \
--tokenizer "Qwen/Qwen2.5-7B-Instruct" \
--all-mask \
--output data/policies.jsonl
✓ scanned 47 files (12 PDF, 8 DOCX, 27 MD)
✓ extracted 12,240 chunks (avg 743 tokens)
✓ masked 18 PII matches, 0 secret matches
✓ wrote data/policies.jsonl (8.2 MB)
Ingestion summary
Output JSONL : /srv/project/data/policies.jsonl
Files (in/out): processed=47 skipped=0
Chunks : 12240
Total chars : 28410553
By format : docx=8, md=27, pdf=12
Note : PII masking redacted 18 span(s): email=11, phone=7
Note : Secrets masking enabled — no credentials detected in this corpus

Next: forgelm audit /srv/project/data/policies.jsonl --output ./audit/
Or train: forgelm --config <your.yaml> (set data.dataset_name_or_path: /srv/project/data/policies.jsonl)
```

`--all-mask` is the documented shorthand for `--secrets-mask --pii-mask`
Expand Down
16 changes: 12 additions & 4 deletions docs/usermanuals/tr/data/ingestion.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,20 @@ $ forgelm ingest ./policies/ \
--recursive \
--strategy markdown \
--chunk-tokens 1024 \
--tokenizer "Qwen/Qwen2.5-7B-Instruct" \
--all-mask \
--output data/policies.jsonl
✓ 47 dosya tarandı (12 PDF, 8 DOCX, 27 MD)
✓ 12,240 chunk çıkarıldı (ortalama 743 token)
✓ 18 PII eşleşmesi maskelendi, 0 sır
✓ data/policies.jsonl yazıldı (8.2 MB)
Ingestion summary
Output JSONL : /srv/project/data/policies.jsonl
Files (in/out): processed=47 skipped=0
Chunks : 12240
Total chars : 28410553
By format : docx=8, md=27, pdf=12
Note : PII masking redacted 18 span(s): email=11, phone=7
Note : Secrets masking enabled — no credentials detected in this corpus

Next: forgelm audit /srv/project/data/policies.jsonl --output ./audit/
Or train: forgelm --config <your.yaml> (set data.dataset_name_or_path: /srv/project/data/policies.jsonl)
```

`--all-mask`, `--secrets-mask --pii-mask`'in doğru sıradaki belgelenen
Expand Down
43 changes: 43 additions & 0 deletions tests/test_usermanual_reference_consistency.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import bisect
import re
import shlex
from pathlib import Path

import pytest
Expand Down Expand Up @@ -621,6 +622,48 @@ def test_gpu_cost_page_lists_every_priced_gpu_and_cost_source(lang: str) -> None
assert documented == {"user_config", "auto_detected", "fuzzy_match"}, f"{lang}/operations/gpu-cost.md: {documented}"


@pytest.mark.parametrize("lang", ["en", "tr"])
class TestIngestionPageQuickExampleStaysRunnable:
"""CR-A12-004: the Document Ingestion quick example must be a command that runs and its real output."""

@staticmethod
def _page(lang: str) -> str:
return (_REPO_ROOT / "docs" / "usermanuals" / lang / "data" / "ingestion.md").read_text(encoding="utf-8")

def _quick_example_tokens(self, lang: str) -> list[str]:
text = self._page(lang)
block = next(
(block for block in re.findall(r"```shell[ \t]*\n(.*?)```", text, re.DOTALL) if "forgelm ingest" in block),
None,
)
assert block is not None, f"ingestion.md ({lang}) must show a shell block with `forgelm ingest`"
command = re.sub(r"\\\n\s*", " ", block)
line = next(line for line in command.splitlines() if "forgelm ingest" in line)
return shlex.split(line.lstrip("$ ").strip())

def test_chunk_tokens_is_paired_with_a_tokenizer(self, lang: str) -> None:
# `forgelm ingest --chunk-tokens` refuses to run without `--tokenizer` (exit 1), so the first command a reader
# copies must carry both.
tokens = self._quick_example_tokens(lang)
assert "--chunk-tokens" in tokens, f"ingestion.md ({lang}) must keep the documented --chunk-tokens example"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
assert "--tokenizer" in tokens, f"ingestion.md ({lang}) must pass --tokenizer with --chunk-tokens"
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
chunk_tokens_at = tokens.index("--chunk-tokens")
chunk_value = tokens[chunk_tokens_at + 1] if chunk_tokens_at + 1 < len(tokens) else ""
assert chunk_value.isdigit() and int(chunk_value) > 0, (
f"ingestion.md ({lang}) must give --chunk-tokens a positive token count, not just the bare flag"
)
tokenizer_at = tokens.index("--tokenizer")
assert tokenizer_at + 1 < len(tokens) and not tokens[tokenizer_at + 1].startswith("-"), (
f"ingestion.md ({lang}) must give --tokenizer a model name, not just the bare flag"
)

def test_page_shows_the_real_summarize_result_labels(self, lang: str) -> None:
# The old block showed tick-mark progress lines that `summarize_result` never prints.
text = self._page(lang)
assert "Ingestion summary" in text, f"ingestion.md ({lang}) must show the real summarize_result heading"
assert "Files (in/out)" in text, f"ingestion.md ({lang}) must show the real Files (in/out) line"


@pytest.mark.parametrize("lang", ["en", "tr"])
def test_pii_masking_page_counts_and_validates_the_real_detectors(lang: str) -> None:
from forgelm.data_audit import PII_TYPES
Expand Down
Loading