stackformers 4.3.1__tar.gz → 5.0.0rc8__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 (165) hide show
  1. {stackformers-4.3.1 → stackformers-5.0.0rc8}/.gitignore +2 -1
  2. stackformers-5.0.0rc8/AGENTS.md +144 -0
  3. {stackformers-4.3.1 → stackformers-5.0.0rc8}/CHANGELOG.md +275 -0
  4. stackformers-5.0.0rc8/Justfile +27 -0
  5. stackformers-5.0.0rc8/PKG-INFO +319 -0
  6. stackformers-5.0.0rc8/README.md +270 -0
  7. stackformers-5.0.0rc8/examples/README.md +58 -0
  8. stackformers-5.0.0rc8/examples/causal_language_model.py +99 -0
  9. stackformers-5.0.0rc8/examples/encoder_decoder_onnxruntime.py +357 -0
  10. stackformers-5.0.0rc8/examples/image_encoder.py +90 -0
  11. stackformers-5.0.0rc8/examples/padded_and_packed.py +77 -0
  12. stackformers-5.0.0rc8/examples/point_cloud_encoder.py +87 -0
  13. stackformers-5.0.0rc8/examples/subclassed_encoder.py +153 -0
  14. stackformers-5.0.0rc8/pyproject.toml +51 -0
  15. stackformers-5.0.0rc8/pyrefly.toml +31 -0
  16. stackformers-5.0.0rc8/pytest.ini +4 -0
  17. stackformers-5.0.0rc8/ruff.toml +21 -0
  18. stackformers-5.0.0rc8/stackformers/DEVLOG.md +492 -0
  19. stackformers-5.0.0rc8/stackformers/__init__.py +262 -0
  20. stackformers-5.0.0rc8/stackformers/_result.py +18 -0
  21. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/README.md +10 -0
  22. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/__init__.py +16 -1
  23. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/bias.py +4 -1
  24. stackformers-5.0.0rc8/stackformers/attention/cache.py +40 -0
  25. stackformers-5.0.0rc8/stackformers/attention/cache_validation.py +52 -0
  26. stackformers-5.0.0rc8/stackformers/attention/cached.py +181 -0
  27. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/config.py +27 -10
  28. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/cross_attn.py +35 -10
  29. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/distance_bias.py +17 -5
  30. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/factory.py +6 -2
  31. stackformers-5.0.0rc8/stackformers/attention/layout.py +32 -0
  32. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/ops.py +25 -11
  33. stackformers-5.0.0rc8/stackformers/attention/protocols.py +46 -0
  34. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/attention/self_attn.py +23 -8
  35. stackformers-5.0.0rc8/stackformers/attention/varlen_backend.py +188 -0
  36. stackformers-5.0.0rc8/stackformers/cross_attender.py +183 -0
  37. stackformers-5.0.0rc8/stackformers/decoder.py +227 -0
  38. stackformers-5.0.0rc8/stackformers/decoder_cache.py +405 -0
  39. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/encoder.py +16 -4
  40. stackformers-5.0.0rc8/stackformers/feedforward/DEVLOG.md +27 -0
  41. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/feedforward/README.md +3 -1
  42. stackformers-5.0.0rc8/stackformers/feedforward/__init__.py +33 -0
  43. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/feedforward/config.py +28 -4
  44. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/feedforward/factory.py +17 -2
  45. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/feedforward/geglu.py +13 -3
  46. stackformers-5.0.0rc8/stackformers/feedforward/gelu.py +36 -0
  47. stackformers-5.0.0rc8/stackformers/feedforward/hardswish_glu.py +47 -0
  48. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/feedforward/protocols.py +5 -7
  49. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/feedforward/relu_squared.py +13 -3
  50. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/feedforward/swiglu.py +13 -3
  51. stackformers-5.0.0rc8/stackformers/layers.py +158 -0
  52. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/mlm/README.md +3 -1
  53. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/mlm/head.py +14 -2
  54. stackformers-5.0.0rc8/stackformers/mlm/head_cosine.py +39 -0
  55. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/mlm/masking.py +10 -1
  56. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/mlm/protocols.py +8 -11
  57. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/mlm/wrapper.py +17 -7
  58. stackformers-5.0.0rc8/stackformers/norm/__init__.py +5 -0
  59. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/norm/config.py +4 -0
  60. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/norm/factory.py +6 -2
  61. stackformers-5.0.0rc8/stackformers/norm/protocols.py +14 -0
  62. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/__init__.py +12 -0
  63. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/config.py +38 -16
  64. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/factory.py +6 -2
  65. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/learned.py +6 -2
  66. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/none.py +8 -3
  67. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/protocols.py +4 -4
  68. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/rope1d.py +13 -4
  69. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/rope2d.py +16 -4
  70. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/rope_nd.py +9 -3
  71. stackformers-5.0.0rc8/stackformers/positional/validation.py +12 -0
  72. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/presets/README.md +14 -3
  73. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/presets/__init__.py +14 -0
  74. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/presets/cross_attender.py +71 -18
  75. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/presets/decoder.py +91 -29
  76. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/presets/encoder.py +89 -23
  77. stackformers-5.0.0rc8/stackformers/presets/variable_width_encoder.py +363 -0
  78. stackformers-5.0.0rc8/stackformers/py.typed +1 -0
  79. stackformers-5.0.0rc8/stackformers/result/README.md +36 -0
  80. stackformers-5.0.0rc8/stackformers/result/__init__.py +27 -0
  81. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/sequence.py +6 -3
  82. stackformers-5.0.0rc8/tests/attention/legacy_api_rc1.json +1404 -0
  83. stackformers-5.0.0rc8/tests/attention/test_contracts.py +70 -0
  84. stackformers-5.0.0rc8/tests/attention/test_cross_attn_cache.py +135 -0
  85. stackformers-5.0.0rc8/tests/attention/test_empty_rows.py +72 -0
  86. stackformers-5.0.0rc8/tests/attention/test_geometry.py +72 -0
  87. stackformers-5.0.0rc8/tests/attention/test_legacy_api.py +202 -0
  88. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/attention/test_ops.py +42 -18
  89. stackformers-5.0.0rc8/tests/attention/test_sdpa_export.py +104 -0
  90. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/attention/test_self_attn.py +49 -0
  91. stackformers-5.0.0rc8/tests/attention/test_varlen_backend.py +288 -0
  92. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/conftest.py +41 -0
  93. stackformers-5.0.0rc8/tests/export_utils.py +145 -0
  94. stackformers-5.0.0rc8/tests/feedforward/test_gelu.py +72 -0
  95. stackformers-5.0.0rc8/tests/feedforward/test_hardswish_glu.py +75 -0
  96. stackformers-5.0.0rc8/tests/mlm/test_empty_selection.py +93 -0
  97. stackformers-5.0.0rc8/tests/mlm/test_head_cosine.py +48 -0
  98. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/mlm/test_wrapper.py +21 -0
  99. stackformers-5.0.0rc8/tests/norm_topology_helpers.py +87 -0
  100. stackformers-5.0.0rc8/tests/positional/test_cacheable.py +69 -0
  101. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/positional/test_rope.py +148 -44
  102. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/positional/test_rope_nd.py +6 -3
  103. stackformers-5.0.0rc8/tests/presets/test_cross_attender.py +144 -0
  104. stackformers-5.0.0rc8/tests/presets/test_decoder.py +139 -0
  105. stackformers-5.0.0rc8/tests/presets/test_encoder.py +275 -0
  106. stackformers-5.0.0rc8/tests/presets/test_variable_width_encoder.py +566 -0
  107. stackformers-5.0.0rc8/tests/test_core_results.py +246 -0
  108. stackformers-5.0.0rc8/tests/test_cross_attender.py +287 -0
  109. stackformers-5.0.0rc8/tests/test_decoder.py +392 -0
  110. stackformers-5.0.0rc8/tests/test_decoder_cache.py +321 -0
  111. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/test_encoder.py +5 -1
  112. stackformers-5.0.0rc8/tests/test_encoder_decoder_example.py +79 -0
  113. stackformers-5.0.0rc8/tests/test_examples.py +57 -0
  114. stackformers-5.0.0rc8/tests/test_layers.py +289 -0
  115. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/test_norm.py +6 -2
  116. stackformers-5.0.0rc8/tests/test_public_api.py +143 -0
  117. stackformers-5.0.0rc8/tests/test_result.py +150 -0
  118. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/test_sequence.py +67 -22
  119. {stackformers-4.3.1 → stackformers-5.0.0rc8}/uv.lock +289 -46
  120. stackformers-4.3.1/CLAUDE.md +0 -90
  121. stackformers-4.3.1/Justfile +0 -68
  122. stackformers-4.3.1/PKG-INFO +0 -204
  123. stackformers-4.3.1/README.md +0 -156
  124. stackformers-4.3.1/pyproject.toml +0 -72
  125. stackformers-4.3.1/stackformers/__init__.py +0 -150
  126. stackformers-4.3.1/stackformers/attention/protocols.py +0 -44
  127. stackformers-4.3.1/stackformers/attention/varlen_backend.py +0 -88
  128. stackformers-4.3.1/stackformers/cross_attender.py +0 -46
  129. stackformers-4.3.1/stackformers/decoder.py +0 -57
  130. stackformers-4.3.1/stackformers/feedforward/__init__.py +0 -5
  131. stackformers-4.3.1/stackformers/layers.py +0 -31
  132. stackformers-4.3.1/stackformers/norm/__init__.py +0 -3
  133. stackformers-4.3.1/stackformers/norm/protocols.py +0 -16
  134. stackformers-4.3.1/tests/attention/test_varlen_backend.py +0 -122
  135. stackformers-4.3.1/tests/presets/test_cross_attender.py +0 -36
  136. stackformers-4.3.1/tests/presets/test_decoder.py +0 -36
  137. stackformers-4.3.1/tests/presets/test_encoder.py +0 -102
  138. stackformers-4.3.1/tests/test_cross_attender.py +0 -118
  139. stackformers-4.3.1/tests/test_decoder.py +0 -141
  140. stackformers-4.3.1/tests/test_layers.py +0 -71
  141. {stackformers-4.3.1 → stackformers-5.0.0rc8}/.claudeignore +0 -0
  142. {stackformers-4.3.1 → stackformers-5.0.0rc8}/.python-version +0 -0
  143. {stackformers-4.3.1 → stackformers-5.0.0rc8}/.vscode/settings.json +0 -0
  144. {stackformers-4.3.1 → stackformers-5.0.0rc8}/LICENSE +0 -0
  145. {stackformers-4.3.1 → stackformers-5.0.0rc8}/PLAN.md +0 -0
  146. {stackformers-4.3.1 → stackformers-5.0.0rc8}/run-remote-tests.sh +0 -0
  147. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/config.py +0 -0
  148. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/mlm/config.py +0 -0
  149. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/norm/README.md +0 -0
  150. {stackformers-4.3.1 → stackformers-5.0.0rc8}/stackformers/positional/README.md +0 -0
  151. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/__init__.py +0 -0
  152. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/attention/__init__.py +0 -0
  153. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/attention/test_cross_attn.py +0 -0
  154. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/attention/test_distance_bias.py +0 -0
  155. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/attention/test_kernels.py +0 -0
  156. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/feedforward/__init__.py +0 -0
  157. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/feedforward/test_geglu.py +0 -0
  158. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/feedforward/test_relu_squared.py +0 -0
  159. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/feedforward/test_swiglu.py +0 -0
  160. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/mlm/__init__.py +0 -0
  161. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/mlm/test_head.py +0 -0
  162. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/mlm/test_masking.py +0 -0
  163. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/positional/__init__.py +0 -0
  164. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/positional/test_learned_pos.py +0 -0
  165. {stackformers-4.3.1 → stackformers-5.0.0rc8}/tests/presets/__init__.py +0 -0
