Skip to main content

jev-table

CI License

AI columns for CSV files, backed by TypeSafe's Jev: label every row with typed answers, probabilities and a review queue — deduplicated, resumable, with a cost preview before you spend anything.

Independent community tool. Not affiliated with TypeSafe AI.

uvx jev-table messages.csv --spec pack.yaml --dry-run     # estimate cost, send nothing
uvx jev-table messages.csv --spec pack.yaml               # writes messages.jev.csv + stats
message category urgency mentions_money review
WINNER!! Claim your $500 gift card… spam high 0.99
Your appointment is confirmed… ham normal 0.02
[weird message] category;urgency

The column spec is a pack

Columns come from a jev-packs pack (format spec v0) — questions as data, no prompts in code:

spec: 0
id: sms-triage
version: 0.1.0
license: CC0-1.0
tested: null
description: Label messages as spam/ham and score urgency.
state:
  fields: [message]          # CSV columns sent as the state
questions:
  category:
    type: choice
    instructions: "Is `message` unsolicited commercial spam, or a message the recipient expects?"
    options:
      spam: "Unsolicited bulk or promotional message."
      ham: "Personal, transactional or service message."
      unknown: "Cannot tell from the message alone."
  urgency:
    type: score
    instructions: "How soon does `message` need a human look?"
    levels: [low, normal, high, unknown]
  mentions_money:
    type: noul
    instructions: "Does `message` mention an amount of money or a payment?"
thresholds:
  category: {spam: 0.8, ham: 0.8}
  urgency: {low: 0.7, normal: 0.7, high: 0.7}
  mentions_money: {true: 0.85, false: 0.85}

See examples/sms-triage/ for a runnable pair. Specs are parsed by the same canonical loader the rest of the suite uses (jevassert.packs); golden cases are optional for table specs.

Review is the point

A label auto-accepts only when its probability clears its floor in thresholds. Everything else — low probability, a label with no floor (like unknown), or a question with no thresholds at all — lands in the review column:

data.jev.csv               # your rows + answers + confidence + review + error
data.jev.corrections.csv   # one row per flagged state (needs a human)
data.jev.stats.json/.md    # cost, latency, automation rate, distributions
data.jev.cache.jsonl       # resume cache (delete to force a full re-run)

review lists the flagged question ids (category;urgency), so you can filter in any spreadsheet. category_confidence and urgency_confidence carry the model's confidence for choice/score answers.

Fix a row by editing its answer cell, or delete the id from review to confirm it as-is, then replay — corrected rows make no API calls and their flags clear:

jev-table messages.csv --spec pack.yaml \
  --corrections messages.jev.corrections.csv \
  --emit-cases messages.jev.cases.jsonl

--emit-cases writes the corrected rows as a cases.jsonl in jev-packs format v0: a golden set you can hand to jevassert record for evidence. That is the suite flywheel — jev-table labels, you review, jevassert measures, jev-packs publishes.

How it works

  • Dedupe — identical rows (same state, questions and model) are sent once and fanned back out. Duplicates are free, and corrections list one row per unique state.
  • All questions, one call per row — Jev evaluates every question in parallel against one state; batching is an order of magnitude cheaper and faster than one call per question.
  • Resume — every successful row is appended to the cache, so a rerun only calls what's missing. Errors are never cached and are retried next run.
  • Concurrency — 8 in-flight requests by default, auto-capped when states are large so you stay under the token-rate limit. --concurrency overrides.
  • Cost preview--dry-run estimates tokens across the whole input (~3 chars/token plus per-request framing, calibrated on the live smoke below) and prints expected dollars at $0.042/Mtok input (output is free); every real run reports actual vs estimated tokens (within ~5% on the smoke).
  • Safety cap — files over 10,000 rows need --yes after a dry run.

Retries and 429/529 backoff come from the official TypeSafe SDK.

Live smoke (2026-09-19)

200 random rows from the UCI SMS Spam Collection (CC BY 4.0, Almeida et al. 2011), spec examples/sms-triage/pack.yaml, jev-1.13.0:

  • 97.4% accuracy on the 194 comparable rows (6 unknown excluded) — and the 93 auto-accepted rows were 100% correct: every one of the 5 errors landed in the review queue.
  • 46.5% automated (93/200 rows) with the spec's conservative floors; 107 rows reviewed, 0 errors. Lower the floors if you want more automation.
  • $0.0040 for 200 rows (~477 input tokens/row), 8.9 s wall, per-call latency p50 4.7 s / p95 8.4 s. Dry-run estimated 91,871 tokens vs 95,313 actual (1.04x) — and a rerun costs $0 from the resume cache.

