sqlite-jev
Natural-language predicates, classification, and scoring for SQLite, powered by
TypeSafe Jev. It is inspired by
pg-jev, but shaped around SQLite's loadable-extension
and virtual-table APIs.
.load ./build/jev
SELECT t.id, t.subject, round(j.probability, 3) AS urgency
FROM jev_rows(
'tickets',
'The customer explicitly expresses urgency or says work is blocked',
'noul',
NULL,
json_array('subject', 'message')
) AS j
JOIN tickets AS t ON t.rowid = j.source_rowid
WHERE j.probability >= 0.6;
jev_rows reads the selected columns, puts up to 40 rows in one shared state, asks one
question per row, and returns a virtual table that can be joined to the source by rowid.
Results are cached for the lifetime of the SQLite connection.
Build
Requirements:
- SQLite 3.45 or newer, built with JSON support and loadable extensions
- A C11 compiler and
make - The
libcurlruntime (libcurl.so.4on Linux orlibcurl.4.dylibon macOS) - A TypeSafe API key
The current development machine already has all of these; no extra system package is needed.
make
export TYPESAFE_API_KEY=...
sqlite3 my.db
Inside SQLite:
.load ./build/jev
SELECT jev_version();
TYPESAFE_API_KEY is the only environment variable used for the API key.
Batched table queries
The signature is:
jev_rows(table_name, question [, kind [, criteria [, columns]]])
It returns source_rowid, answer, probability, choice, score, and confidence.
Columns that do not apply to the chosen primitive are NULL.
Boolean judgment (Noul)
SELECT
t.id,
j.probability AS yes_probability,
1.0 - j.probability AS no_probability,
CASE WHEN j.probability >= 0.7 THEN 'yes' ELSE 'no' END AS answer
FROM jev_rows(
'tickets',
'The ticket explicitly asks for a refund',
'noul',
NULL,
json_array('subject', 'message')
) AS j
JOIN tickets AS t ON t.rowid = j.source_rowid
ORDER BY t.id;
A Noul returns the probability of yes; no is its complement. Choosing the larger one is
equivalent to a 0.5 threshold. Keep the threshold in SQL so it can rise with the cost of a
false positive. The scalar jev(state, condition) uses 0.5 unless given a third argument.
Classification (Choice)
SELECT t.id, j.choice AS team, j.confidence
FROM jev_rows(
'tickets',
'Which team should handle the main request?',
'choice',
json_object(
'billing', 'Charges, invoices, payment methods, or refunds',
'technical', 'Bugs, failures, or integrations',
'sales', 'Pricing, plans, demos, or new purchases',
'other', 'Anything outside those teams'
),
json_array('subject', 'message')
) AS j
JOIN tickets AS t ON t.rowid = j.source_rowid;
Ordered rating (Score)
SELECT t.id, j.score, j.confidence
FROM jev_rows(
'tickets',
'How frustrated is the customer?',
'score',
json_array('Calm and factual', 'Frustrated but civil', 'Very angry or abusive'),
json_array('message')
) AS j
JOIN tickets AS t ON t.rowid = j.source_rowid;
For accuracy and cost, include only the columns the judgment needs. jev_rows requires a
rowid table. To prefilter a large data set, materialize the filtered rows into a temporary
table and evaluate that table.
Scalar functions
Scalar functions are convenient for one record. When scanning a table, prefer jev_rows so
Jev can evaluate many questions in a single request.
| Function | Result |
|---|---|
jev(state, condition [, threshold]) |
Boolean Noul predicate; default threshold is 0.5 |
jev_prob(state, condition) |
Noul probability from 0 to 1 |
jev_choice(state, question, criteria_json) |
Most likely Choice key |
jev_score(state, question, levels_json) |
Probability-weighted Score level |
jev_score_norm(state, question, levels_json) |
Score normalized to 0 through 1 |
jev_confidence(state, question, kind, criteria_json) |
Choice or Score confidence |
jev_eval(state, question [, kind [, criteria_json]]) |
Full answer JSON |
jev_stats() |
Connection-local usage and cache statistics |
jev_cache_clear() |
Clears the connection-local answer cache |
jev_version() |
Extension version |
Pass structured state with SQLite JSON functions:
SELECT jev_prob(
json_object('subject', subject, 'message', message),
'The customer explicitly expresses urgency'
)
FROM tickets
WHERE id = 42;
Configuration
Configuration is connection-local:
SELECT jev_config('model', 'jev-latest');
SELECT jev_config('batch_size', 40);
SELECT jev_config('max_rows', 500);
SELECT jev_config('timeout', 90);
SELECT jev_config('api_url', 'https://api.typesafe.ai/v1/systemone');
SELECT jev_config('api_key', '...');
max_rows is a spend guard. A jev_rows scan above the limit fails before sending any data.
The API key is never returned by jev_config; it reports only set or unset.
Python
Add the platform package to a project and load it with the Python wrapper:
uv add sqlite-jev
import sqlite3
import sqlite_jev
connection = sqlite_jev.load(sqlite3.connect(":memory:"))
The package contains the same native extension as the standalone release, has no runtime
Python dependencies, and supports maintained Python versions starting with Python 3.10. The
interpreter's sqlite3 module must have loadable-extension support enabled.
To load a standalone build manually instead:
import sqlite3
connection = sqlite3.connect(":memory:")
connection.enable_load_extension(True)
connection.load_extension("/path/to/jev.so") # use jev.dylib on macOS
connection.enable_load_extension(False)
version = connection.execute("select jev_version()").fetchone()[0]
Demo and tests
The deterministic suite uses a local mock server and never calls TypeSafe:
make test
The ticket-triage demo makes two live API requests, one for urgency and one for routing:
make live-test
See examples/ticket_triage.sql for the complete query.
make integration-test runs a smaller live smoke test. In GitHub Actions it runs once every
two months and on manual dispatch using the TYPESAFE_API_KEY repository secret. It checks the API contract,
batching, and answer shapes; semantic expectations remain in the deterministic mock suite so
normal model variation cannot make pull requests flaky.
Releases
Tags named vX.Y.Z build and publish four archives through GitHub Actions:
- Linux x86_64 and arm64
- macOS Intel and Apple Silicon
Each archive contains the native extension, this README, and its pyproject.toml. The release
also includes a py3-none-<platform> Python wheel and SHA256SUMS. CI installs each wheel
with cibuildwheel and verifies that sqlite_jev.load() enables the SQL API. The same tag
publishes the wheels and source distribution to PyPI through trusted publishing.
Important limits
- Row contents are sent to TypeSafe. Do not evaluate data you are not allowed to share.
- This is a semantic full scan, not an index. Apply deterministic SQLite filters first and materialize a small candidate table.
- Cache entries live only for the current database connection and are keyed by row content, model, question, primitive, and criteria.
- Jev should make narrow judgments. Keep counting, arithmetic, and date comparison in SQL.
- Text stored in a row can steer model behavior. Test adversarial content and use conservative probability or confidence thresholds before automating consequential actions.
libcurlis loaded dynamically so building does not require the curl development headers. Linux and macOS are the currently tested platforms.
Release files for sqlite-jev 0.1.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 | |
|---|---|---|---|
| sqlite_jev-0.1.0.tar.gz | 24.4 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| sqlite_jev-0.1.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl | Python 3 | none | Linux glibc 2.28+ x86-64, Linux glibc 2.17+ x86-64 | Details |
| sqlite_jev-0.1.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl | Python 3 | none | Linux glibc 2.28+ ARM64, Linux glibc 2.17+ ARM64 | Details |
| sqlite_jev-0.1.0-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
| sqlite_jev-0.1.0-py3-none-macosx_10_9_x86_64.whl | Python 3 | none | macOS 10.9+ x86-64 | Details |
Total release size: 111.4 kB
Release files / sqlite_jev-0.1.0.tar.gz
| Download URL | sqlite_jev-0.1.0.tar.gz |
|---|---|
| Size | 24.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc505afe094f49e882e39f8d4e46b8c69235bbe7cddce84293629e5d0de51fab
|
|
BLAKE2b-256 checksum How to use checksums |
4bb1dc2c46ee4f30195eb88388388a7ff7a7402f35dd6322d65cf120f9bf9b75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","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 / sqlite_jev-0.1.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
| Download URL | sqlite_jev-0.1.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 22.6 kB |
| Tags | Linux glibc 2.17+ x86-64 Linux glibc 2.28+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
069ed9ed25d950f6d8881985dfb560d89cb923147bd4613e387baa1c8fbdc932
|
|
BLAKE2b-256 checksum How to use checksums |
6892bc098bc3c909f666784efb47d73b98c168981f6b35cae06aea694e2f6906
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","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 / sqlite_jev-0.1.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
| Download URL | sqlite_jev-0.1.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl |
|---|---|
| Size | 23.2 kB |
| Tags | Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
0e7ed6c971f5bbd0eab6d62c1c6457baf53d06d99f1158b8470efaff70048e31
|
|
BLAKE2b-256 checksum How to use checksums |
96ff7f0cdbb2b32eece3f8853a4a26c15a8405413dc241e3cb9c355932cfb917
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","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 / sqlite_jev-0.1.0-py3-none-macosx_11_0_arm64.whl
| Download URL | sqlite_jev-0.1.0-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 20.6 kB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
12a93b87e908e2b5488b19c9bcc705f071b7a45a2e8521225b50b42290cecdff
|
|
BLAKE2b-256 checksum How to use checksums |
2d706fe3047ddaea9785eb748707131e854784bc5de9068b3e037f357051b3f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","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 / sqlite_jev-0.1.0-py3-none-macosx_10_9_x86_64.whl
| Download URL | sqlite_jev-0.1.0-py3-none-macosx_10_9_x86_64.whl |
|---|---|
| Size | 20.7 kB |
| Tags | Python 3 macOS 10.9+ x86-64 |
|
SHA-256 checksum How to use checksums |
7eafde96a69fb04d1caca95c3254df26639d237bc4d533a62803c77797c99c23
|
|
BLAKE2b-256 checksum How to use checksums |
801ad142281f2a73a72dbd57b3e9df271fb6e9ec9a2f10630e500c8a0afc2e3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","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}
|