fromscratch-pyllm 0.5.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 (131) hide show
  1. fromscratch_pyllm-0.5.0/.github/workflows/ci.yml +14 -0
  2. fromscratch_pyllm-0.5.0/.github/workflows/docs.yml +16 -0
  3. fromscratch_pyllm-0.5.0/.github/workflows/release.yml +58 -0
  4. fromscratch_pyllm-0.5.0/.gitignore +10 -0
  5. fromscratch_pyllm-0.5.0/.pre-commit-config.yaml +56 -0
  6. fromscratch_pyllm-0.5.0/CLAUDE.md +72 -0
  7. fromscratch_pyllm-0.5.0/LICENSE +21 -0
  8. fromscratch_pyllm-0.5.0/PKG-INFO +231 -0
  9. fromscratch_pyllm-0.5.0/README.md +194 -0
  10. fromscratch_pyllm-0.5.0/conftest.py +21 -0
  11. fromscratch_pyllm-0.5.0/docs/concepts/README.md +20 -0
  12. fromscratch_pyllm-0.5.0/docs/concepts/attention.md +39 -0
  13. fromscratch_pyllm-0.5.0/docs/concepts/autograd.md +63 -0
  14. fromscratch_pyllm-0.5.0/docs/concepts/embeddings.md +28 -0
  15. fromscratch_pyllm-0.5.0/docs/concepts/grow-your-own-data.md +62 -0
  16. fromscratch_pyllm-0.5.0/docs/concepts/how-a-model-learns.md +35 -0
  17. fromscratch_pyllm-0.5.0/docs/concepts/sampling.md +33 -0
  18. fromscratch_pyllm-0.5.0/docs/concepts/the-referee.md +98 -0
  19. fromscratch_pyllm-0.5.0/docs/concepts/tokens.md +34 -0
  20. fromscratch_pyllm-0.5.0/docs/concepts/transformer.md +56 -0
  21. fromscratch_pyllm-0.5.0/docs/index.md +166 -0
  22. fromscratch_pyllm-0.5.0/docs/property-testing.md +320 -0
  23. fromscratch_pyllm-0.5.0/docs/superpowers/plans/2026-06-22-pyllm-01-autograd.md +1139 -0
  24. fromscratch_pyllm-0.5.0/docs/superpowers/plans/2026-06-22-pyllm-02-nn-tokenizers.md +1891 -0
  25. fromscratch_pyllm-0.5.0/docs/superpowers/plans/2026-06-22-pyllm-roadmap.md +70 -0
  26. fromscratch_pyllm-0.5.0/docs/superpowers/plans/2026-07-21-pyllm-03-models-training-generation.md +2250 -0
  27. fromscratch_pyllm-0.5.0/docs/superpowers/plans/2026-07-21-pyllm-04-pebble-flagship.md +1539 -0
  28. fromscratch_pyllm-0.5.0/docs/superpowers/plans/2026-07-25-pyllm-05-pystack-integration-docs.md +559 -0
  29. fromscratch_pyllm-0.5.0/docs/superpowers/plans/2026-10-16-pyllm-06-grammar-constrained-decoding.md +1136 -0
  30. fromscratch_pyllm-0.5.0/docs/superpowers/specs/2026-06-22-pyllm-design.md +235 -0
  31. fromscratch_pyllm-0.5.0/docs/superpowers/specs/2026-10-16-pyllm-06-grammar-constrained-decoding-design.md +134 -0
  32. fromscratch_pyllm-0.5.0/mkdocs.yml +85 -0
  33. fromscratch_pyllm-0.5.0/overrides/main.html +7 -0
  34. fromscratch_pyllm-0.5.0/pyproject.toml +146 -0
  35. fromscratch_pyllm-0.5.0/scripts/build_pebble_corpus.py +81 -0
  36. fromscratch_pyllm-0.5.0/scripts/train_pebble.py +60 -0
  37. fromscratch_pyllm-0.5.0/scripts/train_pokemon.py +60 -0
  38. fromscratch_pyllm-0.5.0/src/pyllm/__init__.py +5 -0
  39. fromscratch_pyllm-0.5.0/src/pyllm/_types.py +10 -0
  40. fromscratch_pyllm-0.5.0/src/pyllm/_version.py +24 -0
  41. fromscratch_pyllm-0.5.0/src/pyllm/api.py +72 -0
  42. fromscratch_pyllm-0.5.0/src/pyllm/autograd/__init__.py +5 -0
  43. fromscratch_pyllm-0.5.0/src/pyllm/autograd/gradcheck.py +34 -0
  44. fromscratch_pyllm-0.5.0/src/pyllm/autograd/tensor.py +378 -0
  45. fromscratch_pyllm-0.5.0/src/pyllm/checkpoint.py +125 -0
  46. fromscratch_pyllm-0.5.0/src/pyllm/cli.py +202 -0
  47. fromscratch_pyllm-0.5.0/src/pyllm/data/__init__.py +18 -0
  48. fromscratch_pyllm-0.5.0/src/pyllm/data/pebble.npz +0 -0
  49. fromscratch_pyllm-0.5.0/src/pyllm/data/pebble_corpus.txt +19652 -0
  50. fromscratch_pyllm-0.5.0/src/pyllm/data/pokemon.npz +0 -0
  51. fromscratch_pyllm-0.5.0/src/pyllm/data/pokemon_corpus.txt +149 -0
  52. fromscratch_pyllm-0.5.0/src/pyllm/generate.py +213 -0
  53. fromscratch_pyllm-0.5.0/src/pyllm/models/__init__.py +44 -0
  54. fromscratch_pyllm-0.5.0/src/pyllm/models/base.py +27 -0
  55. fromscratch_pyllm-0.5.0/src/pyllm/models/bigram.py +41 -0
  56. fromscratch_pyllm-0.5.0/src/pyllm/models/gpt.py +73 -0
  57. fromscratch_pyllm-0.5.0/src/pyllm/models/mlp.py +56 -0
  58. fromscratch_pyllm-0.5.0/src/pyllm/nn/__init__.py +34 -0
  59. fromscratch_pyllm-0.5.0/src/pyllm/nn/attention.py +79 -0
  60. fromscratch_pyllm-0.5.0/src/pyllm/nn/dropout.py +30 -0
  61. fromscratch_pyllm-0.5.0/src/pyllm/nn/embedding.py +30 -0
  62. fromscratch_pyllm-0.5.0/src/pyllm/nn/functional.py +119 -0
  63. fromscratch_pyllm-0.5.0/src/pyllm/nn/linear.py +37 -0
  64. fromscratch_pyllm-0.5.0/src/pyllm/nn/module.py +84 -0
  65. fromscratch_pyllm-0.5.0/src/pyllm/nn/normalization.py +31 -0
  66. fromscratch_pyllm-0.5.0/src/pyllm/nn/transformer.py +65 -0
  67. fromscratch_pyllm-0.5.0/src/pyllm/pebble/__init__.py +25 -0
  68. fromscratch_pyllm-0.5.0/src/pyllm/pebble/ast.py +141 -0
  69. fromscratch_pyllm-0.5.0/src/pyllm/pebble/corpus.py +70 -0
  70. fromscratch_pyllm-0.5.0/src/pyllm/pebble/generator.py +145 -0
  71. fromscratch_pyllm-0.5.0/src/pyllm/pebble/harvest.py +35 -0
  72. fromscratch_pyllm-0.5.0/src/pyllm/pebble/referee.py +440 -0
  73. fromscratch_pyllm-0.5.0/src/pyllm/pebble/render.py +109 -0
  74. fromscratch_pyllm-0.5.0/src/pyllm/pebble/score.py +41 -0
  75. fromscratch_pyllm-0.5.0/src/pyllm/py.typed +0 -0
  76. fromscratch_pyllm-0.5.0/src/pyllm/tokenizer/__init__.py +7 -0
  77. fromscratch_pyllm-0.5.0/src/pyllm/tokenizer/base.py +24 -0
  78. fromscratch_pyllm-0.5.0/src/pyllm/tokenizer/bpe.py +117 -0
  79. fromscratch_pyllm-0.5.0/src/pyllm/tokenizer/char.py +33 -0
  80. fromscratch_pyllm-0.5.0/src/pyllm/training/__init__.py +7 -0
  81. fromscratch_pyllm-0.5.0/src/pyllm/training/data.py +44 -0
  82. fromscratch_pyllm-0.5.0/src/pyllm/training/loop.py +51 -0
  83. fromscratch_pyllm-0.5.0/src/pyllm/training/optim.py +76 -0
  84. fromscratch_pyllm-0.5.0/tests/autograd/test_gradcheck.py +99 -0
  85. fromscratch_pyllm-0.5.0/tests/autograd/test_learning.py +43 -0
  86. fromscratch_pyllm-0.5.0/tests/autograd/test_reshape.py +33 -0
  87. fromscratch_pyllm-0.5.0/tests/autograd/test_tensor.py +205 -0
  88. fromscratch_pyllm-0.5.0/tests/autograd/test_transpose.py +28 -0
  89. fromscratch_pyllm-0.5.0/tests/models/test_bigram.py +45 -0
  90. fromscratch_pyllm-0.5.0/tests/models/test_build_model.py +60 -0
  91. fromscratch_pyllm-0.5.0/tests/models/test_gpt.py +81 -0
  92. fromscratch_pyllm-0.5.0/tests/models/test_mlp.py +74 -0
  93. fromscratch_pyllm-0.5.0/tests/nn/test_attention.py +56 -0
  94. fromscratch_pyllm-0.5.0/tests/nn/test_dropout.py +37 -0
  95. fromscratch_pyllm-0.5.0/tests/nn/test_embedding.py +31 -0
  96. fromscratch_pyllm-0.5.0/tests/nn/test_functional.py +127 -0
  97. fromscratch_pyllm-0.5.0/tests/nn/test_linear.py +43 -0
  98. fromscratch_pyllm-0.5.0/tests/nn/test_module.py +92 -0
  99. fromscratch_pyllm-0.5.0/tests/nn/test_normalization.py +36 -0
  100. fromscratch_pyllm-0.5.0/tests/nn/test_public_api.py +25 -0
  101. fromscratch_pyllm-0.5.0/tests/nn/test_transformer.py +68 -0
  102. fromscratch_pyllm-0.5.0/tests/pebble/test_bundled_corpus.py +23 -0
  103. fromscratch_pyllm-0.5.0/tests/pebble/test_generator.py +35 -0
  104. fromscratch_pyllm-0.5.0/tests/pebble/test_harvest.py +56 -0
  105. fromscratch_pyllm-0.5.0/tests/pebble/test_pebble_available.py +20 -0
  106. fromscratch_pyllm-0.5.0/tests/pebble/test_pebble_bundled_checkpoint.py +17 -0
  107. fromscratch_pyllm-0.5.0/tests/pebble/test_pebble_corpus.py +90 -0
  108. fromscratch_pyllm-0.5.0/tests/pebble/test_referee.py +208 -0
  109. fromscratch_pyllm-0.5.0/tests/pebble/test_render.py +67 -0
  110. fromscratch_pyllm-0.5.0/tests/pebble/test_score.py +41 -0
  111. fromscratch_pyllm-0.5.0/tests/pebble/test_uses_public_api.py +17 -0
  112. fromscratch_pyllm-0.5.0/tests/test_api.py +94 -0
  113. fromscratch_pyllm-0.5.0/tests/test_build_pebble_corpus_script.py +55 -0
  114. fromscratch_pyllm-0.5.0/tests/test_bundled_checkpoint.py +17 -0
  115. fromscratch_pyllm-0.5.0/tests/test_checkpoint.py +248 -0
  116. fromscratch_pyllm-0.5.0/tests/test_cli.py +66 -0
  117. fromscratch_pyllm-0.5.0/tests/test_cli_pebble.py +69 -0
  118. fromscratch_pyllm-0.5.0/tests/test_corpus.py +22 -0
  119. fromscratch_pyllm-0.5.0/tests/test_dependency_pins.py +40 -0
  120. fromscratch_pyllm-0.5.0/tests/test_docs.py +87 -0
  121. fromscratch_pyllm-0.5.0/tests/test_generate.py +259 -0
  122. fromscratch_pyllm-0.5.0/tests/test_overfit.py +26 -0
  123. fromscratch_pyllm-0.5.0/tests/test_properties.py +575 -0
  124. fromscratch_pyllm-0.5.0/tests/test_smoke.py +8 -0
  125. fromscratch_pyllm-0.5.0/tests/tokenizer/test_bpe.py +58 -0
  126. fromscratch_pyllm-0.5.0/tests/tokenizer/test_char.py +30 -0
  127. fromscratch_pyllm-0.5.0/tests/training/test_data.py +56 -0
  128. fromscratch_pyllm-0.5.0/tests/training/test_determinism.py +39 -0
  129. fromscratch_pyllm-0.5.0/tests/training/test_loop.py +44 -0
  130. fromscratch_pyllm-0.5.0/tests/training/test_optim.py +67 -0
  131. fromscratch_pyllm-0.5.0/uv.lock +1161 -0
