laya

receptronMIT活跃维护

在 Node.js/TypeScript 里通过 ONNX Runtime 本地跑 Laya 这个兼容 Jev 的开源 System-1 决策模型,做分类和推理,不用依赖云端。

Run Laya, the open-source Jev-compatible System-1 decision model, from Node.js / TypeScript via ONNX Runtime

v0.2.0发布于 2026-10-17 00:18 UTC1,062star +12 / 7 天88fork同步于 2026-11-10 17:53 UTC

怎么上手

由 README 提炼,执行前请确认
npm install @receptron/laya
node -e 'import("@receptron/laya").then((m) => m.ensureBundle())'
硬件要求按 README 说明

README 未说明 GPU 要求,默认使用 cpu 执行提供器;ONNX 权重约 1.7 GB(fp32),加载后约需 2 GB 内存,每批问题另需几百 MB。

支持 Apple Silicon
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

在 Node.js/TypeScript 里用 ONNX Runtime 跑 Laya 决策模型,一次前向就给出带概率的结构化答案

在 Node.js/TypeScript 里通过 ONNX Runtime 本地跑 Laya 这个兼容 Jev 的开源 System-1 决策模型,做分类和推理,不用依赖云端。

核心能力

  • 支持 choice、score、noul 三类带校准概率的问题
  • 单次 systemOne 调用把所有问题合并成一次批量推理
  • 用 ONNX Runtime 执行,运行时不需要 Python 和 PyTorch
  • 输出与 Python 参考实现对齐到小数点后四位
  • 提供 ensureBundle 预下载权重,便于构建或镜像预热

容易踩的坑

首次 Laya.load() 会从 Hugging Face 下载约 1.7 GB 权重并缓存到 ~/.cache/receptron-laya,耗时要把下载算进去;每个问题的选项须在 head_max_len(192)token 内,超出会抛错;choice 以列表给出的标签必须互不相同,重复会抛错。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

