Getting started
Quick start
From an empty file to a decision your code can act on, in five steps.
This page builds a support-ticket triage for Northwind, a fictional B2B payments API. By the end, a ticket goes in and plain TypeScript decides who gets paged.
Install and set the key
$npm i jevascript.env JEV_API_KEY=apikey_...With the key in the environment, nothing else needs configuring. See Installation for package managers and how to load
.env.Ask a yes/no question
is()takes a proposition and returns a boolean. Write the proposition as a claim about the text, not as a question or a label.triage.ts import { semantic } from "jevascript" const ticket = { plan: "enterprise", subject: "All card payments failing since 14:02", body: "Every charge returns gateway_timeout since 14:02.", } const blocked = await semantic(ticket).is( "The customer cannot take money from their own customers right now.", ) // trueThe state can be a string, an array of strings, or any object. Objects are serialised as JSON, so field names carry meaning too.
Ask for a number
score()returns a number on a range,0to100by default. Under the hood it is a probability mapped onto your range, so the same rules about propositions apply.triage.ts const urgency = await semantic(ticket).score( "A human needs to act on this ticket urgently.", ) // 91The threshold lives in your code, where it can be reviewed and tested:
if (urgency > 80) pageOnCall()Pick one of a fixed set
choose()returns one of the options you pass. The return type is the literal union, with noas constneeded.triage.ts const team = await semantic(ticket).choose(["billing", "integration", "payments", "security"]) // ^? "billing" | "integration" | "payments" | "security"Describing each option instead of naming it improves accuracy noticeably:
const team = await semantic(ticket).choose({ integration: "SDK usage, API errors, webhooks, authentication", payments: "Declines, settlement, payouts, chargebacks", billing: "Our own invoices, pricing, plan changes", security: "Credential exposure, suspicious access, vulnerability reports", })Ask everything at once
Three separate
awaits are three requests.batch()sends them together, and the state travels once:triage.ts import { semantic, is, score, choose } from "jevascript" const t = await semantic(ticket).batch({ urgency: score("A human needs to act on this ticket urgently."), frustration: score("The person writing this sounds frustrated or angry."), blocked: is("The customer cannot take money from their own customers right now."), team: choose({ integration: "SDK usage, API errors, webhooks, authentication", payments: "Declines, settlement, payouts, chargebacks", billing: "Our own invoices, pricing, plan changes", security: "Credential exposure, suspicious access, vulnerability reports", }), }) // One request. Then the consequences are ordinary code. const priority = t.blocked && ticket.plan !== "free" ? "critical" : t.urgency >= 80 || t.frustration >= 90 ? "high" : "normal" if (priority === "critical") pageOnCall() route(t.team)Notice that the plan check is deterministic. The model judges meaning; facts you already have stay in code.
Put it in one file#
In an application, create the instance once and import it everywhere, the same way you would a database client. Naming the export semantic keeps every call site identical to the examples above.
import { createSemantic, jev } from "jevascript"
export const semantic = createSemantic({
provider: jev(),
defaults: { timeoutMs: 5_000, cache: "10m" },
})import { semantic } from "./lib/semantic"
const t = await semantic(ticket).batch({ /* ... */ })createSemantic returns a private instance with its own provider, cache and hooks. It never touches the module-level semantic, so a test can swap the provider without affecting anything else. See Configuration.