Jevex

Jevex builds a semantic index over Convex tables using Jev, storing AI classification answers as queryable indexed data so reads never call a model.

Installation

npm install @mbilskilets/jevex

About Jevex

Jevex builds a semantic index over Convex tables by sending rows to the Jev classification API when data changes and storing typed answers in an indexed table. Queries filter and sort by those answers using normal indexed reads, with no model calls at query time. It handles batching, caching, stale state, and reactivity automatically through a Convex trigger on your mutation.

Benefits

Use cases

how to filter Convex table rows by AI classification without calling a model on every read

Jevex stores Jev classification answers in an indexed Convex table keyed by question, label, and value. You define questions like 'is this a bug report?' using noul, choice, or score helpers, and Jevex writes answers when rows change via a convex-helpers trigger. Reads like feedbackJudge.top(ctx, 'kind', { label: 'bug' }) hit an index range and never call a model.

how to triage support tickets automatically in Convex

Jevex lets you define a semantic index over a feedback or tickets table with questions such as category classification and churn probability. When a new message is inserted, the trigger queues it for Jev, and once answered the onJudged callback fires so you can alert on high-churn scores or route to a queue. The demo inbox sorts messages into bugs, requests, billing, questions, and praise in under one second per message.

how to run content moderation in Convex without blocking writes

Jevex hooks into your mutation via a convex-helpers trigger and queues rows for Jev asynchronously so your transaction is never blocked on a network call. You define spam and abusive questions as noul scores, then query postJudge.top(ctx, 'spam', { min: 0.3, max: 0.7 }) to surface uncertain posts for human review while keeping clean posts live.

semantic search and classification on Convex database tables

Jevex is not a vector similarity search tool. It runs structured Jev questions against row content on write and stores typed answers in an indexed table. You get classification, scoring, and multi-label filtering over large tables using Convex index ranges, which scales the same way on 100 rows as on 1 million rows.

Frequently asked questions

Does Jevex make model API calls during Convex queries?

No. Jevex stores Jev answers in a Convex table with an index on (index, question, label, value). Queries like feedbackJudge.top(ctx, 'churn', { min: 0.8 }) read an index range and return immediately. Model calls happen on write, triggered once per changed row, and results are cached so identical content is never sent to Jev twice.

How does Jevex avoid slowing down Convex mutations?

The trigger that Jevex registers with convex-helpers does a few indexed reads, a hash check, and one insert inside the same transaction as your mutation. No network call happens in the transaction. If the state hash is unchanged the trigger returns early. New rows are queued and processed by a dispatcher that batches up to 20 rows per Jev request with a 100 ms debounce.

What API key do I need to use Jevex?

Jevex requires access to the Jev model, which is available through four providers: TypeSafe (TYPESAFE_API_KEY), Vercel AI Gateway (AI_GATEWAY_API_KEY), OpenRouter (OPENROUTER_API_KEY), or the Convex AI Gateway (JEV_PROVIDER=convex, no key needed). The Convex AI Gateway option only works on cloud deployments with the gateway enabled, not on a local backend.

What question types does Jevex support for classification?

Jevex exposes three question helpers: noul for a 0-to-1 probability score ('the user is likely to cancel'), choice for a typed enum label ('what is this feedback about?' with options like bug, feature, or billing), and score for an ordered categorical value ('how urgent is this?'). Each answer type is reflected in the TypeScript types returned by index.get and index.top.

How does Jevex handle a row that changes while Jev is processing the previous version?

Each batch carries a claim token tied to the row's content at the time it was queued. If the row is edited before Jev responds, the late answer is written to the cache but dropped for that row, and the new content is queued separately. The most recent edit always wins. Jevex also runs a cron that re-queues any rows whose batch disappeared without reporting back, such as after a backend restart.

Links