StackMap
Subscribe
Explore / pg-jev
realZachi

pg-jev

Postgres extension for plain-language WHERE clauses: jev(row,'the customer is angry') filters, ranks and classifies rows via TypeSafe's Jev decision model. No embeddings, no vector column.

1,264 105 Shell NOASSERTIONupdated 1 months ago
View on GitHubDispute this mapping →
Curator's take

Use it when the condition lives in free text and a person would have to read each row — triage tickets, tag products, rank by vibe — and you want the answer inside ordinary SQL next to joins, GROUP BY and LIMIT. Put cheap predicates first so fewer rows get judged, and lean on the per-session cache when iterating. NOT for managed Postgres (Supabase, Neon, RDS lack superuser/plpython3u), not for semantic search over millions of rows, and every new condition sends row contents to TypeSafe's API, priced per token.

Mapped by ShipWithAI editors · links verified

Continue your stack

What teams reach for next — and why each earns a place beside pg-jev. Ranked by curator confidence.

pairs wellpairs wellalternativetypesafe-computer-usepgGraphfenicpg-jev
pairs wellalternativebuilt withpick a node for the why · open it from the panel
Weekly digest
README.md2 min read

pg-jev — ask your Postgres tables questions in plain language

realZachi%2Fpg-jev | Trendshift

jev — ask your Postgres tables questions in plain language

CI PGXN License Website

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). jev.max_prefetch_rows limits how far it searches for a requested row and how many skipped rows it retains; it does not limit the answer cache or total session memory.
  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 (about 435 tokens for one row alone vs 175 per row in batches of 20).
  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 (WHERE age > 60 AND jev(...)) are skipped rather than judged.
  4. Answers are cached per row content and question for the backend session, so re-running, changing the threshold or sorting by probability is free while those answers remain cached. The cache can grow with unique row/question pairs until jev_cache_clear() clears it or the backend session ends. 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.

Measured on a 2,000-row table from Europe (~190 ms to the API): first run ≈ 3.5 s in 100 requests, ≈ 296k input tokens, ≈ $0.012; second run ≈ 50 ms; LIMIT 3 on a new condition ≈ 0.6 s. A new condition in a session that still holds its pooled connections (idle for less than jev.keepalive) takes ≈ 2.3 s: the fir