Back to Devoured - September 21, 2026
DEVOURED
pg-jev (GitHub Repo)

pg-jev (GitHub Repo)

Data GitHub
The pg-jev extension enables PostgreSQL to run natural language classification queries directly on tables by using an external 'System One' model.
What: pg-jev adds custom SQL functions that allow users to filter, rank, and classify database rows using plain English. It batches rows into small groups to minimize API costs and latency while returning calibrated probabilities for conditions rather than generating text.
Why it matters: This integrates AI-driven logic into the database layer, allowing developers to perform semantic queries without the heavy overhead of maintaining vector embeddings or index updates.
Takeaway: Use `jev_prob()` to analyze data distributions before setting a hard threshold for filtering with the `jev()` boolean function.
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, and ORDER 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

  1. 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; OFFSET pages for views), so memory stays constant whatever the table size.
  2. 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.
  3. Up to 2 × jev.concurrency requests are in flight over persistent HTTPS connections, and every row is answered as soon as its batch returns, so a LIMIT stops the read-ahead after the in-flight window, and rows that cheaper predicates filter out before jev() runs are skipped rather than judged.
  4. 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 record type) can't be read ahead and are judged one request at a time; put jev() 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.
  • plpython3u is an untrusted language: only superusers can create the extension.

License

PostgreSQL License.

Explore all 67 articles in today's digest

Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi