pg-jev (GitHub Repo)
The pg-jev extension enables PostgreSQL to run natural language classification queries directly on tables by using an external 'System One' model.
Deep dive
- Batching: Sends rows in batches of 20 to the API, amortizing overhead and keeping memory consumption constant regardless of table size.
- Integration: Compatible with standard SQL patterns like
WHERE,GROUP BY, andORDER BY, allowing it to function as a native filtering predicate. - Operational Efficiency: Rows filtered out by cheaper standard SQL predicates (e.g., age > 40) are skipped, preventing unnecessary API calls.
- Session Caching: Results are cached in the backend session to make re-sorting or threshold adjustments near-instantaneous.
Decoder
- System One: A design pattern for AI models focused on rapid classification decisions instead of long-form generation, ideal for embedding AI directly into procedural workflows.
- PL/Python3u: A PostgreSQL procedural language that allows execution of Python code within the database; the 'u' suffix denotes it is an 'untrusted' language requiring superuser privileges.
Original article
jev — ask your Postgres tables questions in plain language
Write the condition the way you would say it. Postgres does the rest.
jev lets you filter, rank and classify rows with plain-language conditions. Every row is judged by TypeSafe's Jev, a System One model that returns calibrated probabilities instead of generated text. No index, no embeddings, no vector column.
Website: pgjev.com
CREATE EXTENSION jev CASCADE;
SELECT * FROM people WHERE jev(people, 'the name is European');
SELECT subject, jev_prob(tickets, 'the customer is angry') AS p
FROM tickets ORDER BY p DESC LIMIT 20;
SELECT jev_choice(tickets, 'which team should handle this?',
ARRAY['billing', 'technical', 'security', 'sales']) AS team, count(*)
FROM tickets GROUP BY 1;
SELECT name, jev_score(products, 'how luxurious is this product?',
ARRAY['budget', 'mid-range', 'premium', 'luxury']) AS luxury
FROM products ORDER BY luxury DESC;
jev() is an ordinary boolean function, so it composes with everything else in SQL: AND age > 40, joins, GROUP BY, LIMIT, ORDER BY jev_prob(...).
How it works
jev(table, 'condition')receives the row as a composite value. The first call for a table + condition starts a read-ahead that streams the table in physical order (TID range scans;OFFSETpages for views), so memory stays constant whatever the table size.- Rows are packed
jev.batch_size(20) per request into one shared state ({"condition": ..., "rows": [...]}) with one yes/no Noul question per row. Jev evaluates all questions over one state in parallel, which amortises the ~270-token request overhead. - Up to 2 ×
jev.concurrencyrequests are in flight over persistent HTTPS connections, and every row is answered as soon as its batch returns, so aLIMITstops the read-ahead after the in-flight window, and rows that cheaper predicates filter out beforejev()runs are skipped rather than judged. - Answers are cached per row content for the session, so re-running, changing the threshold or sorting by probability is free. Rows from a subquery or CTE (anonymous
recordtype) can't be read ahead and are judged one request at a time; putjev()on base tables or views when you can.
Why 20 rows per request
Jev has to find rows[i] by position in the array, and that gets unreliable in long arrays. Against ground truth from structured columns (job title, EU membership, a phrase in a free-text field; 400 rows each), batches of 1–20 rows were 100 % correct, batches of 40 were 92–98 % and batches of 80 were 77–94 %. Wider rows (1,000 characters) made no difference at 20.
Install
Requirements: PostgreSQL 14–17 with plpython3u, a superuser, and a TypeSafe API key from https://console.typesafe.ai.
With an AI agent (easiest)
npx skills add realZachi/pg-jev
Install pgjev on this server and set it up.
From PGXN
pip install pgxnclient
pgxn install jev
psql -c "CREATE EXTENSION jev CASCADE"
From source (PGXS)
git clone https://github.com/realZachi/pg-jev.git && cd pg-jev
make install
psql -c "CREATE EXTENSION jev CASCADE"
Docker
docker build -t pg-jev .
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=pw -e TYPESAFE_API_KEY=your-key pg-jev
psql postgres://postgres:pw@localhost/postgres -c "CREATE EXTENSION jev CASCADE"
API key
SET jev.api_key = 'your-key';
ALTER ROLE analyst SET jev.api_key = 'your-key';
Functions
| Function | Returns | Purpose |
|---|---|---|
jev(row, condition [, threshold]) |
boolean | WHERE predicate. |
jev_prob(row, condition) |
float8 | Probability 0..1 that the row satisfies the condition |
jev_score(row, question, levels text[]) |
float8 | Probability-weighted position on ordered levels |
jev_choice(row, question, options text[]) |
text | The most likely option for the row |
Settings
| Setting | Default | Meaning |
|---|---|---|
jev.api_key |
env TYPESAFE_API_KEY |
TypeSafe API key |
jev.threshold |
0.5 |
Probability at which jev() returns true |
jev.batch_size |
20 |
Rows per API request. |
Writing good conditions
- State the exact condition:
'the customer threatens to leave, dispute a charge, or take legal action'beats'churn risk'. - Keep arithmetic, dates and exact matches in SQL; let the model judge meaning.
- Look at the distribution with
jev_prob()before picking a threshold. - Send only the columns the judgment needs: create a view with the relevant columns and call
jev(view_alias, ...)on the view.
Caveats
- This is a full scan by design: every row the executor asks about goes to the API.
- Row contents are sent to a third-party API. Do not use it on data you may not share.
- The cache lives in the backend session.
plpython3uis an untrusted language: only superusers can create the extension.
License
PostgreSQL License.
Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi