JevKit core
Distribution jevkit-runtime, import jevkit_runtime. The PyPI name jevkit-core belongs
to a different project.
One request pipeline, one answer store, one provider catalog for jgrep, jsort, jlink, jselect, and jcol. Each tool remains its own package and repository; the core imports none of them, and a tool's adapter is a few lines naming which providers it offers.
What a tool gets
from jevkit_runtime import AnswerStore, Client, catalog, resolve
PROVIDERS = catalog("typesafe", "openrouter", "gateway")
backend = resolve(PROVIDERS, name=None, model=None) # or JEV_API / JEV_MODEL, else the first configured
async with Client(backend, store=AnswerStore()) as client:
answers = await client.ask(state, {"q": {"type": "noul", "instructions": "..."}})
Client.ask does the whole thing: computes each question's identity, serves what the store already
knows, joins an identical request already in flight, sends only the misses, validates the entire
response before storing any of it, and meters the call before validation so a billed but malformed
answer still counts. It returns Answers, a dict by question id whose origins say who answered
each one and whether it came from the API, the store, or a shared call. Per-call policy is keyword
arguments: allow_paid=False for cache-only runs, on_cost for the caller who should be charged,
hedge_after to resend a slow call, and keys for callers whose reuse unit is not the request.
HTTP/2 is used whenever the http2 extra is installed.
| Module | Owns |
|---|---|
settings.py |
Every environment and filesystem convention, read in one place: XDG_*, JEV_API, JEV_URL, JEV_MODEL, JEV_PRICE_PER_MTOK, provider keys and URL files |
providers.py |
The catalog (Provider), a tool's selection of it or its own entries, and resolve() to one Backend: endpoint, model, key |
protocol.py |
Request bodies, typed answer validation (noul, choice, score), usage parsing, answer identity, provenance |
transport.py |
One HTTP call with a total deadline, retries with backoff and Retry-After, structured status errors |
store.py |
SQLite answers with their provenance in one row, one versioned schema |
client.py |
The pipeline above, request sharing, hedging |
meter.py |
Calls, cache hits, retries, hedges, tokens, cost, and which models actually answered |
errors.py |
JevError, JevFatal, JevBudgetExceeded, RequestExhausted, ProviderError, ProviderFatal |
Conventions every tool shares
- Answer identity is
answer_key(backend, state, question): provider, endpoint, model, state and question. An answer from one provider or model is never served for another. - The store lives at
$XDG_CACHE_HOME/jev/answers.sqlite(default~/.cache/jev), is created private to the user, and resets itself when it finds an older schema. Version 0.2 cannot read caches written by 0.1 tools; the first run after upgrading re-asks. - Credentials come from the provider's variable, then
$XDG_CONFIG_HOME/jev/<provider>.key. Gateways take their URL fromJEV_GATEWAY_URLor<provider>.url.JEV_URLoverrides any endpoint. - Metering refuses malformed usage rather than under-counting; a response without a reported
cost is priced from its tokens at the provider's price, zero for local servers, or the list price.
JEV_PRICE_PER_MTOKoverrides both. - Errors keep their wording across tools: a fatal status reads
PROVIDER said 401: detail, a bad request readsHTTP 400: detail, and exhaustion readsgave up after 15s (last failure). Both status errors carryprovider,statusanddetailfor tools that word or redact them.
Local servers
Two catalog entries point at System One servers on your own machine: diffusiongemma, an
OpenJev server on port 8080, and laya, a
laya-mlx server on port 8081. Every JevKit tool names
them in its catalog, so --api laya or JEV_API=laya works everywhere. They are never chosen
in place of a configured hosted provider, need no key, and are metered at zero API fees unless
JEV_PRICE_PER_MTOK says otherwise. JEV_LAYA_URL and JEV_DIFFUSIONGEMMA_URL, or the matching
.url files, point at a server elsewhere.
DiffusionGemma reads every question in a batch together, so the runtime keys each of its answers on the whole ordered batch and re-sends a batch whole when any slot is missing.
No package ships the models. docs/diffusiongemma.md and
docs/laya.md explain how to run the servers, and scripts/laya_server.py is the
adapter the Laya guide starts.
Development
Keep the six checkouts as siblings. Each consumer depends on jevkit-runtime>=0.2.0,<0.3.0 and
overrides it for development with jevkit-runtime = { path = "../jevkit-core", editable = true }
under [tool.uv.sources].
python3 scripts/dev.py setup # uv sync every checkout, fetch jselect's tokenizer data
python3 scripts/dev.py check # core and consumer suites, credentials stripped, sockets blocked
python3 scripts/dev.py wheel-check # build and exercise real wheel installs in temporary environments
python3 scripts/dev.py run jgrep -- --help
check also proves each consumer imports this exact source tree; wheel-check proves the installed
wheel, not the checkout. --tool NAME limits either to one consumer and --suffix selects
alternatively named checkouts. The GitHub workflows run the core suite on Python 3.10 and 3.13 and
the downstream matrix against each consumer's main branch.
Releasing
Tag the verified core vX.Y.Z and dispatch the publish workflow with that tag; the workflow checks
the tag matches the package version and publishes through PyPI Trusted Publishing. Then release each
consumer through its own process, bumping its supported core range, lockfile, and CI core reference
together.
Release files for jevkit-runtime 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jevkit_runtime-0.3.0.tar.gz | 42.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jevkit_runtime-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.5 kB
Release files / jevkit_runtime-0.3.0.tar.gz
| Download URL | jevkit_runtime-0.3.0.tar.gz |
|---|---|
| Size | 42.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8658d1250d03e0f064032638f9bb4f9bf931b998ff345481e9758d6cda4379c0
|
|
BLAKE2b-256 checksum How to use checksums |
5a5f9a114c7e860c7d61a9804d8afd83905510341eb16cb34c37447be9b83e86
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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}
|
Release files / jevkit_runtime-0.3.0-py3-none-any.whl
| Download URL | jevkit_runtime-0.3.0-py3-none-any.whl |
|---|---|
| Size | 19.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
38d571dd08c9d4ff899530fd83eccead47adb7d11315d968a2276088ade792a6
|
|
BLAKE2b-256 checksum How to use checksums |
c45d9f279f9165576755ad99a8c7714ec2904730f70316f1403b214fd574c4cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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}
|