DEV Community

Cover image for sqljev: TypeSafe Jev's jev() for SQL Server, Postgres, Snowflake, BigQuery and DuckDB, on Jev or Open-Weight Laya
AI Explore
AI Explore

Posted on

sqljev: TypeSafe Jev's jev() for SQL Server, Postgres, Snowflake, BigQuery and DuckDB, on Jev or Open-Weight Laya

There is a whole class of questions SQL cannot ask. Not "tickets created this week"; SQL is good at that. "Tickets where the customer threatens to cancel." "Contracts that mention a price guarantee." "Adverse-event reports that describe liver injury." You cannot say those with =, LIKE or a regex, so they get exported to a notebook, judged by a model there, and the answers never make it back into the database where the rest of the question lives.

sqljev, released today as 0.1.0, puts that judgment back inside the query. It adds jev(), jev_prob() and jev_choice() to SQL Server, PostgreSQL, MySQL, Snowflake, Databricks, BigQuery, Redshift and DuckDB, and answers them with an open-weight model running on your own hardware.

-- Snowflake / Databricks / DuckDB / BigQuery / Redshift: the row goes in as JSON
SELECT * FROM tickets t WHERE jev(OBJECT_CONSTRUCT(t.*), 'the customer threatens to cancel');

SELECT subject, jev_prob(to_json(t), 'the customer is angry') AS p
FROM tickets t ORDER BY p DESC LIMIT 20;

SELECT jev_choice(to_json(t), 'which team should handle this?',
 ['billing', 'technical', 'security', 'sales']) AS team, count(*)
FROM tickets t GROUP BY team;

-- SQL Server / Azure SQL
EXEC jev.judge N'dbo.tickets', N'the customer is angry';
SELECT * FROM dbo.tickets AS t
WHERE jev.prob((SELECT t.* FOR JSON PATH, WITHOUT_ARRAY_WRAPPER), N'the customer is angry') >= 0.5;
Enter fullscreen mode Exit fullscreen mode

jev() is an ordinary boolean function, so it composes with everything else in SQL: AND created_at >..., joins, GROUP BY, LIMIT. Keep arithmetic, dates and exact matches in SQL; let the model judge meaning.

The model does not write; it decides

This is not an LLM generating text and hoping it parses. Every row is judged by a decision model: given a row and a typed question (yes/no, a choice from options, a score on ordered levels), it returns a calibrated probability in a single forward pass. No prompt engineering, no JSON to repair, no temperature. By default that model is Laya from Convai Innovations: Apache 2.0, open weights, about 33 ms per decision on a T4 and about 7 ms batched. Row data never leaves your network and there is no per-token bill. TypeSafe's hosted Jev is one setting away if you want its stronger zero-shot accuracy and can send rows out.

The idea comes from pg-jev, a PostgreSQL extension that does this on Jev, and parts of sqljev are ported from it under its licence. sqljev takes the idea to every other database and swaps in an open model you can train.

One question, many rows, very little work

A SQL condition is one question asked of a great many rows, and the engine is built around that:

  • Rows go in as compact JSON, NULL columns dropped; pass only the columns the judgment needs.
  • Every database already batches UDF calls. Snowflake, BigQuery, Redshift, Spark and DuckDB hand over hundreds to thousands of rows per call; SQL Server's jev.judge sends 500 at a time. Each batch is one engine call.
  • De-duplicate, then cache. Identical rows are judged once; answers are cached by row content and question, so re-running, changing the threshold or sorting by probability is free.
  • Shared forward passes. Misses are packed into one forward pass, 64 rows by default, sorted by length. On CPU: 0.19 s a row batched against 0.35 s one at a time.
  • Streaming when it pays. --where... --limit N stops after the first N matches.

On the built-in benchmark: 140,000 decisions in 271 seconds, 516 a second, through real SQL on an RTX 4090 Laptop GPU that another model was sharing. Re-running all 13 queries: 0.9 seconds, every answer from the cache. With an instant stand-in model, sqljev's own overhead measured about 98,000 decisions a second; the model is the only cost.

What the base model gets right, and where it does not

Ten synthetic but realistic tables with exact labels, 100,000 rows, 13 questions, one plain SQL query each, base Laya English checkpoint with no training. Reproducible with python -m sqljev.demo and bench/run.py.

Base Laya, no training: accuracy against the always-majority baseline on 13 questions

Question Rows Accuracy Always-majority baseline Rows/s
Contract clause: which type? (5) 10000 0.995 0.203 1606
Job post: fully remote? 10000 0.922 0.602 375
Support ticket: is the customer angry? 10000 0.894 0.697 567
Advisor email: guarantees returns? 10000 0.865 0.747 514
Support ticket: which team? 10000 0.854 0.352 580
Job post: how senior? (3) 10000 0.853 0.336 402
Expense: which category? (5) 10000 0.851 0.209 607
Product review: reports a defect? 10000 0.850 0.702 855
Product review: sentiment (3) 10000 0.828 0.453 879
Adverse event: was it serious? 10000 0.699 0.650 345
Two records: same company? (join) 20000 0.685 0.506 531
Adverse event: which body system? (5) 10000 0.680 0.203 371
Rental listing: pets allowed? 10000 0.595 0.546 384

