Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
125 commits
Select commit Hold shift + click to select a range
4959427
attributions: move device mapping
AntoninPoche Mar 9, 2026
a20812a
attributions perturbations: remove device
AntoninPoche Mar 9, 2026
7a8ee55
attribution input embeddings: limit to a single embedder in perturbat…
AntoninPoche Mar 9, 2026
21d19bc
attributions: better specification of device
AntoninPoche Mar 9, 2026
3b15f3a
fixup! attributions: better specification of device
AntoninPoche Mar 9, 2026
1ab881f
test attributions: add test with large number of samples to stress me…
AntoninPoche Mar 9, 2026
3f7f541
Merge branch 'main' of github.com:FOR-sight-ai/interpreto into attr-i…
AntoninPoche Mar 26, 2026
021741a
attributions: make sanity checks pass after merge
AntoninPoche Mar 26, 2026
bdf1e3b
agents instructions
AntoninPoche Mar 26, 2026
ce9e794
inference wrapper: complete refacto
AntoninPoche Apr 3, 2026
2a4973f
classification inference wrapper: adapt to refacto
AntoninPoche Apr 3, 2026
36d4915
generation inference wrapper: adapt to refacto
AntoninPoche Apr 3, 2026
c5d03b5
base attributions: adapt to inference wrapper refacto
AntoninPoche Apr 6, 2026
54735d1
base: cleaner typing
AntoninPoche Apr 14, 2026
6f49c5f
insertion deletion: cleaner typing
AntoninPoche Apr 14, 2026
aedebab
aggregations: automatic mask to device
AntoninPoche Apr 14, 2026
089a948
gradient perturbations: simplify perturbation
AntoninPoche Apr 14, 2026
2dbf0ba
test attributions: adapt to refacto
AntoninPoche Apr 14, 2026
e89a974
test inference wrapper: add two unit tests
AntoninPoche Apr 14, 2026
6f67aef
test cls inference wrapper: add detailed test for the three behaviors
AntoninPoche Apr 14, 2026
39f8056
test gen inference wrapper: add detailed test for the two behaviors
AntoninPoche Apr 14, 2026
e18f5f4
atributions: robustify pad token setup
AntoninPoche Apr 15, 2026
8436c2d
nnsight wrappers: introduce a wrapper specific for sequence classific…
AntoninPoche Apr 25, 2026
f86b8ad
concepts: add the inputs_to_concepts
AntoninPoche Apr 29, 2026
cdb849f
attributions: add an inputs to concepts inference wrapper
AntoninPoche Apr 25, 2026
c1bdfec
attributions: add inputs to concepts to typing
AntoninPoche Apr 30, 2026
e984c92
imports: solve circular import issue
AntoninPoche Apr 30, 2026
6fb07cf
test split sequence classifier: add tests
AntoninPoche Apr 30, 2026
35b5d8c
test inputs to concepts attributions: add tests
AntoninPoche Apr 30, 2026
dff064c
concepts docs: rename methods to concept_spaces
AntoninPoche May 19, 2026
14ad284
concepts docs: add split sequence classification
AntoninPoche May 19, 2026
93e3607
concepts docs: split interpretations and add attributions
AntoninPoche May 19, 2026
3dca072
concepts interpretations: default granularity to cls token for split …
AntoninPoche May 20, 2026
6baf706
notebooks: update with new API znd concepts attributions
AntoninPoche May 20, 2026
9055e78
inference wrapper: clean offset_mapping
AntoninPoche May 21, 2026
a8b404b
inference wrappers: simplify padding_side attribute
AntoninPoche May 21, 2026
201d959
minor rewriting
AntoninPoche May 21, 2026
dc709d6
Merge branch 'main' of github.com:FOR-sight-ai/interpreto into attr-i…
AntoninPoche May 21, 2026
984e664
Merge branch 'attr-inference-refacto' of github.com:FOR-sight-ai/inte…
AntoninPoche May 21, 2026
1ede9c7
pyproject: update nnsight version
AntoninPoche May 21, 2026
631cb9e
concepts: clean unused directory
AntoninPoche May 25, 2026
358b91d
concepts: cleaner typing for the concept model
AntoninPoche May 25, 2026
863f814
0.4.20 -> 0.5.0dev0
AntoninPoche May 25, 2026
1549cfa
Merge pull request #142 from FOR-sight-ai/attr-inference-refacto
AntoninPoche May 25, 2026
41fe370
probes: define interface via base classes
AntoninPoche May 25, 2026
eda2a5c
probes: introduce normalization and bias calibrators
AntoninPoche May 25, 2026
47330f9
probes: add linear probes
AntoninPoche May 25, 2026
16e95d1
probes: add centroid-based probes
AntoninPoche May 25, 2026
8f675c4
test probes: test probe models
AntoninPoche May 25, 2026
2130711
test probes: test probe explainers
AntoninPoche May 25, 2026
4396d8f
probes: allow probes from sklearn models
AntoninPoche May 25, 2026
50a8302
test probes: test sklearn probes
AntoninPoche May 25, 2026
4be599c
probes: fill init to import them easily
AntoninPoche May 25, 2026
2ea83ca
probe docs: add documentation for probes
AntoninPoche May 25, 2026
14aa26a
doc: fix colors in python code snippet
AntoninPoche May 25, 2026
ff09c68
linting
AntoninPoche May 25, 2026
72db140
probe notebooks: make one for classification and one for generation
AntoninPoche May 25, 2026
2fbcf3a
model with split points: limit to a single split points
AntoninPoche May 26, 2026
55e4c2f
test mwsp: adapt to single split point and anticipate plural removal.
AntoninPoche May 26, 2026
48af966
concepts base: adapt to single split point
AntoninPoche May 26, 2026
1b58564
concepts: adapt to single split point
AntoninPoche May 26, 2026
140400d
test concepts: adapt to single split point
AntoninPoche May 26, 2026
0b66214
concepts notebook: adapt to single split point
AntoninPoche May 26, 2026
0f4eb59
concepts doc: adapt to single split point
AntoninPoche May 26, 2026
0f75eaa
mwsp: move get activations output to torch tensors
AntoninPoche May 27, 2026
307c95b
mwsp tests: adapt to non-dict activations
AntoninPoche May 27, 2026
2f38da8
base concepts: adapt to non-dict activations
AntoninPoche May 27, 2026
7ca6033
concepts sklearn wrappers: shortcut unecessary inheritance level
AntoninPoche May 27, 2026
6233751
concepts: adapt to non-dict activations and checks removal
AntoninPoche May 27, 2026
1f9e189
concepts tests: adapt to non-dict activations
AntoninPoche May 27, 2026
e5cbb9f
concepts doc: adapt to non-dict activations
AntoninPoche May 27, 2026
fa7059e
concepts notebooks: adapt to non-dict activations
AntoninPoche May 27, 2026
60e6862
splitters: abstract splitting in BaseSplitter
AntoninPoche Jun 1, 2026
5a66815
splitters: rename the SplitSequenceClassification into SplitterForCla…
AntoninPoche Jun 1, 2026
7d63470
splitters: add a splitter for generation
AntoninPoche Jun 1, 2026
2e2a654
test splitters: test the new generation splitter
AntoninPoche Jun 1, 2026
d5d3416
doc splitters: document the new splitter
AntoninPoche Jun 1, 2026
9b8b46c
concepts: rename model_with_split_points attribute to splitter
AntoninPoche Jun 2, 2026
45e71f4
docs: add probes to readme and index
AntoninPoche Jun 3, 2026
b6b913e
probes: add references in docstrings
AntoninPoche Jun 4, 2026
4c816c9
doc: update readme and index
AntoninPoche Jun 4, 2026
4c9dda8
Merge branch 'concepts-attributions' of github.com:FOR-sight-ai/inter…
AntoninPoche Jun 4, 2026
e495a65
Merge branch 'probes' of github.com:FOR-sight-ai/interpreto into conc…
AntoninPoche Jun 4, 2026
52a2ba7
model wrapping: split between attributions and concepts
AntoninPoche Jun 4, 2026
6ad40c0
adapt paths of old model_wrapping
AntoninPoche Jun 4, 2026
c61bbf4
splitters: add inputs_to_activations method for harmonization
AntoninPoche Jun 4, 2026
9d1ed0c
concepts: rename inputs_to_concepts property to get_inputs_to_concept…
AntoninPoche Jun 4, 2026
c9f0e4e
concepts: rename encode_activations and decode_concepts to activation…
AntoninPoche Jun 4, 2026
b4b360d
doc: fix doc by removing unecessary elements
AntoninPoche Jun 5, 2026
f96cec5
splitters doc: group splitters doc in a folder
AntoninPoche Jun 5, 2026
d6cf4a8
generation splitters: support model in bfloat16
AntoninPoche Jun 8, 2026
16f76e0
linting: reorder imports after model wrapping moving
AntoninPoche Jun 8, 2026
82647da
generation splitter: get latent shape use trace instead of scan
AntoninPoche Jun 8, 2026
eac6efa
splitters: small typing and simplification
AntoninPoche Jun 8, 2026
113d2d5
concept explainers: add a device property
AntoninPoche Jun 8, 2026
efc1d43
concepts: remove device management from interpretations and put forwa…
AntoninPoche Jun 8, 2026
486d622
concept notebooks: update with new API and rerun to ensure validity
AntoninPoche Jun 8, 2026
1107f1a
vendored scripts: ignore if only the date changes
AntoninPoche Jun 8, 2026
e5a24fb
Merge branch 'dev' of github.com:FOR-sight-ai/interpreto into concept…
AntoninPoche Jun 9, 2026
d55d819
concepts inference wrapper: make padding_side an attribute and not a …
AntoninPoche Jun 9, 2026
f33ddbf
splitter for classification: remove outdated docstring part
AntoninPoche Jun 9, 2026
f97f5cd
linting
AntoninPoche Jun 9, 2026
6cb1662
Merge pull request #150 from FOR-sight-ai/concepts-attributions
AntoninPoche Jun 9, 2026
122ef9c
Merge branch 'dev' of github.com:FOR-sight-ai/interpreto into probes
AntoninPoche Jun 9, 2026
bd3dfe3
Merge pull request #153 from FOR-sight-ai/probes
AntoninPoche Jun 9, 2026
3ab5d42
Merge branch 'dev' of github.com:FOR-sight-ai/interpreto into concept…
AntoninPoche Jun 9, 2026
05cc6c4
0.5.0dev0 -> 0.5.0dev1
AntoninPoche Jun 10, 2026
a3c02c1
concepts base: simplify inputs_to_activations
AntoninPoche Jun 15, 2026
f638619
test model wrapping: split between test inference wrappers and test s…
AntoninPoche Jun 16, 2026
bfb1733
generation splitters: remove the output_tuple_index from arguments
AntoninPoche Jun 16, 2026
b344680
splitters: set default concepts_x_gradients to True
AntoninPoche Jun 16, 2026
42d0ad9
test generation splitter: add assertion messages
AntoninPoche Jun 16, 2026
c180e5c
concepts: change base attribute from model_with_split_points to splitter
AntoninPoche Jun 16, 2026
8788e05
splitter doc: pass talk about granularity to the mode lwith split points
AntoninPoche Jun 16, 2026
7a64f0f
concepts interpretations: nltk fix
AntoninPoche Jun 16, 2026
16d4667
splitters for classification: small fix with kwargs
AntoninPoche Jun 16, 2026
6beb1f2
splitters doc: pass granularity stuff to model with split points
AntoninPoche Jun 16, 2026
b5b7bec
notebooks: refresh and small fixes
AntoninPoche Jun 16, 2026
90fc0e2
concepts base: change normalization from private to protected
AntoninPoche Jun 17, 2026
1b7fc40
github actions: debug build workflow
AntoninPoche Jun 17, 2026
3d7f6f9
interpretations: drastically accelerate topk inputs (at least x10)
AntoninPoche Jun 22, 2026
a396e71
concepts generation notebook: rerun and add HuggingFaceLLM interface …
AntoninPoche Jun 22, 2026
164d41d
Merge branch 'dev' of github.com:FOR-sight-ai/interpreto into concept…
AntoninPoche Jun 22, 2026
2523875
notebooks: align mkdocs pointing and banner path
AntoninPoche Jun 22, 2026
b8228f9
0.5.0.dev1 -> 0.5.0
AntoninPoche Jun 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ on:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30 # minutes
timeout-minutes: 10 # minutes
if: github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]'
strategy:
max-parallel: 1
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]

