Ask your tables questions in plain language — filter, rank and classify rows with a condition you write as a sentence
Installing and Loading
INSTALL jev FROM community;
LOAD jev;
Example
-- Judging rows needs a TypeSafe API key (https://console.typesafe.ai)
SET jev_api_key = '...';
CREATE TABLE tickets AS SELECT * FROM (VALUES
('card declined again, I want my money back'),
('how do I export a dashboard to PDF?')) t(body);
-- A boolean, so it composes with the rest of SQL
SELECT * FROM tickets WHERE jev(tickets, 'the customer wants a refund');
-- The probability behind it, for ranking and for picking a threshold
SELECT body, jev_prob(tickets, 'the customer is angry') AS p
FROM tickets ORDER BY p DESC;
-- One of n, per row
SELECT jev_choice(tickets, 'which team should handle this?',
['billing', 'technical', 'sales']) AS team, count(*)
FROM tickets GROUP BY 1;
About jev
jev lets you filter, rank and classify rows with plain-language conditions. Every row is judged by
TypeSafe's Jev, a model that returns calibrated probabilities instead of
generated text, so there is no index, no embeddings and no vector column.
| Function | Returns | Purpose |
|---|---|---|
jev(row, condition [, threshold]) |
BOOLEAN |
WHERE predicate. Threshold: argument → jev_threshold → 0.5 |
jev_prob(row, condition) |
DOUBLE |
Probability 0..1 that the row satisfies the condition |
jev_score(row, question, levels) |
DOUBLE |
Probability-weighted position on ordered levels (0 .. n-1) |
jev_score_norm(row, question, levels) |
DOUBLE |
The same, normalised to 0..1 |
jev_choice(row, question, options) |
VARCHAR |
The most likely option, returned verbatim |
jev_confidence(row, question, kind, options) |
DOUBLE |
Confidence of a score / choice answer |
jev_eval(row, question [, kind [, options]]) |
JSON |
The full answer: probabilities, legend, confidence |
jev_stats() |
JSON |
Requests, tokens, estimated cost, cache hits |
jev_cache_clear() |
BOOLEAN |
Forget the cached judgments |
row is the table itself, an alias, a subquery alias, or any single expression: DuckDB hands the
whole row over as a struct, and the column names are part of what the model reads.
How it works
DuckDB gives a scalar function a vector of up to 2048 rows at a time, so the batch is already there.
Rows go out jev_batch_size (20) per request — one shared state, one question per row, which
amortises the per-request overhead — over a process-wide pool of jev_concurrency (16) kept-alive
connections. Answers are cached by row content, so re-running a query, changing the threshold or
sorting by probability is free, and rows that a cheaper predicate rejects first are never judged.
This is a port of pg-jev (pgjev.com), which does the same for PostgreSQL, and speaks the same API.
Settings
jev_api_key (falls back to the TYPESAFE_API_KEY environment variable), jev_api_url, jev_model,
jev_threshold, jev_batch_size, jev_concurrency, jev_timeout, jev_max_retries,
jev_max_rows_per_statement, jev_max_chars_per_statement, jev_cache_max_entries.
Row contents are sent to a third-party API, so do not point it at data you may not share.
Added Functions
| function_name | function_type | description | comment | examples |
|---|---|---|---|---|
| jev | scalar | NULL | NULL | |
| jev_cache_clear | scalar | NULL | NULL | |
| jev_choice | scalar | NULL | NULL | |
| jev_confidence | scalar | NULL | NULL | |
| jev_eval | scalar | NULL | NULL | |
| jev_prob | scalar | NULL | NULL | |
| jev_score | scalar | NULL | NULL | |
| jev_score_norm | scalar | NULL | NULL | |
| jev_stats | scalar | NULL | NULL | |
| jev_version | scalar | NULL | NULL |
Overloaded Functions
This extension does not add any function overloads.
Added Types
This extension does not add any types.
Added Settings
| name | description | input_type | scope | aliases |
|---|---|---|---|---|
| jev_api_key | TypeSafe API key. Falls back to the TYPESAFE_API_KEY environment variable | VARCHAR | GLOBAL | [] |
| jev_api_url | Endpoint that answers the questions (proxies, mocks, self-hosted) | VARCHAR | GLOBAL | [] |
| jev_batch_size | Rows per API request. Accuracy drops measurably above ~20-25 | UBIGINT | GLOBAL | [] |
| jev_cache_max_entries | Answers kept in the session cache before the oldest are dropped | UBIGINT | GLOBAL | [] |
| jev_concurrency | Requests in flight at once, across all DuckDB threads | UBIGINT | GLOBAL | [] |
| jev_max_chars_per_statement | Refuse to send more characters of row data than this per statement. 0 = no limit | UBIGINT | GLOBAL | [] |
| jev_max_retries | Attempts for a retryable failure (429, 5xx, dropped connection) | UBIGINT | GLOBAL | [] |
| jev_max_rows_per_statement | Refuse to send more rows than this per statement. 0 = no limit | UBIGINT | GLOBAL | [] |
| jev_model | Model name, or a pinned version such as jev-1.13.0 | VARCHAR | GLOBAL | [] |
| jev_threshold | Probability at which jev() returns true | DOUBLE | GLOBAL | [] |
| jev_timeout | Seconds a single API request may take | DOUBLE | GLOBAL | [] |