sqljev 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,450 @@
1
+ Metadata-Version: 2.5
2
+ Name: sqljev
3
+ Version: 0.1.0
4
+ Summary: Ask your SQL rows questions in plain English: jev(), jev_prob(), jev_choice(), jev_score() for SQL Server, PostgreSQL, MySQL, Snowflake, Databricks, BigQuery, Redshift and DuckDB, answered by Laya (open weights, fine-tunable) or TypeSafe Jev.
5
+ Project-URL: Homepage, https://singhpratech.github.io/sqljev/
6
+ Project-URL: Source, https://github.com/singhpratech/sqljev
7
+ Project-URL: Issues, https://github.com/singhpratech/sqljev/issues
8
+ Project-URL: Changelog, https://github.com/singhpratech/sqljev/blob/main/CHANGELOG.md
9
+ Project-URL: Fine-tune in Colab, https://colab.research.google.com/github/singhpratech/sqljev/blob/main/notebooks/sqljev_finetune_colab.ipynb
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ License-File: NOTICE
13
+ Keywords: bigquery,databricks,duckdb,fine-tuning,jev,laya,mysql,pg-jev,postgresql,redshift,snowflake,sql,sql-server,text-classification
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: License :: OSI Approved :: Apache Software License
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: SQL
24
+ Classifier: Topic :: Database
25
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
26
+ Requires-Python: >=3.10
27
+ Provides-Extra: all
28
+ Requires-Dist: duckdb>=1.1; extra == 'all'
29
+ Requires-Dist: laya>=0.3.20; extra == 'all'
30
+ Requires-Dist: pyarrow>=14; extra == 'all'
31
+ Requires-Dist: sqlalchemy>=2.0; extra == 'all'
32
+ Provides-Extra: db
33
+ Requires-Dist: sqlalchemy>=2.0; extra == 'db'
34
+ Provides-Extra: dev
35
+ Requires-Dist: duckdb>=1.1; extra == 'dev'
36
+ Requires-Dist: pandas>=1.5; extra == 'dev'
37
+ Requires-Dist: pyarrow>=14; extra == 'dev'
38
+ Requires-Dist: pytest>=8; extra == 'dev'
39
+ Requires-Dist: sqlalchemy>=2.0; extra == 'dev'
40
+ Provides-Extra: duckdb
41
+ Requires-Dist: duckdb>=1.1; extra == 'duckdb'
42
+ Requires-Dist: pyarrow>=14; extra == 'duckdb'
43
+ Provides-Extra: laya
44
+ Requires-Dist: laya>=0.3.20; extra == 'laya'
45
+ Provides-Extra: spark
46
+ Requires-Dist: pandas>=1.5; extra == 'spark'
47
+ Requires-Dist: pyarrow>=14; extra == 'spark'
48
+ Requires-Dist: pyspark>=3.4; extra == 'spark'
49
+ Description-Content-Type: text/markdown
50
+
51
+ <p align="center">
52
+ <a href="https://singhpratech.github.io/sqljev/"><img src="https://raw.githubusercontent.com/singhpratech/sqljev/main/site/assets/banner.png" alt="sqljev: ask your SQL rows questions in plain English, answered by Laya" width="100%"></a>
53
+ </p>
54
+
55
+ <p align="center">
56
+ <a href="https://pypi.org/project/sqljev/"><img src="https://img.shields.io/pypi/v/sqljev?color=F0A202&label=pypi" alt="PyPI"></a>
57
+ <a href="https://pypi.org/project/sqljev/"><img src="https://img.shields.io/pypi/pyversions/sqljev?color=152238" alt="Python"></a>
58
+ <a href="https://github.com/singhpratech/sqljev/actions/workflows/ci.yml"><img src="https://github.com/singhpratech/sqljev/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
59
+ <a href="https://github.com/singhpratech/sqljev/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-2F8F5B" alt="License"></a>
60
+ <a href="https://colab.research.google.com/github/singhpratech/sqljev/blob/main/notebooks/sqljev_finetune_colab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open in Colab"></a>
61
+ </p>
62
+
63
+ <p align="center">
64
+ <a href="https://singhpratech.github.io/sqljev/"><b>Website</b></a> &nbsp;·&nbsp;
65
+ <a href="#quickstart">Quickstart</a> &nbsp;·&nbsp;
66
+ <a href="#per-database">Databases</a> &nbsp;·&nbsp;
67
+ <a href="#benchmark-100000-rows-13-questions">Benchmark</a> &nbsp;·&nbsp;
68
+ <a href="#fine-tune-laya-on-your-own-tables">Fine-tune</a>
69
+ </p>
70
+
71
+ # sqljev
72
+
73
+ **Ask your SQL rows questions in plain English.** Write the condition the way you would say it, and your
74
+ database does the rest, on **SQL Server, PostgreSQL, MySQL / MariaDB, Snowflake, Databricks, BigQuery, Redshift,
75
+ DuckDB** and anything SQLAlchemy can reach.
76
+
77
+ ```sql
78
+ -- Snowflake / Databricks / DuckDB / BigQuery / Redshift: the row goes in as JSON
79
+ SELECT * FROM tickets t WHERE jev(OBJECT_CONSTRUCT(t.*), 'the customer threatens to cancel');
80
+
81
+ SELECT subject, jev_prob(to_json(t), 'the customer is angry') AS p
82
+ FROM tickets t ORDER BY p DESC LIMIT 20;
83
+
84
+ SELECT jev_choice(to_json(t), 'which team should handle this?',
85
+ ['billing', 'technical', 'security', 'sales']) AS team, count(*)
86
+ FROM tickets t GROUP BY team;
87
+
88
+ -- SQL Server / Azure SQL
89
+ EXEC jev.judge N'dbo.tickets', N'the customer is angry';
90
+ SELECT * FROM dbo.tickets AS t
91
+ WHERE jev.prob((SELECT t.* FOR JSON PATH, WITHOUT_ARRAY_WRAPPER), N'the customer is angry') >= 0.5;
92
+ ```
93
+
94
+ Every row is judged by a **System One decision model**, a model that does not generate text but returns
95
+ calibrated probabilities for typed questions (yes/no, choice, score). By default that is
96
+ **[Laya](https://github.com/NandhaKishorM/laya)** (Convai Innovations, Apache 2.0, open weights), running on
97
+ *your* hardware: row data never leaves your network, costs nothing per token, and **you can fine-tune it on
98
+ your own tables**. TypeSafe's hosted [Jev](https://docs.typesafe.ai) is one setting away.
99
+
100
+ Inspired by, and partly ported from, [pg-jev](https://github.com/realZachi/pg-jev), which does this inside
101
+ PostgreSQL. sqljev takes the idea to every other database and swaps in an open model you can train.
102
+
103
+ ## Quickstart
104
+
105
+ ```bash
106
+ pip install "sqljev[laya,db]"
107
+ sqljev query "sqlite:///support.db" "SELECT * FROM tickets" --where "the customer is angry" --limit 10
108
+ ```
109
+
110
+ That downloads Laya once (~1.7 GB), judges the rows on your machine, and prints the angry tickets with their
111
+ probability. Then pick your database below, or open the [website](https://singhpratech.github.io/sqljev/) for copy-paste setup per database.
112
+
113
+ ## What it is for
114
+
115
+ The questions SQL cannot express, asked right inside the queries you already write:
116
+
117
+ | | Example |
118
+ | --- | --- |
119
+ | Support / CRM | `jev(t, 'the customer threatens to cancel')`, route with `jev_choice(... ['billing','technical','sales'])` |
120
+ | Risk & compliance | flag contracts, emails or chat logs: *"mentions a price guarantee"*, *"contains health information"* |
121
+ | Pharma & life sciences | *"the adverse event report mentions liver injury"*, classify free-text indications by therapeutic area |
122
+ | Data cleanup | *"this address is a business, not a residence"*, *"the product description is not in English"* |
123
+ | Triage & ranking | `ORDER BY jev_prob(...)` for the most urgent incidents, likeliest leads, riskiest claims |
124
+
125
+ `jev()` is an ordinary boolean function, so it composes with everything else in SQL: `AND created_at > ...`,
126
+ joins, `GROUP BY`, `LIMIT`. Keep arithmetic, dates and exact matches in SQL; let the model judge *meaning*.
127
+
128
+ ## How it works
129
+
130
+ ```
131
+ your SQL ──► database UDF / procedure ──► sqljev engine ──► Laya (in-process, GPU/CPU)
132
+ (rows arrive in batches) dedupe · cache · or a gateway, laya-serve, or Jev
133
+ forward-pass batching
134
+ ```
135
+
136
+ A SQL question is *one question over many rows*, and everything is organised around that:
137
+
138
+ 1. **Rows go in as compact JSON objects** (column → value). NULL columns are dropped, which matters for
139
+ Laya's 512–1,024-token window. Pass only the columns the judgment needs (a view, `struct(subject, body)`).
140
+ 2. **Every database already batches UDF calls.** Snowflake, BigQuery, Redshift, Spark and DuckDB hand
141
+ over hundreds to thousands of rows per call, and SQL Server's `jev.judge` sends 500 at a time. Each batch
142
+ becomes one engine call.
143
+ 3. **De-duplicate, then cache.** Identical rows are judged once. Answers are cached by row content and
144
+ question, so re-running, changing the threshold or sorting by probability is free, and only rows that
145
+ changed are judged again.
146
+ 4. **Shared forward passes.** Misses go to Laya's `predict_batch`, which packs many rows into one forward
147
+ pass (`batch_size`, default 64), sorted by length to minimise padding. Measured on CPU (English
148
+ checkpoint): 0.19 s/row batched vs 0.35 s/row one at a time. On a GPU, Laya is ~33 ms for a single
149
+ decision and ~7 ms per decision batched.
150
+ 5. **Streaming when it pays.** `sqljev query --where ... --limit N` judges rows in order with a bounded
151
+ read-ahead, so it stops after the first N matches instead of scanning everything.
152
+
153
+ ### Backends
154
+
155
+ | `backend` | Model | Rows per call | Use it for |
156
+ | --- | --- | --- | --- |
157
+ | `local` (default) | Laya in this process | `batch_size` per forward pass (64) | CLI, DuckDB, Spark/Databricks executors, the gateway itself |
158
+ | `gateway` | a `sqljev gateway` (which runs Laya) | 256 per HTTP request | SQL Server, Snowflake UDFs, Redshift Lambda, anything remote |
159
+ | `laya-serve` | stock `laya-serve` (`POST /v1/systemone`) | 1 (Laya reads one state) | an existing Laya deployment |
160
+ | `jev` | TypeSafe Jev, hosted | 20 in one shared state (pg-jev's measured optimum) | best zero-shot accuracy, if data may leave your network |
161
+
162
+ ## Benchmark: 100,000 rows, 13 questions
163
+
164
+ Ten synthetic but realistic tables with exact labels (`python -m sqljev.demo`), one plain SQL query per question
165
+ through DuckDB, answered by the **base Laya English checkpoint with no training**, on an RTX 4090 Laptop GPU
166
+ that another model was sharing:
167
+
168
+ | Question | Rows | Accuracy | Always-majority baseline | Rows/s |
169
+ | --- | --- | --- | --- | --- |
170
+ | Contract clause: which type? (5) | 10000 | **0.995** | 0.203 | 1606 |
171
+ | Job post: fully remote? | 10000 | **0.922** | 0.602 | 375 |
172
+ | Support ticket: is the customer angry? | 10000 | **0.894** | 0.697 | 567 |
173
+ | Advisor email: guarantees returns? | 10000 | **0.865** | 0.747 | 514 |
174
+ | Support ticket: which team? | 10000 | **0.854** | 0.352 | 580 |
175
+ | Job post: how senior? (3 levels) | 10000 | **0.853** | 0.336 | 402 |
176
+ | Expense: which category? (5) | 10000 | **0.851** | 0.209 | 607 |
177
+ | Product review: reports a defect? | 10000 | **0.850** | 0.702 | 855 |
178
+ | Product review: sentiment (3 levels) | 10000 | **0.828** | 0.453 | 879 |
179
+ | Adverse event: was it serious? | 10000 | **0.699** | 0.650 | 345 |
180
+ | Two company records: same company? (join) | 20000 | **0.685** | 0.506 | 531 |
181
+ | Adverse event: which body system? (5) | 10000 | **0.680** | 0.203 | 371 |
182
+ | Rental listing: pets allowed? | 10000 | **0.595** | 0.546 | 384 |
183
+
184
+ **140,000 decisions in 271 s (516/s). Re-running all 13 queries: 0.9 s**, every answer from the cache. With an
185
+ instant stand-in model, sqljev's own overhead measured about 98,000 decisions/s: the model is the only cost.
186
+ Reproduce with `python -m sqljev.demo --out bench/bench.duckdb && python bench/run.py --device cuda`.
187
+
188
+ The weak rows are where fine-tuning pays: the model has to learn *your* definition (what counts as "serious"
189
+ in pharmacovigilance, what a pet policy sentence means). See below.
190
+
191
+ ## Which model? Laya vs Jev, honestly
192
+
193
+ | | Laya (default) | Jev |
194
+ | --- | --- | --- |
195
+ | License / weights | Apache 2.0, open weights | proprietary API |
196
+ | Where it runs | your server, GPU or CPU | TypeSafe's cloud |
197
+ | Cost | $0 per token | $0.042 / 1M input tokens |
198
+ | Latency | ~33 ms / decision (T4) | ~250 ms / request |
199
+ | **Zero-shot** accuracy (typed-decisions) | 0.362 (base checkpoint) | **0.727** |
200
+ | **Fine-tuned** accuracy (typed-decisions) | **0.766** | not fine-tunable |
201
+ | Wide option sets (Banking77) | 0.425 | **0.870** |
202
+
203
+ *(Figures from Laya's published benchmarks.)* Laya out of the box is good at clear-cut yes/no conditions
204
+ and weaker at fine-grained choices. **Its strength is that you can train it on your data**, and SQL tables
205
+ are full of labels. So sqljev ships the loop:
206
+
207
+ ## Fine-tune Laya on your own tables
208
+
209
+ ```bash
210
+ # 1. Labelled rows -> training/eval data. The label column is never shown to the model, and the state and
211
+ # question are built by the same code the runtime uses, so the checkpoint learns exactly what it will be asked.
212
+ sqljev dataset "$DB_URL" "SELECT subject, body, team FROM tickets WHERE team IS NOT NULL" \
213
+ --label team --choice "which team should handle this?" --test-fraction 0.2 -o tickets.jsonl
214
+ # -> tickets.train.jsonl, tickets.test.jsonl (--format typed-decisions for Laya's fine-tuning notebook)
215
+
216
+ # 2. Baseline: how does the base model (or Jev) do on YOUR rows?
217
+ sqljev eval tickets.test.jsonl # Laya base
218
+ sqljev eval tickets.test.jsonl --backend jev # Jev, for comparison
219
+
220
+ # 3. Fine-tune (one GPU; a free Colab T4 is enough. --train-layers 12 for GPUs with less than ~10 GB free)
221
+ sqljev finetune tickets.train.jsonl --out checkpoints/tickets --epochs 3
222
+
223
+ # 4. Measure again on the same held-out rows, then share it with the team
224
+ sqljev eval tickets.test.jsonl --model checkpoints/tickets --min-accuracy 0.85
225
+ HF_TOKEN=... sqljev publish checkpoints/tickets --repo your-org/laya-tickets
226
+ SQLJEV_MODEL=your-org/laya-tickets sqljev gateway --host 0.0.0.0 # every database now uses it
227
+ ```
228
+
229
+ Measured on the built-in pharma demo (*"the adverse event was serious"*, 3,000 training rows, 1,000 held out):
230
+ **69.4% → 100% after 2 minutes** of training on an RTX 4090 Laptop GPU (`--train-layers 12`, 3 epochs). The demo
231
+ reports come from templates and are easy to learn; expect a smaller jump on real data, and measure it the same way.
232
+
233
+ **No terminal?** Open [`notebooks/sqljev_finetune_colab.ipynb`](https://github.com/singhpratech/sqljev/blob/main/notebooks/sqljev_finetune_colab.ipynb) in
234
+ [Colab](https://colab.research.google.com/github/singhpratech/sqljev/blob/main/notebooks/sqljev_finetune_colab.ipynb),
235
+ pick a question (or the built-in pharma demo) and press *Run all*: it measures, fine-tunes, measures again and
236
+ publishes. The training loop follows Laya's own fine-tuning notebook (proper-scoring-rule policy gradient plus
237
+ cross-entropy, temperature calibration on held-out rows), adapted to a single GPU.
238
+
239
+ `laya-evals run tickets.test.jsonl` works on the same files for calibration (ECE) and per-slice reports.
240
+
241
+ ## Install
242
+
243
+ ```bash
244
+ pip install "sqljev[laya,db]" # engine + Laya + SQLAlchemy CLI (Python 3.10+)
245
+ pip install "sqljev" # engine, gateway client, Lambda handler only: stdlib, no dependencies
246
+ ```
247
+
248
+ For a GPU, install the matching PyTorch build first. The first use downloads the Laya checkpoint (~1.7 GB)
249
+ from Hugging Face.
250
+
251
+ ## Per database
252
+
253
+ <details open><summary><b>Any database: the CLI</b> (Oracle, Db2, Trino, and everything above)</summary>
254
+
255
+ ```bash
256
+ sqljev query "$DB_URL" "SELECT * FROM tickets" --where "the customer is angry" --limit 20
257
+ sqljev query "$DB_URL" "SELECT * FROM tickets" --rank "the customer is angry" --limit 10 --format csv
258
+ sqljev query "$DB_URL" "SELECT * FROM tickets" --choice "which team?" --options billing,technical,sales
259
+ sqljev query "$DB_URL" "SELECT * FROM tickets" --prob "mentions a refund" --columns subject,body
260
+ # Write judgments back as a table you can join in any database:
261
+ sqljev materialize "$DB_URL" "SELECT id, subject, body FROM tickets" --key id \
262
+ --prob "the customer is angry" --into ticket_anger
263
+ ```
264
+ </details>
265
+
266
+ <details><summary><b>SQL Server / Azure SQL</b></summary>
267
+
268
+ Run [`sql/sqlserver/install.sql`](https://github.com/singhpratech/sqljev/blob/main/sql/sqlserver/install.sql). It creates schema `jev` with `jev.judge`,
269
+ `jev.prob`, `jev.matches`, `jev.choice`, `jev.score`, `jev.score_norm`, `jev.eval`, `jev.answers_for`
270
+ (set-based join) and `jev.forget`.
271
+
272
+ - **SQL Server 2025 / Azure SQL:** `jev.judge` calls a `sqljev gateway` through
273
+ `sp_invoke_external_rest_endpoint` (HTTPS on 443, publicly trusted certificate). See the header of the script.
274
+ - **SQL Server 2016–2022**, or no outbound HTTPS: fill the same answer table from outside, and every function
275
+ works the same:
276
+ ```bash
277
+ sqljev judge "mssql+pymssql://user:pw@host/db" --source dbo.tickets --prob "the customer is angry"
278
+ ```
279
+
280
+ Row JSON (`FOR JSON`) and hashes are computed by SQL Server itself, so a row edited later is judged again
281
+ on the next `jev.judge` run, and unchanged rows are skipped.
282
+ </details>
283
+
284
+ <details><summary><b>PostgreSQL</b> (RDS, Aurora, Cloud SQL, AlloyDB, Azure, Supabase, Neon, ...)</summary>
285
+
286
+ Run [`sql/postgres/install.sql`](https://github.com/singhpratech/sqljev/blob/main/sql/postgres/install.sql): no extension, no superuser. Judge once, then read
287
+ with ordinary functions:
288
+
289
+ ```bash
290
+ sqljev judge "postgresql://..." --source public.tickets --prob "the customer is angry"
291
+ ```
292
+ ```sql
293
+ SELECT * FROM tickets t WHERE jev.prob(to_jsonb(t), 'the customer is angry') >= 0.5;
294
+ SELECT jev.choice(to_jsonb(t), 'which team?', ARRAY['billing', 'technical', 'sales']) FROM tickets t;
295
+ ```
296
+
297
+ Rows are keyed by their `jsonb` content, so edited rows are judged again on the next run. Self-hosted Postgres
298
+ with `plpython3u` can also run [pg-jev](https://github.com/realZachi/pg-jev) on Laya:
299
+ `SET jev.api_url = 'http://<sqljev gateway>:8765/v1/systemone'`.
300
+ </details>
301
+
302
+ <details><summary><b>MySQL / MariaDB</b> (RDS, Aurora, Cloud SQL, Azure)</summary>
303
+
304
+ Run [`sql/mysql/install.sql`](https://github.com/singhpratech/sqljev/blob/main/sql/mysql/install.sql) (stored functions and a `jev_answers` table), then:
305
+
306
+ ```bash
307
+ sqljev judge "mysql+pymysql://..." --source tickets --prob "the customer is angry"
308
+ # prints the exact row expression to use, e.g. JSON_OBJECT('id', t.id, 'subject', t.subject, 'body', t.body)
309
+ ```
310
+ ```sql
311
+ SELECT * FROM tickets t
312
+ WHERE jev_prob(JSON_OBJECT('id', t.id, 'subject', t.subject, 'body', t.body), 'the customer is angry') >= 0.5;
313
+ ```
314
+ </details>
315
+
316
+ <details><summary><b>Snowflake</b></summary>
317
+
318
+ - [`sql/snowflake/install_spcs.sql`](https://github.com/singhpratech/sqljev/blob/main/sql/snowflake/install_spcs.sql): **Laya inside Snowflake** on Snowpark
319
+ Container Services (GPU). Rows never leave your account; service functions batch up to 1,000 rows per call.
320
+ - [`sql/snowflake/install_udf.sql`](https://github.com/singhpratech/sqljev/blob/main/sql/snowflake/install_udf.sql): a vectorized Python UDF (engine inlined)
321
+ calling your gateway or Jev through an External Access Integration.
322
+
323
+ ```sql
324
+ SELECT * FROM tickets t WHERE jev(OBJECT_CONSTRUCT(t.*), 'the customer is angry');
325
+ ```
326
+ </details>
327
+
328
+ <details><summary><b>Databricks / Spark</b></summary>
329
+
330
+ ```python
331
+ import sqljev.spark
332
+ sqljev.spark.register(spark, device="cuda") # Laya on every executor (or backend="gateway")
333
+ ```
334
+ ```sql
335
+ SELECT * FROM tickets WHERE jev(to_json(struct(subject, body)), 'the customer is angry');
336
+ ```
337
+ See [`sql/databricks/README.md`](https://github.com/singhpratech/sqljev/blob/main/sql/databricks/README.md).
338
+ </details>
339
+
340
+ <details><summary><b>BigQuery</b></summary>
341
+
342
+ Remote functions backed by the gateway on Cloud Run (GPU optional):
343
+ [`sql/bigquery/install.sql`](https://github.com/singhpratech/sqljev/blob/main/sql/bigquery/install.sql), [`deploy/cloudrun`](https://github.com/singhpratech/sqljev/blob/main/deploy/cloudrun/README.md).
344
+ ```sql
345
+ SELECT * FROM `proj.support.tickets` t WHERE jev.jev(TO_JSON_STRING(t), 'the customer is angry');
346
+ ```
347
+ </details>
348
+
349
+ <details><summary><b>Amazon Redshift</b></summary>
350
+
351
+ Lambda UDFs (`sqljev.aws_lambda.handler`, stdlib-only zip) forwarding to a gateway:
352
+ [`sql/redshift/install.sql`](https://github.com/singhpratech/sqljev/blob/main/sql/redshift/install.sql), [`deploy/lambda`](https://github.com/singhpratech/sqljev/blob/main/deploy/lambda/README.md).
353
+ </details>
354
+
355
+ <details><summary><b>DuckDB</b></summary>
356
+
357
+ ```python
358
+ import duckdb, sqljev.duckdb
359
+ con = duckdb.connect("support.duckdb"); sqljev.duckdb.register(con)
360
+ con.sql("SELECT * FROM tickets t WHERE jev(to_json(t), 'the customer is angry')")
361
+ ```
362
+ </details>
363
+
364
+ ## The gateway
365
+
366
+ One small HTTP service that speaks every database's batch protocol and runs Laya in-process:
367
+
368
+ ```bash
369
+ sqljev gateway --host 0.0.0.0 --port 8765 # SQLJEV_GATEWAY_TOKEN=... to require a token
370
+ docker build -f deploy/Dockerfile -t sqljev . # checkpoint baked in; CUDA via --build-arg TORCH_INDEX=...
371
+ ```
372
+
373
+ | Route | Caller |
374
+ | --- | --- |
375
+ | `POST /v1/eval` | sqljev clients (`backend=gateway`), Snowflake UDF |
376
+ | `POST /sqlserver` | `jev.judge` via `sp_invoke_external_rest_endpoint` |
377
+ | `POST /snowflake/<fn>` | Snowflake service / external functions |
378
+ | `POST /bigquery` | BigQuery remote functions |
379
+ | `POST /redshift` | Redshift Lambda payloads |
380
+ | `POST /v1/systemone` | Jev clients such as pg-jev (Jev's own API, answered by Laya) |
381
+ | `GET /health`, `GET /stats` | probes, cache/throughput stats |
382
+
383
+ ## Functions
384
+
385
+ Identical names everywhere (SQL Server uses the `jev.` schema: `jev.prob`, `jev.matches`, ...).
386
+
387
+ | Function | Returns | Purpose |
388
+ | --- | --- | --- |
389
+ | `jev(row, condition [, threshold])` | boolean | `WHERE` predicate; threshold defaults to 0.5 |
390
+ | `jev_prob(row, condition)` | float | probability 0..1 that the row satisfies the condition |
391
+ | `jev_score(row, question, levels)` | float | probability-weighted position on ordered levels (0..n-1) |
392
+ | `jev_score_norm(row, question, levels)` | float | same, normalised to 0..1 |
393
+ | `jev_choice(row, question, options)` | text | the most likely option |
394
+ | `jev_confidence(row, question, kind, options)` | float | confidence of a choice / score answer |
395
+ | `jev_eval(row, question, kind, options)` | json | the full answer: probabilities, confidence |
396
+
397
+ ## Settings
398
+
399
+ Environment variables `SQLJEV_<NAME>`, keyword arguments (`Jev(backend="gateway")`,
400
+ `register(con, device="cuda")`) or CLI flags.
401
+
402
+ | Setting | Default | Meaning |
403
+ | --- | --- | --- |
404
+ | `backend` | `local` | `local`, `gateway`, `laya-serve`, `jev` |
405
+ | `model` | Laya router | `english`, `multilingual`, `typed-decisions`, a fine-tuned checkpoint path / Hub id, or a Jev model id |
406
+ | `device` | auto | `cpu`, `cuda`, `mps` (local) |
407
+ | `api_url` | per backend | gateway / laya-serve / Jev endpoint |
408
+ | `api_key` | env | Jev: `TYPESAFE_API_KEY`; gateway: `SQLJEV_GATEWAY_TOKEN`; laya-serve: `LAYA_API_KEY` |
409
+ | `batch_size` | 64 / 256 / 1 / 20 | rows per forward pass (local) or per request |
410
+ | `concurrency` | 1 / 4 / 4 / 16 | parallel requests |
411
+ | `threshold` | 0.5 | for `jev()` |
412
+ | `max_len` | checkpoint | Laya token budget per row (multilingual reads up to 8,192) |
413
+ | `drop_nulls` | true | omit NULL columns from what the model sees |
414
+ | `cache_size` | 200,000 | answers kept in memory (LRU); 0 disables |
415
+ | `max_rows` / `max_chars` | 0 (off) | spend guard: refuse to send more than this |
416
+ | `timeout` / `keepalive` | 60 / 600 s | HTTP backends |
417
+
418
+ ## Caveats
419
+
420
+ - **Limit before you judge.** Databases compute the `SELECT` list before `ORDER BY ... LIMIT`, so
421
+ `SELECT jev_prob(...) FROM t ORDER BY id LIMIT 100` judges *every* row. Limit in a subquery first:
422
+ `SELECT jev_prob(...) FROM (SELECT * FROM t ORDER BY id LIMIT 100) t`. In `WHERE`, put cheap predicates first.
423
+ - **Every row that reaches `jev()` is judged** (once, thanks to the cache). No index can answer a
424
+ plain-language condition. Filter with cheap SQL first; `LIMIT` and streaming stop early.
425
+ - **Laya reads 512 tokens** (English) or 1,024 (multilingual, up to 8,192 with `max_len`). Wide rows are cut
426
+ off, so send only the columns that matter.
427
+ - **Zero-shot Laya is not Jev.** Measure on your data with `sqljev eval` before trusting a threshold, and
428
+ fine-tune when it matters.
429
+ - With `backend=jev`, row contents go to a third-party API. Don't use it on data you may not share.
430
+ - Answers can change between checkpoints. Pin `model` when results feed reports.
431
+
432
+ ## Development
433
+
434
+ ```bash
435
+ uv venv && uv pip install -e ".[dev]"
436
+ pytest # never calls a real model: tests/mock_api.py + a fake laya module
437
+ python scripts/build_sql.py # regenerate sql/snowflake/install_udf.sql after editing core.py
438
+ ```
439
+
440
+ Releasing is one command, `scripts/release.sh 0.1.0`: it dates the CHANGELOG entry, runs the tests, tags and
441
+ pushes. GitHub Actions then builds the package, creates the GitHub Release with the wheel and every database's
442
+ install scripts attached, and publishes to PyPI once `PYPI_PUBLISH` is enabled.
443
+
444
+ See [AGENTS.md](https://github.com/singhpratech/sqljev/blob/main/AGENTS.md) for the architecture.
445
+
446
+ ## License
447
+
448
+ Apache 2.0. Parts ported from [pg-jev](https://github.com/realZachi/pg-jev) (PostgreSQL License); see
449
+ [NOTICE](https://github.com/singhpratech/sqljev/blob/main/NOTICE). Laya is by Convai Innovations (Apache 2.0). Jev and TypeSafe are trademarks of their
450
+ respective owners; this project is not affiliated with TypeSafe or Convai Innovations.
@@ -0,0 +1,16 @@
1
+ sqljev/__init__.py,sha256=ztEPrzWr-t66tPH6f0AI1p-WPF7CTPM8ZEJAT1f0XQo,392
2
+ sqljev/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
3
+ sqljev/aws_lambda.py,sha256=M6JpJSKnX-j_Dsv4WGfECy1Fs5KDmRQjjqlic3Jb-i4,651
4
+ sqljev/cli.py,sha256=6oF1H3xwSIha7Mg2aSKrduz6EedYTQsP2uBp6K9hfrQ,24808
5
+ sqljev/core.py,sha256=-Lr3JJQrm5UaaUBvdv7oJ81kgq8_Mfue-n5RxhaHicM,29008
6
+ sqljev/demo.py,sha256=_rthrpfDWDLJMC9XN5eZpEg0A-xR-HVbTITZ5T1_-MM,19543
7
+ sqljev/duckdb.py,sha256=v3i73FJHH1TQRTVPH9OjZqeRWweSL-srToORSu_6ObQ,2462
8
+ sqljev/finetune.py,sha256=bmdjf2S-JWjovQmMU0mPva5crADaLlDea-KnVG6fCmQ,17294
9
+ sqljev/gateway.py,sha256=H-zwF4m0sKoxav-0JcPYDiQTZLAz9JO90xg9ahv0kIM,10137
10
+ sqljev/spark.py,sha256=95Ojpo1s-vF9j76tP38DZ8jBulnxgXkcAjrG4IPRKlk,2591
11
+ sqljev-0.1.0.dist-info/METADATA,sha256=kOBbMAZNAY0sGK5dyGaM3zQgK9TGbj7_YLSba8-n4To,24546
12
+ sqljev-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
13
+ sqljev-0.1.0.dist-info/entry_points.txt,sha256=Sdtv8fnWOdLvrdV45yyE62wndL1QEVLA8WEGKx9QDE8,43
14
+ sqljev-0.1.0.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
15
+ sqljev-0.1.0.dist-info/licenses/NOTICE,sha256=nCvmQrFKWqB3JoY2f8y3Id9Ez_TPC7MlILQ9tl71eMs,1810
16
+ sqljev-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sqljev = sqljev.cli:main
@@ -0,0 +1,176 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS