pg-jev
★ 1.2k▲ 24realZachi/pg-jev
PostgreSQL extension to filter, rank, and classify rows with plain-language conditions.
A .jev accessor for pandas and Polars: the request is built from the columns you name and nothing else in the row, and a failed row raises naming the row instead of becoming a null.
Hands-on review
A .jev accessor for pandas and Polars. We put canaries in the columns we did not name, and none of them left the machine.
Good for
Watch out for
Tested Sep 22, 2026 at 16bd3eae69b7 · python:3.12-slim in Docker, its 108 tests and full CI, then the accessor driven against a stand-in that logged every request
How we reviewed this: we ran its full CI, then drove the accessor against a stand-in that logged every request, with canary strings planted in the columns we did not pass. We made no Jev calls.
df["dissatisfied"] = await df.jev.noul(
"Is this customer dissatisfied?",
state=["title", "review"],
)
An accessor for pandas and Polars that asks a typed question about each row and gives you back ordinary Series and DataFrames. Built on the official typesafe-sdk.
This is the design decision worth the review. state= is a list of column names, so the request is built from those columns and not from the row. We gave it a frame with four columns, named two, and put distinct strings in the other two:
state on the wire: {"title": "Charged twice", "review": "Please refund the duplicate charge today."}
SSN_CANARY on the wire: 0
ID_CANARY on the wire: 0
Charged twice on the wire: 1
duplicate charge on the wire: 1
Nothing from secret_notes or internal_id left the machine. That sounds obvious until you consider the alternative — a library that serialises the whole row because it is easier — which is what we have found elsewhere. Here the columns you send are the columns you typed.
The pandas index survived too: a frame indexed [10, 20, 30] came back as a Series indexed [10, 20, 30].
res = await df.jev.evaluate(
state=["title", "review"],
questions={
"urgent": Noul(instructions="Does the customer need help today?"),
"topic": Choice(instructions="What is the main issue?",
criteria={"billing": "Charges and refunds", "bug": "Broken behavior", "other": None}),
},
)
Three rows, two questions each, produced three requests of two questions. Questions about a row share its request; rows do not share with each other.
That is the right shape for a dataframe accessor and the wrong shape for bulk work, which is worth saying plainly: a million rows is a million requests. If that is your job, jev-ultralightspeed exists for exactly it. jevframe is for the notebook, where you have a few thousand rows and want them back as columns.
And you do get them as columns — the whole distribution, not just the winner:
['urgent__probability', 'topic__label', 'topic__confidence',
'topic__p__billing', 'topic__p__bug', 'topic__p__other']
No silent nulls, and it tells you which row:
| what the provider did | what happened |
|---|---|
| HTTP 500 | EvaluationError: Row 2: inference failed (TypeSafeInternalServerError) |
a 200 with an empty answers |
EvaluationError: Row 0: inference failed (ValueError) |
| no API key | No API key was provided. Pass api_key or set the TYPESAFE_API_KEY environment variable. |
For a library whose output is a column you are about to filter on, a NaN that means “the API was down” and a NaN that means “the model was unsure” would be indistinguishable and dangerous. This one raises, and names the row.
MIT, Python 3.10+, pandas and Polars both optional extras — importing an integration registers its accessor. 108 tests pass, ruff check and ruff format --check are clean across 22 files, marimo check --strict passes on the example notebook, and uv build works. That is its whole CI and it ran green on the first try.
Our first attempt at the multi-question API failed because we passed the instruction positionally; the constructors are keyword-only, and the README’s examples are correct. Worth a note only because it is the kind of thing a reader will hit too.
Thirteen stars, one author, version 0.1.0, four days old when we tested it.
A small, well-made thing that does one job and does not overreach. The column-selection design means the privacy question has a precise answer — the columns you name, and nothing else — and we checked it rather than assuming it.
Use it in a notebook on the scale a notebook implies. Do not reach for it to label a million rows; that is a different tool, and the author has not claimed otherwise.
For bulk work over the same kind of data, see jev-ultralightspeed; for checking that the question you are about to run over a column actually works, jev-calibrate.
See how it compares with other tools in Best Jev tools, tested hands-on.
Review updated Sep 22, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.
realZachi/pg-jev
PostgreSQL extension to filter, rank, and classify rows with plain-language conditions.
jerryjliu/docjev
Classifies and splits PDF, DOCX and PPTX with Jev and local OCR, asking one typed question per page and per boundary in a single request. Ships the manifest, per-call records and error analysis behind its benchmark.
kyotofin/tax-doc-classifier
Classifies tax-document pages into IRS forms and page kinds with one Jev request per page, driven by a JSON file of form descriptions.
New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.
Powered by Buttondown. See our privacy policy.