Read the weak rows honestly. "Was the adverse event serious?" is 69.9% against a 65.0% baseline; "pets allowed?" is 59.5% against 54.6%. Those are the questions where the model has to learn your definition, and that is where the second half of the project comes in.

Laya (default) Jev (hosted)
Licence, weights Apache 2.0, open weights proprietary API
Where it runs your server, GPU or CPU TypeSafe's cloud
Cost $0 per token $0.042 / 1M input tokens
Latency ~33 ms per decision (T4) ~250 ms per request
Zero-shot accuracy, typed decisions 0.362 (base) 0.727
Fine-tuned accuracy, typed decisions 0.766 not fine-tunable
Wide option sets (Banking77) 0.425 0.870

Figures from Laya's published benchmarks, as the README reports them.

Fine-tune it on your own tables, in four commands

Zero-shot Laya is not Jev; the table says so. What Laya has that Jev does not is that you can train it, and a table with a label column is a training set. The label column is never shown to the model: the state and question are built by the same code the runtime uses, so the checkpoint learns exactly what it will be asked.

# 1. labelled rows -> train/test; the label column is never shown to the model
sqljev dataset "$DB_URL" "SELECT subject, body, team FROM tickets WHERE team IS NOT NULL" \
 --label team --choice "which team should handle this?" --test-fraction 0.2 -o tickets.jsonl
# 2. how does the base model do on YOUR rows?
sqljev eval tickets.test.jsonl
# 3. fine-tune on one GPU (a free Colab T4 is enough; --train-layers 12 under ~10 GB)
sqljev finetune tickets.train.jsonl --out checkpoints/tickets --epochs 3
# 4. measure again on the same held-out rows, publish, point every database at it
sqljev eval tickets.test.jsonl --model checkpoints/tickets --min-accuracy 0.85
HF_TOKEN=... sqljev publish checkpoints/tickets --repo your-org/laya-tickets
SQLJEV_MODEL=your-org/laya-tickets sqljev gateway --host 0.0.0.0
Enter fullscreen mode Exit fullscreen mode

Measured on the built-in pharma demo ("the adverse event was serious", 3,000 training rows, 1,000 held out): 69.4% to 100% after two minutes on the same laptop GPU. The README says the next part in the same breath, and so will I: the demo reports come from templates and are easy to learn. Expect a smaller jump on real data, and measure it the same way with sqljev eval on held-out rows before you trust a threshold. A Colab notebook runs the whole loop on a free T4.

Eight databases, one set of names

function returns use
jev(row, condition) boolean a WHERE predicate; threshold 0.5
jev_prob(row, condition) float 0..1 ORDER BY it: most urgent incidents, likeliest leads
jev_choice(row, question, options) text route: which team, which category
jev_score(row, question, levels) float probability-weighted position on ordered levels
jev_eval(row, question, kind, options) json the full answer: probabilities and confidence

SQL Server 2025 and Azure SQL call a small gateway through sp_invoke_external_rest_endpoint; SQL Server 2016 to 2022 fill the same answer table from outside with one command. PostgreSQL and MySQL need no extension and no superuser. Snowflake can run Laya inside your account on Snowpark Container Services. Databricks registers pandas UDFs on every executor; BigQuery uses remote functions on Cloud Run; Redshift a Lambda; DuckDB Arrow UDFs in-process. The gateway also speaks Jev's own API, so pg-jev on a self-hosted Postgres can run on Laya too.

pip install "sqljev[laya,db]" # engine + Laya + SQLAlchemy CLI, Python 3.10+
sqljev query "sqlite:///support.db" "SELECT * FROM tickets" --where "the customer is angry" --limit 10
# downloads Laya once (~1.7 GB), judges the rows on your machine, prints the angry tickets with their probability
Enter fullscreen mode Exit fullscreen mode

Caveats, from the README

  • Limit before you judge. Databases compute the SELECT list before ORDER BY... LIMIT, so a jev_prob() next to a LIMIT 100 judges every row. Limit in a subquery first.
  • No index can answer a plain-language condition. Every row that reaches jev() is judged, once. Filter with cheap SQL first.
  • Laya reads 512 tokens in English, 1,024 multilingual, up to 8,192 with max_len. Send the columns that matter.
  • Measure before you trust a threshold. Answers can change between checkpoints; pin model when results feed reports.

sqljev is Apache 2.0 and independent: not affiliated with TypeSafe or Convai Innovations, version 0.1.0 as of today. pip install "sqljev[laya,db]", the site has copy-paste setup per database, and if you run the benchmark on your own tables the weak rows are the numbers I would most like to see.

Top comments (0)