Text Generation
Transformers
Safetensors
minspark
language-model
transformer
rope
gqa
custom_code
tiny
looped
slm
custom-architecture
custom-tokenizer
Instructions to use MinimaLabs/min-spark with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use MinimaLabs/min-spark with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="MinimaLabs/min-spark", trust_remote_code=True)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("MinimaLabs/min-spark", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use MinimaLabs/min-spark with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "MinimaLabs/min-spark" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MinimaLabs/min-spark", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/MinimaLabs/min-spark
- SGLang
How to use MinimaLabs/min-spark with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "MinimaLabs/min-spark" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MinimaLabs/min-spark", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "MinimaLabs/min-spark" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "MinimaLabs/min-spark", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use MinimaLabs/min-spark with Docker Model Runner:
docker model run hf.co/MinimaLabs/min-spark
remove dev tooling + staging notes from package (tests, make_charts, UPLOAD, ARCHITECTURE-VIEWER, pycache, benchmark_table.md)
Browse files- ARCHITECTURE-VIEWER.md +0 -35
- UPLOAD.md +0 -83
- __pycache__/configuration_minspark.cpython-312.pyc +0 -0
- __pycache__/generate.cpython-312.pyc +0 -0
- __pycache__/meiosis.cpython-312.pyc +0 -0
- __pycache__/modeling_minspark.cpython-312.pyc +0 -0
- __pycache__/run_lmeval.cpython-312.pyc +0 -0
- charts/benchmark_table.md +0 -5
- make_charts.py +0 -104
- tests/__init__.py +0 -0
- tests/__pycache__/__init__.cpython-312.pyc +0 -0
- tests/__pycache__/conftest.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_architecture.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_charts.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_config.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_generate.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_lmeval.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_modeling.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_pipeline.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_provenance.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_raw_path.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_readme.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_remote_code.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_tokenizer.cpython-312-pytest-9.1.1.pyc +0 -0
- tests/conftest.py +0 -6
- tests/test_architecture.py +0 -19
- tests/test_charts.py +0 -35
- tests/test_config.py +0 -40
- tests/test_generate.py +0 -101
- tests/test_lmeval.py +0 -59
- tests/test_modeling.py +0 -115
- tests/test_pipeline.py +0 -34
- tests/test_provenance.py +0 -38
- tests/test_raw_path.py +0 -31
- tests/test_readme.py +0 -69
- tests/test_remote_code.py +0 -70
- tests/test_tokenizer.py +0 -60
ARCHITECTURE-VIEWER.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
# Hugging Face native architecture viewer — investigation verdict
|
| 2 |
-
|
| 3 |
-
**Question:** Does the Hugging Face model page render a native architecture viewer
|
| 4 |
-
for a custom-model (`model_type: minspark`, `trust_remote_code`) private repo?
|
| 5 |
-
|
| 6 |
-
**Verdict: No.** The Hub's model-page architecture display is driven by a curated
|
| 7 |
-
mapping of *recognized* transformers architectures (Llama, Mistral, GPT-2, BERT,
|
| 8 |
-
Qwen, …) plus `safetensors` parameter metadata. It does not:
|
| 9 |
-
|
| 10 |
-
- derive a diagram from a custom `config.json` + `auto_map` remote-code model, or
|
| 11 |
-
- compute a parameter/architecture readout for an unknown `model_type`.
|
| 12 |
-
|
| 13 |
-
Sources consulted (2026-08-06):
|
| 14 |
-
|
| 15 |
-
- `huggingface.js` `packages/tasks/src/model-data.ts` — `ModelData.config` is a
|
| 16 |
-
passthrough of `architectures`/`model_type`/`auto_map`; no formula or per-arch
|
| 17 |
-
computation, no unknown-model fallback.
|
| 18 |
-
- `packages/tasks/src/model-libraries-snippets.ts` — snippet generation branches
|
| 19 |
-
on `pipeline_tag`/tags, not on a custom `model_type`.
|
| 20 |
-
- Hub model-card docs (`docs/hub/model-cards`) — the card renders metadata
|
| 21 |
-
(license, pipeline_tag, library_name, eval results) and markdown images; there
|
| 22 |
-
is no universal architecture diagram for arbitrary custom code.
|
| 23 |
-
- transformers `PreTrainedModel.from_pretrained` internals — `trust_remote_code`
|
| 24 |
-
loads the custom module for *inference*; nothing emits a Hub-side diagram.
|
| 25 |
-
|
| 26 |
-
For a **private** repo the effect is even more pronounced: the model page shows
|
| 27 |
-
limited metadata and no architecture visualization for any unrecognized
|
| 28 |
-
architecture.
|
| 29 |
-
|
| 30 |
-
**Shipping decision:** the model card embeds a hand-authored `architecture.svg`
|
| 31 |
-
(horizontal dataflow diagram) as the robust fallback. If Hugging Face later
|
| 32 |
-
adds native viewer support for custom remote-code models, the SVG remains a
|
| 33 |
-
correct, dependency-free description of the architecture.
|
| 34 |
-
|
| 35 |
-
(Staging-only note — excluded from the Hub upload.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UPLOAD.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
| 1 |
-
# min-spark Upload Plan (staging-only — no upload performed)
|
| 2 |
-
|
| 3 |
-
**Target repo:** `MinimaLabs/min-spark` — **PRIVATE**, `type=model`.
|
| 4 |
-
|
| 5 |
-
> This document is the upload-ready summary. No `hf repos create`/`hf upload`
|
| 6 |
-
> was run during staging. The user reviews the staged package and authorizes
|
| 7 |
-
> the upload.
|
| 8 |
-
|
| 9 |
-
## Tooling verified
|
| 10 |
-
|
| 11 |
-
- `hf` CLI version: **1.17.0** (`hf version`)
|
| 12 |
-
- `huggingface_hub` in the packaging venv (`~/.venvs/pico-hf`): **1.26.0**
|
| 13 |
-
|
| 14 |
-
## Staging → Hub file mapping
|
| 15 |
-
|
| 16 |
-
| Staging file | Hub path | Included? |
|
| 17 |
-
|--------------|----------|-----------|
|
| 18 |
-
| `model.safetensors` | `model.safetensors` | ✅ weights |
|
| 19 |
-
| `tokenizer.json` | `tokenizer.json` | ✅ tokenizer |
|
| 20 |
-
| `config.json` | `config.json` | ✅ |
|
| 21 |
-
| `configuration_minspark.py` | `configuration_minspark.py` | ✅ |
|
| 22 |
-
| `modeling_minspark.py` | `modeling_minspark.py` | ✅ |
|
| 23 |
-
| `meiosis.py` | `meiosis.py` | ✅ vendored model |
|
| 24 |
-
| `tokenizer_config.json` | `tokenizer_config.json` | ✅ |
|
| 25 |
-
| `special_tokens_map.json` | `special_tokens_map.json` | ✅ |
|
| 26 |
-
| `generation_config.json` | `generation_config.json` | ✅ |
|
| 27 |
-
| `README.md` | `README.md` | ✅ model card |
|
| 28 |
-
| `LICENSE` | `LICENSE` | ✅ Apache-2.0 |
|
| 29 |
-
| `requirements.txt` | `requirements.txt` | ✅ |
|
| 30 |
-
| `requirements-eval.txt` | `requirements-eval.txt` | ✅ |
|
| 31 |
-
| `run_lmeval.py` | `run_lmeval.py` | ✅ reproduction |
|
| 32 |
-
| `record_deltas.py` | `record_deltas.py` | ✅ |
|
| 33 |
-
| `deltas.json` | `deltas.json` | ✅ measured deltas |
|
| 34 |
-
| `charts/accuracy.svg` | `charts/accuracy.svg` | ✅ |
|
| 35 |
-
| `charts/byteppl.svg` | `charts/byteppl.svg` | ✅ |
|
| 36 |
-
| `architecture.svg` | `architecture.svg` | ✅ |
|
| 37 |
-
| `make_charts.py` | — | ❌ dev tool |
|
| 38 |
-
| `UPLOAD.md` | — | ❌ staging note |
|
| 39 |
-
| `ARCHITECTURE-VIEWER.md` | — | ❌ staging note |
|
| 40 |
-
| `tests/` | — | ❌ test suite |
|
| 41 |
-
| `__pycache__/`, `*.pyc` | — | ❌ |
|
| 42 |
-
|
| 43 |
-
## Exact upload command
|
| 44 |
-
|
| 45 |
-
```bash
|
| 46 |
-
cd models/2026-07-meiosis/hf/min-spark
|
| 47 |
-
|
| 48 |
-
# 1. Create the PRIVATE model repo (user runs this)
|
| 49 |
-
hf repos create MinimaLabs/min-spark --type model --private
|
| 50 |
-
|
| 51 |
-
# 2. Upload the full staging dir, excluding dev tooling + staging notes
|
| 52 |
-
# (model.safetensors + tokenizer.json ARE included — not in the exclude set)
|
| 53 |
-
hf upload MinimaLabs/min-spark . \
|
| 54 |
-
--include "*" \
|
| 55 |
-
--exclude "tests/ make_charts.py UPLOAD.md ARCHITECTURE-VIEWER.md __pycache__/** *.pyc .*_cache/**"
|
| 56 |
-
```
|
| 57 |
-
|
| 58 |
-
The `hf upload` uses `--include "*"` to pull everything in the staging dir, and
|
| 59 |
-
the `--exclude` set removes the dev tooling and the two staging-only notes. The
|
| 60 |
-
weights and tokenizer (gitignored in PICO git) are uploaded because they are
|
| 61 |
-
not in the exclude set.
|
| 62 |
-
|
| 63 |
-
> Note: `hf upload` excludes are space-separated globs. Verify the resulting
|
| 64 |
-
> file list with `hf upload MinimaLabs/min-spark . --dry-run ...` (if supported)
|
| 65 |
-
> before the real upload.
|
| 66 |
-
|
| 67 |
-
## Post-upload checklist (user verifies on the private page)
|
| 68 |
-
|
| 69 |
-
- [ ] README renders — quickstart, effort table, benchmark table, charts, SVG.
|
| 70 |
-
- [ ] Benchmark table values match `charts/benchmark_table.md` exactly.
|
| 71 |
-
- [ ] `Use in Transformers` button shows the model card snippet.
|
| 72 |
-
- [ ] "Use in Transformers" snippet works: `AutoModelForCausalLM.from_pretrained(
|
| 73 |
-
"MinimaLabs/min-spark", trust_remote_code=True)`.
|
| 74 |
-
- [ ] License badge shows Apache-2.0.
|
| 75 |
-
- [ ] Architecture viewer verdict recorded (no native viewer for custom
|
| 76 |
-
`model_type`; `architecture.svg` is the fallback).
|
| 77 |
-
- [ ] `pipeline_tag: text-generation` shows the text-generation widget.
|
| 78 |
-
- [ ] The repo is private (verified under the org).
|
| 79 |
-
|
| 80 |
-
## No upload was performed during staging
|
| 81 |
-
|
| 82 |
-
The staging suite is fully green; the two integration paths are validated; the
|
| 83 |
-
stock-HFLM deltas are measured. Upload awaits explicit user authorization.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__pycache__/configuration_minspark.cpython-312.pyc
DELETED
|
Binary file (5.05 kB)
|
|
|
__pycache__/generate.cpython-312.pyc
DELETED
|
Binary file (5.26 kB)
|
|
|
__pycache__/meiosis.cpython-312.pyc
DELETED
|
Binary file (25.5 kB)
|
|
|
__pycache__/modeling_minspark.cpython-312.pyc
DELETED
|
Binary file (10.6 kB)
|
|
|
__pycache__/run_lmeval.cpython-312.pyc
DELETED
|
Binary file (11.7 kB)
|
|
|
charts/benchmark_table.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
| Effort | BLiMP | ARC-Easy | ARC-Challenge | HellaSwag | PIQA | WikiText-2 byte-ppl |
|
| 2 |
-
|--------|-------|----------|---------------|-----------|------|---------------------|
|
| 3 |
-
| min-spark-low | 67.11% | 35.10% | 23.21% | 27.91% | 54.13% | 2.8783 |
|
| 4 |
-
| min-spark-medium | 69.19% | 37.08% | 22.78% | 27.92% | 54.30% | 2.7747 |
|
| 5 |
-
| min-spark-high | 69.18% | 37.08% | 22.87% | 27.91% | 54.35% | 2.7747 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
make_charts.py
DELETED
|
@@ -1,104 +0,0 @@
|
|
| 1 |
-
"""Generate model-card charts from results_all_k.json (staging-only tool).
|
| 2 |
-
|
| 3 |
-
Reads the AGGREGATE `blimp` key (top-level "blimp" -> "acc,none"), never the
|
| 4 |
-
mean of the 67 blimp_* subtasks. Accuracy tasks use acc_norm,none
|
| 5 |
-
(arc_easy/arc_challenge/hellaswag/piqa); byte-perplexity uses
|
| 6 |
-
byte_perplexity,none. Effort colors are fixed across every chart.
|
| 7 |
-
Also writes charts/benchmark_table.md (the source-of-truth table).
|
| 8 |
-
"""
|
| 9 |
-
from __future__ import annotations
|
| 10 |
-
|
| 11 |
-
import json
|
| 12 |
-
from pathlib import Path
|
| 13 |
-
|
| 14 |
-
import matplotlib
|
| 15 |
-
matplotlib.use("Agg")
|
| 16 |
-
# Real <text> elements (not glyph paths) so labels are inspectable in the SVG
|
| 17 |
-
# and the model-card renders them as selectable text.
|
| 18 |
-
matplotlib.rcParams["svg.fonttype"] = "none"
|
| 19 |
-
import matplotlib.pyplot as plt
|
| 20 |
-
|
| 21 |
-
HERE = Path(__file__).resolve().parent
|
| 22 |
-
RESULTS = (HERE / ".." / ".." / "results" / "decay-p09" / "results_all_k.json").resolve()
|
| 23 |
-
OUT = HERE / "charts"
|
| 24 |
-
|
| 25 |
-
EFFORTS = ["2", "3", "4"]
|
| 26 |
-
EFFORT_LABELS = ["min-spark-low", "min-spark-medium", "min-spark-high"]
|
| 27 |
-
EFFORT_COLORS = {"2": "#4C72B0", "3": "#DD8452", "4": "#55A868"}
|
| 28 |
-
ACC_TASKS = [
|
| 29 |
-
("BLiMP", "blimp", "acc,none"),
|
| 30 |
-
("ARC-Easy", "arc_easy", "acc_norm,none"),
|
| 31 |
-
("ARC-Challenge", "arc_challenge", "acc_norm,none"),
|
| 32 |
-
("HellaSwag", "hellaswag", "acc_norm,none"),
|
| 33 |
-
("PIQA", "piqa", "acc_norm,none"),
|
| 34 |
-
]
|
| 35 |
-
KEY = "acc_norm,none"
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def load() -> dict:
|
| 39 |
-
return json.loads(RESULTS.read_text())
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
def make_accuracy(data: dict) -> None:
|
| 43 |
-
fig, ax = plt.subplots(figsize=(9, 4.6))
|
| 44 |
-
x = range(len(ACC_TASKS))
|
| 45 |
-
width = 0.26
|
| 46 |
-
for i, k in enumerate(EFFORTS):
|
| 47 |
-
vals = [data[k][jkey][key] * 100 for _, jkey, key in ACC_TASKS]
|
| 48 |
-
ax.bar([xi + (i - 1) * width for xi in x], vals, width,
|
| 49 |
-
label=EFFORT_LABELS[i], color=EFFORT_COLORS[k])
|
| 50 |
-
ax.set_ylabel("Accuracy (%)")
|
| 51 |
-
ax.set_ylim(0, 100)
|
| 52 |
-
ax.set_xticks(list(x))
|
| 53 |
-
ax.set_xticklabels([t for t, _, _ in ACC_TASKS])
|
| 54 |
-
ax.legend(frameon=False)
|
| 55 |
-
ax.set_title("min-spark accuracy by effort")
|
| 56 |
-
fig.tight_layout()
|
| 57 |
-
fig.savefig(OUT / "accuracy.svg")
|
| 58 |
-
plt.close(fig)
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
def make_byteppl(data: dict) -> None:
|
| 62 |
-
fig, ax = plt.subplots(figsize=(6, 4))
|
| 63 |
-
vals = [data[k]["wikitext"]["byte_perplexity,none"] for k in EFFORTS]
|
| 64 |
-
ax.bar(EFFORT_LABELS, vals, color=[EFFORT_COLORS[k] for k in EFFORTS])
|
| 65 |
-
ax.set_ylabel("WikiText-2 byte perplexity (lower is better)")
|
| 66 |
-
for i, v in enumerate(vals):
|
| 67 |
-
ax.text(i, v + 0.01, f"{v:.4f}", ha="center")
|
| 68 |
-
ax.set_title("min-spark byte perplexity by effort")
|
| 69 |
-
fig.tight_layout()
|
| 70 |
-
fig.savefig(OUT / "byteppl.svg")
|
| 71 |
-
plt.close(fig)
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
def make_table(data: dict) -> str:
|
| 75 |
-
lines = [
|
| 76 |
-
"| Effort | BLiMP | ARC-Easy | ARC-Challenge | HellaSwag | PIQA | WikiText-2 byte-ppl |",
|
| 77 |
-
"|--------|-------|----------|---------------|-----------|------|---------------------|",
|
| 78 |
-
]
|
| 79 |
-
for k in EFFORTS:
|
| 80 |
-
e = data[k]
|
| 81 |
-
row = (
|
| 82 |
-
f"| {EFFORT_LABELS[int(k)-2]} "
|
| 83 |
-
f"| {e['blimp']['acc,none']*100:.2f}% "
|
| 84 |
-
f"| {e['arc_easy']['acc_norm,none']*100:.2f}% "
|
| 85 |
-
f"| {e['arc_challenge']['acc_norm,none']*100:.2f}% "
|
| 86 |
-
f"| {e['hellaswag']['acc_norm,none']*100:.2f}% "
|
| 87 |
-
f"| {e['piqa']['acc_norm,none']*100:.2f}% "
|
| 88 |
-
f"| {e['wikitext']['byte_perplexity,none']:.4f} |"
|
| 89 |
-
)
|
| 90 |
-
lines.append(row)
|
| 91 |
-
return "\n".join(lines) + "\n"
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
def main() -> None:
|
| 95 |
-
OUT.mkdir(parents=True, exist_ok=True)
|
| 96 |
-
data = load()
|
| 97 |
-
make_accuracy(data)
|
| 98 |
-
make_byteppl(data)
|
| 99 |
-
(OUT / "benchmark_table.md").write_text(make_table(data))
|
| 100 |
-
print(f"wrote {OUT/'accuracy.svg'}, {OUT/'byteppl.svg'}, {OUT/'benchmark_table.md'}")
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
if __name__ == "__main__":
|
| 104 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/__init__.py
DELETED
|
File without changes
|
tests/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (178 Bytes)
|
|
|
tests/__pycache__/conftest.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (659 Bytes)
|
|
|
tests/__pycache__/test_architecture.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (5.21 kB)
|
|
|
tests/__pycache__/test_charts.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (10.9 kB)
|
|
|
tests/__pycache__/test_config.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (14.8 kB)
|
|
|
tests/__pycache__/test_generate.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (15.3 kB)
|
|
|
tests/__pycache__/test_lmeval.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (10.6 kB)
|
|
|
tests/__pycache__/test_modeling.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (11.7 kB)
|
|
|
tests/__pycache__/test_pipeline.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (5.08 kB)
|
|
|
tests/__pycache__/test_provenance.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (7.73 kB)
|
|
|
tests/__pycache__/test_raw_path.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (3.16 kB)
|
|
|
tests/__pycache__/test_readme.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (15 kB)
|
|
|
tests/__pycache__/test_remote_code.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (9.73 kB)
|
|
|
tests/__pycache__/test_tokenizer.cpython-312-pytest-9.1.1.pyc
DELETED
|
Binary file (15.2 kB)
|
|
|
tests/conftest.py
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
import sys
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 5 |
-
if str(STAGING) not in sys.path:
|
| 6 |
-
sys.path.insert(0, str(STAGING))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_architecture.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
| 1 |
-
import xml.etree.ElementTree as ET
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
def test_architecture_svg_is_valid_and_complete():
|
| 8 |
-
p = STAGING / "architecture.svg"
|
| 9 |
-
tree = ET.parse(p)
|
| 10 |
-
root = tree.getroot()
|
| 11 |
-
text = ET.tostring(root, encoding="unicode")
|
| 12 |
-
for label in ["embed", "prelude", "body", "loop", "coda", "unembed"]:
|
| 13 |
-
assert label.lower() in text.lower(), label
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def test_viewer_note_records_verdict():
|
| 17 |
-
note = (STAGING / "ARCHITECTURE-VIEWER.md").read_text()
|
| 18 |
-
assert "native" in note.lower() or "viewer" in note.lower()
|
| 19 |
-
assert "private" in note.lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_charts.py
DELETED
|
@@ -1,35 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
def test_charts_generated():
|
| 8 |
-
import subprocess, sys
|
| 9 |
-
out = subprocess.run([sys.executable, str(STAGING / "make_charts.py")],
|
| 10 |
-
capture_output=True, text=True, cwd=str(STAGING), timeout=120)
|
| 11 |
-
assert out.returncode == 0, out.stderr[-1000:]
|
| 12 |
-
assert (STAGING / "charts" / "accuracy.svg").exists()
|
| 13 |
-
assert (STAGING / "charts" / "byteppl.svg").exists()
|
| 14 |
-
assert (STAGING / "charts" / "benchmark_table.md").exists()
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
def test_table_uses_aggregate_blimp_and_exact_keys():
|
| 18 |
-
table = (STAGING / "charts" / "benchmark_table.md").read_text()
|
| 19 |
-
# aggregate BLiMP values (NOT an average of the 67 subtasks)
|
| 20 |
-
assert "67.11" in table and "69.19" in table
|
| 21 |
-
# byte-perplexity values
|
| 22 |
-
assert "2.8783" in table and "2.7747" in table
|
| 23 |
-
# effort names, not K labels, in headings
|
| 24 |
-
assert "min-spark-low" in table and "min-spark-medium" in table and "min-spark-high" in table
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
def test_accuracy_chart_has_single_0_100_axis():
|
| 28 |
-
import xml.etree.ElementTree as ET
|
| 29 |
-
svg = (STAGING / "charts" / "accuracy.svg").read_text()
|
| 30 |
-
root = ET.fromstring(svg)
|
| 31 |
-
labels = [t.text for t in root.iter() if t.tag.endswith("text") and t.text]
|
| 32 |
-
assert "Accuracy (%)" in labels
|
| 33 |
-
# 0-100 axis: y-axis tick labels reach 100, not a 0-1 fraction axis
|
| 34 |
-
assert any(lbl.strip() == "100" for lbl in labels)
|
| 35 |
-
assert not any(lbl.strip().startswith("0.") for lbl in labels) # no 0.7x fractions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_config.py
DELETED
|
@@ -1,40 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
def test_config_json_round_trips():
|
| 8 |
-
from configuration_minspark import MinSparkConfig
|
| 9 |
-
raw = json.loads((STAGING / "config.json").read_text())
|
| 10 |
-
cfg = MinSparkConfig.from_dict(raw)
|
| 11 |
-
assert cfg.vocab_size == 4096 and cfg.dim == 288 and cfg.max_loops == 4
|
| 12 |
-
assert cfg.effort == "medium" and cfg.use_cache is False
|
| 13 |
-
assert cfg.max_position_embeddings == 512
|
| 14 |
-
back = json.loads(cfg.to_json_string())
|
| 15 |
-
assert back["model_type"] == "minspark"
|
| 16 |
-
assert back["doc_mask_eos"] == 2 and abs(back["ddl_k_eps"] - 0.01) < 1e-9
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def test_to_meiosis_preserves_training_fields():
|
| 20 |
-
from configuration_minspark import MinSparkConfig
|
| 21 |
-
from meiosis import MeiosisConfig
|
| 22 |
-
cfg = MinSparkConfig.from_dict(json.loads((STAGING / "config.json").read_text()))
|
| 23 |
-
m = cfg.to_meiosis()
|
| 24 |
-
assert isinstance(m, MeiosisConfig)
|
| 25 |
-
assert m.max_seq_len == 512 and m.doc_mask_eos == 2
|
| 26 |
-
assert abs(m.ddl_k_eps - 1e-2) < 1e-9
|
| 27 |
-
assert m.n_kv_heads == 2
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
def test_max_position_embeddings_derived_from_max_seq_len():
|
| 31 |
-
from configuration_minspark import MinSparkConfig
|
| 32 |
-
cfg = MinSparkConfig()
|
| 33 |
-
assert cfg.max_position_embeddings == cfg.max_seq_len == 512
|
| 34 |
-
cfg2 = MinSparkConfig(max_seq_len=1024)
|
| 35 |
-
assert cfg2.max_position_embeddings == 1024
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def test_effort_default_and_validation_source():
|
| 39 |
-
from configuration_minspark import MinSparkConfig
|
| 40 |
-
assert MinSparkConfig().effort == "medium"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_generate.py
DELETED
|
@@ -1,101 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import pytest
|
| 3 |
-
|
| 4 |
-
STAGING = None
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
@pytest.fixture(scope="module", autouse=True)
|
| 8 |
-
def setup():
|
| 9 |
-
global STAGING
|
| 10 |
-
from pathlib import Path
|
| 11 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 12 |
-
return
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
@pytest.fixture(scope="module")
|
| 16 |
-
def model():
|
| 17 |
-
from modeling_minspark import MinSparkForCausalLM
|
| 18 |
-
import json
|
| 19 |
-
from configuration_minspark import MinSparkConfig
|
| 20 |
-
from safetensors.torch import load_file
|
| 21 |
-
m = MinSparkForCausalLM(MinSparkConfig.from_dict(json.loads((STAGING / "config.json").read_text())))
|
| 22 |
-
m.model.load_state_dict(load_file(str(STAGING / "model.safetensors")), strict=False)
|
| 23 |
-
m.eval()
|
| 24 |
-
return m
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
def test_generate_shape_and_stop(model):
|
| 28 |
-
ids = torch.tensor([[2, 3, 4, 5]])
|
| 29 |
-
out = model.generate(ids, max_new_tokens=8, do_sample=False)
|
| 30 |
-
assert out.ndim == 2 and out.shape[0] == 1
|
| 31 |
-
# stops at eos or hits max_new; never exceeds input+8
|
| 32 |
-
assert out.shape[1] <= ids.shape[1] + 8
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
def test_generate_effort_passthrough(model):
|
| 36 |
-
# Deterministic: effort="low" must reproduce the loops=2 run EXACTLY (and
|
| 37 |
-
# high == loops=4). Equality, not inequality — greedy decoding is exact, and
|
| 38 |
-
# an inequality check could flake when two loop counts coincide on a few
|
| 39 |
-
# tokens or, worse, pass while effort is silently ignored.
|
| 40 |
-
ids = torch.tensor([[2, 3, 4, 5]])
|
| 41 |
-
with torch.no_grad():
|
| 42 |
-
a = model.generate(ids, effort="low", max_new_tokens=4, do_sample=False)
|
| 43 |
-
b = model.generate(ids, loops=2, max_new_tokens=4, do_sample=False)
|
| 44 |
-
c = model.generate(ids, effort="high", max_new_tokens=4, do_sample=False)
|
| 45 |
-
d = model.generate(ids, loops=4, max_new_tokens=4, do_sample=False)
|
| 46 |
-
assert torch.equal(a, b)
|
| 47 |
-
assert torch.equal(c, d)
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
def test_generate_batched_raises(model):
|
| 51 |
-
ids = torch.tensor([[2, 3, 4, 5], [2, 6, 7, 8]])
|
| 52 |
-
with pytest.raises(ValueError, match="batched generation"):
|
| 53 |
-
model.generate(ids, max_new_tokens=2)
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
def test_generate_invalid_effort_raises(model):
|
| 57 |
-
ids = torch.tensor([[2, 3, 4, 5]])
|
| 58 |
-
with pytest.raises(ValueError):
|
| 59 |
-
model.generate(ids, effort="turbo", max_new_tokens=2)
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
def test_prepare_prepends_eos_then_truncates_to_last_512(model):
|
| 63 |
-
# Short prompt: the EOS prefix survives.
|
| 64 |
-
short = torch.tensor([[7, 8, 9]])
|
| 65 |
-
prep = model.prepare_inputs_for_generation(short)
|
| 66 |
-
assert prep["input_ids"].tolist() == [[2, 7, 8, 9]]
|
| 67 |
-
# Long prompt: [EOS] + 600 tokens -> last 512 of the concatenation, so the
|
| 68 |
-
# EOS drops off the front. Exact infer.py/loader.py parity (spec: "including
|
| 69 |
-
# the >512 edge where EOS drops").
|
| 70 |
-
long = torch.randint(0, 4096, (1, 600))
|
| 71 |
-
prep = model.prepare_inputs_for_generation(long)
|
| 72 |
-
ids = prep["input_ids"]
|
| 73 |
-
assert ids.shape[1] == 512
|
| 74 |
-
eos = torch.full((1, 1), 2, dtype=long.dtype)
|
| 75 |
-
assert torch.equal(ids, torch.cat([eos, long], dim=1)[:, -512:])
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
def test_prepare_keeps_attention_mask_in_sync(model):
|
| 79 |
-
# generate may hand prepare a mask (tokenizer output in the quickstart, or
|
| 80 |
-
# an auto-created all-ones mask); after the EOS prepend it must still match
|
| 81 |
-
# input_ids in length, or forward's shape validation raises.
|
| 82 |
-
ids = torch.tensor([[2, 3, 4, 5]])
|
| 83 |
-
prep = model.prepare_inputs_for_generation(ids, attention_mask=torch.ones_like(ids))
|
| 84 |
-
assert prep["attention_mask"].shape == prep["input_ids"].shape
|
| 85 |
-
assert prep["attention_mask"].tolist() == [[1, 1, 1, 1, 1]]
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
def test_generate_does_not_leak_machinery(model):
|
| 89 |
-
# If prepare echoed cache_position/position_ids/use_cache, forward would
|
| 90 |
-
# raise TypeError (it has no **kwargs). A clean generate is the proof.
|
| 91 |
-
# Bounded, not exact, length: eos_token_id=2 may stop greedy decoding early.
|
| 92 |
-
ids = torch.tensor([[2, 3, 4, 5]])
|
| 93 |
-
out = model.generate(ids, max_new_tokens=3, do_sample=False)
|
| 94 |
-
assert ids.shape[1] < out.shape[1] <= ids.shape[1] + 3
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
def test_generation_config_matches_contract():
|
| 98 |
-
import json
|
| 99 |
-
gc = json.loads((STAGING / "generation_config.json").read_text())
|
| 100 |
-
assert gc["eos_token_id"] == 2 and gc["pad_token_id"] == 0 and gc["bos_token_id"] == 1
|
| 101 |
-
assert gc["use_cache"] is False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_lmeval.py
DELETED
|
@@ -1,59 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import subprocess
|
| 3 |
-
import sys
|
| 4 |
-
from pathlib import Path
|
| 5 |
-
|
| 6 |
-
import pytest
|
| 7 |
-
|
| 8 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 9 |
-
PY = Path(sys.executable)
|
| 10 |
-
|
| 11 |
-
PUBLISHED = {
|
| 12 |
-
"arc_easy": {"key": "acc_norm,none", "value": 0.3707912457912458},
|
| 13 |
-
"wikitext": {"key": "byte_perplexity,none", "value": 2.7746719015990515},
|
| 14 |
-
}
|
| 15 |
-
TOL = {"acc_norm,none": 0.002, "byte_perplexity,none": 0.01}
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def test_run_lmeval_reproduces_arc_easy_k3():
|
| 19 |
-
# --effort medium reproduces the K=3 published value (K=3/K=4 happen to be
|
| 20 |
-
# bit-identical for arc_easy, so --effort high would pass by coincidence).
|
| 21 |
-
out = subprocess.run(
|
| 22 |
-
[str(PY), str(STAGING / "run_lmeval.py"), "--effort", "medium", "--tasks", "arc_easy", "--json"],
|
| 23 |
-
capture_output=True, text=True, cwd=str(STAGING), timeout=2400,
|
| 24 |
-
)
|
| 25 |
-
assert out.returncode == 0, out.stderr[-2000:]
|
| 26 |
-
# --json prints results["results"] (task -> metrics), not the full envelope
|
| 27 |
-
res = json.loads(out.stdout)["arc_easy"]
|
| 28 |
-
assert abs(res["acc_norm,none"] - PUBLISHED["arc_easy"]["value"]) <= TOL["acc_norm,none"]
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
def test_run_lmeval_reproduces_wikitext_byteppl_k3():
|
| 32 |
-
out = subprocess.run(
|
| 33 |
-
[str(PY), str(STAGING / "run_lmeval.py"), "--effort", "medium", "--tasks", "wikitext", "--json"],
|
| 34 |
-
capture_output=True, text=True, cwd=str(STAGING), timeout=2400,
|
| 35 |
-
)
|
| 36 |
-
assert out.returncode == 0, out.stderr[-2000:]
|
| 37 |
-
res = json.loads(out.stdout)["wikitext"]
|
| 38 |
-
assert abs(res["byte_perplexity,none"] - PUBLISHED["wikitext"]["value"]) <= TOL["byte_perplexity,none"]
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
def test_minspark_lm_instantiates():
|
| 42 |
-
# Catches the LM-vs-TemplateLM abstract-property mistake at construction
|
| 43 |
-
# time, before any scoring runs.
|
| 44 |
-
from run_lmeval import MinSparkLM
|
| 45 |
-
lm = MinSparkLM("medium")
|
| 46 |
-
assert lm.eot_token_id == 2 and lm.max_length == 512
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def test_stock_hflm_smoke_and_deltas():
|
| 50 |
-
# Stock --model hf must run a long-context task without crashing (R3), and
|
| 51 |
-
# record per-task deltas vs the SAME-subset run_lmeval value into deltas.json.
|
| 52 |
-
out = subprocess.run(
|
| 53 |
-
[str(PY), str(STAGING / "record_deltas.py"), "--tasks", "arc_easy,wikitext", "--limit", "200"],
|
| 54 |
-
capture_output=True, text=True, cwd=str(STAGING), timeout=2400,
|
| 55 |
-
)
|
| 56 |
-
assert out.returncode == 0, out.stderr[-2000:]
|
| 57 |
-
deltas = json.loads((STAGING / "deltas.json").read_text())
|
| 58 |
-
assert "arc_easy" in deltas and "wikitext" in deltas
|
| 59 |
-
assert "delta" in deltas["arc_easy"] and "delta" in deltas["wikitext"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_modeling.py
DELETED
|
@@ -1,115 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import pytest
|
| 3 |
-
|
| 4 |
-
STAGING = None # set in fixture
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
@pytest.fixture(scope="module", autouse=True)
|
| 8 |
-
def setup():
|
| 9 |
-
global STAGING
|
| 10 |
-
from pathlib import Path
|
| 11 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 12 |
-
return
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def load_raw():
|
| 16 |
-
from safetensors.torch import load_file
|
| 17 |
-
from meiosis import Meiosis, MeiosisConfig
|
| 18 |
-
model = Meiosis(MeiosisConfig())
|
| 19 |
-
model.load_state_dict(load_file(str(STAGING / "model.safetensors")), strict=False)
|
| 20 |
-
model.eval()
|
| 21 |
-
return model
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
def load_wrapper():
|
| 25 |
-
from modeling_minspark import MinSparkForCausalLM
|
| 26 |
-
from configuration_minspark import MinSparkConfig
|
| 27 |
-
import json
|
| 28 |
-
model = MinSparkForCausalLM(MinSparkConfig.from_dict(json.loads((STAGING / "config.json").read_text())))
|
| 29 |
-
from safetensors.torch import load_file
|
| 30 |
-
model.model.load_state_dict(load_file(str(STAGING / "model.safetensors")), strict=False)
|
| 31 |
-
model.eval()
|
| 32 |
-
return model
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
@pytest.fixture(scope="module")
|
| 36 |
-
def tokens():
|
| 37 |
-
from tokenizers import Tokenizer
|
| 38 |
-
tok = Tokenizer.from_file(str(STAGING / "tokenizer.json"))
|
| 39 |
-
ids = [2] + tok.encode("The quick brown fox jumps over the lazy dog.").ids
|
| 40 |
-
return torch.tensor([ids])
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
def test_logits_parity_all_efforts(tokens):
|
| 44 |
-
raw, wrap = load_raw(), load_wrapper()
|
| 45 |
-
for loops in (2, 3, 4):
|
| 46 |
-
with torch.no_grad():
|
| 47 |
-
a = raw(tokens, loops=loops)
|
| 48 |
-
b = wrap(tokens, loops=loops).logits
|
| 49 |
-
assert torch.allclose(a, b, atol=1e-6), loops
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
def test_effort_maps_to_loops(tokens):
|
| 53 |
-
wrap = load_wrapper()
|
| 54 |
-
with torch.no_grad():
|
| 55 |
-
low = wrap(tokens, effort="low").logits
|
| 56 |
-
med = wrap(tokens, effort="medium").logits
|
| 57 |
-
assert not torch.allclose(low, med)
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
def test_invalid_effort_and_loops_raise(tokens):
|
| 61 |
-
wrap = load_wrapper()
|
| 62 |
-
with pytest.raises(ValueError):
|
| 63 |
-
wrap(tokens, effort="turbo")
|
| 64 |
-
with pytest.raises(ValueError):
|
| 65 |
-
wrap(tokens, loops=0)
|
| 66 |
-
with pytest.raises(ValueError):
|
| 67 |
-
wrap(tokens, loops=5) # > max_loops
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
def test_use_cache_raises(tokens):
|
| 71 |
-
wrap = load_wrapper()
|
| 72 |
-
with pytest.raises(NotImplementedError, match="KV cache"):
|
| 73 |
-
wrap(tokens, use_cache=True)
|
| 74 |
-
with pytest.raises(NotImplementedError, match="KV cache"):
|
| 75 |
-
wrap(tokens, past_key_values=("x",))
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
def test_labels_loss_matches_shifted_ce(tokens):
|
| 79 |
-
wrap = load_wrapper()
|
| 80 |
-
labels = torch.randint(0, 4096, tokens.shape)
|
| 81 |
-
out = wrap(tokens, labels=labels)
|
| 82 |
-
import torch.nn.functional as F
|
| 83 |
-
logits = wrap(tokens).logits
|
| 84 |
-
shift_logits = logits[:, :-1, :].contiguous()
|
| 85 |
-
shift_labels = labels[:, 1:].contiguous()
|
| 86 |
-
expected = F.cross_entropy(shift_logits.view(-1, 4096), shift_labels.view(-1), ignore_index=-100)
|
| 87 |
-
assert torch.allclose(out.loss, expected, atol=1e-6)
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
def test_padding_guards(tokens):
|
| 91 |
-
wrap = load_wrapper()
|
| 92 |
-
b = torch.tensor([[2, 5, 6, 0], [2, 5, 6, 7]]) # left pad
|
| 93 |
-
with pytest.raises(ValueError, match="left-padded"):
|
| 94 |
-
wrap(b, attention_mask=torch.tensor([[0, 1, 1, 1], [1, 1, 1, 1]]))
|
| 95 |
-
interior = torch.tensor([[2, 5, 6, 7]])
|
| 96 |
-
with pytest.raises(ValueError, match="suffix"):
|
| 97 |
-
wrap(interior, attention_mask=torch.tensor([[1, 1, 0, 1]]))
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
def test_right_padded_scoring_matches_unbatched(tokens):
|
| 101 |
-
# Real right-padding: a PAD TOKEN (id 0) sits in position 3. The guard must
|
| 102 |
-
# pass it, and logits at the REAL positions must equal the unpadded forward.
|
| 103 |
-
# Tolerance is atol=1e-4, not 1e-6: cross-length comparison (4 vs 3 columns)
|
| 104 |
-
# changes SDPA's softmax reduction order, producing ~2.6e-5 fp noise even with
|
| 105 |
-
# the doc-mask OFF (measured). A pad-token LEAK would be O(1) in the logits —
|
| 106 |
-
# same-length pad-vs-nonpad token comparison is bit-exact (0.0), proving the
|
| 107 |
-
# mask/causal logic is correct and the residual diff is shape noise.
|
| 108 |
-
wrap = load_wrapper()
|
| 109 |
-
padded = torch.tensor([[2, 5, 6, 0]]) # token 0 = <pad> in the tail
|
| 110 |
-
mask = torch.tensor([[1, 1, 1, 0]])
|
| 111 |
-
unpadded = torch.tensor([[2, 5, 6]])
|
| 112 |
-
with torch.no_grad():
|
| 113 |
-
a = wrap(padded, attention_mask=mask).logits # real positions 0..2
|
| 114 |
-
b = wrap(unpadded).logits # unpadded reference, 3 tokens
|
| 115 |
-
assert torch.allclose(a[:, :3], b[:, :3], atol=1e-4)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_pipeline.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
| 1 |
-
import pytest
|
| 2 |
-
|
| 3 |
-
STAGING = None
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
@pytest.fixture(scope="module", autouse=True)
|
| 7 |
-
def setup():
|
| 8 |
-
global STAGING
|
| 9 |
-
from pathlib import Path
|
| 10 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 11 |
-
return
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
@pytest.fixture(scope="module")
|
| 15 |
-
def pipe():
|
| 16 |
-
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
|
| 17 |
-
model = AutoModelForCausalLM.from_pretrained(str(STAGING), trust_remote_code=True)
|
| 18 |
-
tok = AutoTokenizer.from_pretrained(str(STAGING), trust_remote_code=True)
|
| 19 |
-
return pipeline("text-generation", model=model, tokenizer=tok)
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
def test_pipeline_generates_text(pipe):
|
| 23 |
-
out = pipe("Hello,", max_new_tokens=6, do_sample=False)
|
| 24 |
-
assert isinstance(out, list) and len(out) == 1
|
| 25 |
-
assert out[0]["generated_text"].strip()
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def test_pipeline_effort_passthrough(pipe):
|
| 29 |
-
# Equality-based: effort="low" must reproduce the loops=2 run exactly. An
|
| 30 |
-
# inequality check could pass while effort is silently ignored, or flake
|
| 31 |
-
# when two loop counts coincide on a few greedy tokens.
|
| 32 |
-
a = pipe("The cat", effort="low", max_new_tokens=4, do_sample=False)[0]["generated_text"]
|
| 33 |
-
b = pipe("The cat", loops=2, max_new_tokens=4, do_sample=False)[0]["generated_text"]
|
| 34 |
-
assert a == b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_provenance.py
DELETED
|
@@ -1,38 +0,0 @@
|
|
| 1 |
-
import hashlib
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
import pytest
|
| 5 |
-
import torch
|
| 6 |
-
|
| 7 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 8 |
-
EXPECT = {
|
| 9 |
-
"meiosis.py": "81aeb026c7ea1b7e8a7961e74c81441706b0ff5a00bd49c02099a1ff28b963c3",
|
| 10 |
-
"tokenizer.json": "d671f013ebdb06a55e48d047f12621c3311310fdcc344683d434d294a909ac51",
|
| 11 |
-
"model.safetensors": "a121c6a316e54e490918d93d3d265460d45addbdad49b8448c514e5f61232c41",
|
| 12 |
-
}
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def sha256(p: Path) -> str:
|
| 16 |
-
return hashlib.sha256(p.read_bytes()).hexdigest()
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
def test_artifact_shas_match_sources():
|
| 20 |
-
for name, expected in EXPECT.items():
|
| 21 |
-
assert sha256(STAGING / name) == expected, name
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
def test_safetensors_is_raw_meiosis_state():
|
| 25 |
-
from safetensors.torch import load_file
|
| 26 |
-
state = load_file(str(STAGING / "model.safetensors"))
|
| 27 |
-
assert len(state) == 81
|
| 28 |
-
assert not any(k.startswith("model.") for k in state)
|
| 29 |
-
assert not any("embed_tokens" in k or "lm_head" in k for k in state)
|
| 30 |
-
assert not any(k.endswith(("rope_cos", "rope_sin", "last_loop_rms")) for k in state)
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
def test_vendored_meiosis_loads_with_strict_false():
|
| 34 |
-
from meiosis import Meiosis, MeiosisConfig
|
| 35 |
-
from safetensors.torch import load_file
|
| 36 |
-
model = Meiosis(MeiosisConfig())
|
| 37 |
-
model.load_state_dict(load_file(str(STAGING / "model.safetensors")), strict=False)
|
| 38 |
-
assert sum(p.numel() for p in model.parameters()) == 5_758_572
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_raw_path.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
| 1 |
-
import sys
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
import torch
|
| 5 |
-
|
| 6 |
-
REPO = Path(__file__).resolve().parents[5] # PICO/ (parents[4] is models/)
|
| 7 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def test_generate_py_matches_space_loader_under_fixed_seed():
|
| 11 |
-
sys.path.insert(0, str(STAGING))
|
| 12 |
-
from generate import generate as staged_gen, load_model as staged_load
|
| 13 |
-
from tokenizers import Tokenizer
|
| 14 |
-
|
| 15 |
-
# Space loader (imports its own vendored meiosis from assets)
|
| 16 |
-
sys.path.insert(0, str(REPO / "spaces" / "min-spark-preview"))
|
| 17 |
-
sys.path.insert(0, str(REPO / "spaces" / "min-spark-preview" / "assets"))
|
| 18 |
-
from loader import generate as space_gen
|
| 19 |
-
from loader import load_model as space_load
|
| 20 |
-
|
| 21 |
-
tok = Tokenizer.from_file(str(STAGING / "tokenizer.json"))
|
| 22 |
-
prompt = "The meaning of life is"
|
| 23 |
-
kw = dict(loops=3, max_new=20, temperature=0.8, top_k=50, device="cpu")
|
| 24 |
-
|
| 25 |
-
torch.manual_seed(42)
|
| 26 |
-
out_a = "".join(staged_gen(staged_load(str(STAGING / "model.safetensors"), "cpu"),
|
| 27 |
-
tok, prompt, **kw))
|
| 28 |
-
torch.manual_seed(42)
|
| 29 |
-
out_b = "".join(c for c, _n, _t in space_gen(space_load("cpu"), tok, prompt, **kw))
|
| 30 |
-
|
| 31 |
-
assert out_a == out_b
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_readme.py
DELETED
|
@@ -1,69 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
def test_frontmatter_present():
|
| 8 |
-
text = (STAGING / "README.md").read_text()
|
| 9 |
-
assert "license: apache-2.0" in text
|
| 10 |
-
assert "pipeline_tag: text-generation" in text
|
| 11 |
-
assert "library_name: transformers" in text
|
| 12 |
-
assert "custom_code" in text
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
def test_benchmark_table_values_match_json():
|
| 16 |
-
table = (STAGING / "charts" / "benchmark_table.md").read_text()
|
| 17 |
-
# results_all_k.json lives at models/2026-07-meiosis/results/decay-p09/
|
| 18 |
-
results = Path(__file__).resolve().parents[3] / "results" / "decay-p09" / "results_all_k.json"
|
| 19 |
-
data = json.loads(results.read_text())
|
| 20 |
-
for k in ("2", "3", "4"):
|
| 21 |
-
assert f"{data[k]['blimp']['acc,none']*100:.2f}%" in table
|
| 22 |
-
assert f"{data[k]['wikitext']['byte_perplexity,none']:.4f}" in table
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
def test_readme_body_inlines_table_no_placeholders():
|
| 26 |
-
readme = (STAGING / "README.md").read_text()
|
| 27 |
-
table = (STAGING / "charts" / "benchmark_table.md").read_text()
|
| 28 |
-
# No unresolved {embed}/{replace} scaffolding leaks into the card.
|
| 29 |
-
# (Precise markers only — a bare "{" would be false-positive on any
|
| 30 |
-
# legitimate code snippet with braces, e.g. {"key": ...}.)
|
| 31 |
-
assert "{embed" not in readme and "replace with:" not in readme
|
| 32 |
-
# The table content is actually inline (real rows, real values).
|
| 33 |
-
for line in table.strip().splitlines():
|
| 34 |
-
if line.startswith("|"):
|
| 35 |
-
assert line in readme, f"README is missing table row: {line}"
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def test_no_k_in_user_facing_benchmark_copy():
|
| 39 |
-
text = (STAGING / "README.md").read_text()
|
| 40 |
-
# effort names everywhere user-facing
|
| 41 |
-
assert "min-spark-low" in text and "min-spark-high" in text
|
| 42 |
-
# K loop-counts may appear ONLY in the technical note section.
|
| 43 |
-
# Gate the ENTIRE card minus the technical note block — including sections
|
| 44 |
-
# AFTER it (Provenance, License) which a "everything before technical note"
|
| 45 |
-
# check would miss.
|
| 46 |
-
tech = text.split("## Technical note")[1].split("## ")[0]
|
| 47 |
-
non_tech = text.replace("## Technical note" + tech, "")
|
| 48 |
-
# non_tech is the whole card with the technical note body removed
|
| 49 |
-
assert "K=" not in non_tech
|
| 50 |
-
assert ("K=" in tech) or ("loop" in tech.lower())
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
def test_provenance_and_single_sequence_notes():
|
| 54 |
-
text = (STAGING / "README.md").read_text()
|
| 55 |
-
assert "meiosis-decay-p09" in text
|
| 56 |
-
assert "a121c6a316e54e490918d93d3d265460d45addbdad49b8448c514e5f61232c41" in text
|
| 57 |
-
assert "single-sequence" in text
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
def test_license_is_apache_2():
|
| 61 |
-
lic = (STAGING / "LICENSE").read_text()
|
| 62 |
-
assert "Apache License" in lic and "Version 2.0" in lic
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
def test_requirements_floor_match_installed():
|
| 66 |
-
reqs = (STAGING / "requirements.txt").read_text()
|
| 67 |
-
import transformers, tokenizers
|
| 68 |
-
assert f"transformers>={'.'.join(transformers.__version__.split('.')[:2])}" in reqs
|
| 69 |
-
assert f"tokenizers>={tokenizers.__version__.split('.')[0]}" in reqs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_remote_code.py
DELETED
|
@@ -1,70 +0,0 @@
|
|
| 1 |
-
import logging
|
| 2 |
-
import shutil
|
| 3 |
-
import tempfile
|
| 4 |
-
import warnings
|
| 5 |
-
from pathlib import Path
|
| 6 |
-
|
| 7 |
-
import torch
|
| 8 |
-
import pytest
|
| 9 |
-
|
| 10 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 11 |
-
# Mirrors the Hub-upload exclude set (UPLOAD.md), plus charts/deltas which are
|
| 12 |
-
# unneeded for the load test and keep the temp snapshot small.
|
| 13 |
-
_EXCLUDE = {"tests", "__pycache__", "make_charts.py", "benchmark_table.md",
|
| 14 |
-
"deltas.json", "charts", "UPLOAD.md", "ARCHITECTURE-VIEWER.md"}
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
@pytest.fixture(scope="module")
|
| 18 |
-
def snapshot():
|
| 19 |
-
with tempfile.TemporaryDirectory() as td:
|
| 20 |
-
dst = Path(td)
|
| 21 |
-
for item in STAGING.iterdir():
|
| 22 |
-
if item.name in _EXCLUDE:
|
| 23 |
-
continue
|
| 24 |
-
shutil.copytree(item, dst / item.name) if item.is_dir() else shutil.copy2(item, dst / item.name)
|
| 25 |
-
yield dst
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
def load_snapshot(snapshot):
|
| 29 |
-
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 30 |
-
with warnings.catch_warnings(record=True) as caught:
|
| 31 |
-
warnings.simplefilter("always")
|
| 32 |
-
tok = AutoTokenizer.from_pretrained(str(snapshot), trust_remote_code=True)
|
| 33 |
-
model = AutoModelForCausalLM.from_pretrained(str(snapshot), trust_remote_code=True)
|
| 34 |
-
model.eval()
|
| 35 |
-
return tok, model, caught
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
def test_remote_code_loads_clean(snapshot):
|
| 39 |
-
tok, model, caught = load_snapshot(snapshot)
|
| 40 |
-
# No phantom keys, no dropped params: transformers warns on both.
|
| 41 |
-
bad = [str(w.message) for w in caught if "were not used" in str(w.message) or "not initialized" in str(w.message)]
|
| 42 |
-
assert bad == [], bad
|
| 43 |
-
# Weights actually loaded (not zero/random-init)
|
| 44 |
-
assert model.model.embed.weight.abs().sum().item() > 1e-6
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
def test_remote_code_logits_match_raw(snapshot):
|
| 48 |
-
from tokenizers import Tokenizer
|
| 49 |
-
from safetensors.torch import load_file
|
| 50 |
-
from meiosis import Meiosis, MeiosisConfig
|
| 51 |
-
tok, model, _ = load_snapshot(snapshot)
|
| 52 |
-
|
| 53 |
-
raw = Meiosis(MeiosisConfig())
|
| 54 |
-
raw.load_state_dict(load_file(str(STAGING / "model.safetensors")), strict=False)
|
| 55 |
-
raw.eval()
|
| 56 |
-
|
| 57 |
-
ids = torch.tensor([[2] + tok.encode("The quick brown fox jumps over the lazy dog.")])
|
| 58 |
-
with torch.no_grad():
|
| 59 |
-
a = raw(ids, loops=3)
|
| 60 |
-
b = model(ids, effort="medium").logits
|
| 61 |
-
assert torch.allclose(a, b, atol=1e-6)
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
def test_remote_code_tokenizer_matches_raw(snapshot):
|
| 65 |
-
from tokenizers import Tokenizer
|
| 66 |
-
from transformers import AutoTokenizer
|
| 67 |
-
tok = AutoTokenizer.from_pretrained(str(snapshot), trust_remote_code=True)
|
| 68 |
-
raw = Tokenizer.from_file(str(STAGING / "tokenizer.json"))
|
| 69 |
-
for text in ["hello world", "Ünïcode 你好 123", " spaced "]:
|
| 70 |
-
assert tok.encode(text) == raw.encode(text).ids
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_tokenizer.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
import pytest
|
| 5 |
-
|
| 6 |
-
STAGING = Path(__file__).resolve().parents[1]
|
| 7 |
-
|
| 8 |
-
BATTERY = [
|
| 9 |
-
"",
|
| 10 |
-
"hello world",
|
| 11 |
-
"The meaning of life is 42.",
|
| 12 |
-
"Ünïcode 你好, mixed 12345",
|
| 13 |
-
" leading and trailing spaces ",
|
| 14 |
-
"a\tb\nc",
|
| 15 |
-
"x" * 700, # longer than max_seq_len; encode must still work
|
| 16 |
-
]
|
| 17 |
-
|
| 18 |
-
RAW_TOK = None
|
| 19 |
-
HF_TOK = None
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
@pytest.fixture(scope="module", autouse=True)
|
| 23 |
-
def loaders():
|
| 24 |
-
global RAW_TOK, HF_TOK
|
| 25 |
-
from tokenizers import Tokenizer
|
| 26 |
-
from transformers import AutoTokenizer
|
| 27 |
-
RAW_TOK = Tokenizer.from_file(str(STAGING / "tokenizer.json"))
|
| 28 |
-
# trust_remote_code=True: config.json's auto_map marks the repo as custom
|
| 29 |
-
# code; the tokenizer is stock but without this the interactive prompt
|
| 30 |
-
# fires (and pollutes captured stdout in subprocess tests).
|
| 31 |
-
HF_TOK = AutoTokenizer.from_pretrained(str(STAGING), trust_remote_code=True)
|
| 32 |
-
return
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
def test_specials_and_model_max_length():
|
| 36 |
-
tc = json.loads((STAGING / "tokenizer_config.json").read_text())
|
| 37 |
-
assert tc["pad_token"] == "<pad>" and tc["bos_token"] == "<bos>" and tc["eos_token"] == "<eos>"
|
| 38 |
-
assert tc["add_bos_token"] is False and tc["add_eos_token"] is False
|
| 39 |
-
assert tc["model_max_length"] == 512
|
| 40 |
-
assert HF_TOK.pad_token_id == 0 and HF_TOK.bos_token_id == 1 and HF_TOK.eos_token_id == 2
|
| 41 |
-
assert HF_TOK.model_max_length == 512
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
def test_encode_ids_match_raw_tokenizer():
|
| 45 |
-
for text in BATTERY:
|
| 46 |
-
assert HF_TOK.encode(text) == RAW_TOK.encode(text).ids, repr(text)
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
def test_decode_round_trips_byte_level():
|
| 50 |
-
for text in BATTERY:
|
| 51 |
-
if not text:
|
| 52 |
-
continue
|
| 53 |
-
ids = RAW_TOK.encode(text).ids
|
| 54 |
-
assert HF_TOK.decode(ids) == RAW_TOK.decode(ids), repr(text)
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
def test_no_auto_added_specials():
|
| 58 |
-
# A bare encode must not inject <bos>/<eos> (matches training/eval).
|
| 59 |
-
assert HF_TOK.encode("hi") == RAW_TOK.encode("hi").ids
|
| 60 |
-
assert HF_TOK.encode("hi", add_special_tokens=False) == HF_TOK.encode("hi")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|