Expand All @@ -35,6 +36,12 @@ jobs:
path: .venv
key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('pyproject.toml') }}

- name: Cache Hugging Face models
uses: actions/cache@v4
with:
path: ~/.cache/huggingface
key: hf-${{ runner.os }}-${{ hashFiles('.github/workflows/build.yml', 'pyproject.toml') }}

- name: Recreate virtual environment if cache is invalid
run: |
if [ ! -f .venv/bin/python ]; then
Expand Down Expand Up @@ -63,6 +70,9 @@ jobs:
AutoTokenizer,
)

# A test also needs a real tokenizer
AutoTokenizer.from_pretrained("bert-base-uncased")

# List all of the hf-internal-testing model IDs that your tests need:
model_ids = [
"hf-internal-testing/tiny-random-bert",
Expand All @@ -73,14 +83,14 @@ jobs:
"textattack/bert-base-uncased-imdb",
]

# We will call .from_pretrained(...) once per model_id,
# and also download its tokenizer. Because HF_HUB_TOKEN
# is set in the environment, these calls are authenticated
# We will call .from_pretrained(...) once per model_id,
# and also download its tokenizer. Because HF_HUB_TOKEN
# is set in the environment, these calls are authenticated
# and will not exhaust the low unauthenticated rate limit.
for m in set(model_ids):
# Sequence classification or masked-LM vs causal-LM
# is determined by your conftest/test code.
# We can try all possible AutoModel types here, but
for m in set(model_ids):
# Sequence classification or masked-LM vs causal-LM
# is determined by your conftest/test code.
# We can try all possible AutoModel types here, but
# since your conftest already splits them out, we just do:
try:
AutoModelForSequenceClassification.from_pretrained(m)
Expand All @@ -107,8 +117,6 @@ jobs:
- name: Run style checks
run: |
source .venv/bin/activate
make update-deps
uv pip install -r requirements-dev.txt
make lint

