"""Vela 2.0 decoder member: package-free inference (drop this file next to the checkpoint). Dependencies: numpy, tokenizers; torch + transformers (Qwen3.5) + safetensors for the model (modeling_vela2.py). Optional: flash-linear-attention (`fla`) for the Gated-DeltaNet kernel on GPU (the research evaluation kernel). No ONNX backend. Loading: m = transformers.AutoModel.from_pretrained(repo_id_or_dir, trust_remote_code=True) (modeling_vela2.Vela2DecoderModel: same methods as below, delegated to this engine on the model's device; parameters FP32) or Vela2.from_pretrained(repo_id_or_dir, device="cuda") (bf16 autocast on GPU by default) A Hub repo id is fetched with huggingface_hub.snapshot_download. Three interfaces over one engine (the public API of the Vela 2.0 encoder release, unchanged): SystemOne (TypeSafe POST /v1/systemone request / response; the primary interface; vela2_serve.py serves it over HTTP) m.system_one("I was charged twice.", {"billing": {"type": "noul", "instructions": "Is this about billing?"}}) -> {"model", "answers": {"billing": {"type": "noul", "noul": 0.97}}, "usage": {...}} Choice / Noul / Score as in SystemOne; extension types "span" and "set" add "spans" / "sets" / "thresholds" to the response plus a Noul view of each in "answers"; a JSON-object state is mapped onto typed parts (SYSTEM_ONE_NOTES). GLiNER2-style m.classify_text(text, ["positive", "negative"]) -> {"label": "positive"} m.extract_entities(text, ["person", "company"], threshold=None) -> {"entities": {"person": [...]}} m.detect_hallucination(context, answer, question=None) -> unsupported spans of the answer m.score_relevance(query, document) -> relevance in [0, 1] + distribution Kai/Jev-style JSON m.predict({"parts": [{"type": "user"|"context"|"answer", "text": ...}], "questions": [{"id", "type": "choice"|"set"|"score"|"span", "text", "options" | "labels": [{"name", "description"}], "target_part": "user" | [...]}]}) -> {"answers": [...], "meta": {...}} (probabilities per option; spans with code-point char offsets) Everything below reproduces the research decoder scorer (dec_scorer_d3_20261001.D3Scorer over train_vela2_decoder_d3_20261001.render_row(train=False), schema_lib_r4 truncation order and word units, span_decode_r4 decoder) without importing it. Layout (DEV2.0 prompt form, one context pass + one block per question): parts "Context:\\n" + per part '\\n' TEXT '\\n\\n' block "\\n\\nTask type: T\\nTarget: R\\nQuestion:\\nQ\\nOptions:" + per option "\\n" [+ the [ABS] option] + "\\n\\nSelect the single option ... / Select every option ...\\nDecision:" span "\\n\\nTask type: span\\nTarget: R\\nQuestion:\\nQ\\nLabels:" + per label "\\n" [layout c: + "\\n\\nText:\\n" + the target segment again] + "\\n\\nMark every span ...\\nSpans:" Every question block attends to the parts and to itself only (tree forward, modeling_vela2), so a question's answer does not depend on the other questions of the request (bundled == single). Span targets of <= 2,048 tokens use layout c (the target repeated after the labels); longer targets are scored in windows of <= 1,800 tokens (stride 1,536, at word starts), each its own layout-c sequence, word logits averaged over the windows that cover a word. Inputs are limited to 16,384 tokens (the part no question is asked over is shortened first, then the labelled parts). Calibration: one temperature per question type, dev thresholds, the PII length rule + sparse gate (calibration.json), exactly as the encoder release. Probabilities of a choice / score question are over the real options; the [ABS] (abstain) probability is reported separately (softmax including [ABS]). Optional (off by default): calibration.json `noul_calibration`, one temperature + bias on the tempered yes-logit fitted for general-decision noul questions, noul = sigmoid(logit(P(yes)) / T + b), applied to SystemOne noul answers only when enabled with Vela2(..., noul_calibration=True) or VELA2_NOUL_CALIBRATION=1 (no extra forward pass; nothing else changes). Two span heads (2H round, research/vela2_twohead_rule_20261003.md): router the trained span head v2 of the release checkpoint (span2.*; PII, hallucination, toxic spans), unchanged; broad a second span head (same SpanHeadV2 form, own weights, FP32) on the same frozen backbone, trained for open labels (NER, relations, linking mentions, extractive evidence); weights in broad_head.safetensors (span_broad.*, attached as model.span_broad by modeling_vela2.attach_broad_head), calibration in calibration.json "broad_head" (temperature, threshold). Dispatch per span question (span_head_for): "head": "router" | "broad" overrides; else question id pii / halu / toxic, a label set exactly equal to a trained router set (the 17 PII types, {unsupported}, the 47 toxic categories), a non-empty subset of the 17 PII types, or one label that is a hallucination alias (unsupported, unsupported claim, hallucinated, hallucination, hallucinated span, not supported, unsupported span, fabricated, fabrication; case-insensitive, ignoring _ / - / spaces; asked as the trained "unsupported" label, answered with the user's label name) -> router; every other span question -> broad (router when the broad head is not loaded). The span answer names its head ("head", "head_reason"). Rows are rendered and batched exactly as in the release module and only the span block's head differs, so every decision output and every router span output is bit-identical to the release module (with or without broad questions in the same request). """ import json import math import os import threading import random import re from collections import defaultdict from pathlib import Path import numpy as np ROLES = ("user", "context", "answer") # ===================================================================================================================== # decoder layout (schema_lib_dec_20260930; DEV2.0 canonical JSON) # ===================================================================================================================== SUFFIX = {"choice": "\n\nSelect the single option best supported by the context and instructions.\nDecision:", "score": "\n\nSelect the single option best supported by the context and instructions.\nDecision:", "set": "\n\nSelect every option supported by the context and instructions.\nDecision:", "span": "\n\nMark every span of the target text that carries one of the labels.\nSpans:"} ABS_KEY, ABS_DESC = "[ABS]", "None of the shown options applies." PARTS_HEAD = "Context:\n" SEG_CLOSE = "\n\n" TEXT_HEAD = "\n\nText:\n" HYBRID_RMAX = 2048 # span target <= this many tokens: layout c on the whole target WIN, STRIDE, RMAX = 1800, 1536, 2048 # longer targets: windows (schema_lib_d3_20261001) def canonical(value): return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False) def seg_open(role): return f'\n' def option_block(key, desc, tag="option"): return f"\n<{tag}>\n" + canonical({"key": key, "description": desc}) + f"\n" def q_head(typ, over, qtext, span=False): tgt = ", ".join(over) if isinstance(over, list) else over return f"\n\nTask type: {typ}\nTarget: {tgt}\nQuestion:\n{qtext}\n" + ("Labels:" if span else "Options:") def span_windows(ft, n, win=WIN, stride=STRIDE): """Token windows [a, b) over a part of n tokens, starting at word starts (ft = first token of each word); b cut at a word start so no word is split. Covers every word at least once (schema_lib_d3_20261001.windows).""" starts = sorted(set(int(x) for x in ft)) if not starts: return [(0, n)] out, a = [], starts[0] while True: lim = a + win nxt = [s for s in starts if s > a and s < lim] b = n if lim >= n else (max(s for s in starts if s < lim and s > a) if nxt else min(n, lim)) out.append((a, b)) if b >= n: break cand = [s for s in starts if s >= a + stride and s < b] a = cand[0] if cand else b return out def window_part(part, a, b): role, ids, offs, words = part wo, ft = words sel = (ft >= a) & (ft < b) return (role, ids[a:b], None if offs is None else offs[a:b], (wo[sel], ft[sel] - a)) def assemble_budget(questions, elabels, segments, max_len, protected, shrink, prompt_floor=64): """schema_lib_r4.assemble, eval path (cap_schema=False): the truncation order on the decoder's token costs. Returns the kept segment lengths and the truncation record (the decoder renders its own ids).""" qs = list(questions) seg = [(r, len(t)) for r, t in segments] fixed = 2 + 1 + len(seg) tr = {"shrink_cut": 0, "protected_cut": 0} def over(): return fixed + sum(q["len"] for q in qs) + sum(e["len"] for e in elabels) + sum(n for _, n in seg) - max_len si = next((i for i, (r, _) in enumerate(seg) if r == shrink), None) if over() > 0 and si is not None: keep = max(min(seg[si][1], prompt_floor), seg[si][1] - over()) tr["shrink_cut"] += seg[si][1] - keep; seg[si] = (seg[si][0], keep) if over() > 0 and si is not None: keep = max(1, seg[si][1] - over()) tr["shrink_cut"] += seg[si][1] - keep; seg[si] = (seg[si][0], keep) if over() > 0: for i, (r, n) in enumerate(seg): if r in protected and over() > 0: keep = max(1, n - over()) tr["protected_cut"] += n - keep; seg[i] = (r, keep) if over() > 0: raise ValueError(f"schema alone does not fit max_length ({over()} tokens over)") return dict(seg_lens=dict(seg), trunc=tr) # ===================================================================================================================== # word units (schema_lib_r4._WORD, GLiNER2 WhitespaceTokenSplitter + CJK char level, r4 URL rule) # ===================================================================================================================== _CJK = ("⺀-⿿ -〿぀-ヿ㄀-ㇿ㐀-䶿一-鿿ꀀ-꓏" "가-힯豈-﫿︰-﹏＀-￯") _TRAIL = ".,;:!?)\\]}>\"'”’»" _WORD = re.compile(rf"""(?:https?://|www\.)[^\s{_CJK}]+?(?=[{_TRAIL}]*(?![^\s{_CJK}])) |[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{{2,}} |@[a-z0-9_]+ |[{_CJK}] |[^\W{_CJK}]+(?:[-_][^\W{_CJK}]+)* |\S""", re.VERBOSE | re.IGNORECASE) def split_words(text): return [(m.start(), m.end()) for m in _WORD.finditer(text)] def word_first_tokens(text, offs): """(word char offsets (W, 2), index of each word's first overlapping sub-word token (W,)) (schema_lib_r4).""" w = split_words(text) if not w or offs is None or len(offs) == 0: return np.zeros((0, 2), np.int32), np.zeros(0, np.int32) offs = np.asarray(offs) st, en = offs[:, 0], offs[:, 1] valid = en > st ends = np.where(valid, en, -1) wo, ft = [], [] cummax_end = np.maximum.accumulate(ends) for a, b in w: t = int(np.searchsorted(cummax_end, a, side="right")) while t < len(offs) and not valid[t]: t += 1 if t < len(offs) and st[t] < b: wo.append((a, b)); ft.append(t) return np.asarray(wo, np.int32).reshape(-1, 2), np.asarray(ft, np.int32) # ===================================================================================================================== # span decoder (span_decode_r4.decode_v2; word readout -> extend=False) # ===================================================================================================================== _UBODY = rf"[^\s\]\)\}}>「」『』\"'“”{_CJK}]" _UNIT = re.compile(rf"(?:https?://|www\.){_UBODY}+?(?=[.,;:!?]*(?!{_UBODY}))" rf"|(?:(?![{_CJK}])[^\W_]|[@._\-+])+" rf"|\S") _ALNUM = re.compile(r"[^\W_]") EDGE = set("[](){}<>「」『』【】《》〈〉\"'“”‘’«»‹›") _URL_START = re.compile(r"(?:https?://|www\.)", re.IGNORECASE) _CJK_RE = re.compile(rf"[{_CJK}]") URL_TRAIL = set(".,;:!?)") def _units(text): u = np.full(len(text) + 1, -1, dtype=np.int64) for k, m in enumerate(_UNIT.finditer(text)): u[m.start():m.end()] = k return u def _first_nonspace(text, a, b): while a < b and text[a].isspace(): a += 1 return a def _trim_edges(text, s, e): while s < e and (text[s].isspace() or text[s] in EDGE): s += 1 while e > s and (text[e - 1].isspace() or text[e - 1] in EDGE): e -= 1 return s, e def _trim_url(text, s, e): if not _URL_START.match(text, s): return s, e m = _CJK_RE.search(text, s, e) if m: e = m.start() while e > s and (text[e - 1] in URL_TRAIL or text[e - 1] in EDGE or text[e - 1].isspace()): e -= 1 return s, e def _trim(text, s, e): s, e = _trim_edges(text, s, e) return _trim_url(text, s, e) def decode_spans(probs, offs, names, text, thr, fill=True, extend=False, unit_vote=True): """span_decode_r4.decode_v2 with the scorer's word-readout arguments (extend=False, trim_edges=True, no bridge).""" probs = np.asarray(probs, dtype=np.float32); offs = np.asarray(offs) T = len(offs) if T == 0: return [] valid = offs[:, 1] > offs[:, 0] k = probs.argmax(1); pk = probs[np.arange(T), k] lab = np.where(valid & (pk > thr), k, -1) u = _units(text) tu = np.full(T, -1, dtype=np.int64) alnum = np.zeros(T, dtype=bool) for t in range(T): if not valid[t]: continue a, b = int(offs[t, 0]), int(offs[t, 1]) f = _first_nonspace(text, a, b) tu[t] = u[f] if f < b and f < len(text) else -1 alnum[t] = bool(_ALNUM.search(text[a:b])) if unit_vote or fill or extend: t = 0 while t < T: if tu[t] < 0: t += 1; continue t2 = t while t2 + 1 < T and (tu[t2 + 1] == tu[t] or not valid[t2 + 1]): t2 += 1 idx = [i for i in range(t, t2 + 1) if valid[i]] L = [i for i in idx if lab[i] >= 0] if L: if unit_vote and len({int(lab[i]) for i in L}) > 1: sc = {} for i in L: sc[int(lab[i])] = sc.get(int(lab[i]), 0.0) + float(pk[i]) w = max(sc, key=sc.get) for i in L: lab[i] = w if fill: for c in set(int(lab[i]) for i in L): pos = [i for i in L if lab[i] == c] for i in idx: if pos[0] < i < pos[-1] and lab[i] < 0: lab[i] = c if extend: for c in set(int(lab[i]) for i in idx if lab[i] >= 0): pos = [idx.index(i) for i in idx if lab[i] == c] if extend == "unit": an = [j for j, i in enumerate(idx) if alnum[i]] lo, hi = min([pos[0]] + an[:1]), max([pos[-1]] + an[-1:]) if all(lab[idx[j]] < 0 or lab[idx[j]] == c for j in range(lo, hi + 1)): for j in range(lo, hi + 1): lab[idx[j]] = c else: j = pos[0] - 1 while j >= 0 and lab[idx[j]] < 0 and alnum[idx[j]]: lab[idx[j]] = c; j -= 1 j = pos[-1] + 1 while j < len(idx) and lab[idx[j]] < 0 and alnum[idx[j]]: lab[idx[j]] = c; j += 1 t = t2 + 1 spans, cur = [], None for t in range(T): if not valid[t]: continue c = int(lab[t]) if cur is not None and c == cur[0]: cur[2] = int(offs[t, 1]); cur[3].append(t); continue if cur is not None: spans.append(cur) cur = [c, int(offs[t, 0]), int(offs[t, 1]), [t]] if c >= 0 else None if cur is not None: spans.append(cur) res = [] for c, s, e, ts in spans: s, e = _trim(text, s, e) if e <= s: continue res.append(dict(start=s, end=e, label=names[c], probability=float(np.mean(probs[ts, c])))) return res def _softmax(x): x = np.asarray(x, dtype=np.float64); e = np.exp(x - x.max()); return e / e.sum() def _sigmoid(x): return 1 / (1 + np.exp(-np.asarray(x, dtype=np.float64))) # ===================================================================================================================== # span-head dispatch (2H) # ===================================================================================================================== BROAD_FILE = "broad_head.safetensors" ROUTER_SPAN_IDS = ("pii", "halu", "toxic") SPAN_HEADS = ("router", "broad") HALU_LABEL = "unsupported" HALU_ALIASES = frozenset({"unsupported", "unsupportedclaim", "hallucinated", "hallucination", "hallucinatedspan", "notsupported", "unsupportedspan", "fabricated", "fabrication"}) # normalised: lower case, no "_", "-", spaces PII_TYPES = frozenset({"AGE", "CREDIT_CARD", "DATE_TIME", "DOMAIN_NAME", "EMAIL_ADDRESS", "GPE", "IBAN_CODE", "IP_ADDRESS", "NRP", "ORGANIZATION", "PERSON", "PHONE_NUMBER", "STREET_ADDRESS", "TITLE", "US_DRIVER_LICENSE", "US_SSN", "ZIP_CODE"}) def _norm_label(name): return re.sub(r"[\s_\-]+", "", str(name)).lower() def halu_alias(q): """The user's label name when the span question has exactly one label that is a hallucination alias, else None.""" names = list(q.get("labels") or {}) return names[0] if len(names) == 1 and _norm_label(names[0]) in HALU_ALIASES else None def span_head_for(q, router_label_sets, broad_available=True): """Span question (generic contract: id, labels, optional head) -> (head, reason). In order: "head" override; id pii / halu / toxic; label set equal to a router set (17 PII types, {unsupported}, the toxic labels); a non-empty subset of the 17 PII types; one label that is a hallucination alias (HALU_ALIASES); else broad (router when the broad head is not loaded).""" h = q.get("head") if h is not None: if h not in SPAN_HEADS: raise ValueError(f"head must be one of {list(SPAN_HEADS)}, got {h!r}") if h == "broad" and not broad_available: raise ValueError("the broad span head is not loaded (needs broad_head.safetensors)") return h, "override" if q.get("id") in ROUTER_SPAN_IDS: return "router", "id" names = frozenset(q.get("labels") or {}) if any(names == s for s in router_label_sets): return "router", "label_set" if names and names <= PII_TYPES: return "router", "pii_subset" if halu_alias(q) is not None: return "router", "halu_alias" if not broad_available: return "router", "broad_unavailable" return "broad", "default" # ===================================================================================================================== # backend # ===================================================================================================================== class _TorchBackend: def __init__(self, path, device="cpu", amp_bf16=False, model=None): import torch self.torch = torch if model is None: import sys sys.path.insert(0, str(path)) try: # try-wrapped so transformers' remote-code import check skips it (standalone path only) from modeling_vela2 import Vela2DecoderModel except ImportError: raise model = Vela2DecoderModel.from_pretrained(path).to(device) self.model = model.eval() self.amp_bf16 = amp_bf16 if getattr(model, "span_broad", None) is None and (Path(path) / BROAD_FILE).exists(): import sys sys.modules[type(model).__module__].attach_broad_head(model, Path(path) / BROAD_FILE) self.has_broad = getattr(model, "span_broad", None) is not None self._lock = threading.Lock() def __call__(self, recs): """recs carry "head" ("router" | "broad"); with no broad span block this is the release call itself. Otherwise span2 is replaced for this one run_batch by a switch that sends the k-th span block to the head of its row.""" seq = [r.get("head") or "router" for r in recs for b in r["blocks"] if b["kind"] == "span" and not b.get("skip")] if "broad" not in seq: return self.model.run_batch(recs, amp_bf16=self.amp_bf16) if not self.has_broad: raise ValueError("the broad span head is not loaded (needs broad_head.safetensors)") router, broad = self.model.span2, self.model.span_broad class _Switch(self.torch.nn.Module): def __init__(self): super().__init__() self.i = 0 def forward(self, w, lab): h = seq[self.i]; self.i += 1 return (broad if h == "broad" else router)(w, lab) sw = _Switch() with self._lock: self.model.span2 = sw try: out = self.model.run_batch(recs, amp_bf16=self.amp_bf16) finally: self.model.span2 = router assert sw.i == len(seq), (sw.i, len(seq)) return out # ===================================================================================================================== # engine # ===================================================================================================================== class Vela2: PII_QUESTION = "Which spans are personal information?" def __init__(self, path, device="cpu", amp_bf16=None, token_budget=None, batch_size=None, model=None, revision=None, backend="torch", noul_calibration=None): """path: a release dir or a Hub repo id. device: "cpu" | "cuda" | "cuda:N". amp_bf16: bf16 autocast for the backbone (default: on for GPU = the research evaluation setting, off on CPU = FP32); the heads always run in FP32. model: an already loaded modeling_vela2.Vela2DecoderModel to run (`device` should then be the model's device). noul_calibration: True applies calibration.json noul_calibration to SystemOne noul answers, False never; None (default) = env VELA2_NOUL_CALIBRATION=1 / 0 if set, else the block's "default" (false in this release).""" from tokenizers import Tokenizer if backend != "torch": raise ValueError("the decoder members have a torch backend only (no ONNX export)") if not Path(path).is_dir(): from huggingface_hub import snapshot_download need = ["config.json", "calibration.json", "tokenizer.json", "tokenizer_config.json"] if model is None: need += ["*.safetensors", "model.safetensors.index.json", "modeling_vela2.py", "configuration_vela2.py", "vela2_inference.py"] else: need += [BROAD_FILE] path = snapshot_download(str(path), revision=revision, allow_patterns=need) path = Path(path) self.path = path self.config = json.loads((path / "config.json").read_text()) self.cal = json.loads((path / "calibration.json").read_text()) if noul_calibration is None: env = os.environ.get("VELA2_NOUL_CALIBRATION") noul_calibration = env.strip().lower() in ("1", "true", "yes", "on") if env else \ bool((self.cal.get("noul_calibration") or {}).get("default", False)) self.noul_cal = self.cal.get("noul_calibration") if noul_calibration else None self.model_name = self.config.get("model_name") or DEFAULT_MODEL_NAME self.tok = Tokenizer.from_str((path / "tokenizer.json").read_text(encoding="utf-8")) self.tok.no_padding(); self.tok.no_truncation() self._tc = {} self.max_len = int(self.config["max_length"]) self.budget = token_budget or 24576 self.bs = batch_size or 32 if amp_bf16 is None: amp_bf16 = str(device).startswith("cuda") self.amp_bf16 = bool(amp_bf16) self.run = _TorchBackend(path, device=device, amp_bf16=self.amp_bf16, model=model) self.stats = defaultdict(int) bh = self.cal.get("broad_head") or {} self.has_broad = bool(getattr(self.run, "has_broad", False)) and bool(bh) self.router_label_sets = [frozenset(x) for x in bh.get("router_label_sets", [])] or \ [frozenset(self.cal.get("pii_types", [])), frozenset(self.cal["halu_schema"]["labels"])] @classmethod def from_pretrained(cls, path, **kw): return cls(path, **kw) # ------------------------------------------------------------------ tokenisation def _ids(self, s): if s not in self._tc: self._tc[s] = self.tok.encode(s, add_special_tokens=False).ids return self._tc[s] def _ids_offs(self, s): e = self.tok.encode(s, add_special_tokens=False) return np.asarray(e.ids, np.int32), np.asarray(e.offsets, np.int32).reshape(-1, 2) # ------------------------------------------------------------------ generic contract -> internal row def _prep(self, row): """Generic-contract row {parts: [{role, text}], questions: [{id, type, over, text, options, levels, labels, abstain}]} -> tokenised internal row (the research pool rows: parts (role, ids, offs | None, words | None)).""" roles = [p["role"] for p in row["parts"]] if len(set(roles)) != len(roles): raise ValueError(f"one part per role, got {roles}") for r in roles: if r not in ROLES: raise ValueError(f"part role must be one of {ROLES}, got {r}") span_roles = {q["over"] for q in row["questions"] if q["type"] == "span"} if len([q for q in row["questions"] if q["type"] == "span"]) > 1: raise ValueError("one span question per row (predict() splits requests with several)") parts = [] for p in row["parts"]: ids, offs = self._ids_offs(p["text"]) words = word_first_tokens(p["text"], offs) if p["role"] in span_roles else None parts.append((p["role"], ids, offs if p["role"] in span_roles else None, words)) for q in row["questions"]: for o in (q["over"] if isinstance(q["over"], list) else [q["over"]]): if o not in roles: raise ValueError(f"question {q.get('id')} is over '{o}' but the row has parts {roles}") sq = next((q for q in row["questions"] if q["type"] == "span"), None) head, why = span_head_for(sq, self.router_label_sets, self.has_broad) if sq is not None else ("router", None) qs, alias = row["questions"], None if head == "router" and sq is not None and halu_alias(sq) is not None and halu_alias(sq) != HALU_LABEL: alias = {HALU_LABEL: halu_alias(sq)} # rendered as the trained label; the answer gets the user's name back desc = (self.cal.get("halu_schema") or {}).get("labels", {}).get(HALU_LABEL, "a claim not supported by the context") qs = [dict(q, labels={HALU_LABEL: desc}) if q is sq else q for q in row["questions"]] return dict(parts=parts, qs=qs, head=head, head_reason=why, label_alias=alias) def _render(self, r): """train_vela2_decoder_d3_20261001.render_row(train=False) on a prepared row -> rendered rec (p_ids, blocks).""" T = self._ids qs, el, span_over, span_text = [], [], None, None for q in r["qs"]: typ = q["type"] if typ == "span": span_over, span_text = q["over"], q["text"] labels = q["labels"] for n in labels: t = T(option_block(n, labels[n], "label")); el.append(dict(name=n, toks=t, len=1 + len(t))) continue opts = q.get("options") or {} if typ == "score": names = list(q["levels"]); abstain = False elif typ == "set": names = list(opts); abstain = False elif typ == "choice": names = list(opts); abstain = bool(q.get("abstain")) else: raise ValueError(typ) otoks = {} for n in names: d = opts.get(n, "") otoks[n] = T(option_block(n, d if d is not None else "")) head = T(q_head(typ, q["over"], q["text"])); suf = T(SUFFIX[typ]) ab = T(option_block(ABS_KEY, ABS_DESC)) if abstain else [] length = 1 + len(head) + len(suf) + len(ab) + sum(1 + len(otoks[n]) for n in names) + (1 if abstain else 0) qs.append(dict(qid=q["id"], qtype=typ, over=q["over"], names=names, otoks=otoks, abstain=abstain, len=length, head=head, suf=suf, ab=ab)) overs = set() for q in qs: overs |= set(q["over"] if isinstance(q["over"], list) else [q["over"]]) if span_over: overs.add(span_over) roles = [p[0] for p in r["parts"]] shrink = next((x for x in roles if x not in overs), None) part_of = {p[0]: p for p in r["parts"]} layout = "c" if span_over and len(part_of[span_over][1]) <= HYBRID_RMAX else "a" segs = [(p[0], p[1]) for p in r["parts"]] ov = len(T(PARTS_HEAD)) + sum(len(T(seg_open(x))) + len(T(SEG_CLOSE)) for x in roles) if span_over: sh = T(q_head("span", span_over, span_text, span=True)); ss = T(SUFFIX["span"]) ov += len(sh) + len(ss) if layout == "c": sp_len = len(part_of[span_over][1]) ov += len(T(TEXT_HEAD)) + len(T(seg_open(span_over))) + len(T(SEG_CLOSE)) ov += min(sp_len, max(0, self.max_len - ov) // 2) a = assemble_budget(qs, el, segs, max(64, self.max_len - ov), overs, shrink) L = a["seg_lens"] p_ids = list(T(PARTS_HEAD)); start = {} for x, t in segs: p_ids += T(seg_open(x)); start[x] = len(p_ids); p_ids += t[:L[x]].tolist(); p_ids += T(SEG_CLOSE) blocks = [] for q in qs: ids = list(q["head"]); ends = [] for n in q["names"]: ids += q["otoks"][n]; ends.append(len(ids) - 1) if q["abstain"]: ids += q["ab"]; ends.append(len(ids) - 1) ids += q["suf"] blocks.append(dict(kind="q", ids=ids, ends=ends, query=len(ids) - 1, qid=q["qid"], qtype=q["qtype"], names=q["names"], has_abs=q["abstain"])) rec = dict(p_ids=p_ids, blocks=blocks, trunc=a["trunc"], seg_lens=L, layout=layout if span_over else None) if span_over and el: ids = list(sh); ends, starts = [], [] for e in el: starts.append(len(ids)); ids += e["toks"]; ends.append(len(ids) - 1) part = part_of[span_over]; n = L[span_over] wo, ft = part[3]; keep = ft < n blk = dict(kind="span", ends=ends, label_ranges=list(zip(starts, ends))) if layout == "c": ids += T(TEXT_HEAD); ids += T(seg_open(span_over)); c0 = len(ids); ids += part[1][:n].tolist(); ids += T(SEG_CLOSE) w_pos = (c0 + ft[keep]).astype(np.int64); blk["words_in_block"] = True else: w_pos = (start[span_over] + ft[keep]).astype(np.int64); blk["words_in_block"] = False ids += ss blk.update(ids=ids, query=len(ids) - 1, w_pos=w_pos) blocks.append(blk) rec.update(names=[e["name"] for e in el], w_offs=wo[keep], w_pos=w_pos, span_over=span_over) rec["n_tokens"] = len(p_ids) + sum(len(b["ids"]) for b in blocks) return rec def _window_rows(self, r): """D3 windowed c: a span target of > 2,048 tokens -> one row per window (the span question only).""" sq = next((q for q in r["qs"] if q["type"] == "span"), None) if sq is None: return None pi = next(j for j, p in enumerate(r["parts"]) if p[0] == sq["over"]) part = r["parts"][pi] if len(part[1]) <= RMAX or part[3] is None: return None return [dict(r, qs=[sq], parts=[window_part(p, a0, b0) if j == pi else p for j, p in enumerate(r["parts"])]) for a0, b0 in span_windows(part[3][1], len(part[1]))] def _run_all(self, recs): """DecScorer.predict batching: sort by token count, greedy batches under the token budget and batch size.""" outs = [None] * len(recs) order = sorted(range(len(recs)), key=lambda k: recs[k]["n_tokens"]); cur = [] def flush(ks): for k, o in zip(ks, self.run([recs[k] for k in ks])): outs[k] = o for k in order: w = max([recs[j]["n_tokens"] for j in cur] + [recs[k]["n_tokens"]]) if cur and (len(cur) + 1 > self.bs or w * (len(cur) + 1) > self.budget): flush(cur); cur = [] cur.append(k) if cur: flush(cur) return outs def predict_raw(self, rows): """Generic-contract rows -> per row dict(qs=[dict(qid, qtype, names, logits (incl. [ABS] last if any), has_abs)], span=dict(names, offs (word char offsets), logits (W, L)) | None, n_tokens, windows, n_input).""" prepped = [self._prep(r) for r in rows] full = [self._render(p) for p in prepped] for rec, p in zip(full, prepped): rec["head"] = p["head"] wins, wjobs = [], [] for k, p in enumerate(prepped): if full[k]["trunc"]["protected_cut"]: self.stats["labelled_cut"] += 1 W = self._window_rows(p) if W: for b in full[k]["blocks"]: if b["kind"] == "span": b["skip"] = True # the full-render span block (layout a) is replaced by the windows self.stats["windowed_rows"] += 1; self.stats["windows"] += len(W) for wr in W: wins.append(self._render(wr)); wjobs.append(k); wins[-1]["head"] = p["head"] out_full = self._run_all(full) out_win = self._run_all(wins) if wins else [] agg = {} for k, rec, o in zip(wjobs, wins, out_win): if o["span"] is None: continue if k not in agg: sq = next(q for q in prepped[k]["qs"] if q["type"] == "span") wo = next(p for p in prepped[k]["parts"] if p[0] == sq["over"])[3][0] agg[k] = dict(names=rec["names"], pos={(int(s), int(e)): u for u, (s, e) in enumerate(wo)}, offs=wo, acc=np.zeros((len(wo), len(rec["names"]))), cnt=np.zeros(len(wo)), n=0) A = agg[k]; A["n"] += 1 for (s, e), lg in zip(np.asarray(rec["w_offs"]), o["span"]): u = A["pos"].get((int(s), int(e))) if u is not None: A["acc"][u] += lg; A["cnt"][u] += 1 n_in = defaultdict(int) for k, rec in zip(wjobs, wins): n_in[k] += rec["n_tokens"] final = [] for k, (p, rec, o) in enumerate(zip(prepped, full, out_full)): qb = [b for b in rec["blocks"] if b["kind"] == "q"] qs = [dict(qid=b["qid"], qtype=b["qtype"], names=b["names"], logits=np.asarray(lg), has_abs=b["has_abs"]) for b, lg in zip(qb, o["qs"])] span = None if k in agg: A = agg[k]; cov = A["cnt"] > 0 self.stats["units_uncovered"] += int((~cov).sum()) span = dict(names=A["names"], offs=np.asarray(A["offs"]), logits=np.where(cov[:, None], A["acc"] / np.maximum(A["cnt"], 1)[:, None], -30.0)) elif o["span"] is not None: span = dict(names=rec["names"], offs=np.asarray(rec["w_offs"]), logits=o["span"].astype(np.float64)) if span is not None: span["head"] = p["head"]; span["head_reason"] = p["head_reason"] span["label_alias"] = p.get("label_alias") final.append(dict(qs=qs, span=span, n_tokens={x[0]: len(x[1]) for x in p["parts"]}, windows=agg[k]["n"] if k in agg else 0, n_input=rec["n_tokens"] + n_in[k])) return final # ------------------------------------------------------------------ calibration def temperature(self, qtype): return float(self.cal["temperature"].get(qtype, 1.0)) def span_temperature(self, head="router"): """T of the span head: the release span temperature (router) or calibration.json broad_head.temperature.""" if head == "broad": return float(self.cal["broad_head"]["temperature"]) return self.temperature("span") def is_pii(self, qid, names): return qid == "pii" or bool(names and set(names) <= set(self.cal.get("pii_types", []))) def pii_length_threshold(self, n_tokens): """calibration.json pii_length_rule: log(thr) piecewise-linear in log(n_tokens) between anchors, constant outside.""" a = self.cal["pii_length_rule"]["anchors"] return float(np.exp(np.interp(np.log(max(n_tokens, 1)), [np.log(x["n_tokens"]) for x in a], [np.log(x["threshold"]) for x in a]))) def pii_sparse_gate(self, probs, offs, names, text, thr, rule): """calibration.json pii_sparse_gate: a doc with <= K spans decoded at probe_threshold is decoded at max(thr, t_sparse). Second decode pass over the same probabilities (no second forward).""" g = self.cal.get("pii_sparse_gate") if not g or g.get("K", -1) < 0 or thr >= g["t_sparse"] or not len(offs): return thr, rule if len(decode_spans(probs, offs, names, text, float(g.get("probe_threshold", 0.5)))) <= g["K"]: return float(g["t_sparse"]), rule + "+sparse" return thr, rule def span_threshold(self, qid, names, n_tokens, head="router"): """Threshold before the (probability-dependent) PII sparse gate; calibrated() applies the gate. The broad head has one threshold (calibration.json broad_head.threshold, fitted on its own zero-shot dev).""" if head == "broad": return float(self.cal["broad_head"]["threshold"]), "broad" th = self.cal["thresholds"] if self.is_pii(qid, names): if "pii_length_rule" in self.cal: return self.pii_length_threshold(n_tokens), "pii:length" for b in self.cal["pii_buckets"]: # fallback: v1 calibration.json (no pii_length_rule) if n_tokens <= b["max_tokens"]: return float(b["threshold"]), f"pii:{b['name']}" for fam in ("halu", "toxic"): if qid == fam: return float(th[f"span:{fam}"]), fam return float(th.get(f"span:{qid}", th["span:*"])), ("span:" + qid if f"span:{qid}" in th else "span:*") def set_threshold(self, qid): th = self.cal["thresholds"] return float(th.get(f"set:{qid}", th["set:*"])) def calibrated(self, raw, row, thresholds=None): """Raw logits of one row -> calibrated answers (list aligned with row['questions']).""" thresholds = thresholds or {} texts = {p["role"]: p["text"] for p in row["parts"]} qmap = {q["id"]: q for q in row["questions"]} ans = [] for q in raw["qs"]: sch = qmap[q["qid"]]; lg = np.asarray(q["logits"], np.float64) n = len(q["names"]); T = self.temperature(q["qtype"]) if q["qtype"] == "set": p = _sigmoid(lg[:n] / T); thr = float(thresholds.get(q["qid"], self.set_threshold(q["qid"]))) probs = dict(zip(q["names"], p.tolist())) ans.append(dict(id=q["qid"], type="set", answer=[k for k, v in probs.items() if v > thr], probabilities=probs, threshold=thr)) continue p = _softmax(lg[:n] / T); probs = dict(zip(q["names"], p.tolist())) a = dict(id=q["qid"], type=q["qtype"], answer=q["names"][int(np.argmax(lg[:n]))], probabilities=probs) if q["has_abs"]: a["abstain_probability"] = float(_softmax(lg / T)[-1]) if q["qtype"] == "score": vals = sch.get("values") or {nm: (i / (n - 1) if n > 1 else 0.0) for i, nm in enumerate(q["names"])} a["expected_index"] = float(sum(i * pp for i, pp in enumerate(p))) a["score"] = float(sum(vals[nm] * pp for nm, pp in zip(q["names"], p))) ans.append(a) if raw["span"] is not None: sq = next(q for q in row["questions"] if q["type"] == "span") role = sq["over"]; text = texts[role] head = raw["span"].get("head", "router") thr, rule = (float(thresholds[sq["id"]]), "given") if sq["id"] in thresholds else \ self.span_threshold(sq["id"], raw["span"]["names"], raw["n_tokens"][role], head) probs = _sigmoid(raw["span"]["logits"] / self.span_temperature(head)) if rule.startswith("pii:"): thr, rule = self.pii_sparse_gate(probs, raw["span"]["offs"], raw["span"]["names"], text, thr, rule) sp = decode_spans(probs, raw["span"]["offs"], raw["span"]["names"], text, thr) if len(raw["span"]["offs"]) else [] alias = raw["span"].get("label_alias") or {} for s in sp: s["text"] = text[s["start"]:s["end"]] s["label"] = alias.get(s["label"], s["label"]) ans.append(dict(id=sq["id"], type="span", target_part=role, spans=sp, threshold=thr, threshold_rule=rule, head=head, head_reason=raw["span"].get("head_reason"))) return ans def predict_generic(self, rows, thresholds=None): raws = self.predict_raw(rows) return [dict(answers=self.calibrated(r, row, thresholds), meta=dict(tokens=r["n_tokens"], windows=r["windows"])) for r, row in zip(raws, rows)] # ------------------------------------------------------------------ Kai / Jev JSON @staticmethod def _names(lst): if isinstance(lst, dict): return dict(lst) out = {} for o in lst: if isinstance(o, str): out[o] = "" else: out[o["name"]] = o.get("description") or "" return out def _to_generic(self, req): parts = [dict(role=p["type"], text=p["text"]) for p in req["parts"]] roles = [p["role"] for p in parts] for r in roles: if r not in ROLES: raise ValueError(f"part type must be one of user / context / answer, got {r}") qs, thr = [], {} for i, q in enumerate(req["questions"]): typ = q["type"]; qid = str(q.get("id", f"q{i}")) tp = q.get("target_part") or ("user" if "user" in roles else roles[0]) g = dict(id=qid, type=typ, over=tp, text=q.get("text") or "") if typ == "span": g["labels"] = self._names(q.get("labels") or q.get("options")) if q.get("head") is not None: g["head"] = q["head"] else: opts = self._names(q.get("options") or q.get("labels") or q.get("levels")) g["options"] = opts if typ == "score": g["levels"] = list(opts) if q.get("values"): g["values"] = dict(zip(opts, q["values"])) if isinstance(q["values"], list) else dict(q["values"]) if typ == "choice": g["abstain"] = bool(q.get("abstain", True)) if q.get("threshold") is not None: thr[qid] = float(q["threshold"]) qs.append(g) return parts, qs, thr def predict(self, req): """Kai/Jev-style request (dict or list of dicts) -> {"answers": [...], "meta": {...}} in question order. Several span questions are run as separate sequences (the model takes one span question per sequence).""" if isinstance(req, list): return [self.predict(r) for r in req] return self._predict(req)[0] def _predict(self, req, span_limits=None): """predict() plus what the SystemOne view needs from the raw pass: (result, {span qid: max word probability}, input tokens over all sequences). span_limits {qid: (start, end)} restricts the max to words inside a range.""" parts, qs, thr = self._to_generic(req) spans = [q for q in qs if q["type"] == "span"]; rest = [q for q in qs if q["type"] != "span"] rows = [dict(parts=parts, questions=rest + spans[:1])] + [dict(parts=parts, questions=[s]) for s in spans[1:]] rows = [r for r in rows if r["questions"]] raws = self.predict_raw(rows) res = [dict(answers=self.calibrated(r, row, thr), meta=dict(tokens=r["n_tokens"], windows=r["windows"])) for r, row in zip(raws, rows)] # = predict_generic(rows, thr) span_max = {} for r, row in zip(raws, rows): if r["span"] is None: continue sq = next(q for q in row["questions"] if q["type"] == "span") p = _sigmoid(r["span"]["logits"] / self.span_temperature(r["span"].get("head", "router"))).max(1) if len(r["span"]["offs"]) else np.zeros(0) lim = (span_limits or {}).get(sq["id"]) if lim is not None and len(p): o = r["span"]["offs"]; p = p[(o[:, 0] >= lim[0]) & (o[:, 1] <= lim[1])] span_max[sq["id"]] = float(p.max()) if len(p) else 0.0 by = {a["id"]: a for r in res for a in r["answers"]} out = dict(answers=[by[q["id"]] for q in qs], meta=dict(tokens=res[0]["meta"]["tokens"], windows=max(r["meta"]["windows"] for r in res), sequences=len(rows))) return out, span_max, sum(r["n_input"] for r in raws) # ------------------------------------------------------------------ SystemOne def system_one(self, state, questions, model=None): """TypeSafe SystemOne request -> SystemOne response (the primary interface). state str | JSON object | JSON array (a str holding a JSON object is read as that object) questions {qid: question}: {"type": "noul", "instructions", "criteria"?: {"true", "false"}} | {"type": "choice", "instructions", "criteria": {key: description | None}} | {"type": "score", "instructions", "criteria": [level description, ...]} (dicts, or the typesafe-sdk Noul / Choice / Score objects); extensions: {"type": "span" | "set", "instructions", "criteria": {label: description | None}, "over"?, "threshold"?}. Every question may name the state field(s) it is asked "over". returns {"model", "answers": {qid: {"type": "noul", "noul"} | {"type": "choice", "choice", "confidence", "probabilities"} | {"type": "score", "score", "confidence", "legend", "probabilities"}}, "usage": {"input_tokens", "output_tokens": 0}}, plus for span / set questions "spans" / "sets" / "thresholds" and their Noul views in "answers" (see SYSTEM_ONE_NOTES).""" return system_one_response(self, state, questions, model) def system_one_request(self, body): """The body of POST /v1/systemone ({"state", "model", "questions"}) -> SystemOne response.""" return system_one_request(self, body) # ------------------------------------------------------------------ GLiNER2-style def classify_text(self, text, labels, multi_label=False, threshold=None, include_confidence=False, question=None, abstain=True, context=None): """labels: list | {name: description} (one task) or {task: list | {name: desc} | {"labels", "multi_label", "cls_threshold", "question"}} (several tasks, one forward pass). Returns {task: label | [labels]} (GLiNER2).""" single = isinstance(labels, list) or (isinstance(labels, dict) and labels and all(isinstance(v, str) for v in labels.values())) tasks = {"label": labels} if single else labels qs, thr = [], {} for task, spec in tasks.items(): ml, th, qt = multi_label, threshold, question if isinstance(spec, dict) and "labels" in spec: ml = spec.get("multi_label", ml); th = spec.get("cls_threshold", th); qt = spec.get("question", qt); spec = spec["labels"] opts = self._names(spec) if qt is None: qt = ("Which labels apply to this text?" if ml else "Which label applies to this text?") if single else \ (f"Which {task} labels apply to this text?" if ml else f"Which {task} applies to this text?") q = dict(id=task, type="set" if ml else "choice", text=qt, options=opts, target_part="user") if not ml: q["abstain"] = abstain if th is not None: q["threshold"] = th qs.append(q) parts = [dict(type="user", text=text)] + ([dict(type="context", text=context)] if context else []) out = self.predict(dict(parts=parts, questions=qs)) res = {} for a in out["answers"]: if a["type"] == "set": v = [dict(label=l, confidence=a["probabilities"][l]) for l in a["answer"]] if include_confidence else a["answer"] else: v = dict(label=a["answer"], confidence=a["probabilities"][a["answer"]]) if include_confidence else a["answer"] res[a["id"]] = v return res def extract_entities(self, text, labels, threshold=None, include_confidence=False, include_spans=False, question=None, head=None): """labels: list | {name: description}. Returns {"entities": {label: [text | {text, start, end, confidence}]}}. The 17 Vela PII types (no descriptions given) use the trained PII schema and the per-length PII thresholds. head: None (dispatch rule: a PII label set -> router head, other labels -> broad head) | "router" | "broad".""" opts = self._names(labels) pii = self.cal.get("pii_schema", {}) if all(not d for d in opts.values()) and set(opts) <= set(pii.get("labels", {})): opts = {n: pii["labels"][n] for n in opts}; question = question or pii["text"]; qid = "pii" else: qid = "entities" q = dict(id=qid, type="span", text=question or "Which spans are named entities of these types?", labels=opts, target_part="user") if head is not None: q["head"] = head if threshold is not None: q["threshold"] = threshold a = self.predict(dict(parts=[dict(type="user", text=text)], questions=[q]))["answers"][0] ents = {n: [] for n in opts} for s in a["spans"]: ents[s["label"]].append(dict(text=s["text"], start=s["start"], end=s["end"], confidence=s["probability"]) if (include_confidence or include_spans) else s["text"]) return dict(entities=ents) def detect_hallucination(self, context, answer, question=None, threshold=None): """Spans of `answer` not supported by `context` (the trained halu schema; context = 'User request: \\n\\n' when a question is given, as in training).""" hs = self.cal["halu_schema"] ctx = f"User request: {question}\n\n{context}" if question else context q = dict(id="halu", type="span", text=hs["text"], labels=hs["labels"], target_part="answer") if threshold is not None: q["threshold"] = threshold a = self.predict(dict(parts=[dict(type="context", text=ctx), dict(type="answer", text=answer)], questions=[q]))["answers"][0] return dict(hallucinated=bool(a["spans"]), spans=a["spans"], threshold=a["threshold"]) def score_relevance(self, query, document): """Relevance of `document` to `query` as a score question over the user + context parts (0 / 0.5 / 1 levels).""" rs = self.cal["relevance_schema"] q = dict(id="relevance", type="score", text=rs["text"], options=[dict(name=k, description=v) for k, v in rs["levels"].items()], values=rs["values"], target_part=["user", "context"]) a = self.predict(dict(parts=[dict(type="user", text=query), dict(type="context", text=document)], questions=[q]))["answers"][0] return dict(score=a["score"], label=a["answer"], probabilities=a["probabilities"]) def score(self, text, question, levels, context=None, values=None, target_part="user"): """Generic score question: levels = ordered list | {level: description}.""" q = dict(id="score", type="score", text=question, options=levels, values=values, target_part=target_part) parts = [dict(type="user", text=text)] + ([dict(type="context", text=context)] if context else []) return self.predict(dict(parts=parts, questions=[q]))["answers"][0] # ===================================================================================================================== # SystemOne (TypeSafe) interface: request/response shape of POST /v1/systemone, mapped onto predict() # ===================================================================================================================== DEFAULT_MODEL_NAME = "vllm-sr/Vela-2.0" # the served name is config.json "model_name" NOUL_DEFAULT_NO = "No. The statement or question is not satisfied." # the Vela-family Noul defaults of the NOUL_DEFAULT_YES = "Yes. The statement or question is satisfied." # Decision runtime (Kai) STATE_KEY_ROLES = {"request": "user", "user": "user", "prompt": "user", "source": "context", "context": "context", "document": "context", "tools": "context", "previous_answer": "context", "answer": "answer", "response": "answer"} ROLE_ORDER = ("user", "context", "answer") SYSTEM_ONE_TYPES = ("noul", "choice", "score", "span", "set") SYSTEM_ONE_NOTES = """ Mapping onto the typed-parts engine (predict()): choice -> choice question (options key: description; a None description renders the key alone; [ABS] present as in predict()); probabilities over the criteria keys; confidence = top-two probability margin. score -> score question whose ordered levels are the criteria; probabilities / legend keyed "0".."n-1"; score = expected zero-based level; confidence = 1 - variance / variance of the uniform distribution. noul -> choice over ("no": criteria.false, "yes": criteria.true), defaults NOUL_DEFAULT_NO / NOUL_DEFAULT_YES; noul = P(yes). span -> span question; "spans": {qid: [{label, start, end, text, probability}]} (code-point offsets into the text of the `over` field; when that field shares a part with other fields, spans are clipped to it); "thresholds": {qid: t}; answers[qid] = {"type": "noul", "noul": max word probability over that field} (above t exactly when a word is labelled, so spans are non-empty up to edge trimming). set -> set question; "sets": {qid: {"selected": [...], "probabilities": {label: p}}}; "thresholds": {qid: t}; answers[".