@@ -0,0 +1,14 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ ci:
14
+ uses: robcox2910/robcox2910.github.io/.github/workflows/python-ci.yml@v1
@@ -0,0 +1,16 @@
1
+ name: Deploy Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch: {}
7
+
8
+ jobs:
9
+ docs:
10
+ uses: robcox2910/robcox2910.github.io/.github/workflows/docs.yml@v1
11
+ permissions:
12
+ contents: read
13
+ pages: write
14
+ id-token: write
15
+ with:
16
+ coverage-report: false
@@ -0,0 +1,58 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ build:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v7
17
+ with:
18
+ fetch-depth: 0
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v7
22
+ with:
23
+ version: "latest"
24
+
25
+ - name: Set up Python
26
+ run: uv python install 3.14
27
+
28
+ - name: Build distributions
29
+ run: uv build
30
+
31
+ - name: Upload build artifacts
32
+ uses: actions/upload-artifact@v7
33
+ with:
34
+ name: dist
35
+ path: dist/
36
+
37
+ - name: Upload to GitHub Release
38
+ uses: softprops/action-gh-release@v3
39
+ with:
40
+ files: dist/*
41
+
42
+ publish:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: pypi
47
+ url: https://pypi.org/project/fromscratch-pyllm/
48
+ permissions:
49
+ id-token: write
50
+ steps:
51
+ - name: Download build artifacts
52
+ uses: actions/download-artifact@v8
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+
57
+ - name: Publish to PyPI
58
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
8
+ src/pyllm/_version.py
9
+ .coverage
10
+ .hypothesis/
@@ -0,0 +1,56 @@
1
+ default_language_version:
2
+ python: python3.14
3
+
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks
6
+ rev: v5.0.0
7
+ hooks:
8
+ # Pages paste real pytest output, trailing spaces included.
9
+ - id: trailing-whitespace
10
+ exclude: ^docs/property-testing\.md$
11
+ - id: end-of-file-fixer
12
+ exclude: ^docs/property-testing\.md$
13
+ - id: check-yaml
14
+ args: ['--unsafe']
15
+ - id: check-added-large-files
16
+ - id: check-json
17
+ - id: check-toml
18
+ - id: check-merge-conflict
19
+ - id: debug-statements
20
+
21
+ - repo: https://github.com/astral-sh/ruff-pre-commit
22
+ rev: v0.15.4
23
+ hooks:
24
+ - id: ruff
25
+ args: [--fix, --exit-non-zero-on-fix]
26
+ - id: ruff-format
27
+
28
+ - repo: https://github.com/RobertCraigie/pyright-python
29
+ rev: v1.1.408
30
+ hooks:
31
+ - id: pyright
32
+
33
+ - repo: local
34
+ hooks:
35
+ - id: pip-audit
36
+ name: pip-audit
37
+ language: system
38
+ pass_filenames: false
39
+ files: ^(pyproject\.toml|uv\.lock)$
40
+ entry: >-
41
+ bash -c 'f=$(mktemp) &&
42
+ uv export --frozen --no-emit-project --all-extras --no-hashes -o "$f" >/dev/null &&
43
+ uvx pip-audit==2.10.0 -r "$f" --disable-pip --no-deps;
44
+ status=$?; rm -f "$f"; exit $status'
45
+
46
+ - repo: https://github.com/rhysd/actionlint
47
+ rev: v1.7.7
48
+ hooks:
49
+ - id: actionlint
50
+
51
+ - repo: https://github.com/commitizen-tools/commitizen
52
+ rev: v4.13.8
53
+ hooks:
54
+ - id: commitizen
55
+ stages: [commit-msg]
56
+ additional_dependencies: [typing_extensions]
@@ -0,0 +1,72 @@
1
+ # PyLLM
2
+
3
+ <!-- standard:start -->
4
+ ## Code Style
5
+
6
+ - All public functions, methods, classes, and modules **must** have a docstring
7
+ - Use imperative mood: "Return the result." not "Returns the result."
8
+ - One-line docstrings for simple functions; multi-line with summary + details for complex ones
9
+ - Follow the Google docstring convention (D211, D212 selected via ruff)
10
+
11
+ ## Linting
12
+
13
+ - Ruff enforces a comprehensive rule set -- run `uv run ruff check .` before committing
14
+ - Pyright runs in strict mode -- run `uv run pyright src tests` before committing
15
+ - All violations must be fixed, not ignored. Exceptions are the repo-owned `ignore` and `per-file-ignores` lists in `pyproject.toml`, which belong to this repo; the series sync never edits them
16
+
17
+ ## Testing
18
+
19
+ - TDD workflow: Red-Green-Refactor
20
+ - Coverage is enforced by pytest-cov; the floor in `pyproject.toml` is repo-owned
21
+ - Test classes and methods also require docstrings
22
+ - Use named constants instead of magic values (PLR2004)
23
+ - Run `uv run pytest` before committing
24
+ - Property-based tests (Hypothesis) live in `tests/test_properties.py` and `tests/test_model.py`; the shared profiles are in the root `conftest.py` (owned by `series sync`). Run `uv run pytest --hypothesis-profile=thorough` before a release.
25
+
26
+ ## Commits
27
+
28
+ - Use conventional commits: `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `ci:`, `chore:`
29
+ - All changes go via PR from a feature branch (main is protected); merge when CI is green
30
+
31
+ ## Dependencies
32
+
33
+ - Package manager: uv
34
+ - Run `uv sync --all-extras` after modifying dependencies
35
+ - `uv.lock` is committed; CI and pre-commit audit it with pip-audit through `uvx`, so pip-audit is not a project dependency
36
+ - Sibling projects, where used, are pinned release tags in `[tool.uv.sources]` (git + tag); never use `path` sources
37
+
38
+ ## Releasing
39
+
40
+ - A release is a `vX.Y.Z` tag on main after the PR is merged
41
+ - Docs-only and tooling-only changes do not get a release tag
42
+ - Docs are built with `mkdocs build --strict` and deployed to GitHub Pages by the docs workflow
43
+ <!-- standard:end -->
44
+
45
+ ## Project notes
46
+
47
+ - **Numpy autograd.** There is no torch: `src/pyllm/autograd` is a small reverse-mode
48
+ autodiff engine on numpy arrays (`Tensor`). Layers live in `src/pyllm/nn`, models in
49
+ `src/pyllm/models`, and `pyllm._types` holds the shared `Array` alias. Each op stores a
50
+ backward recipe via `Tensor.set_backward`; check new ops with
51
+ `pyllm.autograd.gradcheck.numerical_grad`.
52
+ - **Bundled checkpoints.** `src/pyllm/data/pokemon.npz` and `src/pyllm/data/pebble.npz` are
53
+ trained models committed to the repo (and force-included in the wheel). Regenerate them with
54
+ `uv run python scripts/train_pokemon.py` and `uv run python scripts/train_pebble.py`. Both
55
+ use fixed seeds, but training changes whenever the model code changes, so rerun them and
56
+ commit the new `.npz` files if you touch the models, optimizers or training loop.
57
+ - **The Pebble referee.** `src/pyllm/pebble/referee.py` is a hand-written, character-by-character
58
+ checker for the canonical slice of Pebble the corpus uses (4-space indents, single spaces
59
+ around operators). `viable(text)` means "could still become valid" and `complete(text)` means
60
+ "valid right now". It must stay a strict subset of real Pebble so anything it accepts, Pebble's
61
+ own parser accepts too (tests check this against `pebble-lang`). `generate(..., referee=...)`
62
+ uses it to veto illegal characters.
63
+ - **Regenerating the Pebble corpus.** `src/pyllm/data/pebble_corpus.txt` is generated, not
64
+ hand-written: `uv run python scripts/build_pebble_corpus.py` grows random valid programs
65
+ (`pebble/generator.py`), mixes in snippets harvested from a sibling `../pebble-lang/docs`
66
+ checkout (kept only if they parse) and canonicalises everything with Pebble's formatter. Use
67
+ `--pebble-docs PATH` to harvest from elsewhere; if the docs folder is missing the script stops
68
+ with an error (it never silently drops the snippets) unless you pass `--no-harvest`. The
69
+ bundled corpus is built *with* the snippets, so only use `--no-harvest` for experiments. A
70
+ similar recipe is available as `pyllm gen-corpus --harvest PATH --canonical` (which also fails
71
+ on a missing `--harvest` folder). It needs the optional `pebble` extra
72
+ (`uv sync --all-extras`). Retrain `pebble.npz` afterwards.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rob Cox
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.5
2
+ Name: fromscratch-pyllm
3
+ Version: 0.5.0
4
+ Summary: An educational large language model built from scratch in Python
5
+ Project-URL: Homepage, https://github.com/robcox2910/pyllm
6
+ Project-URL: Repository, https://github.com/robcox2910/pyllm
7
+ Project-URL: Issues, https://github.com/robcox2910/pyllm/issues
8
+ Project-URL: Documentation, https://robcox2910.github.io/pyllm/
9
+ Author: Rob Cox
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: autograd,education,gpt,learning,llm,transformer
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Education
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.14
22
+ Requires-Dist: numpy>=2.1
23
+ Provides-Extra: dev
24
+ Requires-Dist: commitizen>=4.4.0; extra == 'dev'
25
+ Requires-Dist: hypothesis>=6.170; extra == 'dev'
26
+ Requires-Dist: pip-audit>=2.10.0; extra == 'dev'
27
+ Requires-Dist: pre-commit>=4.0.1; extra == 'dev'
28
+ Requires-Dist: pyright>=1.1.400; extra == 'dev'
29
+ Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
30
+ Requires-Dist: pytest>=8.3.4; extra == 'dev'
31
+ Requires-Dist: ruff>=0.9.3; extra == 'dev'
32
+ Provides-Extra: docs
33
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
34
+ Provides-Extra: pebble
35
+ Requires-Dist: fromscratch-pebble>=0.3.0; extra == 'pebble'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # PyLLM
39
+
40
+ An educational large language model built from scratch in Python.
41
+
42
+ Part of the "from scratch in Python" series. Built incrementally with TDD,
43
+ every concept explained with analogies a 12-year-old can follow. The only
44
+ dependency is numpy (for fast array math) — the autograd engine, neural-net
45
+ layers, attention, the Transformer, the tokenizer, the optimizer, the training
46
+ loop, and sampling are all ours.
47
+
48
+ ## Quick Start
49
+
50
+ ```bash
51
+ uv sync --all-extras
52
+ uv run pytest
53
+ ```
54
+
55
+ ## Dream up new Pokémon (generates instantly)
56
+
57
+ PyLLM ships with a tiny pre-trained GPT, so it invents Pokémon-ish names the
58
+ moment you launch it — no training wait:
59
+
60
+ ```console
61
+ $ uv run pyllm --seed 0 --max-new-tokens 40
62
+ machop
63
+ machoke
64
+ machamp
65
+ bellsprout
66
+ weepin
67
+ ```
68
+
69
+ Turn the boldness dial with `--temperature` (0 = safe/greedy, higher = wilder)
70
+ and add a `--top-k` guardrail. Start from a prompt with `--prompt "pi"`.
71
+
72
+ ## Train your own model
73
+
74
+ Pick a rung on the teaching ladder (`bigram`, `mlp`, or `gpt`) and point it at
75
+ any text file (defaults to the bundled Pokémon corpus):
76
+
77
+ ```console
78
+ $ uv run pyllm train --model gpt --steps 2000 --out mine.npz
79
+ step 0: loss 3.31
80
+ ...
81
+ final loss 0.42; saved checkpoint to mine.npz
82
+
83
+ $ uv run pyllm --checkpoint mine.npz --max-new-tokens 60
84
+ ```
85
+
86
+ ## The flagship: teach PyLLM to write Pebble
87
+
88
+ There's no corpus of [Pebble](https://github.com/robcox2910/pebble-lang) code in
89
+ the wild — so we *grow our own*. A generator emits **random-but-provably-valid**
90
+ Pebble ASTs (100% parse under Pebble's own parser), a harvester scrapes real
91
+ snippets from Pebble's docs, and — because we own the language toolchain — we
92
+ **grade** the trained model by running its output back through Pebble's real
93
+ parser and reporting what percentage parses:
94
+
95
+ ```console
96
+ $ uv run pyllm pebble --seed 7 --temperature 0.5 --score
97
+ let result = 62
98
+ }
99
+ if count < 47 {
100
+ let c = 51 + (83 + 32)
101
+ ...
102
+ parse rate: 0.0% of 1 programs
103
+ ```
104
+
105
+ The bundled checkpoint is *tiny* (a few-minute char-level GPT), so it writes
106
+ Pebble-*flavoured* code and only some of it parses — and that's the whole point:
107
+ the parser is an honest, objective judge, so you can watch the score go up as you
108
+ train longer or bigger. Almost no LLM project can grade its own output like this.
109
+
110
+ ### Call in the referee: always-valid Pebble
111
+
112
+ Add `--constrained` and a from-scratch grammar **referee** vetoes every character
113
+ that would break Pebble's rules, so the model rolls again until it picks a legal
114
+ one. Same tiny brain — but now every program it returns parses:
115
+
116
+ ```console
117
+ $ uv run pyllm pebble --seed 7 --constrained --score
118
+ let total = n * (63 - 99)
119
+
120
+ let result = c
121
+ fount = 55 + (n - b) % (6 - b)
122
+
123
+ parse rate: 100.0% of 2 programs
124
+ ```
125
+
126
+ From Python: `generate_pebble(constrained=True)`. See
127
+ [`docs/concepts/the-referee.md`](docs/concepts/the-referee.md).
128
+
129
+ Grow a fresh synthetic corpus with `uv run pyllm gen-corpus`. Live scoring needs
130
+ the optional `fromscratch-pebble` dependency (`uv sync --all-extras`); generation and
131
+ training work without it. See [`docs/concepts/grow-your-own-data.md`](docs/concepts/grow-your-own-data.md).
132
+
133
+ ## See how text becomes tokens
134
+
135
+ ```console
136
+ $ uv run pyllm tokenize --text "pika"
137
+ 'p' -> 16
138
+ 'i' -> 9
139
+ 'k' -> 11
140
+ 'a' -> 1
141
+ ```
142
+
143
+ ## The teaching ladder
144
+
145
+ Each rung is a real, trainable language model that fixes a weakness of the one
146
+ below it, so you feel *why* each idea exists:
147
+
148
+ 1. **Bigram** — "what character usually comes next?" A lookup table.
149
+ 2. **MLP** — a small neural net over the last few characters (embeddings + a
150
+ hidden layer).
151
+ 3. **GPT** — stacked Transformer blocks (multi-head attention + feed-forward +
152
+ LayerNorm + residuals) with positional information. The real thing.
153
+
154
+ ## Learn the ideas
155
+
156
+ Kid-friendly explanations live in [`docs/concepts/`](docs/concepts/README.md):
157
+ the breadcrumb trail (autograd), tokens, embeddings, attention, how a Transformer
158
+ is built, how a model learns, sampling, and — the flagship lesson — growing your
159
+ own data.
160
+
161
+ ## Developing alongside a sibling
162
+
163
+ PyLLM uses a released version of [Pebble](https://github.com/robcox2910/pebble-lang)
164
+ from PyPI (`fromscratch-pebble`, the optional `pebble` extra). To try an
165
+ unreleased Pebble change, install your local checkout over the released one:
166
+
167
+ ```bash
168
+ uv pip install -e ../pebble-lang
169
+ uv run --no-sync pytest
170
+ ```
171
+
172
+ Use `uv run --no-sync` while you do this: a plain `uv run` (like `uv sync`)
173
+ puts the pinned release back, which is how you undo the override.
174
+
175
+ ## Releasing
176
+
177
+ 1. Merge the change to `main`.
178
+ 2. Tag it: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`.
179
+ A change to the public API bumps the minor version (`v0.1.0` → `v0.2.0`);
180
+ anything else bumps the patch (`v0.1.0` → `v0.1.1`).
181
+ 3. Projects that use PyLLM (like [PyStack](https://github.com/robcox2910/pystack))
182
+ upgrade by editing the tag in their `[tool.uv.sources]` and running
183
+ `uv lock --upgrade-package pyllm`, in a pull request so CI checks the pair.
184
+
185
+ ## PyLLM inside PyStack
186
+
187
+ PyLLM is **the brain** of a family of educational projects, each building a real
188
+ system from scratch to teach how it works. They're tied together by
189
+ **[PyStack](https://github.com/robcox2910/pystack)**, where a Pebble program can
190
+ even `import "llm"` and ask PyLLM to write more code.
191
+
192
+ Once PyLLM is installed as a PyStack plugin, any Pebble program can call it:
193
+
194
+ ```pebble
195
+ import "llm"
196
+ let code = llm_generate("a function that adds two numbers")
197
+ print(code)
198
+ ```
199
+
200
+ <!-- series:start -->
201
+ ## Related Projects
202
+
203
+ This project is one of a series that builds real systems "from scratch in Python" to teach how they work. See the [series home](https://robcox2910.github.io/) for the full story.
204
+
205
+ | Project | What it teaches | Repository | Docs |
206
+ | --- | --- | --- | --- |
207
+ | PyOS | Operating systems | [py-os](https://github.com/robcox2910/py-os) | [Docs](https://robcox2910.github.io/py-os/) |
208
+ | Pebble | Compilers and programming languages | [pebble-lang](https://github.com/robcox2910/pebble-lang) | [Docs](https://robcox2910.github.io/pebble-lang/) |
209
+ | PyDB | Relational databases | [pydb](https://github.com/robcox2910/pydb) | [Docs](https://robcox2910.github.io/pydb/) |
210
+ | PyKV | Key-value stores | [pykv](https://github.com/robcox2910/pykv) | [Docs](https://robcox2910.github.io/pykv/) |
211
+ | PyDocDB | Document databases | [pydoc-db](https://github.com/robcox2910/pydoc-db) | [Docs](https://robcox2910.github.io/pydoc-db/) |
212
+ | PyGraphDB | Graph databases | [pygraphdb](https://github.com/robcox2910/pygraphdb) | [Docs](https://robcox2910.github.io/pygraphdb/) |
213
+ | PyTSDB | Time-series databases | [pytsdb](https://github.com/robcox2910/pytsdb) | [Docs](https://robcox2910.github.io/pytsdb/) |
214
+ | PyVecDB | Vector databases | [pyvecdb](https://github.com/robcox2910/pyvecdb) | [Docs](https://robcox2910.github.io/pyvecdb/) |
215
+ | PyColDB | Column-family databases | [pycoldb](https://github.com/robcox2910/pycoldb) | [Docs](https://robcox2910.github.io/pycoldb/) |
216
+ | PyCrypt | Cryptography | [pycrypt](https://github.com/robcox2910/pycrypt) | [Docs](https://robcox2910.github.io/pycrypt/) |
217
+ | PyNet | Computer networking | [pynet](https://github.com/robcox2910/pynet) | [Docs](https://robcox2910.github.io/pynet/) |
218
+ | PyWeb | HTTP web servers | [pyweb](https://github.com/robcox2910/pyweb) | [Docs](https://robcox2910.github.io/pyweb/) |
219
+ | PyGit | Version control | [pygit](https://github.com/robcox2910/pygit) | [Docs](https://robcox2910.github.io/pygit/) |
220
+ | PySearch | Search engines | [pysearch](https://github.com/robcox2910/pysearch) | [Docs](https://robcox2910.github.io/pysearch/) |
221
+ | PyMQ | Message queues | [pymq](https://github.com/robcox2910/pymq) | [Docs](https://robcox2910.github.io/pymq/) |
222
+ | **PyLLM** | **Language models (the brain)** | [pyllm](https://github.com/robcox2910/pyllm) | [Docs](https://robcox2910.github.io/pyllm/) |
223
+ | PyScheduler | Job scheduling | [pyscheduler](https://github.com/robcox2910/pyscheduler) | [Docs](https://robcox2910.github.io/pyscheduler/) |
224
+ | PyStack | Full-stack integration | [pystack](https://github.com/robcox2910/pystack) | [Docs](https://robcox2910.github.io/pystack/) |
225
+ <!-- series:end -->
226
+
227
+ <!-- docs:start -->
228
+ ## Documentation
229
+
230
+ Full docs (with kid-friendly explanations of every idea): https://robcox2910.github.io/pyllm/
231
+ <!-- docs:end -->
@@ -0,0 +1,194 @@
1
+ # PyLLM
2
+
3
+ An educational large language model built from scratch in Python.
4
+
5
+ Part of the "from scratch in Python" series. Built incrementally with TDD,
6
+ every concept explained with analogies a 12-year-old can follow. The only
7
+ dependency is numpy (for fast array math) — the autograd engine, neural-net
8
+ layers, attention, the Transformer, the tokenizer, the optimizer, the training
9
+ loop, and sampling are all ours.
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ uv sync --all-extras
15
+ uv run pytest
16
+ ```
17
+
18
+ ## Dream up new Pokémon (generates instantly)
19
+
20
+ PyLLM ships with a tiny pre-trained GPT, so it invents Pokémon-ish names the
21
+ moment you launch it — no training wait:
22
+
23
+ ```console
24
+ $ uv run pyllm --seed 0 --max-new-tokens 40
25
+ machop
26
+ machoke
27
+ machamp
28
+ bellsprout
29
+ weepin
30
+ ```
31
+
32
+ Turn the boldness dial with `--temperature` (0 = safe/greedy, higher = wilder)
33
+ and add a `--top-k` guardrail. Start from a prompt with `--prompt "pi"`.
34
+
35
+ ## Train your own model
36
+
37
+ Pick a rung on the teaching ladder (`bigram`, `mlp`, or `gpt`) and point it at
38
+ any text file (defaults to the bundled Pokémon corpus):
39
+
40
+ ```console
41
+ $ uv run pyllm train --model gpt --steps 2000 --out mine.npz
42
+ step 0: loss 3.31
43
+ ...
44
+ final loss 0.42; saved checkpoint to mine.npz
45
+
46
+ $ uv run pyllm --checkpoint mine.npz --max-new-tokens 60
47
+ ```
48
+
49
+ ## The flagship: teach PyLLM to write Pebble
50
+
51
+ There's no corpus of [Pebble](https://github.com/robcox2910/pebble-lang) code in
52
+ the wild — so we *grow our own*. A generator emits **random-but-provably-valid**
53
+ Pebble ASTs (100% parse under Pebble's own parser), a harvester scrapes real
54
+ snippets from Pebble's docs, and — because we own the language toolchain — we
55
+ **grade** the trained model by running its output back through Pebble's real
56
+ parser and reporting what percentage parses:
57
+
58
+ ```console
59
+ $ uv run pyllm pebble --seed 7 --temperature 0.5 --score
60
+ let result = 62
61
+ }
62
+ if count < 47 {
63
+ let c = 51 + (83 + 32)
64
+ ...
65
+ parse rate: 0.0% of 1 programs
66
+ ```
67
+
68
+ The bundled checkpoint is *tiny* (a few-minute char-level GPT), so it writes
69
+ Pebble-*flavoured* code and only some of it parses — and that's the whole point:
70
+ the parser is an honest, objective judge, so you can watch the score go up as you
71
+ train longer or bigger. Almost no LLM project can grade its own output like this.
72
+
73
+ ### Call in the referee: always-valid Pebble
74
+
75
+ Add `--constrained` and a from-scratch grammar **referee** vetoes every character
76
+ that would break Pebble's rules, so the model rolls again until it picks a legal
77
+ one. Same tiny brain — but now every program it returns parses:
78
+
79
+ ```console
80
+ $ uv run pyllm pebble --seed 7 --constrained --score
81
+ let total = n * (63 - 99)
82
+
83
+ let result = c
84
+ fount = 55 + (n - b) % (6 - b)
85
+
86
+ parse rate: 100.0% of 2 programs
87
+ ```
88
+
89
+ From Python: `generate_pebble(constrained=True)`. See
90
+ [`docs/concepts/the-referee.md`](docs/concepts/the-referee.md).
91
+
92
+ Grow a fresh synthetic corpus with `uv run pyllm gen-corpus`. Live scoring needs
93
+ the optional `fromscratch-pebble` dependency (`uv sync --all-extras`); generation and
94
+ training work without it. See [`docs/concepts/grow-your-own-data.md`](docs/concepts/grow-your-own-data.md).
95
+
96
+ ## See how text becomes tokens
97
+
98
+ ```console
99
+ $ uv run pyllm tokenize --text "pika"
100
+ 'p' -> 16
101
+ 'i' -> 9
102
+ 'k' -> 11
103
+ 'a' -> 1
104
+ ```
105
+
106
+ ## The teaching ladder
107
+
108
+ Each rung is a real, trainable language model that fixes a weakness of the one
109
+ below it, so you feel *why* each idea exists:
110
+
111
+ 1. **Bigram** — "what character usually comes next?" A lookup table.
112
+ 2. **MLP** — a small neural net over the last few characters (embeddings + a
113
+ hidden layer).
114
+ 3. **GPT** — stacked Transformer blocks (multi-head attention + feed-forward +
115
+ LayerNorm + residuals) with positional information. The real thing.
116
+
117
+ ## Learn the ideas
118
+
119
+ Kid-friendly explanations live in [`docs/concepts/`](docs/concepts/README.md):
120
+ the breadcrumb trail (autograd), tokens, embeddings, attention, how a Transformer
121
+ is built, how a model learns, sampling, and — the flagship lesson — growing your
122
+ own data.
123
+
124
+ ## Developing alongside a sibling
125
+
126
+ PyLLM uses a released version of [Pebble](https://github.com/robcox2910/pebble-lang)
127
+ from PyPI (`fromscratch-pebble`, the optional `pebble` extra). To try an
128
+ unreleased Pebble change, install your local checkout over the released one:
129
+
130
+ ```bash
131
+ uv pip install -e ../pebble-lang
132
+ uv run --no-sync pytest
133
+ ```
134
+
135
+ Use `uv run --no-sync` while you do this: a plain `uv run` (like `uv sync`)
136
+ puts the pinned release back, which is how you undo the override.
137
+
138
+ ## Releasing
139
+
140
+ 1. Merge the change to `main`.
141
+ 2. Tag it: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`.
142
+ A change to the public API bumps the minor version (`v0.1.0` → `v0.2.0`);
143
+ anything else bumps the patch (`v0.1.0` → `v0.1.1`).
144
+ 3. Projects that use PyLLM (like [PyStack](https://github.com/robcox2910/pystack))
145
+ upgrade by editing the tag in their `[tool.uv.sources]` and running
146
+ `uv lock --upgrade-package pyllm`, in a pull request so CI checks the pair.
147
+
148
+ ## PyLLM inside PyStack
149
+
150
+ PyLLM is **the brain** of a family of educational projects, each building a real
151
+ system from scratch to teach how it works. They're tied together by
152
+ **[PyStack](https://github.com/robcox2910/pystack)**, where a Pebble program can
153
+ even `import "llm"` and ask PyLLM to write more code.
154
+
155
+ Once PyLLM is installed as a PyStack plugin, any Pebble program can call it:
156
+
157
+ ```pebble
158
+ import "llm"
159
+ let code = llm_generate("a function that adds two numbers")
160
+ print(code)
161
+ ```
162
+
163
+ <!-- series:start -->
164
+ ## Related Projects
165
+
166
+ This project is one of a series that builds real systems "from scratch in Python" to teach how they work. See the [series home](https://robcox2910.github.io/) for the full story.
167
+
168
+ | Project | What it teaches | Repository | Docs |
169
+ | --- | --- | --- | --- |
170
+ | PyOS | Operating systems | [py-os](https://github.com/robcox2910/py-os) | [Docs](https://robcox2910.github.io/py-os/) |
171
+ | Pebble | Compilers and programming languages | [pebble-lang](https://github.com/robcox2910/pebble-lang) | [Docs](https://robcox2910.github.io/pebble-lang/) |
172
+ | PyDB | Relational databases | [pydb](https://github.com/robcox2910/pydb) | [Docs](https://robcox2910.github.io/pydb/) |
173
+ | PyKV | Key-value stores | [pykv](https://github.com/robcox2910/pykv) | [Docs](https://robcox2910.github.io/pykv/) |
174
+ | PyDocDB | Document databases | [pydoc-db](https://github.com/robcox2910/pydoc-db) | [Docs](https://robcox2910.github.io/pydoc-db/) |
175
+ | PyGraphDB | Graph databases | [pygraphdb](https://github.com/robcox2910/pygraphdb) | [Docs](https://robcox2910.github.io/pygraphdb/) |
176
+ | PyTSDB | Time-series databases | [pytsdb](https://github.com/robcox2910/pytsdb) | [Docs](https://robcox2910.github.io/pytsdb/) |
177
+ | PyVecDB | Vector databases | [pyvecdb](https://github.com/robcox2910/pyvecdb) | [Docs](https://robcox2910.github.io/pyvecdb/) |
178
+ | PyColDB | Column-family databases | [pycoldb](https://github.com/robcox2910/pycoldb) | [Docs](https://robcox2910.github.io/pycoldb/) |
179
+ | PyCrypt | Cryptography | [pycrypt](https://github.com/robcox2910/pycrypt) | [Docs](https://robcox2910.github.io/pycrypt/) |
180
+ | PyNet | Computer networking | [pynet](https://github.com/robcox2910/pynet) | [Docs](https://robcox2910.github.io/pynet/) |
181
+ | PyWeb | HTTP web servers | [pyweb](https://github.com/robcox2910/pyweb) | [Docs](https://robcox2910.github.io/pyweb/) |
182
+ | PyGit | Version control | [pygit](https://github.com/robcox2910/pygit) | [Docs](https://robcox2910.github.io/pygit/) |
183
+ | PySearch | Search engines | [pysearch](https://github.com/robcox2910/pysearch) | [Docs](https://robcox2910.github.io/pysearch/) |
184
+ | PyMQ | Message queues | [pymq](https://github.com/robcox2910/pymq) | [Docs](https://robcox2910.github.io/pymq/) |
185
+ | **PyLLM** | **Language models (the brain)** | [pyllm](https://github.com/robcox2910/pyllm) | [Docs](https://robcox2910.github.io/pyllm/) |
186
+ | PyScheduler | Job scheduling | [pyscheduler](https://github.com/robcox2910/pyscheduler) | [Docs](https://robcox2910.github.io/pyscheduler/) |
187
+ | PyStack | Full-stack integration | [pystack](https://github.com/robcox2910/pystack) | [Docs](https://robcox2910.github.io/pystack/) |
188
+ <!-- series:end -->
189
+
190
+ <!-- docs:start -->
191
+ ## Documentation
192
+
193
+ Full docs (with kid-friendly explanations of every idea): https://robcox2910.github.io/pyllm/
194
+ <!-- docs:end -->