Reproduce:

python scripts/fetch_sms_spam.py sms-spam-sample.csv 200
TYPESAFE_API_KEY=... jev-table sms-spam-sample.csv --spec examples/sms-triage/pack.yaml
python scripts/report_accuracy.py sms-spam-sample.jev.csv --question category

Privacy

Row data (only the state.fields you declared) is sent to the endpoint you configure — https://api.typesafe.ai by default, or anything Jev-compatible via --base-url. Nothing else leaves your machine: no telemetry, no analytics, no uploads. --dry-run sends nothing at all. TypeSafe's data handling: requests are not used for training.

CLI

jev-table INPUT --spec SPEC [--out PATH] [--model NAME] [--base-url URL]
           [--limit N] [--concurrency N] [--dry-run]
           [--corrections PATH] [--emit-cases PATH] [--yes] [--no-cache]

INPUT is a CSV or a flat JSONL (scalar values only; JSONL keeps numbers and booleans as-is in the state sent to Jev).

flag meaning
--spec pack.yaml file or its directory (required)
--out output CSV (default <input>.jev.csv)
--model Jev model or alias (default jev-latest; pin a version to freeze behavior)
--base-url Jev-compatible endpoint (local replicas welcome)
--limit N process the first N rows
--dry-run estimate tokens/cost; sends nothing, needs no key
--corrections PATH replay human edits from a *.corrections.csv (needs _row)
--emit-cases PATH write corrected rows as cases.jsonl (jev-packs format v0)
--yes allow more than 10,000 rows
--no-cache disable the resume cache

Exit codes: 0 done (review rows are fine), 1 at least one row errored, 2 bad input or refused run. Requires TYPESAFE_API_KEY (any non-empty value works for local endpoints).

Non-goals

No web UI, no hosted service, no Excel/PDF (CSV in, CSV out), no auto-written criteria, no telemetry. Columns come from the spec, never from prompts in code.

jev-table vs jev-agent-tool

jev-agent-tool gives you the primitive: evaluate_batch over 1–50 records. jev-table is the workflow on top: dedupe, resume, cost preview, review queue, corrections and stats — for whole files, not batches. If you are writing a script, use the primitive. If you are labeling a spreadsheet, use jev-table.

Development

uv sync --all-groups
uv run ruff check .
uv run pytest
uv run jev-table examples/sms-triage/sample.csv --spec examples/sms-triage/pack.yaml --dry-run

jev-table imports the pack loader from jevassert (jevassert.packs). Locally, uv sync resolves it from the sibling checkout via [tool.uv.sources]; CI resolves it from PyPI (uv sync --no-sources), so jevassert must be published before CI can run.

Live smoke against the public UCI SMS Spam dataset (which ships gold labels):

python scripts/fetch_sms_spam.py sms-spam-sample.csv 200
TYPESAFE_API_KEY=... uv run jev-table sms-spam-sample.csv --spec examples/sms-triage/pack.yaml
uv run python scripts/report_accuracy.py sms-spam-sample.jev.csv --question category

Releases: push a v* tag; GitHub Actions builds and publishes to PyPI via trusted publishing.

License

Apache-2.0. The SMS Spam Collection dataset is CC BY 4.0 (Almeida et al., 2011) and fetched, never vendored.

Release files for jev-table 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jev-table 0.1.1
File Size Uploaded
jev_table-0.1.1.tar.gz 63.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jev-table 0.1.1
File Interpreter ABI Platform
jev_table-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 90.7 kB

Release files / jev_table-0.1.1.tar.gz

Download URL jev_table-0.1.1.tar.gz
Size 63.4 kB
Tags Source
SHA-256 checksum
How to use checksums
6f8b4a4b41b4fa68bcca5ed58791281616b5727f2b9307d31099043b2695ae7b
BLAKE2b-256 checksum
How to use checksums
f101445d42563afe8598d89de80ee87c2bafbb6fa4add6c2b17aa86f31c004e4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / jev_table-0.1.1-py3-none-any.whl

Download URL jev_table-0.1.1-py3-none-any.whl
Size 27.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
00a716fdf2067a55950f83f293dc1bf615a3843188866221fca7aca1fa616731
BLAKE2b-256 checksum
How to use checksums
f4aaca36a8ea8b866c270271ee051cc99647b0d34c50f141fce6c3ec54020a26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page