- name: Run fast tests
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/sync-overcomplete.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ jobs:

- name: Apply vendored patch
run: |
if [[ -z "$(git status --porcelain)" ]]; then
echo "No vendored changes to patch."
exit 0
fi
git apply --check interpreto/_vendor/overcomplete/overcomplete.patch
git apply --whitespace=fix interpreto/_vendor/overcomplete/overcomplete.patch

Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ repos:
exclude: LICENSE

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.14
rev: v0.15.10
hooks:
- id: ruff-format
- id: ruff
Expand Down
214 changes: 214 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# AGENTS.md

## Goal

`interpreto` is a modular interpretability toolkit for transformer models. The repository aims to provide:

- an easy-to-use public API for attribution and concept-based explanations,
- detailed documentation with concrete examples,
- precise internal representations for tensors, targets, and activations,
- reusable building blocks that can be combined without rewriting the whole pipeline.

The main product surface is:

- attribution methods for classification and generation,
- concept discovery and concept interpretation workflows,
- evaluation metrics,
- HTML visualizations,
- docs and notebooks showing real usage.

## Repository Map

- `interpreto/__init__.py`
- Curated public API. If a feature is meant to be user-facing, it usually belongs here too.
- `interpreto/concepts.splitters/`
- Bridges raw Hugging Face models to Interpreto internals.
- `inference_wrapper.py`: shared batching, device handling, logits/gradient access, padding helpers.
- `classification_inference_wrapper.py`: targeted scoring for classification tasks.
- `generation_inference_wrapper.py`: targeted scoring for generation tasks.
- `model_with_split_points.py`: `nnsight`-based model splitting and activation extraction for concept methods.
- `llm_interface.py`: abstraction layer for LLM-based concept labeling.
- `interpreto/attributions/`
- Attribution framework.
- `base.py`: shared explainers, normalization, output dataclasses, classification/generation glue.
- `methods/`: LIME, KernelShap, Occlusion, Sobol, Saliency, Integrated Gradients, SmoothGrad, etc.
- `perturbations/`: perturbation generators used by attribution methods.
- `aggregations/`: score aggregation logic.
- `metrics/`: insertion/deletion evaluation.
- `interpreto/concepts/`
- Concept-based interpretability framework.
- `base.py`: base concept explainer interfaces.
- `methods/`: neurons-as-concepts, overcomplete/SAE methods, sklearn-based methods, Cockatiel.
- `interpretations/`: `TopKInputs`, `LLMLabels`, and related interpretation utilities.
- `metrics/`: reconstruction, sparsity, stability, and ConSim.
- `interpreto/commons/`
- Shared utilities such as granularity handling, generator helpers, and distances.
- `interpreto/typing.py`
- Central typing aliases and protocols. This file expresses the intended normalized internal shapes and interfaces.
- `interpreto/visualizations/`
- HTML/CSS/JS renderers for attribution and concept outputs.
- Visualizations should consume normalized outputs, not recompute model logic.
- `interpreto/_vendor/overcomplete/`
- Vendored dependency for concept learning backends. Avoid touching it unless the change really belongs there.
- `tests/`
- Pytest suite. Reuse fixtures from `tests/conftest.py` whenever possible.
- `docs/`
- MkDocs source, API pages, and notebooks.
- `site/`
- Generated documentation output. Prefer editing `docs/`, not `site/`.

