[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-96423":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":17,"stars90d":15,"forks30d":15,"starsTrendScore":18,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":15,"starSnapshotCount":15,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},96423,"pg-jev","realZachi\u002Fpg-jev","realZachi","Ask your Postgres tables questions in plain language. A PostgreSQL extension powered by TypeSafe's Jev.","https:\u002F\u002Fpgjev.com",null,"Python",236,12,137,0,35,99,169,3.34,"Other",false,"master",[],"2026-09-21 02:04:32","\u003Cp align=\"center\">\n  \u003Cimg src=\"docs\u002Fassets\u002Fheader.svg\" alt=\"pg-jev — ask your Postgres tables questions in plain language\" width=\"100%\">\n\u003C\u002Fp>\n\n# jev — ask your Postgres tables questions in plain language\n\n[![CI](https:\u002F\u002Fgithub.com\u002FrealZachi\u002Fpg-jev\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002FrealZachi\u002Fpg-jev\u002Factions\u002Fworkflows\u002Fci.yml)\n[![PGXN](https:\u002F\u002Fbadge.fury.io\u002Fpg\u002Fjev.svg)](https:\u002F\u002Fpgxn.org\u002Fdist\u002Fjev\u002F)\n[![License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-PostgreSQL-blue.svg)](LICENSE)\n[![Website](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fwebsite-pgjev.com-0a56cf.svg)](https:\u002F\u002Fpgjev.com)\n\nWrite the condition the way you would say it. Postgres does the rest.\n\n`jev` lets you filter, rank and classify rows with plain-language conditions. Every row is judged by\n[TypeSafe's Jev](https:\u002F\u002Fdocs.typesafe.ai), a System One model that returns calibrated probabilities\ninstead of generated text. No index, no embeddings, no vector column.\n\nWebsite: [pgjev.com](https:\u002F\u002Fpgjev.com)\n\n```sql\nCREATE EXTENSION jev CASCADE;\n\nSELECT * FROM people WHERE jev(people, 'the name is European');\n\nSELECT subject, jev_prob(tickets, 'the customer is angry') AS p\nFROM tickets ORDER BY p DESC LIMIT 20;\n\nSELECT jev_choice(tickets, 'which team should handle this?',\n                  ARRAY['billing', 'technical', 'security', 'sales']) AS team, count(*)\nFROM tickets GROUP BY 1;\n\nSELECT name, jev_score(products, 'how luxurious is this product?',\n                       ARRAY['budget', 'mid-range', 'premium', 'luxury']) AS luxury\nFROM products ORDER BY luxury DESC;\n```\n\n`jev()` is an ordinary boolean function, so it composes with everything else in SQL: `AND age > 40`,\njoins, `GROUP BY`, `LIMIT`, `ORDER BY jev_prob(...)`.\n\n## How it works\n\n1. `jev(table, 'condition')` receives the row as a composite value. The first call for a table + condition starts a\n   read-ahead that streams the table in physical order (TID range scans; `OFFSET` pages for views), so memory stays\n   constant whatever the table size.\n2. Rows are packed `jev.batch_size` (20) per request into one shared *state*\n   (`{\"condition\": ..., \"rows\": [...]}`) with one yes\u002Fno [Noul](https:\u002F\u002Fdocs.typesafe.ai\u002Fprimitives\u002Fnoul)\n   question per row. Jev evaluates all questions over one state in parallel, which amortises the ~270-token\n   request overhead (about 435 tokens for one row alone vs 175 per row in batches of 20).\n3. Up to 2 × `jev.concurrency` requests are in flight over persistent HTTPS connections, and every row is answered\n   as soon as its batch returns, so a `LIMIT` stops the read-ahead after the in-flight window, and rows that cheaper\n   predicates filter out before `jev()` runs (`WHERE age > 60 AND jev(...)`) are skipped rather than judged.\n4. Answers are cached per row content for the session, so re-running, changing the threshold or sorting by\n   probability is free. Rows from a subquery or CTE (anonymous `record` type) can't be read ahead and are judged\n   one request at a time; put `jev()` on base tables or views when you can.\n\nMeasured on a 2,000-row table from Europe (~190 ms to the API): first run ≈ 3.5 s in 100 requests, ≈ 296k input\ntokens, ≈ $0.012; second run ≈ 50 ms; `LIMIT 3` on a new condition ≈ 0.6 s. A new condition in a session that\nstill holds its pooled connections (idle for less than `jev.keepalive`) takes ≈ 2.3 s: the first request on each\nfresh connection is the slow one. Version 0.1.0 needed 8.5 s (and 338k tokens) for the full query and 8.4 s for\nthe `LIMIT`.\n\n### Why 20 rows per request\n\nJev has to find `rows[i]` by position in the array, and that gets unreliable in long arrays. Against ground truth\nfrom structured columns (job title, EU membership, a phrase in a free-text field; 400 rows each), batches of 1–20\nrows were 100 % correct, batches of 40 were 92–98 % and batches of 80 were 77–94 %. Wider rows (1,000 characters)\nmade no difference at 20. Naming rows instead of indexing them did not help. Batches of 20 cost 4 % more tokens than\nbatches of 40 and are just as fast, because a request's latency barely depends on its size.\n\n## Install\n\nRequirements: PostgreSQL 14–17 with `plpython3u` (package `postgresql-plpython3-NN` on Debian\u002FUbuntu,\nincluded in the EDB and Postgres.app builds), a superuser, and a TypeSafe API key from https:\u002F\u002Fconsole.typesafe.ai.\nManaged hosts that withhold superuser or `plpython3u` (Supabase, Neon, RDS, …) cannot run it; see\n[Where it runs](https:\u002F\u002Fpgjev.com\u002Fdocs\u002Fgetting-started\u002Fwhere-it-runs).\n\n### With an AI agent (easiest)\n\nThe repo ships an [agent skill](.agents\u002Fskills\u002Fpgjev\u002FSKILL.md) on [skills.sh](https:\u002F\u002Fskills.sh). Install it into\nyour project and tell Claude Code, Codex, Cursor or any other skill-aware agent to finish the job:\n\n```bash\nnpx skills add realZachi\u002Fpg-jev\n```\n\n> Install pgjev on this server and set it up.\n\nThe agent runs a preflight (PostgreSQL version, `plpython3u`, superuser), `pgxn install jev` or `make install` against the right\n`pg_config`, `CREATE EXTENSION jev CASCADE`, places the API key and runs a smoke test. Afterwards it also knows how\nto write cost-conscious `jev()` queries (\"find the tickets where the customer threatens to cancel\") and to explain\nwhat pgjev can do. The docs are readable as Markdown for agents too: append `.md` to any page under\nhttps:\u002F\u002Fpgjev.com\u002Fdocs (see [For agents](https:\u002F\u002Fpgjev.com\u002Fdocs\u002Ffor-agents)).\n\n### From PGXN\n\n```bash\npip install pgxnclient       # once; also available as `pgxn-client` in Debian\u002FUbuntu and Homebrew\npgxn install jev             # downloads the release from pgxn.org and runs `make install` against pg_config on PATH\npsql -c \"CREATE EXTENSION jev CASCADE\"\n```\n\nUse `pgxn install jev --pg_config=\u002Fpath\u002Fto\u002Fpg_config` (or `sudo pgxn install jev`) when the server's `pg_config`\nis not on PATH or the extension directory is not writable.\n\n### From source (PGXS)\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002FrealZachi\u002Fpg-jev.git && cd pg-jev\nmake install            # uses pg_config on PATH; or: make install PG_CONFIG=\u002Fpath\u002Fto\u002Fpg_config\npsql -c \"CREATE EXTENSION jev CASCADE\"   # superuser required (plpython3u is untrusted); CASCADE creates plpython3u\n```\n\n### Docker\n\n```bash\ndocker build -t pg-jev .                       # add --build-arg PG_MAJOR=17 for another major\ndocker run -d -p 5432:5432 -e POSTGRES_PASSWORD=pw -e TYPESAFE_API_KEY=your-key pg-jev\npsql postgres:\u002F\u002Fpostgres:pw@localhost\u002Fpostgres -c \"CREATE EXTENSION jev CASCADE\"\n```\n\n### API key\n\nEither export `TYPESAFE_API_KEY` in the environment of the PostgreSQL server process, or set it per session\nor per role:\n\n```sql\nSET jev.api_key = 'your-key';\nALTER ROLE analyst SET jev.api_key = 'your-key';   -- persistent, per role\n```\n\n## Functions\n\n| Function | Returns | Purpose |\n| --- | --- | --- |\n| `jev(row, condition [, threshold])` | boolean | `WHERE` predicate. Threshold: argument → `jev.threshold` → 0.5 |\n| `jev_prob(row, condition)` | float8 | Probability 0..1 that the row satisfies the condition |\n| `jev_score(row, question, levels text[])` | float8 | Probability-weighted position on ordered levels (0 .. n-1) |\n| `jev_score_norm(row, question, levels)` | float8 | Same, normalised to 0..1 |\n| `jev_choice(row, question, options text[])` | text | The most likely option for the row |\n| `jev_confidence(row, question, kind, options)` | float8 | Confidence of a `score`\u002F`choice` answer |\n| `jev_eval(row, question, kind, options)` | jsonb | Full raw answer (probabilities, legend, confidence) |\n| `jev_stats()` | jsonb | Requests, tokens, estimated cost, cache hits, in-flight requests and pooled connections for this session |\n| `jev_cache_clear()` | void | Forget cached judgments |\n| `jev_version()` | text | Extension version |\n\n`row` is the table alias itself (`jev(people, ...)`) or a subquery alias.\n\n## Settings\n\nAll settings are plain GUCs: `SET jev.\u003Cname> = ...`, `ALTER ROLE ... SET`, `ALTER DATABASE ... SET`, or `postgresql.conf`.\n\n| Setting | Default | Meaning |\n| --- | --- | --- |\n| `jev.api_key` | env `TYPESAFE_API_KEY` | TypeSafe API key |\n| `jev.model` | `jev-latest` | Model name or pinned version such as `jev-1.13.0` |\n| `jev.threshold` | `0.5` | Probability at which `jev()` returns true |\n| `jev.batch_size` | `20` | Rows per API request. Accuracy drops measurably above ~20–25 (see above) |\n| `jev.concurrency` | `16` | Parallel API requests; up to twice that many are queued ahead of the executor |\n| `jev.max_prefetch_rows` | `5000` | How far past a cache miss the read-ahead scans to find the requested row, and how many skipped rows it keeps for later requests (memory bound) |\n| `jev.notices` | `on` | Emit a progress `NOTICE` per finished request and a summary per table with request count, tokens, estimated cost and time |\n| `jev.api_url` | `https:\u002F\u002Fapi.typesafe.ai\u002Fv1\u002Fsystemone` | Endpoint (proxies, mocks) |\n| `jev.timeout` | `30` | Seconds per API request. Waits are interruptible: `statement_timeout` and cancel requests apply within 250 ms |\n| `jev.keepalive` | `600` | Seconds a pooled API connection may sit idle before it is reconnected. The first request on a fresh connection costs a TLS handshake plus, measured, up to 1.5 s of server-side setup, so keep connections alive across queries; TCP keepalive probes catch silently dropped ones |\n| `jev.max_rows_per_statement` | `0` (off) | Abort a statement that would send more rows than this to the API. Spend guard for shared deployments |\n| `jev.max_chars_per_statement` | `0` (off) | Same, for characters of row data |\n\n## Writing good conditions\n\nJev answers the question you wrote, literally. A few things that help (more in the\n[TypeSafe docs](https:\u002F\u002Fdocs.typesafe.ai\u002Fmodel-jaggedness\u002Fjev-1.13)):\n\n- State the exact condition: `'the customer threatens to leave, dispute a charge, or take legal action'`\n  beats `'churn risk'`.\n- Keep arithmetic, dates and exact matches in SQL; let the model judge meaning.\n- Look at the distribution with `jev_prob()` before picking a threshold. Ambiguous cases really do land\n  near 0.5.\n- Send only the columns the judgment needs: create a view with the relevant columns (and any pre-filter) and call\n  `jev(view_alias, ...)` on the view. Views are read ahead and batched like tables.\n\n## Caveats\n\n- This is a full scan by design: every row the executor asks about goes to the API. Cheaper predicates in the same\n  `WHERE` run first and their rejects are skipped; a `LIMIT` stops early; `jev.max_rows_per_statement` caps spend.\n- Row contents are sent to a third-party API. Do not use it on data you may not share.\n- The cache lives in the backend session (PL\u002FPython `GD`). Connection pools with many sessions each warm their\n  own cache.\n- `plpython3u` is an untrusted language: only superusers can create the extension, and functions run with the\n  server's OS privileges.\n\n## Development\n\n```bash\nmake docker-test                 # builds test\u002FDockerfile and runs the regression suite (PG_MAJOR=16 by default)\nmake docker-test PG_MAJOR=17\n```\n\nLocally with a running server and `pg_config` on `PATH`:\n\n```bash\nmake install\npython3 test\u002Fmock_api.py &       # deterministic stand-in for the TypeSafe API\nmake installcheck                # pg_regress, tests in test\u002Fsql, expected output in test\u002Fexpected\n```\n\nThe regression tests never call the live API. To try the real thing, `SET jev.api_key` and run any query.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) and [docs\u002FPUBLISHING.md](docs\u002FPUBLISHING.md) for release steps.\n\n## License\n\n[PostgreSQL License](LICENSE). Jev and TypeSafe are trademarks of their respective owners; this project is not\naffiliated with TypeSafe.\n","pg-jev 是一个 PostgreSQL 扩展，支持用自然语言条件（如“姓名是欧洲的”）直接查询数据库表。其核心功能包括：提供 jev() 布尔函数用于过滤、jev_prob() 返回校准概率、jev_choice() 和 jev_score() 实现多选分类与标度打分；底层基于 TypeSafe 的 Jev 模型，采用无嵌入、无向量列、无索引的轻量架构，通过批量 TID 扫描与会话级行内容缓存实现高效低开销推理。适用于需要快速原型验证、临时探索性分析或低代码数据筛选的业务场景，尤其适合非技术用户参与 SQL 查询构建。",2,"2026-09-19 02:30:08","CREATED_QUERY"]