Filtering and ranking a list
from(items) starts an expression about a list. The same conditions given accepts filter it; a scale ranks it; take limits it. The model judges each item once. Partitioning, sorting, and limiting run locally.
Filtering
Section titled “Filtering”.where(condition) states the filter. .and(), .or(), and .unless() refine it exactly as they do on a predicate, with the same three-valued logic. Each item is judged independently, and a false code predicate on an item drops it without a request for that item.
import { from } from "jevlish";import { tickets } from "./support.js";import { reportsProblem, saysResolved } from "./vocabulary.js";
const result = await from(tickets) .seenAs((t) => ({ subject: t.subject, body: t.body })) .where((t) => t.status === "open") .and(reportsProblem) .unless(saysResolved);
result.accepted; // Ticket[]: the condition resolved to true, in input orderresult.rejected; // Ticket[]: resolved to falseresult.uncertain; // Ticket[]: did not meet the policy; unresolved, not rejectedresult.evidence.requests.length; // one request per item that needed the modelThe result has three buckets, and every item lands in exactly one:
accepted: the condition resolved to true.rejected: it resolved to false.uncertain: the policy would not decide. These are not rejected. They are the items you have not yet made a decision about.
evidence covers the whole query: every request that went out and every judgment that was recorded, across all items.
Ranking
Section titled “Ranking”.rankedBy(scale, order) adds a Score question for every item not rejected by code, in the same request as its semantic filter. After the filter resolves, scores are read only for accepted items and accepted is sorted by them. This avoids a second round trip, but an item the model later rejects or leaves uncertain was still scored. order is "highest first" (the default) or "lowest first".
import { from } from "jevlish";import { tickets } from "./support.js";import { disruption, reportsProblem } from "./vocabulary.js";
const queue = await from(tickets) .seenAs((t) => ({ subject: t.subject, body: t.body })) .where((t) => t.status === "open") .and(reportsProblem) .rankedBy(disruption, "highest first") .take(5);
for (const { item, score, normalized } of queue.scored) { item.id; // an accepted ticket, highest disruption first score; // 0 .. disruption.top normalized; // 0 .. 1}
// Ranking alone, with no filter, scores every item.const byDisruption = await from(tickets).rankedBy(disruption, "lowest first");byDisruption.accepted;A ranked result adds scored: each accepted item with its score (0 to scale.top) and normalized score (0 to 1), in ranked order. An item whose score does not meet policy.score.minConfidence moves to uncertain even if its filter passed.
from(items).rankedBy(scale) with no .where() scores everything.
Limiting
Section titled “Limiting”.take(n) keeps the first n of accepted after ranking. It does not reduce what is asked. To spend less, narrow the list in code first, or put a code predicate in .where() so rejected rows never leave the process.
Projection and policy
Section titled “Projection and policy”.seenAs(fn) on from projects each item, the same as on given, and goes before .where(). .withPolicy({...}) overrides thresholds either before or after the query is built.
Inspecting before running
Section titled “Inspecting before running”.plan() on a query reports subjectCount, decidedByCode, and the requests that would be sent, with notes describing the filter, ranking, and limit. See Running and planning.