jevascript
esc

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

    Get started

    Concepts

    Collections

    filter, some, every, rank, find and compare over lists. Which cost one request and which cost N.

    Batching sends many questions about one state. Collections are the other axis: one question about many items. Because a request carries a single state, most collection operations cost one request per item. Two of them do not.

    Cost at a glance#

    OperationRequestsUse when
    find(items, condition)1You want the single best match, or none.
    compare(a, b, { by })1You want to know which of two is better.
    filter(items, condition)NYou need every match.
    some / everyNBuilt on filter.
    rank(items, { by })NYou need the whole order.

    N-request operations run up to concurrency items at a time, 8 by default.

    find() — the best item, in one request#

    const dupe = await semantic.find(openBugs, "This report describes the same bug as the new one.", {
      label: (bug) => `${bug.title}\n${bug.body}`,
    })
    // the matching bug, or undefined

    The items become the options of a single choice. Alongside it travels an existence check: does any candidate actually satisfy the condition? That second question is not optional. Choice probabilities always sum to 1, so something always wins, including when nothing fits. Without the existence check, an empty match returns confident nonsense. find returns undefined when the existence probability is at or below threshold.

    If there are more items than the provider's maxChoiceOptions (255 for Jev), find falls back to rank and returns the top item if it clears the threshold.

    compare() — two values, one criterion#

    const better = await semantic.compare(draftA, draftB, {
      by: "Answers the customer's actual question directly.",
    })
    // "left" | "right" | "equal"

    Both values are sent in one state under left and right. Use label to control how each is rendered.

    filter(), some(), every()#

    const complaints = await semantic.filter(messages, "This message is a complaint.", {
      falseWhen: "A question, a feature request, or praise.",
      concurrency: 4,
    })
    
    await semantic.some(messages, "This message mentions a competitor.")   // boolean
    await semantic.every(messages, "This message is in English.")           // boolean

    Each item is judged in its own request against the condition and kept when its probability exceeds threshold.

    rank() — order by fit#

    const ordered = await semantic.rank(candidates, {
      by: "This candidate fits the query.",
      context: query,
    })
    // [{ item, score }, ...] sorted by score, descending

    context is shared state every candidate is judged against, sent with each one as { context, candidate }. The same operation is available on a context, with the wrapped value as the context:

    const ordered = await semantic(query).rank(candidates, { by: "This candidate fits the query." })

    Options#

    OptionTypeDefaultDescription
    label(item, index) => string

    How to render an item as state. Strings pass through; other values default to JSON.

    concurrencynumber8

    Items evaluated at the same time for N-request operations.

    thresholdnumber0.5

    Probability above which an item counts as matching.

    trueWhenstring

    What clearly counts (filter, rank).

    falseWhenstring

    What clearly does not (filter, rank).

    cachestring | number

    TTL per item answer.

    timeoutMsnumber

    Per-request timeout.

    samplesnumber

    Rounds per item. Multiplies the request count.

    providerSemanticProvider

    Override the provider for this call.

    When items are small: pack them yourself#

    If each item is short and there are not too many, putting them all into one state and asking one question per item is a single request instead of N. The RAG gate scenario does exactly this with twelve passages:

    const context = semantic({ question, passages: { p0: "...", p1: "...", p2: "..." } })
    
    const verdicts = await context.batch({
      p0: is("Passage p0 contains information that helps answer the question."),
      p1: is("Passage p1 contains information that helps answer the question."),
      p2: is("Passage p2 contains information that helps answer the question."),
    })

    The trade-off is context size: every passage is in every question's state. It pays off while the whole set fits comfortably in maxStateTokens.

    Where they live#

    Collections are available three ways, all with the same signatures:

    import { filter, find } from "jevascript"       // bound to the module-level instance
    semantic.filter(items, "...")                    // on a createSemantic() instance
    semantic(query).rank(items, { by: "..." })       // rank, with the wrapped value as context