2 个版本v0.2.0
  1. v0.2.0最新正式版2026-10-17 · 2026-10-17 00:18 UTC

    Calibration fix for large choice questions, closer parity with upstream laya, and a new answer_confidence field.

    Calibration

    • choice questions with 11+ options are no longer ~99% confident. The upstream checkpoint ships choice:11+ = 0.1006, which multiplied those logits by ~10 (#10). The published bundle (receptron/laya-onnx) now uses 0.945, the held-out fit for the English checkpoint (NandhaKishorM/laya#1245). The cached config refreshes on the next Laya.load() with network access.
    • Temperatures are clamped to [0.5, 5] at load and again at the point of use, so a malformed bundle or a later edit to laya.config cannot bypass it. Out-of-range or invalid values print a warning; an invalid per-option entry falls back to the question type's temperature. Bundles without temperature_by_options (fine-tuned exports) now load (#11, thanks @kuishou68).
    • export/export_onnx.py replaces the known-bad choice:11+ value, so a re-export reproduces the published config.

    Confidences for 11+ option choice questions intentionally differ from the Python laya package, which clamps the same entry to 0.5.

    New

    • answer_confidence on every answer (choice, score, noul): max(p), the probability of the reported answer. It is the quantity the temperatures are fitted for and means the same thing across question types, so use it for confidence gating. confidence on choice / score is still 1 − normalized entropy.

    Fixes (parity with upstream laya)

    • A single-option choice no longer crashes in the ONNX graph's TopK node.
    • Object instructions are serialized with Python json.dumps separators, matching the reference tokenization.
    • A list (conversation) state that does not fit keeps its newest turns, as in training.

    Behavior changes

    • Duplicate or null labels in a list-form choice (["a", "a"], ["1", 1]) now throw instead of being silently merged into fewer options.
    • Long array states are truncated from the start instead of the end.
    • Answers for object-valued instructions shift slightly (they now match Python).

    Full changelog: https://github.com/receptron/laya/compare/v0.1.2...v0.2.0

  2. v0.1.12026-09-19 · 2026-09-19 15:37 UTC
    查看更新说明

    First public release of @receptron/laya on npm.

    • Laya.load() / systemOne() — Jev-compatible choice / score / noul answers from Node.js via ONNX Runtime, matching the Python reference to four decimal places
    • ONNX bundle download from Hugging Face with local caching, or modelDir for a local export
    • export/export_onnx.py to build the bundle from the Hugging Face checkpoint

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    @receptron/laya

    Run Laya — the open-source, Jev-compatible System 1 decision model by Convai Innovations — from Node.js / TypeScript.

    Laya does not generate text. You hand it a state (a ticket, an email, a JSON object) and typed questions, and it returns every answer with calibrated probabilities in one forward pass:

    • choice — pick one option, with a probability per option
    • score — an expected level on an ordered rubric, with the distribution
    • noul — a calibrated P(true) for a yes/no statement

    This package runs the model with ONNX Runtime; PyTorch and Python are not needed at runtime. The request/response shape is the same as the Python reference implementation (RLAgent.system_one) and as TypeSafe Jev's system_one API, and the output matches the Python implementation to four decimal places.

    Install

    npm install @receptron/laya
    

    Node.js 20 or newer; CI runs on Node 20, 22 and 24 (Linux, macOS, Windows). The @types/node version in devDependencies is only for type-checking this package and does not require Node 24.

    The ONNX weights (about 1.7 GB, fp32) are downloaded from Hugging Face on first use and cached under ~/.cache/receptron-laya (override with LAYA_CACHE). Budget roughly 2 GB of RAM for the loaded model plus a few hundred MB per batch of questions.

    The first Laya.load() therefore includes that download, so time it after the cache is warm. To fetch the weights ahead of time (in a build step or container image), call ensureBundle():

    node -e 'import("@receptron/laya").then((m) => m.ensureBundle())'
    

    Later loads only send a cheap freshness check and read from the cache (about 4 s on an Apple M4, measured in #13).

    Usage

    import { Laya } from "@receptron/laya";
    
    const laya = await Laya.load();
    
    const result = await laya.systemOne(
      { subject: "Refund not received", body: "I cancelled two weeks ago and still have no refund..." },
      {
        department: {
          type: "choice",
          instructions: "Which team should handle this ticket?",
          criteria: { billing: "payments, refunds, invoices", support: "product help and bugs", sales: "new purchases" },
        },
        urgency: {
          type: "score",
          instructions: "How urgent is this ticket?",
          criteria: ["not urgent", "somewhat urgent", "urgent", "critical"],
        },
        churn_risk: { type: "noul", instructions: "Is the customer likely to cancel or dispute?" },
      },
    );
    
    result.answers.department.choice; // "billing"
    result.answers.department.probabilities; // { billing: 0.9415, support: 0.031, sales: 0.0275 }
    result.answers.urgency.score; // 1.3886   (expected level, 0..3)
    result.answers.churn_risk.noul; // 0.0988   (P(true))
    result.answers.department.answer_confidence; // 0.9415   (max probability, on every answer type)
    result.usage.input_tokens; // 267
    
    await laya.close();
    

    The answer types follow the question types, so result.answers.department is a ChoiceAnswer and result.answers.churn_risk a NoulAnswer without any casting.

    To gate on confidence, use answer_confidence: the probability of the reported answer (max(p)), which is the quantity the checkpoint's temperatures are fitted for and means the same thing for every question type. confidence on choice / score answers is a different quantity (1 − normalized entropy).

    Options

    await Laya.load({
      modelDir: "./onnx", // use a local export instead of downloading (see below)
      repo: "receptron/laya-onnx", // Hugging Face repo that holds the ONNX bundle
      subfolder: "multilingual", // a checkpoint variant inside that repo
      revision: "main", // pin a commit hash for reproducible results; "main" follows the repo
      cacheDir: "/var/cache/laya",
      token: process.env.HF_TOKEN, // for private repos
      onProgress: ({ file, received, total }) => {}, // download progress
      executionProviders: ["cpu"], // onnxruntime-node execution providers
      sessionOptions: { intraOpNumThreads: 4 },
    });
    

    Every question of one systemOne call is batched into a single run; a call with three questions takes about 140 ms on an Apple-silicon CPU once the model is warm.

    Exporting the ONNX bundle yourself

    export/export_onnx.py turns the Hugging Face checkpoint (ModernBERT encoder + Laya's decision head) into one ONNX graph and copies the tokenizer and calibration values next to it. You only need this to build a bundle from a newer checkpoint or from a variant that is not published:

    cd export
    uv venv -p 3.12 .venv
    uv pip install -p .venv/bin/python torch transformers safetensors onnx onnxscript onnxruntime huggingface_hub
    .venv/bin/python -c "from huggingface_hub import snapshot_download; snapshot_download('convaiinnovations/laya', local_dir='model', allow_patterns=['model.safetensors','encoder/*','tokenizer/*','rl_agent_config.json','rl_common.py','rl_agent_api.py'])"
    .venv/bin/python export_onnx.py model ../onnx   # prints the max logit difference vs. PyTorch (≈1e-5)
    

    Then Laya.load({ modelDir: "./onnx" }). The bundle is the five files listed in BUNDLE_FILES: laya.onnx, laya.onnx.data, laya_config.json, tokenizer/tokenizer.json, tokenizer/tokenizer_config.json.

    Limits

    • Each question's options must fit in head_max_len (192) tokens; systemOne throws otherwise. Fewer than about 20 options per choice question is the model's own recommendation.
    • The state is truncated to max_len (512 tokens for the English checkpoint) after the question header. A string or object state keeps its start; a list state (a conversation) keeps its newest end, as in training.
    • choice labels given as a list must be distinct; a repeated label throws instead of being merged.
    • Temperatures are clamped to [0.5, 5]. The published bundle uses 0.945 for choice questions with 11 or more options, where the upstream checkpoint ships 0.1006 (clamped to 0.5 by the Python laya package), so confidences for those questions differ from upstream by design.
    • A JSON state is serialized like Python's json.dumps(ensure_ascii=False) so that tokens match the reference implementation; non-integer numbers may format differently between JS and Python.

    Development

    yarn install
    yarn test        # unit tests; the model test runs when ./onnx holds a bundle (or LAYA_MODEL_DIR)
    yarn typecheck
    yarn build
    LAYA_MODEL_DIR=./onnx yarn example
    

    License

    MIT. The Laya model weights are published by Convai Innovations under Apache 2.0.