Same model. Same input. Find the first divergence.
Project description
Eleanity
Same model. Same input. Find the first causal divergence.
CLI-first tool for LLM runtime parity. It compares inference stacks on the same scenario and reports:
- status (
PASS,PASS_WITH_TOLERANCE,PASS_WITH_LIMITED_COVERAGE,INCONCLUSIVE,DIVERGENT,ERROR) - first divergent layer (when present)
- coverage of required layers and diagnosis confidence
- functional impact (separate from internal parity)
- a reproduction command
It does not score model quality (no MMLU). It checks whether two runtimes still share the same causal path: artifact → template → special tokens → token IDs → generation / API.
Product surfaces: CLI (text / json / quiet / sarif) and a Python API for embedding without subprocess — see docs/api.md.
Try the core idea offline
uvx eleanity demo
status: DIVERGENT
first_divergence: template
baseline_template: 'user: Hello\nassistant:'
candidate_template:'user: Hello'
probable_cause: CHAT_TEMPLATE_DIFFERENT (confidence=0.92)
No model download, GPU, server, account, or network endpoint is required.
Hosting note: the repository currently lives at
TheusHen/eleanity. Transfer toeleanity/eleanityis planned when the org is available (ROADMAP.md).
Install (clean machine)
From Git (available now)
# one-shot CLI without a permanent install
uvx --from git+https://github.com/TheusHen/eleanity.git eleanity --help
# or install into an environment
pip install "git+https://github.com/TheusHen/eleanity.git"
# optional HF backend (PEP 508 direct reference + extras)
pip install "eleanity[transformers] @ git+https://github.com/TheusHen/eleanity.git"
uv add git+https://github.com/TheusHen/eleanity.git
# or with transformers extra:
uv add "eleanity[transformers] @ git+https://github.com/TheusHen/eleanity.git"
From a local checkout
git clone https://github.com/TheusHen/eleanity.git
cd eleanity
uv sync --group dev
uv run eleanity --help
PyPI
pip install eleanity
# pin the stable API line:
pip install "eleanity==1.0.0"
When a reviewed version change reaches main, release.yml
runs the complete quality suite, publishes that exact version to PyPI, then
creates an immutable vX.Y.Z tag and GitHub Release with the wheel, sdist, and
SHA256 checksums. The workflow is idempotent and uses the built-in GitHub token;
it never writes version commits directly to the protected branch.
# Required repository secret (once):
gh secret set PYPI_API_TOKEN -R TheusHen/eleanity
The job verifies wheel/sdist metadata and writes SHA256SUMS.
Requires Python 3.11+.
Offline parity check
uv run eleanity compare --model demo --backends fake,fake \
--format quiet --no-parallel --no-gates
Exact output from a live run:
status=PASS impact=NONE coverage=100.0 confidence=0.85 first_divergence=none gates=True run_id=16c3f05b-70f9-4c6a-9170-3e5942f91bd6
Real model self-parity (PASS)
Model: HuggingFaceTB/SmolLM2-135M-Instruct (CPU).
Commands
uv sync --group dev --extra transformers
uv run eleanity pull HuggingFaceTB/SmolLM2-135M-Instruct --tokenizer-only
uv run eleanity compare \
--model HuggingFaceTB/SmolLM2-135M-Instruct \
--backends transformers,transformers \
--format quiet --no-parallel --no-gates \
--observe artifact,template,special_tokens,tokens,generation
Exact quiet output (live run)
status=PASS impact=NONE coverage=100.0 confidence=0.85 first_divergence=none gates=True run_id=0aba046a-5846-45b2-94d9-4584bb4fe98a
| Metric | Value |
|---|---|
| Engine total | ~10276 ms |
| First observation | ~9259 ms |
| Second (warm) | ~1017 ms |
| Token match | 31 / 31 |
| Required coverage | 100% (min 75%) |
uv run eleanity report 0aba046a-5846-45b2-94d9-4584bb4fe98a --format text
Layer table excerpt:
Layer Baseline obs Candidate obs Compare
artifact OBSERVED OBSERVED PASS
special_tokens OBSERVED OBSERVED PASS
template OBSERVED OBSERVED PASS
tokens OBSERVED OBSERVED PASS
generation OBSERVED OBSERVED PASS
Template diff: PASS
Token diff: PASS (count: 31)
This validates the engine + Transformers path.
Cross-runtime DIVERGENT (Transformers × real HF OpenAI-compat HTTP)
Both sides run real Hugging Face weights (SmolLM2-135M-Instruct):
| Side | Runtime path |
|---|---|
| Baseline | transformers adapter (in-process) |
| Candidate | vllm adapter → local OpenAI-compat server that loads the same HF model via Transformers |
The HTTP server does not expose template/tokenize endpoints (same honesty profile as many LM Studio / gateway setups). By default it applies the chat template without the assistant generation prompt — a common production bug — so generation diverges for a causal reason, not a hard-coded string.
Commands (exact)
uv sync --group dev --extra transformers
# starts real HF server, runs doctor + compare, stops server
uv run python scripts/examples/run_cross_runtime_demo.py
Under the hood:
# terminal A
uv run python scripts/examples/hf_openai_server.py \
--port 8765 --preload --omit-generation-prompt \
--model HuggingFaceTB/SmolLM2-135M-Instruct
# terminal B
export ELEANITY_VLLM_URL=http://127.0.0.1:8765
uv run eleanity doctor --check-backends --backends vllm --format json
uv run eleanity compare \
--model HuggingFaceTB/SmolLM2-135M-Instruct \
--backends transformers,vllm \
--backend-url vllm=http://127.0.0.1:8765 \
--policy quantized \
--format quiet --no-parallel --no-gates \
--observe artifact,template,tokens,generation,api
Parity attempt (server uses full AGP): ELEANITY_DEMO_MATCH=1 uv run python scripts/examples/run_cross_runtime_demo.py
Point the same commands at LM Studio / vLLM serve by changing --backend-url. Offline fixed-string stub (CI without GPU weights): scripts/examples/mock_openai_diverge.py.
Exact quiet output (live run)
status=DIVERGENT impact=HIGH coverage=50.0 confidence=0.762 first_divergence=generation gates=False run_id=90028893-8848-463f-9331-daf5268f60b5
| Field | Value |
|---|---|
| Baseline | transformers · HuggingFaceTB/SmolLM2-135M-Instruct (HF weights, in-process) |
| Candidate | vllm adapter → hf_openai_server.py (same HF weights over HTTP, --omit-generation-prompt) |
| Policy | quantized |
| Status | DIVERGENT |
| First divergence | generation |
| Coverage | 50% required layers (template/tokens not mutually observed on HTTP) |
| Verified | artifact, generation |
| Not verified | template (NOT_SUPPORTED on HTTP), tokens (NOT_EXPOSED on HTTP) |
| Generation texts | transformers: Hello! How can I help you today? · HTTP: assistant\nHello! How can I help you today? |
| Engine total | ~8287 ms |
Reproduction command stored on the run:
eleanity compare --model HuggingFaceTB/SmolLM2-135M-Instruct --backends transformers,vllm \
--baseline transformers --policy quantized \
--observe artifact,template,tokens,generation,api --no-gates \
--backend-url vllm=http://127.0.0.1:8765 --format text
This is the cross-stack claim: two real runtimes, same model weights, localized first divergence, honest missing layers.
Template first-divergence (character-level)
Classic chat-template bug (missing assistant generation prompt), fully localized:
uv run python scripts/examples/demo_template_divergence.py
Exact output (live run):
status: DIVERGENT
first_divergence: template
character: 11
byte: 11
baseline template: 'user: Hello\nassistant:'
candidate template:'user: Hello'
probable_cause: [CHAT_TEMPLATE_DIFFERENT] conf=0.92
More write-ups: docs/examples/first-divergence.md.
Compared to common alternatives
| Approach | Result type | Causal first layer | CI exit contract | Missing-data honesty |
|---|---|---|---|---|
| Manual print / eyeball | vibes | no | no | no |
| String golden tests | brittle text diff | rare | sometimes | often silent |
| lm-eval / quality benches | capability scores | no | yes | n/a |
| API latency dashboards | ops metrics | no | maybe | n/a |
| Eleanity | parity + location | yes | 0/1/2 | yes (coverage) |
Adapter capability matrix (summary)
Full table: docs/adapter-capabilities.md.
vllm and llamacpp are HTTP-client adapters. Run the corresponding server
runtime in its own restricted environment; Eleanity does not install either
server runtime as an extra.
| Adapter | template | tokens | logits | generation | streaming | API |
|---|---|---|---|---|---|---|
| fake | yes | yes | no | yes | yes | partial |
| transformers | yes | yes | partial | yes | no | no |
| vllm HTTP | partial | partial | no | yes | partial | yes |
| llamacpp HTTP | partial | partial | no | yes | partial | yes |
| ollama | no | no | no | yes | partial | yes |
| sglang / tgi / openai | partial/no | partial/no | no | yes | partial | yes |
Everyday CLI
eleanity doctor --check-backends
eleanity compare --backends transformers,vllm --format text
eleanity test fixtures/public/tokenizer-edge.yaml --backends fake,fake --format quiet
eleanity report <run-id> --format text
eleanity replay <run-id>
eleanity stabilize --backend fake --repetitions 3 --format quiet
eleanity policy-spec --policy quantized
Exit codes: 0 pass family · 1 divergent / gate fail · 2 config / dependency error.
Full reference: docs/cli.md
Python API (no subprocess)
from eleanity import Eleanity
client = Eleanity() # or Eleanity.from_yaml("eleanity.yaml")
result = client.compare(model="demo", backends=["fake", "fake"], no_gates=True)
print(result.status, result.first_divergence, result.coverage)
raise SystemExit(result.exit_code)
Low-level observe/compare: from eleanity.api import observe_backend, compare_traces, make_scenario.
Full reference: docs/api.md.
CI
| Workflow | Role |
|---|---|
ci.yml |
Required quality: actionlint + ruff + mypy + unit/contract/integration + CLI smoke |
ci.yml typecheck job |
Required mypy gate |
parity-local-ai.yml |
Downloads SmolLM2-135M and runs real Transformers self-parity |
parity-public-fixtures.yml |
Public fixture suites |
release.yml |
Idempotent PyPI publish, immutable tag, and GitHub Release for reviewed versions |
eleanity.yml |
Reusable monorepo gate |
Local:
uv run ruff check src tests
uv run ruff format --check src tests
uv run pytest -q
Project docs
| Doc | Purpose |
|---|---|
| docs/cli.md | CLI reference |
| docs/api.md | Python client + low-level API |
| docs/parity-specification.md | Status + comparator tables |
| docs/adapter-capabilities.md | Honesty matrix |
| docs/trace-specification.md | Trace Spec v1 |
| ROADMAP.md | Stable roadmap and known boundaries |
| SUPPORT.md | Where to get help |
| SECURITY.md | Vulnerability reporting |
License
Apache License 2.0 — full text in LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file eleanity-1.2.0.tar.gz.
File metadata
- Download URL: eleanity-1.2.0.tar.gz
- Upload date:
- Size: 352.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b918c586bd084a6b88601e1ffd76d2ac0663911eead6d1d7496294461d96f1c4
|
|
| MD5 |
90ae06384463609d88fd03684cb20eaa
|
|
| BLAKE2b-256 |
7d6a93b11bbf7d098e7cc3b459ac8ea24f47593329cc0fe61541fe2e986c645c
|
File details
Details for the file eleanity-1.2.0-py3-none-any.whl.
File metadata
- Download URL: eleanity-1.2.0-py3-none-any.whl
- Upload date:
- Size: 195.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e46a4ec8d2c76b7647b87d5528a4454543c7734328fa9347d5cbf02236d87328
|
|
| MD5 |
675508684e4a61a49bc03743dde566d4
|
|
| BLAKE2b-256 |
bdb26af3815a52e05037a65d6fcf58c8a6501e2aa9044bf6c99ac98077fd0614
|