## Key Dependencies

- `torch`
- Core tensor and model execution backend.
- `transformers`
- Main model/tokenizer interface and public compatibility target.
- `nnsight`
- Used by `ModelWithSplitPoints` for split points and activation capture.
- `jaxtyping` and `beartype`
- Preferred tools for explicit tensor typing and shape contracts.
- `scikit-learn`, `scipy`, `einops`, `matplotlib`, `nltk`
- Supporting libraries for methods, metrics, preprocessing, and visualization.
- `bitsandbytes`
- Compatibility with quantized transformer loading.
- `mkdocs` stack
- Documentation build system.

## How The Pieces Interact

### Attribution pipeline

User inputs can arrive in several formats: strings, tokenized mappings, tensors, or iterables of those. The code should normalize them early, then keep core computations on one internal format.

Typical flow:

1. User input and targets enter an attribution explainer from `interpreto.attributions`.
2. The explainer normalizes inputs/targets in `attributions/base.py`.
3. A perturbator or gradient path generates the computation stream.
4. A task-specific inference wrapper computes targeted logits or gradients.
5. An aggregator converts raw scores into final attribution values.
6. The result is packaged as `AttributionOutput`.
7. Metrics and visualizations consume `AttributionOutput`.

Important style point: attribution code is intentionally generator-friendly. Many paths are designed to work sample by sample or batch by batch instead of materializing everything eagerly. Preserve that when making changes, especially for generation and prompt construction logic.

