trainsight-preflight 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. trainsight_preflight-0.2.0/LICENSE +17 -0
  2. trainsight_preflight-0.2.0/NOTICE +4 -0
  3. trainsight_preflight-0.2.0/PKG-INFO +322 -0
  4. trainsight_preflight-0.2.0/README.md +285 -0
  5. trainsight_preflight-0.2.0/pyproject.toml +58 -0
  6. trainsight_preflight-0.2.0/setup.cfg +4 -0
  7. trainsight_preflight-0.2.0/src/trainsight/__init__.py +39 -0
  8. trainsight_preflight-0.2.0/src/trainsight/cli/__init__.py +3 -0
  9. trainsight_preflight-0.2.0/src/trainsight/cli/check.py +80 -0
  10. trainsight_preflight-0.2.0/src/trainsight/cli/demo.py +131 -0
  11. trainsight_preflight-0.2.0/src/trainsight/cli/main.py +22 -0
  12. trainsight_preflight-0.2.0/src/trainsight/core/__init__.py +13 -0
  13. trainsight_preflight-0.2.0/src/trainsight/core/contracts.py +15 -0
  14. trainsight_preflight-0.2.0/src/trainsight/core/exceptions.py +14 -0
  15. trainsight_preflight-0.2.0/src/trainsight/core/report.py +48 -0
  16. trainsight_preflight-0.2.0/src/trainsight/inspectors/__init__.py +5 -0
  17. trainsight_preflight-0.2.0/src/trainsight/inspectors/base.py +9 -0
  18. trainsight_preflight-0.2.0/src/trainsight/inspectors/dpo_inspector.py +398 -0
  19. trainsight_preflight-0.2.0/src/trainsight/inspectors/sft_inspector.py +363 -0
  20. trainsight_preflight-0.2.0/src/trainsight/preprocessing/__init__.py +4 -0
  21. trainsight_preflight-0.2.0/src/trainsight/preprocessing/dpo_preprocessor.py +236 -0
  22. trainsight_preflight-0.2.0/src/trainsight/preprocessing/sft_preprocessor.py +224 -0
  23. trainsight_preflight-0.2.0/src/trainsight/reconcilers/__init__.py +5 -0
  24. trainsight_preflight-0.2.0/src/trainsight/reconcilers/callbacks.py +124 -0
  25. trainsight_preflight-0.2.0/src/trainsight/reconcilers/dpo_reconciler.py +36 -0
  26. trainsight_preflight-0.2.0/src/trainsight/reconcilers/sft_reconciler.py +180 -0
  27. trainsight_preflight-0.2.0/src/trainsight/utils/__init__.py +4 -0
  28. trainsight_preflight-0.2.0/src/trainsight/utils/logging.py +19 -0
  29. trainsight_preflight-0.2.0/src/trainsight/utils/token_display.py +50 -0
  30. trainsight_preflight-0.2.0/src/trainsight_preflight.egg-info/PKG-INFO +322 -0
  31. trainsight_preflight-0.2.0/src/trainsight_preflight.egg-info/SOURCES.txt +33 -0
  32. trainsight_preflight-0.2.0/src/trainsight_preflight.egg-info/dependency_links.txt +1 -0
  33. trainsight_preflight-0.2.0/src/trainsight_preflight.egg-info/entry_points.txt +2 -0
  34. trainsight_preflight-0.2.0/src/trainsight_preflight.egg-info/requires.txt +17 -0
  35. trainsight_preflight-0.2.0/src/trainsight_preflight.egg-info/top_level.txt +1 -0
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 Aravind Sundaresan
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,4 @@
1
+ TrainSight
2
+ Copyright 2026 Aravind Sundaresan
3
+
4
+ Originally incubated within Aether Control (https://github.com/Aravind0403/aether-control-llm-infra).
@@ -0,0 +1,322 @@
1
+ Metadata-Version: 2.4
2
+ Name: trainsight-preflight
3
+ Version: 0.2.0
4
+ Summary: Preflight checks for LLM training inputs and loss contracts
5
+ Author: Aravind Sundaresan
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/Aravind0403/transight
8
+ Project-URL: Repository, https://github.com/Aravind0403/transight.git
9
+ Project-URL: Issues, https://github.com/Aravind0403/transight/issues
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ License-File: NOTICE
21
+ Requires-Dist: torch>=2.0.0
22
+ Requires-Dist: transformers>=4.40.0
23
+ Requires-Dist: pydantic>=2.0.0
24
+ Requires-Dist: rich>=13.0.0
25
+ Requires-Dist: typer>=0.9.0
26
+ Requires-Dist: numpy>=1.24.0
27
+ Provides-Extra: trl
28
+ Requires-Dist: trl>=0.8.0; extra == "trl"
29
+ Requires-Dist: datasets>=2.14.0; extra == "trl"
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
34
+ Requires-Dist: build>=1.0.0; extra == "dev"
35
+ Requires-Dist: twine>=5.0.0; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # TrainSight — checks for LLM training inputs and loss masks.
39
+
40
+ TrainSight inspects SFT and DPO datasets and provides utilities for auditing tokenized training batches. It helps identify empty answers, truncation, and supervision that conflicts with the declared training objective.
41
+
42
+ **Status**: Early-stage tooling. See the [tested environments](#-tested-environments--ci-coverage) for tested configurations. Automatic trainer integration is experimental.
43
+
44
+ [![CI](https://github.com/Aravind0403/transight/actions/workflows/ci.yml/badge.svg)](https://github.com/Aravind0403/transight/actions)
45
+ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
46
+ [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
47
+
48
+ Originally developed within [Aether Control](https://github.com/Aravind0403/aether-control-llm-infra).
49
+
50
+ ---
51
+
52
+ ## ⚠️ Important Installation Notice
53
+
54
+ > **Do not install from PyPI via `pip install trainsight`.**
55
+ > The distribution name `trainsight` on PyPI is registered to an unrelated project (`modalgrasp/trainsight`). TrainSight must be installed from source in an isolated environment until an independent distribution name is published.
56
+
57
+ ### Installation from Source
58
+
59
+ Use an isolated virtual environment to prevent package collisions:
60
+
61
+ ```bash
62
+ git clone https://github.com/Aravind0403/transight.git
63
+ cd transight
64
+ python3 -m venv .venv
65
+ source .venv/bin/activate
66
+ pip install -e .
67
+ ```
68
+
69
+ Verify your installation:
70
+ ```bash
71
+ trainsight version
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 🎯 The Problem
77
+
78
+ A training pipeline can produce valid tensors that violate your intended objective—for example, supervising prompt tokens or removing the answer during truncation. TrainSight adds explicit checks for these conditions.
79
+
80
+ Common silent divergence points include:
81
+ - **Truncation Starvation**: When a prompt consumes the sequence length budget, the completion may be clipped partially or entirely. If all answer tokens are dropped, all labels become `-100`, leaving the example with zero supervised tokens.
82
+ - **Empty Answer Training**: If a dataset row has an empty completion, preprocessing may attach an end-of-sequence delimiter (e.g. `<|im_end|>`) and supervise only that delimiter.
83
+ - **Collator Mask Leakage**: Collators that fail to set `labels == -100` over prompt or padded positions result in loss calculation over prompt text or padding artifacts.
84
+ - **Template Drift**: Formatting mismatches (e.g. Alpaca tags in a pipeline expecting ChatML) alter token boundaries and delimiter tokens without triggering parsing errors.
85
+
86
+ Whether these faults cause training to crash, produce NaN gradients, or quietly alter the learned representations depends on the model architecture, loss reduction, and optimizer state. TrainSight verifies these contracts before and during training.
87
+
88
+ ---
89
+
90
+ ## ⚡ 5-Minute Interactive Demo
91
+
92
+ TrainSight includes an interactive demonstration that exercises both preflight file inspection and in-memory batch reconciliation:
93
+
94
+ ```bash
95
+ trainsight demo
96
+ ```
97
+
98
+ *(Note: The first run downloads tokenizer configuration files if not already cached locally.)*
99
+
100
+ The demo demonstrates:
101
+ 1. **Clean Input Verification**: Audits a clean 3-row synthetic dataset and confirms zero false alarms from the actual inspection report.
102
+ 2. **Preflight Truncation Detection**: Evaluates a prompt that consumes the token budget and extracts the resulting failure report.
103
+ 3. **Synthetic Corrupted Batch Audit**: Constructs a synthetic tensor batch with unmasked prompt labels (`labels[:p_len] != -100`) and audits it against the loss contract.
104
+
105
+ ---
106
+
107
+ ## 🔬 Coverage Boundaries: Static vs. In-Memory
108
+
109
+ TrainSight maintains a clear boundary between static dataset files and in-memory tensors:
110
+
111
+ | Boundary | Method | Scope | What It Catches | What It Cannot Catch |
112
+ |---|---|---|---|---|
113
+ | **Tier 1: Static Disk Inspection** | `trainsight check <file>` | Scans entire dataset files on disk | Missing fields, empty completions, sequence truncation against token limits | Post-collation mutations, collator label leaks, runtime padding bugs |
114
+ | **Tier 2: Batch Reconciliation** | `SFTReconciler.audit_batch()` | Audits compiled in-memory batches | Prompt label leakage (`labels != -100`), padding supervision, causal shift ($i \ge 1$) | File-level statistics outside the sampled batch |
115
+
116
+ ---
117
+
118
+ ## 📋 Input Format & Supported Fields
119
+
120
+ TrainSight supports JSONL files with standard SFT and DPO conventions:
121
+
122
+ ### SFT Record Examples
123
+ Key-pair format:
124
+ ```json
125
+ {"prompt": "What is the capital of France?", "completion": "The capital of France is Paris."}
126
+ ```
127
+ OpenAI message list format:
128
+ ```json
129
+ {"messages": [{"role": "user", "content": "Solve: 15 * 8"}, {"role": "assistant", "content": "15 * 8 = 120."}]}
130
+ ```
131
+
132
+ ### DPO Record Example
133
+ ```json
134
+ {"prompt": "Explain recursion.", "chosen": "Recursion occurs when a function calls itself...", "rejected": "It is when things repeat."}
135
+ ```
136
+
137
+ ---
138
+
139
+ ## 💻 CLI Usage & Expected Outputs
140
+
141
+ ### Inspecting an SFT Dataset
142
+ Using the included clean SFT fixture (`tests/fixtures/clean_sft.jsonl`) with a 256-token limit:
143
+
144
+ ```bash
145
+ trainsight check tests/fixtures/clean_sft.jsonl --tokenizer Qwen/Qwen2.5-0.5B-Instruct --max-seq-len 256
146
+ ```
147
+
148
+ #### Expected Output on Clean Data:
149
+ ```text
150
+ TrainSight Preflight Inspection
151
+ File: clean_sft.jsonl | Task: SFT | Max Length: 256
152
+
153
+ ┌───────────────────────┬────────────┬────────┐
154
+ │ Metric │ Value │ Status │
155
+ ├───────────────────────┼────────────┼────────┤
156
+ │ Total Samples │ 25 │ OK │
157
+ │ Valid Samples │ 25 │ OK │
158
+ │ Empty Completions │ 0 │ OK │
159
+ │ Truncated Answers │ 0 │ OK │
160
+ │ Misaligned Loss Masks │ 0 │ OK │
161
+ │ Mean Sequence Length │ 111.2 tok │ Info │
162
+ │ Max Sequence Length │ 246 tok │ Info │
163
+ └───────────────────────┴────────────┴────────┘
164
+
165
+ ✓ Preflight Quality Gate Passed: Dataset meets all loss contract requirements.
166
+ ```
167
+
168
+ #### Expected Output on Contract Violation (Truncation Fault):
169
+ ```text
170
+ TrainSight Preflight Inspection
171
+ File: truncated_answer.jsonl | Task: SFT | Max Length: 256
172
+
173
+ ┌───────────────────────┬────────────┬────────┐
174
+ │ Metric │ Value │ Status │
175
+ ├───────────────────────┼────────────┼────────┤
176
+ │ Total Samples │ 25 │ OK │
177
+ │ Valid Samples │ 24 │ FAIL │
178
+ │ Empty Completions │ 0 │ OK │
179
+ │ Truncated Answers │ 1 │ FAIL │
180
+ │ Misaligned Loss Masks │ 0 │ OK │
181
+ │ Mean Sequence Length │ 119.5 tok │ Info │
182
+ │ Max Sequence Length │ 256 tok │ Info │
183
+ └───────────────────────┴────────────┴────────┘
184
+
185
+ ✗ Preflight Quality Gate Failed: Contract violations detected above.
186
+ ```
187
+
188
+ ### Exit Behavior
189
+ - **Default (`--strict`)**: Exits with returncode `1` if any empty completions, truncated answers, or invalid rows are detected. Exits with `0` if all examples satisfy the contract.
190
+ - **Non-Strict (`--no-strict`)**: Logs metrics and warnings but exits with code `0`.
191
+ - **Parsing Failures**: If the file does not exist, syntax is invalid, or the tokenizer cannot load, execution halts with code `1`.
192
+
193
+ ---
194
+
195
+ ## 🛠️ Python SDK Integration
196
+
197
+ ### Explicit Batch Reconciliation
198
+ To reconcile model input tensors against your loss contract, the audit requires exact row-level correspondence between raw samples and batch tensors.
199
+
200
+ The example below demonstrates this by constructing a model-ready batch directly from verified preprocessed samples (mirroring [`examples/01_quickstart_sft.py`](examples/01_quickstart_sft.py)). If you audit an active PyTorch dataloader instead, ensure your collator explicitly propagates row IDs and prompt boundaries alongside the batch, or evaluate batches using a sequential sampler without shuffling:
201
+
202
+ ```python
203
+ from transformers import AutoTokenizer
204
+ import torch
205
+ from trainsight import SFTReconciler, SFTPreprocessor, LossContract
206
+
207
+ MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
208
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
209
+ preprocessor = SFTPreprocessor(tokenizer_name=MODEL_NAME, max_seq_len=256)
210
+ contract = LossContract(max_seq_len=256, strict=True)
211
+ reconciler = SFTReconciler(tokenizer=tokenizer, contract=contract)
212
+
213
+ # 1. Define raw sample and derive token boundaries from formatted chat template
214
+ sample = {
215
+ "id": "row_001",
216
+ "prompt": "What is the capital of France?",
217
+ "completion": "The capital of France is Paris.",
218
+ }
219
+ clean_ex = preprocessor.process_sample(sample)
220
+
221
+ # 2. Construct model-ready batch directly from preprocessed samples (guarantees 1:1 row alignment)
222
+ batch = {
223
+ "input_ids": torch.tensor([clean_ex.input_ids]),
224
+ "labels": torch.tensor([clean_ex.labels]),
225
+ "attention_mask": torch.tensor([clean_ex.attention_mask]),
226
+ }
227
+
228
+ # 3. Audit batch tensors against declared loss contract
229
+ report = reconciler.audit_batch(
230
+ batch=batch,
231
+ prompt_lengths=[clean_ex.prompt_tokens_len],
232
+ row_ids=[sample["id"]],
233
+ raw_samples=[sample],
234
+ )
235
+ print(f"Batch audit passed: {report.passed}")
236
+ ```
237
+
238
+ ### Experimental: Trainer Callback
239
+ TrainSight provides an experimental `TrainSightCallback` for step-0 verification in Hugging Face / TRL trainers. Attach the callback directly to an initialized trainer:
240
+
241
+ ```python
242
+ from trainsight import TrainSightCallback
243
+
244
+ # Attach callback to an initialized trainer instance
245
+ # (For a complete runnable SFTTrainer setup, see tests/integration/test_trl_integration.py)
246
+ callback = TrainSightCallback(trainer=trainer, fail_closed=True)
247
+ trainer.add_callback(callback)
248
+
249
+ trainer.train()
250
+ ```
251
+
252
+ > **Known Limitations of `TrainSightCallback`**:
253
+ > 1. *Single Sampled Batch*: Audits only the first batch (step 0), not full dataset training batches.
254
+ > 2. *Shuffling Misalignment*: If the dataloader uses random shuffling, dataset row indexing will not match batch row ordering unless sequential sampling is configured.
255
+ > 3. *Circular Fallback Heuristic*: If the trainer's dataset is inaccessible and the callback falls back to deriving boundaries from the first unmasked label index, the check becomes circular: it derives prompt boundaries from the very labels being audited and cannot independently detect if prompt tokens were unmasked.
256
+
257
+ ---
258
+
259
+ ## 📊 Measured Benchmark: Local Fault-Injection Comparison
260
+
261
+ In a local fault-injection comparison, TrainSight and custom assertions detected five selected contract violations. The tested native processing path did not block them.
262
+
263
+ ### Evaluation Qualifications
264
+ 1. **Post-Collation Mutations**: The prompt unmasking and padding unmasking faults were introduced directly on collated batch tensors to test tensor audit capabilities.
265
+ 2. **Assertions Coverage**: A focused set of independent custom assertions matched TrainSight's detection coverage on these specific batches. TrainSight's value is packaged preflight checks and shared contract enforcement, rather than unique tensor arithmetic.
266
+ 3. **Scope**: These results evaluate specific injected faults in a single frozen configuration (`Qwen/Qwen2.5-0.5B-Instruct`, ChatML, CPU) and do not establish broad superiority over TRL.
267
+
268
+ | Case | Native TRL (0.24.0) | Custom Assertions | TrainSight (0.2.0) |
269
+ |---|---|---|---|
270
+ | **Clean Input** *(Control)* | Accepted | Accepted | **Accepted** *(No false alarms)* |
271
+ | **Empty Answer** *(Injected Fault)* | Accepted | Blocked | **Blocked** *(Caught Preflight)* |
272
+ | **Answer Removed by Truncation** *(Injected Fault)* | Accepted | Blocked | **Blocked** *(Caught Preflight)* |
273
+ | **Prompt Included in Loss** *(Post-Collation Mutation)* | Accepted | Blocked | **Blocked** *(Caught Reconciliation)* |
274
+ | **Template Mismatch** *(Injected Fault)* | Accepted | Blocked | **Blocked** *(Caught Reconciliation)* |
275
+ | **Padding Included in Loss** *(Post-Collation Mutation)* | Accepted | Blocked | **Blocked** *(Caught Reconciliation)* |
276
+
277
+ *Full evaluation runner, tensor evidence, and raw outputs are documented in [benchmarks/sft_comparison/report.md](benchmarks/sft_comparison/report.md).*
278
+
279
+ ---
280
+
281
+ ## 🔍 Tested Environments & CI Coverage
282
+
283
+ To maintain transparency between local frozen benchmark results and automated CI checks, their scopes are reported separately:
284
+
285
+ ### Local Benchmark Environment
286
+ The exact pinned package versions used for the reproducible fault-injection evaluation ([benchmarks/sft_comparison/report.md](benchmarks/sft_comparison/report.md)):
287
+
288
+ | Component | Pinned Version / Setting | Scope |
289
+ |---|---|---|
290
+ | **Python** | `3.12.8` | Local runtime (macOS / Linux CPU) |
291
+ | **PyTorch** | `2.10.0` | Tensor manipulation and loss contract checking |
292
+ | **Transformers** | `5.5.0` | FastTokenizer ChatML template processing |
293
+ | **TRL** | `0.24.0` | Pinned comparison with `SFTConfig(completion_only_loss=True)` |
294
+ | **Tokenizer & Template** | `Qwen/Qwen2.5-0.5B-Instruct` | ChatML template formatting |
295
+ | **Declared Objective** | Answer-only causal loss | Masked prompt (`labels=-100`), masked padding, active label at $i \ge 1$ |
296
+
297
+ ### Continuous Integration (CI) Coverage
298
+ Automated testing executed via GitHub Actions on every pull request and push to `main` ([`.github/workflows/ci.yml`](.github/workflows/ci.yml)):
299
+
300
+ | Dimension | CI Scope | Details |
301
+ |---|---|---|
302
+ | **Python Versions** | `3.10`, `3.11`, `3.12` | Matrix build on `ubuntu-latest` |
303
+ | **Resolved Dependencies** | `pip install -e ".[dev,trl]"` | Resolves dependencies according to packaging bounds (`transformers>=4.40.0`, `trl>=0.8.0`, `torch>=2.0.0`). Does **not** pin historical `trl==0.24.0`. |
304
+ | **Executed Test Suite** | `pytest tests/ -v` | Unit tests (`test_sft_inspector`, `test_sft_reconciler`, `test_causal_shift`) and CLI demo flow (`test_demo_flow`). |
305
+ | **Trainer Integration Test** | `tests/integration/test_trl_integration.py` | Instantiates a minimal CPU `SFTTrainer` with sequential sampling and verifies that collated batch tensors reconcile against the loss contract under the resolved CI TRL version. |
306
+
307
+ > **Dependency Bounds Disclaimer**: Permissive dependency ranges in `pyproject.toml` indicate packaging bounds, not verified testing across every intervening release. The benchmark’s recorded dependency versions are listed above.
308
+
309
+ ---
310
+
311
+ ## 🤝 Feedback & Issue Reporting
312
+
313
+ If you encounter issues during early testing, please submit a report using our issue templates:
314
+ - [Report an Installation Problem](.github/ISSUE_TEMPLATE/installation_problem.md)
315
+ - [Report a False Alarm](.github/ISSUE_TEMPLATE/false_alarm.md) (valid input flagged incorrectly)
316
+ - [Report a Missed Violation](.github/ISSUE_TEMPLATE/missed_violation.md) (corrupted tensor unflagged)
317
+
318
+ ---
319
+
320
+ ## 📄 License
321
+
322
+ Apache License 2.0. See [LICENSE](LICENSE) for details.
@@ -0,0 +1,285 @@
1
+ # TrainSight — checks for LLM training inputs and loss masks.
2
+
3
+ TrainSight inspects SFT and DPO datasets and provides utilities for auditing tokenized training batches. It helps identify empty answers, truncation, and supervision that conflicts with the declared training objective.
4
+
5
+ **Status**: Early-stage tooling. See the [tested environments](#-tested-environments--ci-coverage) for tested configurations. Automatic trainer integration is experimental.
6
+
7
+ [![CI](https://github.com/Aravind0403/transight/actions/workflows/ci.yml/badge.svg)](https://github.com/Aravind0403/transight/actions)
8
+ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
9
+ [![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
10
+
11
+ Originally developed within [Aether Control](https://github.com/Aravind0403/aether-control-llm-infra).
12
+
13
+ ---
14
+
15
+ ## ⚠️ Important Installation Notice
16
+
17
+ > **Do not install from PyPI via `pip install trainsight`.**
18
+ > The distribution name `trainsight` on PyPI is registered to an unrelated project (`modalgrasp/trainsight`). TrainSight must be installed from source in an isolated environment until an independent distribution name is published.
19
+
20
+ ### Installation from Source
21
+
22
+ Use an isolated virtual environment to prevent package collisions:
23
+
24
+ ```bash
25
+ git clone https://github.com/Aravind0403/transight.git
26
+ cd transight
27
+ python3 -m venv .venv
28
+ source .venv/bin/activate
29
+ pip install -e .
30
+ ```
31
+
32
+ Verify your installation:
33
+ ```bash
34
+ trainsight version
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 🎯 The Problem
40
+
41
+ A training pipeline can produce valid tensors that violate your intended objective—for example, supervising prompt tokens or removing the answer during truncation. TrainSight adds explicit checks for these conditions.
42
+
43
+ Common silent divergence points include:
44
+ - **Truncation Starvation**: When a prompt consumes the sequence length budget, the completion may be clipped partially or entirely. If all answer tokens are dropped, all labels become `-100`, leaving the example with zero supervised tokens.
45
+ - **Empty Answer Training**: If a dataset row has an empty completion, preprocessing may attach an end-of-sequence delimiter (e.g. `<|im_end|>`) and supervise only that delimiter.
46
+ - **Collator Mask Leakage**: Collators that fail to set `labels == -100` over prompt or padded positions result in loss calculation over prompt text or padding artifacts.
47
+ - **Template Drift**: Formatting mismatches (e.g. Alpaca tags in a pipeline expecting ChatML) alter token boundaries and delimiter tokens without triggering parsing errors.
48
+
49
+ Whether these faults cause training to crash, produce NaN gradients, or quietly alter the learned representations depends on the model architecture, loss reduction, and optimizer state. TrainSight verifies these contracts before and during training.
50
+
51
+ ---
52
+
53
+ ## ⚡ 5-Minute Interactive Demo
54
+
55
+ TrainSight includes an interactive demonstration that exercises both preflight file inspection and in-memory batch reconciliation:
56
+
57
+ ```bash
58
+ trainsight demo
59
+ ```
60
+
61
+ *(Note: The first run downloads tokenizer configuration files if not already cached locally.)*
62
+
63
+ The demo demonstrates:
64
+ 1. **Clean Input Verification**: Audits a clean 3-row synthetic dataset and confirms zero false alarms from the actual inspection report.
65
+ 2. **Preflight Truncation Detection**: Evaluates a prompt that consumes the token budget and extracts the resulting failure report.
66
+ 3. **Synthetic Corrupted Batch Audit**: Constructs a synthetic tensor batch with unmasked prompt labels (`labels[:p_len] != -100`) and audits it against the loss contract.
67
+
68
+ ---
69
+
70
+ ## 🔬 Coverage Boundaries: Static vs. In-Memory
71
+
72
+ TrainSight maintains a clear boundary between static dataset files and in-memory tensors:
73
+
74
+ | Boundary | Method | Scope | What It Catches | What It Cannot Catch |
75
+ |---|---|---|---|---|
76
+ | **Tier 1: Static Disk Inspection** | `trainsight check <file>` | Scans entire dataset files on disk | Missing fields, empty completions, sequence truncation against token limits | Post-collation mutations, collator label leaks, runtime padding bugs |
77
+ | **Tier 2: Batch Reconciliation** | `SFTReconciler.audit_batch()` | Audits compiled in-memory batches | Prompt label leakage (`labels != -100`), padding supervision, causal shift ($i \ge 1$) | File-level statistics outside the sampled batch |
78
+
79
+ ---
80
+
81
+ ## 📋 Input Format & Supported Fields
82
+
83
+ TrainSight supports JSONL files with standard SFT and DPO conventions:
84
+
85
+ ### SFT Record Examples
86
+ Key-pair format:
87
+ ```json
88
+ {"prompt": "What is the capital of France?", "completion": "The capital of France is Paris."}
89
+ ```
90
+ OpenAI message list format:
91
+ ```json
92
+ {"messages": [{"role": "user", "content": "Solve: 15 * 8"}, {"role": "assistant", "content": "15 * 8 = 120."}]}
93
+ ```
94
+
95
+ ### DPO Record Example
96
+ ```json
97
+ {"prompt": "Explain recursion.", "chosen": "Recursion occurs when a function calls itself...", "rejected": "It is when things repeat."}
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 💻 CLI Usage & Expected Outputs
103
+
104
+ ### Inspecting an SFT Dataset
105
+ Using the included clean SFT fixture (`tests/fixtures/clean_sft.jsonl`) with a 256-token limit:
106
+
107
+ ```bash
108
+ trainsight check tests/fixtures/clean_sft.jsonl --tokenizer Qwen/Qwen2.5-0.5B-Instruct --max-seq-len 256
109
+ ```
110
+
111
+ #### Expected Output on Clean Data:
112
+ ```text
113
+ TrainSight Preflight Inspection
114
+ File: clean_sft.jsonl | Task: SFT | Max Length: 256
115
+
116
+ ┌───────────────────────┬────────────┬────────┐
117
+ │ Metric │ Value │ Status │
118
+ ├───────────────────────┼────────────┼────────┤
119
+ │ Total Samples │ 25 │ OK │
120
+ │ Valid Samples │ 25 │ OK │
121
+ │ Empty Completions │ 0 │ OK │
122
+ │ Truncated Answers │ 0 │ OK │
123
+ │ Misaligned Loss Masks │ 0 │ OK │
124
+ │ Mean Sequence Length │ 111.2 tok │ Info │
125
+ │ Max Sequence Length │ 246 tok │ Info │
126
+ └───────────────────────┴────────────┴────────┘
127
+
128
+ ✓ Preflight Quality Gate Passed: Dataset meets all loss contract requirements.
129
+ ```
130
+
131
+ #### Expected Output on Contract Violation (Truncation Fault):
132
+ ```text
133
+ TrainSight Preflight Inspection
134
+ File: truncated_answer.jsonl | Task: SFT | Max Length: 256
135
+
136
+ ┌───────────────────────┬────────────┬────────┐
137
+ │ Metric │ Value │ Status │
138
+ ├───────────────────────┼────────────┼────────┤
139
+ │ Total Samples │ 25 │ OK │
140
+ │ Valid Samples │ 24 │ FAIL │
141
+ │ Empty Completions │ 0 │ OK │
142
+ │ Truncated Answers │ 1 │ FAIL │
143
+ │ Misaligned Loss Masks │ 0 │ OK │
144
+ │ Mean Sequence Length │ 119.5 tok │ Info │
145
+ │ Max Sequence Length │ 256 tok │ Info │
146
+ └───────────────────────┴────────────┴────────┘
147
+
148
+ ✗ Preflight Quality Gate Failed: Contract violations detected above.
149
+ ```
150
+
151
+ ### Exit Behavior
152
+ - **Default (`--strict`)**: Exits with returncode `1` if any empty completions, truncated answers, or invalid rows are detected. Exits with `0` if all examples satisfy the contract.
153
+ - **Non-Strict (`--no-strict`)**: Logs metrics and warnings but exits with code `0`.
154
+ - **Parsing Failures**: If the file does not exist, syntax is invalid, or the tokenizer cannot load, execution halts with code `1`.
155
+
156
+ ---
157
+
158
+ ## 🛠️ Python SDK Integration
159
+
160
+ ### Explicit Batch Reconciliation
161
+ To reconcile model input tensors against your loss contract, the audit requires exact row-level correspondence between raw samples and batch tensors.
162
+
163
+ The example below demonstrates this by constructing a model-ready batch directly from verified preprocessed samples (mirroring [`examples/01_quickstart_sft.py`](examples/01_quickstart_sft.py)). If you audit an active PyTorch dataloader instead, ensure your collator explicitly propagates row IDs and prompt boundaries alongside the batch, or evaluate batches using a sequential sampler without shuffling:
164
+
165
+ ```python
166
+ from transformers import AutoTokenizer
167
+ import torch
168
+ from trainsight import SFTReconciler, SFTPreprocessor, LossContract
169
+
170
+ MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
171
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
172
+ preprocessor = SFTPreprocessor(tokenizer_name=MODEL_NAME, max_seq_len=256)
173
+ contract = LossContract(max_seq_len=256, strict=True)
174
+ reconciler = SFTReconciler(tokenizer=tokenizer, contract=contract)
175
+
176
+ # 1. Define raw sample and derive token boundaries from formatted chat template
177
+ sample = {
178
+ "id": "row_001",
179
+ "prompt": "What is the capital of France?",
180
+ "completion": "The capital of France is Paris.",
181
+ }
182
+ clean_ex = preprocessor.process_sample(sample)
183
+
184
+ # 2. Construct model-ready batch directly from preprocessed samples (guarantees 1:1 row alignment)
185
+ batch = {
186
+ "input_ids": torch.tensor([clean_ex.input_ids]),
187
+ "labels": torch.tensor([clean_ex.labels]),
188
+ "attention_mask": torch.tensor([clean_ex.attention_mask]),
189
+ }
190
+
191
+ # 3. Audit batch tensors against declared loss contract
192
+ report = reconciler.audit_batch(
193
+ batch=batch,
194
+ prompt_lengths=[clean_ex.prompt_tokens_len],
195
+ row_ids=[sample["id"]],
196
+ raw_samples=[sample],
197
+ )
198
+ print(f"Batch audit passed: {report.passed}")
199
+ ```
200
+
201
+ ### Experimental: Trainer Callback
202
+ TrainSight provides an experimental `TrainSightCallback` for step-0 verification in Hugging Face / TRL trainers. Attach the callback directly to an initialized trainer:
203
+
204
+ ```python
205
+ from trainsight import TrainSightCallback
206
+
207
+ # Attach callback to an initialized trainer instance
208
+ # (For a complete runnable SFTTrainer setup, see tests/integration/test_trl_integration.py)
209
+ callback = TrainSightCallback(trainer=trainer, fail_closed=True)
210
+ trainer.add_callback(callback)
211
+
212
+ trainer.train()
213
+ ```
214
+
215
+ > **Known Limitations of `TrainSightCallback`**:
216
+ > 1. *Single Sampled Batch*: Audits only the first batch (step 0), not full dataset training batches.
217
+ > 2. *Shuffling Misalignment*: If the dataloader uses random shuffling, dataset row indexing will not match batch row ordering unless sequential sampling is configured.
218
+ > 3. *Circular Fallback Heuristic*: If the trainer's dataset is inaccessible and the callback falls back to deriving boundaries from the first unmasked label index, the check becomes circular: it derives prompt boundaries from the very labels being audited and cannot independently detect if prompt tokens were unmasked.
219
+
220
+ ---
221
+
222
+ ## 📊 Measured Benchmark: Local Fault-Injection Comparison
223
+
224
+ In a local fault-injection comparison, TrainSight and custom assertions detected five selected contract violations. The tested native processing path did not block them.
225
+
226
+ ### Evaluation Qualifications
227
+ 1. **Post-Collation Mutations**: The prompt unmasking and padding unmasking faults were introduced directly on collated batch tensors to test tensor audit capabilities.
228
+ 2. **Assertions Coverage**: A focused set of independent custom assertions matched TrainSight's detection coverage on these specific batches. TrainSight's value is packaged preflight checks and shared contract enforcement, rather than unique tensor arithmetic.
229
+ 3. **Scope**: These results evaluate specific injected faults in a single frozen configuration (`Qwen/Qwen2.5-0.5B-Instruct`, ChatML, CPU) and do not establish broad superiority over TRL.
230
+
231
+ | Case | Native TRL (0.24.0) | Custom Assertions | TrainSight (0.2.0) |
232
+ |---|---|---|---|
233
+ | **Clean Input** *(Control)* | Accepted | Accepted | **Accepted** *(No false alarms)* |
234
+ | **Empty Answer** *(Injected Fault)* | Accepted | Blocked | **Blocked** *(Caught Preflight)* |
235
+ | **Answer Removed by Truncation** *(Injected Fault)* | Accepted | Blocked | **Blocked** *(Caught Preflight)* |
236
+ | **Prompt Included in Loss** *(Post-Collation Mutation)* | Accepted | Blocked | **Blocked** *(Caught Reconciliation)* |
237
+ | **Template Mismatch** *(Injected Fault)* | Accepted | Blocked | **Blocked** *(Caught Reconciliation)* |
238
+ | **Padding Included in Loss** *(Post-Collation Mutation)* | Accepted | Blocked | **Blocked** *(Caught Reconciliation)* |
239
+
240
+ *Full evaluation runner, tensor evidence, and raw outputs are documented in [benchmarks/sft_comparison/report.md](benchmarks/sft_comparison/report.md).*
241
+
242
+ ---
243
+
244
+ ## 🔍 Tested Environments & CI Coverage
245
+
246
+ To maintain transparency between local frozen benchmark results and automated CI checks, their scopes are reported separately:
247
+
248
+ ### Local Benchmark Environment
249
+ The exact pinned package versions used for the reproducible fault-injection evaluation ([benchmarks/sft_comparison/report.md](benchmarks/sft_comparison/report.md)):
250
+
251
+ | Component | Pinned Version / Setting | Scope |
252
+ |---|---|---|
253
+ | **Python** | `3.12.8` | Local runtime (macOS / Linux CPU) |
254
+ | **PyTorch** | `2.10.0` | Tensor manipulation and loss contract checking |
255
+ | **Transformers** | `5.5.0` | FastTokenizer ChatML template processing |
256
+ | **TRL** | `0.24.0` | Pinned comparison with `SFTConfig(completion_only_loss=True)` |
257
+ | **Tokenizer & Template** | `Qwen/Qwen2.5-0.5B-Instruct` | ChatML template formatting |
258
+ | **Declared Objective** | Answer-only causal loss | Masked prompt (`labels=-100`), masked padding, active label at $i \ge 1$ |
259
+
260
+ ### Continuous Integration (CI) Coverage
261
+ Automated testing executed via GitHub Actions on every pull request and push to `main` ([`.github/workflows/ci.yml`](.github/workflows/ci.yml)):
262
+
263
+ | Dimension | CI Scope | Details |
264
+ |---|---|---|
265
+ | **Python Versions** | `3.10`, `3.11`, `3.12` | Matrix build on `ubuntu-latest` |
266
+ | **Resolved Dependencies** | `pip install -e ".[dev,trl]"` | Resolves dependencies according to packaging bounds (`transformers>=4.40.0`, `trl>=0.8.0`, `torch>=2.0.0`). Does **not** pin historical `trl==0.24.0`. |
267
+ | **Executed Test Suite** | `pytest tests/ -v` | Unit tests (`test_sft_inspector`, `test_sft_reconciler`, `test_causal_shift`) and CLI demo flow (`test_demo_flow`). |
268
+ | **Trainer Integration Test** | `tests/integration/test_trl_integration.py` | Instantiates a minimal CPU `SFTTrainer` with sequential sampling and verifies that collated batch tensors reconcile against the loss contract under the resolved CI TRL version. |
269
+
270
+ > **Dependency Bounds Disclaimer**: Permissive dependency ranges in `pyproject.toml` indicate packaging bounds, not verified testing across every intervening release. The benchmark’s recorded dependency versions are listed above.
271
+
272
+ ---
273
+
274
+ ## 🤝 Feedback & Issue Reporting
275
+
276
+ If you encounter issues during early testing, please submit a report using our issue templates:
277
+ - [Report an Installation Problem](.github/ISSUE_TEMPLATE/installation_problem.md)
278
+ - [Report a False Alarm](.github/ISSUE_TEMPLATE/false_alarm.md) (valid input flagged incorrectly)
279
+ - [Report a Missed Violation](.github/ISSUE_TEMPLATE/missed_violation.md) (corrupted tensor unflagged)
280
+
281
+ ---
282
+
283
+ ## 📄 License
284
+
285
+ Apache License 2.0. See [LICENSE](LICENSE) for details.