jevascript
esc

    Type to search · ↑↓ to move · ↵ to open

    Get started

    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.

    1. 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.

    2. 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.",
      )
      // true

      The state can be a string, an array of strings, or any object. Objects are serialised as JSON, so field names carry meaning too.

    3. Ask for a number

      score() returns a number on a range, 0 to 100 by 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.",
      )
      // 91

      The threshold lives in your code, where it can be reviewed and tested:

      if (urgency > 80) pageOnCall()
    4. Pick one of a fixed set

      choose() returns one of the options you pass. The return type is the literal union, with no as const needed.

      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",
      })
    5. 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.

    lib/semantic.ts
    import { createSemantic, jev } from "jevascript"
    
    export const semantic = createSemantic({
      provider: jev(),
      defaults: { timeoutMs: 5_000, cache: "10m" },
    })
    anywhere.ts
    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.

    Where next#