### Concept pipeline

Typical flow:

1. `ModelWithSplitPoints` wraps a transformer model and exposes split points.
2. `get_activations()` extracts latent activations at a chosen granularity.
3. A concept explainer from `interpreto.concepts.methods` fits or applies a concept model on those activations.
4. Interpretation methods such as `TopKInputs` or `LLMLabels` map concept dimensions to human-readable descriptions.
5. Metrics and visualizations operate on the resulting concept-space artifacts.

`ModelWithSplitPoints` is the bridge between the transformer world and concept methods. Most concept changes should respect that layering instead of bypassing it.

### Granularity and normalization

Granularity is a core abstraction shared across attribution and concept code. The code often accepts flexible user inputs, but should converge quickly toward:

- normalized `TensorMapping`-style model inputs,
- normalized target tensors,
- normalized activation tensors,
- normalized output dataclasses.

This repository prefers a flexible public API and a stricter internal core.

## Repository Vibe

- Keep the public API easy to use.
- Users may provide several input formats.
- Internal computations should still be normalized into a single clear format as early as possible.
- Prefer precise typing.
- `jaxtyping` is valuable here because tensor shapes matter a lot for readability and debugging.
- Be pragmatic at boundaries with `transformers` and `nnsight`; do not make the code worse just to force shape annotations through awkward external APIs.
- Documentation matters.
- Detailed docstrings, examples, file-level comments, and inline comments are a feature of the repository, not noise.
- When adding or changing logic, explain the shape conventions and the intent, especially around generators, token alignment, split points, and concept encoding.
- The repository is modular.
- Prefer plug-and-play building blocks over special-purpose monoliths.
- Reuse wrappers, perturbators, aggregators, metrics, and visualization outputs rather than duplicating logic.
- Prefer one place for validation.
- Do not add repeated guardrails in every layer if the check already belongs at the public boundary or is already enforced by typing/contracts.
- Re-check only if a lower-level function can be called independently or if the invariant genuinely changes.
- Smaller changes are usually better.
- Do not refactor by default.
- If a minimal patch would conflict with the method/class/repository design, then do the slightly larger coherent refactor instead of adding a local hack.
- Keep implementations efficient but simple.
- Prefer straightforward Torch code.
- If a much faster version would add a lot of complexity, it is often better to land the clean version first and leave a focused `TODO`.
- In attribution code, preserve the generator-based pipeline mindset.
- The repository often processes attribution sample by sample, while trying to construct good prompts and avoid unnecessary materialization.