@@ -37,6 +37,7 @@ pip-log.txt
37
37
  pip-delete-this-directory.txt
38
38
 
39
39
  # Unit test / coverage reports
40
+ /work_dir/
40
41
  htmlcov/
41
42
  .tox/
42
43
  .nox/
@@ -216,4 +217,4 @@ __marimo__/
216
217
 
217
218
  # Streamlit
218
219
  .streamlit/secrets.toml
219
- x-transformers
220
+ x-transformers
@@ -0,0 +1,144 @@
1
+ # Repository guidance
2
+
3
+ ## What this project is
4
+
5
+ Typed, composable, SOLID transformer library for PyTorch.
6
+ Behavior comes from injected dependencies, not constructor flags.
7
+
8
+ ## Repository commands
9
+
10
+ Read `Justfile` before running setup, build, formatting, linting, type-checking, testing, or
11
+ cleanup commands. Prefer its existing recipes so local and automated workflows use the same
12
+ tool arguments and defaults.
13
+
14
+ Always use `uv` for Python commands.
15
+
16
+ ```bash
17
+ just fmt # ruff format
18
+ just lint # ruff check
19
+ just types # pyrefly
20
+ just test # pytest
21
+ just check # formatting check, lint, types, and tests
22
+ ```
23
+
24
+ ## Design rules
25
+
26
+ - Encode distinct modes with closed unions, not optional fields in structured records.
27
+ - Use null objects (e.g. `NoPosEncoding`) instead of `if self.x is not None` inside `forward()`.
28
+ - Do not add flags in `__init__` that change `forward()` behavior; introduce a new class.
29
+ - Keep `forward()` traceable by `torch.compile` and `torch.export`; avoid Python control flow
30
+ on tensors.
31
+ - Keep one concept per file; split files that cannot be described in one sentence.
32
+ - Annotate tensor arguments with jaxtyping shape strings using the dimension vocabulary below.
33
+ - Type collaborators accepted by `__init__` as protocols rather than concrete implementations.
34
+
35
+ ## Python structured records
36
+
37
+ Never use `dataclasses` in this repository. PyTorch recognizes `NamedTuple` and `TypedDict`
38
+ reliably across pytree, export, and compilation boundaries, while dataclasses require additional
39
+ registration and handling.
40
+
41
+ - Use `NamedTuple` for fixed-shape records with named fields, especially tensor inputs and outputs.
42
+ - Use `TypedDict` when the value must retain mapping semantics.
43
+ - Do not introduce `dataclass`, `@dataclass`, or imports from `dataclasses`.
44
+
45
+ Ruff enforces the import restriction through `TID251`.
46
+
47
+ ## Expected failures
48
+
49
+ Use `Result[T, E] = Ok[T] | Err[E]` from `stackformers.result` for recoverable core failures.
50
+ The alternatives are generic NamedTuples; do not add an external Result dependency or use
51
+ unchecked unwrap methods. Match `Ok(value=value)` / `Err(error=error)` with keyword patterns
52
+ for PyTorch tracing compatibility, and use `assert_never` for closed-union exhaustiveness.
53
+
54
+ Validate admission before construction instead of raising and catching expected failures.
55
+ Keep exception policy at public adapters for operations that cannot proceed, preserving
56
+ existing API contracts. Catch expected vendor exceptions at the vendor boundary, retaining
57
+ their original causes; unexpected programming failures may propagate.
58
+
59
+ ## Dimension vocabulary
60
+
61
+ | Symbol | Meaning |
62
+ |--------|---------|
63
+ | `b` | Batch size |
64
+ | `n` | Query sequence length |
65
+ | `s` | Key/value sequence length (`n` for self-attention) |
66
+ | `d` | Model dimension |
67
+ | `h` | Number of heads |
68
+ | `dh` | Dimension per head |
69
+ | `w` | Window size |
70
+ | `nt` | Total tokens in a packed sequence |
71
+ | `m` | Number of masked tokens gathered across a batch or pack |
72
+
73
+ ## File organization
74
+
75
+ - All library code lives directly under `stackformers/`. Breaking changes go in a new
76
+ top-level package.
77
+ - Keep configs next to the classes they configure, e.g. `attention/config.py` and
78
+ `feedforward/config.py`.
79
+ - Keep cross-cutting configs (`LayerConfig`, `EncoderConfig`, `DecoderConfig`) in `config.py`.
80
+ - Each module owns a `factory.py` with a `build_*` function that dispatches on config type
81
+ via `match`.
82
+ - Keep opinionated presets in `presets/`, with one file per preset class.
83
+ - Define protocols in their owning module (`attention/protocols.py`, etc.), not a central file.
84
+ - Implement protocols structurally; implementations do not import the protocols they implement.
85
+
86
+ ## Config and factory conventions
87
+
88
+ Every union config type uses a `kind: Literal[...]` discriminator field so configs round-trip
89
+ through JSON unambiguously. Define the union as an
90
+ `Annotated[..., Field(discriminator="kind")]` alias in the same `config.py`.
91
+
92
+ Each module owns its builder in a co-located `factory.py`, with the signature
93
+ `build_*(config: *Config, ...) -> Protocol`. Presets call factories instead of instantiating
94
+ concrete classes directly.
95
+
96
+ ## Module READMEs
97
+
98
+ Each module subdirectory has a `README.md`. When changing a module's design by adding a
99
+ variant, removing a concept, or changing a dispatch strategy, update its README in the same
100
+ commit. Cover its purpose, key design decisions, and how to extend it with a new variant.
101
+ Do not repeat information obvious from the source, such as file tables or copied signatures.
102
+
103
+ ## Testing
104
+
105
+ - Mirror source directory structure, with one test file per source file.
106
+ - Use pytest fixtures for module construction, inputs, and sequence objects. Pure transformation
107
+ and assertion helpers may be ordinary functions.
108
+ - Parametrize compute tests over `device_dtype` (all device/dtype combinations); use `device`
109
+ alone with float32 for gradient tests.
110
+ - Test shape contracts and numerical behavior where correctness or compatibility requires it.
111
+ - Use `atol(dtype)` from `tests/conftest.py` for numerical tolerances; exact compatibility
112
+ assertions may require equality instead.
113
+
114
+ ## Dependencies and PyTorch
115
+
116
+ - Prefer built-in PyTorch implementations over custom ones, especially
117
+ `F.scaled_dot_product_attention` and `torch.nn.attention.varlen.varlen_attn`.
118
+ - Do not add optional third-party kernel dependencies; kernels use PyTorch.
119
+ - Use Pydantic for configs. Prefer `Field(gt=0)` / `Field(ge=0)` over `@field_validator`
120
+ for simple bounds.
121
+ - Prefer `nn.Module` activations (`nn.SiLU()`, `nn.GELU()`) over `torch.nn.functional` calls.
122
+ - Before writing PyTorch code, check the installed version with
123
+ `uv run python -c "import torch; print(torch.__version__)"`.
124
+ - Check `.venv/lib/python3.*/site-packages/torch/nn/modules/` for available implementations.
125
+
126
+ ## x-transformers reference
127
+
128
+ Use the local `x-transformers/` checkout, when available, for math and implementation details
129
+ only. Do not copy its architecture.
130
+
131
+ ## Versioning
132
+
133
+ Maintain the version in `pyproject.toml` under `[project].version`, synchronize it with
134
+ `uv.lock`, and expose it at runtime through `stackformers.__version__`.
135
+
136
+ Use Semantic Versioning:
137
+
138
+ - **MAJOR** for breaking public API changes.
139
+ - **MINOR** for new backwards-compatible features.
140
+ - **PATCH** for bug fixes or internal changes without API impact.
141
+
142
+ Bump the version before committing package or behavior changes on `main`. Version bumps on
143
+ feature or work branches require an explicit user instruction. Changes limited to repository
144
+ guidance, tests, or CI do not require a bump.
@@ -6,6 +6,281 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
6
  adheres to [Semantic Versioning](https://semver.org/): MAJOR for breaking public API changes,
7
7
  MINOR for backwards-compatible features, PATCH for bug fixes and internal changes.
8
8
 
9
+ ## [5.0.0rc8]
10
+
11
+ ### New Features
12
+
13
+ - Allow decoder context to be omitted or passed as `None`, skipping cross-attention and
14
+ its normalization across all four layer topologies. Cached decoding accepts a `None`
15
+ cross cache without requiring context projections. Existing configs and checkpoints
16
+ remain compatible.
17
+
18
+ ## [5.0.0rc7] — 2026-09-28
19
+
20
+ ### Developers
21
+
22
+ - Tighten cached attention admission to the existing attention protocols and retain vendor
23
+ callable parameter/return types during response validation. Give fake kernel requests an
24
+ explicit TypedDict schema instead of broad `object` annotations.
25
+
26
+ ## [5.0.0rc6] — 2026-09-26
27
+
28
+ ### Backwards Incompatible Changes
29
+
30
+ #### Core Result values
31
+
32
+ Core outcomes now use `stackformers.result.Result[T, E]`, the union of generic NamedTuples
33
+ `Ok[T]` and `Err[E]`, instead of `returns.result.Result`. Consumers of core outcomes must
34
+ match `Ok(value=value)` / `Err(error=error)` and access the narrowed payload; `unwrap`,
35
+ `failure`, and `map` methods are no longer available. Public tensor/configuration returns,
36
+ validation exceptions, and backend fallback warnings remain compatible. Remove any reliance
37
+ on Stackformers installing `returns` as a transitive dependency.
38
+
39
+ ### Developers
40
+
41
+ - Validate cached decoder attention admission before constructing executors, returning
42
+ expected failures as data instead of raising and catching constructor exceptions.
43
+
44
+ ## [5.0.0rc5] — 2026-09-26
45
+
46
+ ### Developers
47
+
48
+ - Consolidate repository guidance in `AGENTS.md` and remove `CLAUDE.md`.
49
+ - Reduce the Justfile to eight recipes. Replace `fmt-check`, `lint-fix`, `test-only`, and
50
+ `test-cov` with arguments to `fmt`, `lint`, and `test`; remove `fix` and `clean`.
51
+ Keep `check` as the full quality gate and preserve argument boundaries when forwarding flags.
52
+
53
+ ## [5.0.0rc4] — 2026-09-26
54
+
55
+ ### Developers
56
+
57
+ - Require kernels injected into the experimental attention core to return
58
+ `Result[Tensor, VarlenFallback]`. Normalize PyTorch responses and expected exceptions
59
+ in the vendor adapter, preserving public tensor-or-None returns, warnings, and error causes.
60
+
61
+ ## [5.0.0rc3] — 2026-09-26
62
+
63
+ ### New Features
64
+
65
+ - Accept shared or per-layer `dim_heads`, `heads`, `causal`, `ff_mult`, `dropout`, and
66
+ `norm_placement` in the variable-width encoder factory. Explicit `heads` allows independent
67
+ model and attention widths; omitting it retains exact `d_model // dim_head` inference.
68
+
69
+ ### Bug Fixes
70
+
71
+ - Allow model widths that are not divisible by the query-head count in self- and
72
+ cross-attention configs. Internal query width remains `heads * dim_head`; grouped
73
+ attention still requires `heads` divisible by `kv_heads`.
74
+
75
+ ## [5.0.0rc1] — 2026-09-26
76
+
77
+ ### Backwards Incompatible Changes
78
+
79
+ #### Immutable attention configuration
80
+
81
+ `SelfAttentionConfig` and `CrossAttentionConfig` previously allowed field assignment after
82
+ construction. They are now immutable, preventing a shared config from reinterpreting an
83
+ existing model's projections. Construct a new config and model for different architecture
84
+ settings instead of assigning config fields.
85
+
86
+ #### Matching cross-attention layouts
87
+
88
+ Cross-attention previously accepted mixed padded/packed layouts in its annotations and failed
89
+ during tensor reshaping. Its type contract now requires matching layouts, and mixed layouts
90
+ raise `ValueError` at runtime. Narrow both inputs together when calling through the `CrossAttn`
91
+ protocol, or convert them to the same layout before calling.
92
+
93
+ #### Empty reconstruction selections
94
+
95
+ Custom reconstruction heads must now return a differentiable zero loss for zero selected
96
+ tokens; earlier head contracts left empty-input behavior unspecified. Add an empty-input guard
97
+ that returns `prediction.sum()` to custom heads. Independent sampling is unchanged, so callers
98
+ must continue to support batches in which no tokens are selected.
99
+
100
+ ### Bug Fixes
101
+
102
+ - Return finite zero losses from both built-in MLM heads when no tokens are selected.
103
+ - Exclude masked keys completely and return zero attention contributions and gradients when
104
+ padding, windows, or additive bias exclude every key, including exported ONNX execution.
105
+ - Fix ONNX Runtime attention-mask shape errors for opsets 23–25 by explicitly expanding
106
+ the query dimension, including dynamic batch and sequence lengths.
107
+
108
+ ### Performance
109
+
110
+ - Avoid repadding input features on the packed self-attention path when using `NoAttnBias`.
111
+
112
+ ### Developers
113
+
114
+ - Include the `py.typed` marker so type checkers recognize annotations in installed wheels.
115
+ - Use shared `returns` Result values for core layout, geometry, cache, and backend checks.
116
+ Preserve public tensor/configuration returns, validation exceptions, and fallback warnings;
117
+ retain original backend failure detail for callers that handle outcomes directly.
118
+ - Apply rustic-python's strict Pyrefly and Ruff profile to the library, tests, and examples;
119
+ require Pyrefly 1.3.1 and Ruff 0.16.9 or newer and check Python 3.11 compatibility.
120
+ - Expose typed module-call signatures without replacing PyTorch's runtime hook dispatch,
121
+ type the experimental attention-kernel boundary, and replace stale type suppressions.
122
+ - Require ONNX Runtime parity at opsets 23–25 instead of accepting opset-23 failures or
123
+ excluding newer dynamo exports using PyTorch's legacy exporter limit.
124
+
125
+ ## [4.7.0b6] — 2026-08-31
126
+
127
+ ### Documentation
128
+
129
+ - Add runnable, CPU-friendly examples for equivalent padded and packed batches, causal language
130
+ modeling, image patches with RoPE-2D, continuous point coordinates with RoPE-ND, and both
131
+ focused and fully user-owned encoder subclassing.
132
+
133
+ ## [4.7.0b5] — 2026-08-28
134
+
135
+ ### Highlights
136
+
137
+ - Add an export-oriented decoder path that reuses cross-attention K/V projections and grows a
138
+ self-attention K/V cache one token at a time, while leaving existing encoder, decoder, and
139
+ attention APIs unchanged.
140
+
141
+ ### New Features
142
+
143
+ - Add composition-only `CachedSelfAttentionWrapper`, `CachedCrossAttentionWrapper`,
144
+ `DecoderCrossAttentionCacheBuilder`, and `CachedDecoderWrapper` APIs. The wrappers share the
145
+ existing model's parameters and support every built-in positional encoding and decoder
146
+ normalization topology.
147
+ - Define a C++/ONNX Runtime-friendly tensor boundary with required, non-optional cross and self
148
+ caches, a required `step_i` tensor, dynamically shaped batch/source/history axes, and a returned
149
+ self cache for the next decoder session run. Cached self-attention currently requires global
150
+ causal attention with `NoAttnBias`.
151
+
152
+ ### Documentation
153
+
154
+ - Add an encoder-decoder example that exports separate encoder, cross-cache builder, and cached
155
+ one-token decoder ONNX graphs, then verifies their autoregressive outputs with ONNX Runtime.
156
+ - Document cache layouts, tensor names, ownership, lifecycle, dynamic axes, and the initial
157
+ zero-length self-cache contract for C++ integration.
158
+
159
+ ### Developers
160
+
161
+ - Add eager parity, gradient, export, ONNX Runtime, positional-encoding, and stable `4.6.0` public
162
+ API regression coverage for the cached path.
163
+ - Standardize export-facing structured records on `NamedTuple` or `TypedDict` and enforce the
164
+ repository's no-dataclass policy with Ruff.
165
+
166
+ ## [4.7.0b4] — 2026-08-19
167
+
168
+ ### Backwards Incompatible Changes
169
+
170
+ #### Variable-width encoder configuration
171
+
172
+ `VariableWidthTransformerEncoderConfig` again stores `layers: list[VariableWidthEncoderLayerConfig]`
173
+ instead of the `d_models`/`dim_heads` representation introduced in `4.7.0b3`. Each layer record
174
+ keeps its attention, feed-forward, norm, positional-encoding, and attention-bias choices explicit
175
+ and validates their dimensions together. Migrate direct `4.7.0b3` constructors to complete layer
176
+ records. The convenience config factory now requires explicit keyword arguments; replace
177
+ `variable_width_encoder_config(d_models, dim_heads)` with
178
+ `variable_width_encoder_config(d_models=d_models, dim_heads=dim_heads)`.
179
+
180
+ ### New Features
181
+
182
+ - Export all concrete feed-forward configs and implementations, learned/ND positional encodings,
183
+ attention-bias configs, `VariableWidthEncoderLayerConfig`, and `node_encoder_config` from the
184
+ package root. Variable-width layers can select any public component config without subclassing.
185
+
186
+ ### Developers
187
+
188
+ - Add static and dynamic PyTorch/ONNX export coverage with ONNX Runtime parity for every public
189
+ feed-forward variant and the existing Transformer configuration paths.
190
+
191
+ ## [4.7.0b3] — 2026-08-19
192
+
193
+ ### Backwards Incompatible Changes
194
+
195
+ #### Variable-width encoder configuration
196
+
197
+ `VariableWidthTransformerEncoderConfig` now represents variable block geometry as parallel
198
+ `d_models` and `dim_heads` arrays, with `causal`, `ff_mult`, `dropout`, and `norm_placement` stored
199
+ once as shared settings. Earlier beta configs stored an array of
200
+ `VariableWidthEncoderLayerConfig` objects under `layers`; that class and its public export are
201
+ removed. Explicit constructors and serialized beta configs must migrate from values such as
202
+ `config.layers[index].attn.dim` to `config.d_models[index]`. The preset continues to construct
203
+ global attention with RoPE, RMSNorm, SwiGLU, and no attention bias. Callers of
204
+ `variable_width_encoder_config` are unchanged.
205
+
206
+ ## [4.7.0b2] — 2026-08-19
207
+
208
+ ### Added
209
+
210
+ - **`HardSwishGLU`** — a bias-free, parameter-matched gated feed-forward variant using
211
+ `nn.Hardswish` instead of SwiGLU's `nn.SiLU`. Select it with
212
+ `HardSwishGLUConfig(kind="hardswish_glu")`; serialized configurations preserve the distinction
213
+ between the exact SwiGLU operation and its piecewise-linear HardSwish alternative.
214
+
215
+ ### Compatibility
216
+
217
+ - Existing feed-forward classes, config discriminators, presets, and defaults are unchanged.
218
+ `HardSwishGLU` is opt-in and uses the same input/output shape and hidden-width rule as SwiGLU.
219
+
220
+ ## [4.7.0b1] — 2026-08-18
221
+
222
+ ### Added
223
+
224
+ - **Variable-width Transformer encoder preset** through `VariableWidthTransformerEncoder` and
225
+ `variable_width_encoder_config`. Callers provide one model width and head dimension per block;
226
+ the preset derives head counts and inserts a bias-free learned projection only where adjacent
227
+ model widths differ. Expanded layer configs retain explicit attention, feed-forward, norm,
228
+ positional-encoding, and attention-bias collaborators and support JSON round trips.
229
+
230
+ ### Compatibility
231
+
232
+ - Existing uniform-width encoder classes, configs, serialized payloads, checkpoints, and defaults
233
+ are unchanged. Variable-width models use their own preset and state-dict structure.
234
+
235
+ ## [4.6.0] — 2026-08-14
236
+
237
+ ### Added
238
+
239
+ - **Configurable normalization placement** for encoder, decoder, and cross-attender presets through
240
+ `NormPlacement = Literal["pre", "post", "sandwich", "reordered"]`. Each topology has a focused
241
+ layer class while the existing `TransformerLayer`, `DecoderLayer`, and `CrossAttenderLayer`
242
+ remain the pre-norm implementations. `"reordered"` implements the OLMo 2 residual layout
243
+ (`x + norm(branch(x))`), while sandwich uses distinct norms before and after every residual
244
+ branch—four per encoder/cross-attender layer and six per decoder layer.
245
+
246
+ ### Compatibility
247
+
248
+ - **The default remains `"pre"`.** Omitting `norm_placement` preserves the previous operation
249
+ order, exact outputs and gradients, positional `TransformerLayer` constructor, parameter names,
250
+ and state-dict shapes across encoder, decoder, and cross-attender layers. Config payloads without
251
+ the new field deserialize as pre-norm. The three legacy layer classes retain their original
252
+ constructors and module attributes.
253
+ - Serialized preset-config `model_dump()` output now includes
254
+ `"norm_placement": "pre"` unless callers request default-value exclusion. Sandwich is the only
255
+ mode that adds parameters because its post-branch norms must be independent.
256
+
257
+ ## [4.5.0] — 2026-08-14
258
+
259
+ ### Added
260
+
261
+ - **`GELUFFN`** — a standard bias-free, two-projection Transformer feed-forward network using
262
+ `nn.GELU(approximate="tanh")`. Select it with `GELUConfig(kind="gelu")`; its hidden width is
263
+ `int(dim * mult)`, matching other non-gated FFNs.
264
+
265
+ ## [4.4.0] — 2026-07-17
266
+
267
+ ### Added
268
+
269
+ - **`CosineHead`** (`stackformers/mlm/head_cosine.py`) — a `ReconstructionHead` that scores
270
+ a linear projection of the encoder's masked-position output against the target via
271
+ `1 - cosine_similarity`, instead of `RegressionHead`'s MSE.
272
+
273
+ ### Changed
274
+
275
+ - **`MLMWrapper`'s default `head` is now `CosineHead`, not `RegressionHead`.**
276
+ `mlm_loss`'s target is `input.x`, and nothing constrains its scale: once the tokenizer
277
+ trains purely under the main task (`mlm_loss` has zero gradient into it, per 4.3.1),
278
+ its output distribution is free to drift arbitrarily over training. MSE is sensitive
279
+ to that drift — matching magnitude is part of what it scores — while cosine
280
+ similarity scores direction only, so it stays meaningful regardless of where the
281
+ target's scale currently sits. `RegressionHead` is unaffected and still available;
282
+ pass `MLMWrapper(..., head=RegressionHead(dim))` to keep the previous behaviour.
283
+
9
284
  ## [4.3.1] — 2026-07-17
10
285
 
11
286
  ### Fixed
@@ -0,0 +1,27 @@
1
+ set dotenv-load := false
2
+ set positional-arguments
3
+
4
+ python_sources := "stackformers/ tests/ examples/"
5
+
6
+ default:
7
+ @just --list
8
+
9
+ sync:
10
+ uv sync --group dev
11
+
12
+ build:
13
+ uv build --clear
14
+
15
+ fmt *args:
16
+ uv run ruff format {{ python_sources }} "$@"
17
+
18
+ lint *args:
19
+ uv run ruff check {{ python_sources }} "$@"
20
+
21
+ types:
22
+ uv run pyrefly check {{ python_sources }}
23
+
24
+ test *args:
25
+ uv run pytest -q "$@"
26
+
27
+ check: (fmt "--check") lint types test