Created
June 15, 2026 14:36
-
-
Save trashhalo/ea9048155f3a43c2f77a610ed18d25a0 to your computer and use it in GitHub Desktop.
Bosun + node-llama-cpp: the supported completion+logits path (NOT --rerank/LlamaRankingContext). score = P(yes)/(P(yes)+P(no)) at the final token.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Bosun + node-llama-cpp — the SUPPORTED path (completion + logits), NOT the rerank API. | |
| // | |
| // npm install node-llama-cpp | |
| // node bosun.mjs | |
| // | |
| // Why NOT LlamaRankingContext / llama.cpp --rerank: that path uses rank-pooling + a baked rerank | |
| // template whose instruction is HARDCODED — there is no slot for a per-request rule. Bosun's whole | |
| // value is the programmable <Instruct> you supply each call, so the rerank path silently drops it | |
| // (opposite rules score identically). Bosun's GGUF is also a plain causal LM (full vocab head, no | |
| // rank head), so LlamaRankingContext would refuse it anyway. | |
| // | |
| // The contract (serving.json): prompt = prefix + "<Instruct>/<Query>/<Document>" + suffix, then read | |
| // the next-token distribution at the final position and take | |
| // score = P(yes) / (P(yes) + P(no)) == sigmoid(logit_yes - logit_no) | |
| // (renormalized two-class softmax — exact, and avoids needing raw logits). | |
| import {getLlama, resolveModelFile} from "node-llama-cpp"; | |
| const REPO = "Hanno-Labs/bosun-xs-GGUF"; | |
| const MODEL_URI = `hf:${REPO}/Bosun-XS-Q8_0.gguf`; // f16 / Q8_0 / Q4_K_M available | |
| const cfg = await (await fetch(`https://huggingface.co/${REPO}/resolve/main/serving.json`)).json(); | |
| const llama = await getLlama(); | |
| // MODEL_PATH lets you point at an already-downloaded file; otherwise it pulls from HF. | |
| const modelPath = process.env.MODEL_PATH ?? await resolveModelFile(MODEL_URI); | |
| const model = await llama.loadModel({modelPath}); | |
| const context = await model.createContext({contextSize: cfg.max_len}); | |
| const seq = context.getSequence(); | |
| async function score(instruct, query, document) { | |
| const body = `<Instruct>: ${instruct}\n<Query>: ${query}\n<Document>: ${document}`; | |
| const prompt = cfg.prefix + body + cfg.suffix; // suffix already ends with the empty <think> block | |
| const tokens = model.tokenize(prompt, true); // special tokens on (<|im_start|> ...), no BOS | |
| await seq.eraseContextTokenRanges([{start: 0, end: seq.nextTokenIndex}]); // fresh state per pair | |
| // full next-token distribution after the final prompt token; temperature:1 + no truncation = true softmax | |
| const input = tokens.map((t, i) => | |
| i === tokens.length - 1 | |
| ? [t, {generateNext: {probabilities: true, options: {temperature: 1, topK: 0, topP: 1, minP: 0}}}] | |
| : t | |
| ); | |
| const out = await seq.controlledEvaluate(input); | |
| const probs = out[tokens.length - 1].next.probabilities; // Map<token, probability> | |
| const pYes = probs.get(cfg.yes_id) ?? 0; | |
| const pNo = probs.get(cfg.no_id) ?? 0; | |
| return pYes / (pYes + pNo); | |
| } | |
| // the document is an ORDERED pair — FINDING A then FINDING B (direction can matter) | |
| const Q = "These two findings share the specified relationship."; | |
| const sec = "FINDING A:\nThe SEC proposed new disclosure rules for crypto exchanges.\n\n" + | |
| "FINDING B:\nA startup launched an AI tool for drafting marketing emails."; | |
| // same pair, opposite rules -> the score flips, because the instruction IS the rubric | |
| console.log("same-topic rule :", (await score("Connected ONLY if both findings are about the same broad topic. Otherwise not.", Q, sec)).toFixed(3)); | |
| console.log("diff-topic rule :", (await score("Connected ONLY if the two findings are about DIFFERENT topics. Otherwise not.", Q, sec)).toFixed(3)); | |
| await model.dispose(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment