Changelog

All notable changes to this project are documented here. The format follows Keep a Changelog and the project uses Semantic Versioning.

[Unreleased]

[0.2.0] - 2026-09-18

Measured against the live API on a 2,000-row table (from Europe, ~190 ms RTT to the API): first run 8.5 s → 3.5 s, LIMIT 3 on a fresh condition 8.4 s → 0.6 s, 12 % fewer input tokens, and answers that match single-row evaluation instead of drifting.

Changed

  • Streaming read-ahead. A table is no longer read whole (up to jev.max_prefetch_rows) before the first answer. It is streamed in physical order (TID range scans for tables, OFFSET pages for views), judged in batches with up to 2 × jev.concurrency requests in flight, and each row is answered as soon as its batch returns. Memory is constant whatever the table size; rows beyond the old 5,000-row limit were previously judged one request at a time. A LIMIT stops the read-ahead after the in-flight window. Rows that cheaper predicates filter out before jev() runs are skipped instead of judged. Rows requested out of physical order (backward index scans, joins) are batched with their neighbours instead of judged one by one.
  • Persistent HTTPS connections. Requests reuse keep-alive connections across batches and statements (one TLS handshake per connection instead of one per request: 880 ms → 300 ms per request from Europe).
  • jev.batch_size default 40 → 20. Ground-truth tests (job title, EU membership, a phrase in a free-text field) are 100 % correct up to 20 rows per request and fall to 92–98 % at 40 and to 77–94 % at 80: the model has to find rows[i] by position, and that gets unreliable in long arrays. The cost is +4 % input tokens.
  • Noul questions no longer carry the generic criteria (“the record satisfies the condition”): they cost 16 % of all input tokens and changed no answers.
  • jev.concurrency default 6 → 16. The API handles 16 parallel requests without queueing.
  • jev.timeout default 90 → 30 seconds. Waits are also interruptible now: statement_timeout and cancel requests take effect within 250 ms instead of after the HTTP timeout.
  • 429/529/5xx retries honour Retry-After. Pooled connections are checked before reuse (closed by the server, or idle for more than jev.keepalive = 600 s) and carry TCP keepalive probes, so a request is never sent into a dead socket; a connection that still fails is retried at once on a fresh one. Keeping connections matters: the first request on a fresh connection was measured at 0.9–1.9 s against 0.3 s afterwards.
  • jev.max_prefetch_rows now bounds how far the read-ahead scans past a cache miss (and how many skipped rows it remembers), not the size of the table it can handle.
  • Rows are serialised with to_json (column order preserved) instead of to_jsonb (keys sorted by length).
  • A cache hit costs no SPI call at all; settings are read in one query per cache miss.
  • jev_stats() gains retries, in_flight and connections (idle, pooled).

Added

  • With jev.notices on, one NOTICE per finished API request while a table is being judged (jev: progress 12/50 requests, 480/2000 rows), so clients that stream notices can show a live progress bar.
  • Setting jev.keepalive (default 600 s): how long an idle pooled API connection is kept before it is reconnected. Measured: connections stay usable for at least 10–15 minutes of idle time.
  • Upgrade script jev--0.1.0--0.2.0.sql (ALTER EXTENSION jev UPDATE).
  • Regression tests for streaming, LIMIT, filtered scans, backward index scans and views.

[0.1.0] - 2026-09-17

Added

  • jev(), jev_prob(), jev_score(), jev_score_norm(), jev_choice(), jev_confidence(), jev_eval().
  • Whole-table read-ahead with batched, concurrent requests and a per-session answer cache.
  • jev_stats() and jev_cache_clear().
  • Settings: jev.api_key, jev.model, jev.threshold, jev.batch_size, jev.concurrency, jev.max_prefetch_rows, jev.notices, jev.api_url, jev.timeout, and the spend guards jev.max_rows_per_statement, jev.max_chars_per_statement.
  • Regression suite against a deterministic mock API; CI for PostgreSQL 14–17.