## Coding Expectations

- Write docstrings and the important inline comments at the same time as the code change, or before.
- Prefer file-level comments when the whole module has a specific role or subtle invariant.
- Keep internal data formats explicit.
- If adding a new public class or function, check whether it should be re-exported in a package `__init__.py` and documented in `docs/`.
- Use the existing module boundaries.
- New attribution methods usually belong in `interpreto/attributions/methods/`.
- New perturbation logic belongs in `interpreto/attributions/perturbations/`.
- New concept methods belong in `interpreto/concepts/methods/`.
- New interpretation strategies should use the existing concept explainer interfaces.

## Tests

Testing style in this repository is usually a mix of:

- method-level tests for specific algorithmic behavior,
- class-level tests for API and integration behavior,
- sanity checks for end-to-end invariants.

Guidelines:

- For a new feature, test-driven development is preferred when practical.
- Keep tests reviewable. Do not add large numbers of nearly identical tests.
- Be very clear in test comments/docstrings about what the test is proving.
- Reuse `tests/conftest.py`, `tests/fixtures/`, and existing helpers before inventing new scaffolding.
- Prefer `hf-internal-testing/*` tiny models over large custom placeholders or long fake model definitions.
- Do not test the same invariant in many places unless it protects distinct call paths.

## Change Workflow For Agents

1. Think first.
- Understand which layer should change.
- Prefer the smallest coherent modification.
- If the design tradeoff is uncertain, it is better to ask for an opinion than to guess.
2. Add or update tests.
- For new features or bug fixes, start from the behavior you want to lock in.
- Reuse fixtures and tiny test models whenever possible.
3. Implement the change.
- Keep the code aligned with existing abstractions.
- Avoid clever one-off tricks that only satisfy the immediate patch.
4. Update documentation if needed.
- Public API changes usually need docstring and docs updates.
- Example-driven documentation is part of the repository style.
5. Verify with targeted commands first.

Useful commands:

- `make install-dev`
- `make lint`
- `make fast-test`
- `make test-cpu`
- `python -m pytest -n auto -c pyproject.toml -v path/to/test_file.py`

## Practical Do / Don't

Do:

- Normalize flexible user inputs into one internal format early.
- Use `jaxtyping` where it improves shape clarity.
- Preserve generator-based or streaming-friendly flows.
- Add comments where tensor shapes, batching, or prompt construction are non-obvious.
- Favor small coherent patches.

Don't:

- Add redundant guardrails in every layer.
- Materialize huge intermediate lists if the existing pipeline is intentionally iterable/generator-based.
- Refactor broadly without a concrete design reason.
- Fight external library APIs just to satisfy an idealized typing style.
- Edit generated docs in `site/` when the real source lives in `docs/`.
Loading
Loading