Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- second_brain/__pycache__/__init__.cpython-311.pyc +0 -0
- second_brain/__pycache__/plan.cpython-311.pyc +0 -0
- second_brain/__pycache__/retrieve.cpython-311.pyc +0 -0
- tests/__pycache__/test_app.cpython-311-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_plan.cpython-311-pytest-9.1.1.pyc +0 -0
- tests/__pycache__/test_retrieve.cpython-311-pytest-9.1.1.pyc +0 -0
- train/eval_navigator.py +323 -323
- train/eval_report.json +90 -4
second_brain/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (724 Bytes). View file
|
|
|
second_brain/__pycache__/plan.cpython-311.pyc
ADDED
|
Binary file (5.03 kB). View file
|
|
|
second_brain/__pycache__/retrieve.cpython-311.pyc
ADDED
|
Binary file (18.1 kB). View file
|
|
|
tests/__pycache__/test_app.cpython-311-pytest-9.1.1.pyc
ADDED
|
Binary file (22.6 kB). View file
|
|
|
tests/__pycache__/test_plan.cpython-311-pytest-9.1.1.pyc
ADDED
|
Binary file (6.61 kB). View file
|
|
|
tests/__pycache__/test_retrieve.cpython-311-pytest-9.1.1.pyc
ADDED
|
Binary file (21.5 kB). View file
|
|
|
train/eval_navigator.py
CHANGED
|
@@ -1,323 +1,323 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Named-N retrieve-hit and abstain bench. Train loss is not eval.
|
| 3 |
-
|
| 4 |
-
SOFTWARE index is always scored. Generate is MEASURED only if a local adapter
|
| 5 |
-
loads and emits parseable JSON; otherwise UNAVAILABLE. Never claim 5/5 unless
|
| 6 |
-
the denominator was actually run.
|
| 7 |
-
"""
|
| 8 |
-
from __future__ import annotations
|
| 9 |
-
|
| 10 |
-
import json
|
| 11 |
-
import re
|
| 12 |
-
import sys
|
| 13 |
-
from datetime import datetime, timezone
|
| 14 |
-
from pathlib import Path
|
| 15 |
-
from typing import Any
|
| 16 |
-
|
| 17 |
-
HERE = Path(__file__).resolve().parent
|
| 18 |
-
ROOT = HERE.parent
|
| 19 |
-
if str(ROOT) not in sys.path:
|
| 20 |
-
sys.path.insert(0, str(ROOT))
|
| 21 |
-
|
| 22 |
-
from second_brain.plan import plan_from_handles # noqa: E402
|
| 23 |
-
from second_brain.retrieve import SecondBrainIndex # noqa: E402
|
| 24 |
-
|
| 25 |
-
RETRIEVE_GATE = HERE / "gate_retrieve.jsonl"
|
| 26 |
-
ABSTAIN_GATE = HERE / "gate_abstain.jsonl"
|
| 27 |
-
REPORT = HERE / "eval_report.json"
|
| 28 |
-
ADAPTER = HERE / "brain-navigator-r2-adapter"
|
| 29 |
-
SYS = (
|
| 30 |
-
"You are BrainNavigator-R2, the SZL second-brain retrieval planner. "
|
| 31 |
-
"Capability profile SZL-BrainNavigator-R2. Base Qwen/Qwen3.5-0.8B. "
|
| 32 |
-
"You see HANDLES ONLY, never node text. Emit one JSON object. "
|
| 33 |
-
"decision is NAVIGATE or ABSTAIN. groundedOnly is true. "
|
| 34 |
-
"citedNodeIds must be a subset of offered nodeId values. "
|
| 35 |
-
"If none of the offered handles support the query, ABSTAIN with empty steps. "
|
| 36 |
-
"capabilityProfile must be SZL-BrainNavigator-R2. contentAccess HANDLES_ONLY. "
|
| 37 |
-
"brainBinding.status is NOT_RESOLVED. You never execute retrieval."
|
| 38 |
-
)
|
| 39 |
-
JSON_RE = re.compile(r"\{.*\}", re.S)
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
def _load(path: Path) -> list[dict[str, Any]]:
|
| 43 |
-
rows = []
|
| 44 |
-
for line in path.read_text(encoding="utf-8").splitlines():
|
| 45 |
-
if line.strip():
|
| 46 |
-
rows.append(json.loads(line))
|
| 47 |
-
return rows
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
def _parse_plan(text: str) -> dict[str, Any] | None:
|
| 51 |
-
raw = (text or "").strip()
|
| 52 |
-
if not raw:
|
| 53 |
-
return None
|
| 54 |
-
try:
|
| 55 |
-
return json.loads(raw)
|
| 56 |
-
except json.JSONDecodeError:
|
| 57 |
-
m = JSON_RE.search(raw)
|
| 58 |
-
if not m:
|
| 59 |
-
return None
|
| 60 |
-
try:
|
| 61 |
-
return json.loads(m.group(0))
|
| 62 |
-
except json.JSONDecodeError:
|
| 63 |
-
return None
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
def software_bench(idx: SecondBrainIndex) -> dict[str, Any]:
|
| 67 |
-
retrieve = _load(RETRIEVE_GATE)
|
| 68 |
-
abstain = _load(ABSTAIN_GATE)
|
| 69 |
-
retrieve_cases = []
|
| 70 |
-
hit = 0
|
| 71 |
-
for row in retrieve:
|
| 72 |
-
q = row["query"]
|
| 73 |
-
expect = list(row.get("expect_cite") or [])
|
| 74 |
-
got = idx.search(q, k=5)
|
| 75 |
-
ids = [h["nodeId"] for h in got["handles"]]
|
| 76 |
-
ok = bool(expect) and expect[0] in ids
|
| 77 |
-
if ok:
|
| 78 |
-
hit += 1
|
| 79 |
-
plan = plan_from_handles(q, got["handles"])
|
| 80 |
-
retrieve_cases.append(
|
| 81 |
-
{
|
| 82 |
-
"id": row["id"],
|
| 83 |
-
"query": q,
|
| 84 |
-
"expect_cite": expect,
|
| 85 |
-
"got_ids": ids,
|
| 86 |
-
"hit": ok,
|
| 87 |
-
"plan_decision": plan["decision"],
|
| 88 |
-
"plan_cite": plan["citedNodeIds"],
|
| 89 |
-
}
|
| 90 |
-
)
|
| 91 |
-
abs_cases = []
|
| 92 |
-
abs_ok = 0
|
| 93 |
-
for row in abstain:
|
| 94 |
-
q = row["query"]
|
| 95 |
-
plan = plan_from_handles(q, row.get("handles") or [])
|
| 96 |
-
ok = plan["decision"] == "ABSTAIN" and not plan["citedNodeIds"]
|
| 97 |
-
if ok:
|
| 98 |
-
abs_ok += 1
|
| 99 |
-
abs_cases.append(
|
| 100 |
-
{
|
| 101 |
-
"id": row["id"],
|
| 102 |
-
"query": q,
|
| 103 |
-
"decision": plan["decision"],
|
| 104 |
-
"citedNodeIds": plan["citedNodeIds"],
|
| 105 |
-
"ok": ok,
|
| 106 |
-
}
|
| 107 |
-
)
|
| 108 |
-
return {
|
| 109 |
-
"kind": "SOFTWARE",
|
| 110 |
-
"label": "MEASURED",
|
| 111 |
-
"retrieve_hit": f"{hit}/{len(retrieve)}" if retrieve else "0/0",
|
| 112 |
-
"retrieve_hit_correct": hit,
|
| 113 |
-
"retrieve_hit_total": len(retrieve),
|
| 114 |
-
"abstain": f"{abs_ok}/{len(abstain)}" if abstain else "0/0",
|
| 115 |
-
"abstain_correct": abs_ok,
|
| 116 |
-
"abstain_total": len(abstain),
|
| 117 |
-
"retrieve_cases": retrieve_cases,
|
| 118 |
-
"abstain_cases": abs_cases,
|
| 119 |
-
"honesty": (
|
| 120 |
-
"Lexical rank over the PUBLIC 575-chunk projection. "
|
| 121 |
-
"Score is overlap, never correctness. Named-N gates."
|
| 122 |
-
),
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
def generate_bench() -> dict[str, Any]:
|
| 127 |
-
if not (ADAPTER / "adapter_config.json").is_file():
|
| 128 |
-
return {
|
| 129 |
-
"kind": "GENERATE",
|
| 130 |
-
"label": "UNAVAILABLE",
|
| 131 |
-
"reason": "no local adapter; SOFTWARE navigator is the shipped planner",
|
| 132 |
-
"publication_eligible": False,
|
| 133 |
-
}
|
| 134 |
-
try:
|
| 135 |
-
import torch
|
| 136 |
-
from unsloth import FastLanguageModel
|
| 137 |
-
except Exception as exc: # noqa: BLE001
|
| 138 |
-
return {
|
| 139 |
-
"kind": "GENERATE",
|
| 140 |
-
"label": "UNAVAILABLE",
|
| 141 |
-
"reason": f"unsloth/torch import failed: {exc}",
|
| 142 |
-
"publication_eligible": False,
|
| 143 |
-
}
|
| 144 |
-
if not torch.cuda.is_available():
|
| 145 |
-
return {
|
| 146 |
-
"kind": "GENERATE",
|
| 147 |
-
"label": "UNAVAILABLE",
|
| 148 |
-
"reason": "CUDA UNAVAILABLE for generate",
|
| 149 |
-
"publication_eligible": False,
|
| 150 |
-
}
|
| 151 |
-
try:
|
| 152 |
-
model, tokenizer = FastLanguageModel.from_pretrained(
|
| 153 |
-
model_name=str(ADAPTER),
|
| 154 |
-
max_seq_length=2048,
|
| 155 |
-
load_in_4bit=False,
|
| 156 |
-
load_in_16bit=True,
|
| 157 |
-
)
|
| 158 |
-
FastLanguageModel.for_inference(model)
|
| 159 |
-
except Exception as exc: # noqa: BLE001
|
| 160 |
-
return {
|
| 161 |
-
"kind": "GENERATE",
|
| 162 |
-
"label": "UNAVAILABLE",
|
| 163 |
-
"reason": f"adapter load failed: {type(exc).__name__}: {exc}",
|
| 164 |
-
"publication_eligible": False,
|
| 165 |
-
}
|
| 166 |
-
|
| 167 |
-
def infer(query: str, handles: list[dict[str, Any]]) -> dict[str, Any] | None:
|
| 168 |
-
user = query + "\n\nCANDIDATE_HANDLES_JSON:\n" + json.dumps(handles)
|
| 169 |
-
messages = [
|
| 170 |
-
{"role": "system", "content": SYS},
|
| 171 |
-
{"role": "user", "content": user},
|
| 172 |
-
]
|
| 173 |
-
# Qwen3.5 ships a multimodal processor; tokenize text only.
|
| 174 |
-
try:
|
| 175 |
-
prompt = tokenizer.apply_chat_template(
|
| 176 |
-
messages,
|
| 177 |
-
tokenize=False,
|
| 178 |
-
add_generation_prompt=True,
|
| 179 |
-
enable_thinking=False,
|
| 180 |
-
)
|
| 181 |
-
except TypeError:
|
| 182 |
-
prompt = tokenizer.apply_chat_template(
|
| 183 |
-
messages, tokenize=False, add_generation_prompt=True
|
| 184 |
-
)
|
| 185 |
-
tok = getattr(tokenizer, "tokenizer", tokenizer)
|
| 186 |
-
encoded = tok(prompt, return_tensors="pt", add_special_tokens=False)
|
| 187 |
-
input_ids = encoded["input_ids"].to(model.device)
|
| 188 |
-
attn = encoded.get("attention_mask")
|
| 189 |
-
eos = getattr(tok, "eos_token_id", None)
|
| 190 |
-
gen_kw: dict[str, Any] = {
|
| 191 |
-
"input_ids": input_ids,
|
| 192 |
-
"max_new_tokens": 384,
|
| 193 |
-
"do_sample": False,
|
| 194 |
-
}
|
| 195 |
-
if attn is not None:
|
| 196 |
-
gen_kw["attention_mask"] = attn.to(model.device)
|
| 197 |
-
if eos is not None:
|
| 198 |
-
gen_kw["eos_token_id"] = eos
|
| 199 |
-
out = model.generate(**gen_kw)
|
| 200 |
-
text = tok.decode(out[0][input_ids.shape[-1] :], skip_special_tokens=True)
|
| 201 |
-
return _parse_plan(text)
|
| 202 |
-
|
| 203 |
-
retrieve = _load(RETRIEVE_GATE)
|
| 204 |
-
abstain = _load(ABSTAIN_GATE)
|
| 205 |
-
nav_ok = 0
|
| 206 |
-
abs_ok = 0
|
| 207 |
-
halluc = 0
|
| 208 |
-
cases: list[dict[str, Any]] = []
|
| 209 |
-
parse_fail = 0
|
| 210 |
-
try:
|
| 211 |
-
for row in retrieve:
|
| 212 |
-
plan = infer(row["query"], row["handles"])
|
| 213 |
-
if not plan:
|
| 214 |
-
parse_fail += 1
|
| 215 |
-
cases.append({"id": row["id"], "ok": False, "reason": "unparseable"})
|
| 216 |
-
print(f"[generate] {row['id']} unparseable")
|
| 217 |
-
continue
|
| 218 |
-
offered = {h["nodeId"] for h in row["handles"]}
|
| 219 |
-
cites = list(plan.get("citedNodeIds") or [])
|
| 220 |
-
if any(c not in offered for c in cites):
|
| 221 |
-
halluc += 1
|
| 222 |
-
expect = list(row.get("expect_cite") or [])
|
| 223 |
-
ok = (
|
| 224 |
-
plan.get("decision") == "NAVIGATE"
|
| 225 |
-
and bool(expect)
|
| 226 |
-
and expect[0] in cites
|
| 227 |
-
and all(c in offered for c in cites)
|
| 228 |
-
)
|
| 229 |
-
if ok:
|
| 230 |
-
nav_ok += 1
|
| 231 |
-
print(f"[generate] {row['id']} {plan.get('decision')} ok={ok}")
|
| 232 |
-
cases.append(
|
| 233 |
-
{
|
| 234 |
-
"id": row["id"],
|
| 235 |
-
"decision": plan.get("decision"),
|
| 236 |
-
"citedNodeIds": cites,
|
| 237 |
-
"ok": ok,
|
| 238 |
-
}
|
| 239 |
-
)
|
| 240 |
-
for row in abstain:
|
| 241 |
-
plan = infer(row["query"], row["handles"])
|
| 242 |
-
if not plan:
|
| 243 |
-
parse_fail += 1
|
| 244 |
-
cases.append({"id": row["id"], "ok": False, "reason": "unparseable"})
|
| 245 |
-
continue
|
| 246 |
-
offered = {h["nodeId"] for h in row["handles"]}
|
| 247 |
-
cites = list(plan.get("citedNodeIds") or [])
|
| 248 |
-
if any(c not in offered for c in cites):
|
| 249 |
-
halluc += 1
|
| 250 |
-
ok = plan.get("decision") == "ABSTAIN" and not cites
|
| 251 |
-
if ok:
|
| 252 |
-
abs_ok += 1
|
| 253 |
-
cases.append(
|
| 254 |
-
{
|
| 255 |
-
"id": row["id"],
|
| 256 |
-
"decision": plan.get("decision"),
|
| 257 |
-
"citedNodeIds": cites,
|
| 258 |
-
"ok": ok,
|
| 259 |
-
}
|
| 260 |
-
)
|
| 261 |
-
except Exception as exc: # noqa: BLE001
|
| 262 |
-
return {
|
| 263 |
-
"kind": "GENERATE",
|
| 264 |
-
"label": "UNAVAILABLE",
|
| 265 |
-
"reason": f"generate failed: {type(exc).__name__}: {exc}",
|
| 266 |
-
"publication_eligible": False,
|
| 267 |
-
}
|
| 268 |
-
return {
|
| 269 |
-
"kind": "GENERATE",
|
| 270 |
-
"label": "MEASURED",
|
| 271 |
-
"retrieve_hit": f"{nav_ok}/{len(retrieve)}" if retrieve else "0/0",
|
| 272 |
-
"retrieve_hit_correct": nav_ok,
|
| 273 |
-
"retrieve_hit_total": len(retrieve),
|
| 274 |
-
"abstain": f"{abs_ok}/{len(abstain)}" if abstain else "0/0",
|
| 275 |
-
"abstain_correct": abs_ok,
|
| 276 |
-
"abstain_total": len(abstain),
|
| 277 |
-
"hallucinated_citation_count": halluc,
|
| 278 |
-
"parse_fail": parse_fail,
|
| 279 |
-
"cases": cases,
|
| 280 |
-
"publication_eligible": False,
|
| 281 |
-
"honesty": (
|
| 282 |
-
"Owner-run named-N generate on local LoRA. Not a third-party bench. "
|
| 283 |
-
"Train loss is not this number. publication_eligible stays false."
|
| 284 |
-
),
|
| 285 |
-
}
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
def main() -> int:
|
| 289 |
-
idx = SecondBrainIndex()
|
| 290 |
-
software = software_bench(idx)
|
| 291 |
-
generate = generate_bench()
|
| 292 |
-
report = {
|
| 293 |
-
"schema": "szl.brain-navigator-r2.eval/v1",
|
| 294 |
-
"artifact": "SZLHOLDINGS/brain-navigator-r2",
|
| 295 |
-
"does_not_overwrite": "SZLHOLDINGS/SZL-Khipu-1.5B-BrainNavigator",
|
| 296 |
-
"lambda": "Conjecture 1",
|
| 297 |
-
"doctrine": "v11 LOCKED",
|
| 298 |
-
"publication_eligible": False,
|
| 299 |
-
"maturity": "MEASURED_RESEARCH_ONLY",
|
| 300 |
-
"train_loss_is_eval": False,
|
| 301 |
-
"raw_graph_nodes_admitted_to_gradients": 0,
|
| 302 |
-
"corpus_n": idx.n,
|
| 303 |
-
"software": software,
|
| 304 |
-
"generate": generate,
|
| 305 |
-
"computed_at": datetime.now(timezone.utc).isoformat(),
|
| 306 |
-
"honesty": (
|
| 307 |
-
"Do not claim 5/5 unless MEASURED. Existing 1.5B BrainNavigator "
|
| 308 |
-
"abstain 2/6 is a different SKU and is not restated as this run."
|
| 309 |
-
),
|
| 310 |
-
}
|
| 311 |
-
REPORT.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
|
| 312 |
-
print(
|
| 313 |
-
"SOFTWARE retrieve-hit "
|
| 314 |
-
f"{software['retrieve_hit']} abstain {software['abstain']} "
|
| 315 |
-
f"GENERATE {generate['label']} "
|
| 316 |
-
f"{generate.get('retrieve_hit', 'n/a')} / {generate.get('abstain', 'n/a')}"
|
| 317 |
-
)
|
| 318 |
-
print(f"wrote {REPORT}")
|
| 319 |
-
return 0
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
if __name__ == "__main__":
|
| 323 |
-
raise SystemExit(main())
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Named-N retrieve-hit and abstain bench. Train loss is not eval.
|
| 3 |
+
|
| 4 |
+
SOFTWARE index is always scored. Generate is MEASURED only if a local adapter
|
| 5 |
+
loads and emits parseable JSON; otherwise UNAVAILABLE. Never claim 5/5 unless
|
| 6 |
+
the denominator was actually run.
|
| 7 |
+
"""
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import json
|
| 11 |
+
import re
|
| 12 |
+
import sys
|
| 13 |
+
from datetime import datetime, timezone
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Any
|
| 16 |
+
|
| 17 |
+
HERE = Path(__file__).resolve().parent
|
| 18 |
+
ROOT = HERE.parent
|
| 19 |
+
if str(ROOT) not in sys.path:
|
| 20 |
+
sys.path.insert(0, str(ROOT))
|
| 21 |
+
|
| 22 |
+
from second_brain.plan import plan_from_handles # noqa: E402
|
| 23 |
+
from second_brain.retrieve import SecondBrainIndex # noqa: E402
|
| 24 |
+
|
| 25 |
+
RETRIEVE_GATE = HERE / "gate_retrieve.jsonl"
|
| 26 |
+
ABSTAIN_GATE = HERE / "gate_abstain.jsonl"
|
| 27 |
+
REPORT = HERE / "eval_report.json"
|
| 28 |
+
ADAPTER = HERE / "brain-navigator-r2-adapter"
|
| 29 |
+
SYS = (
|
| 30 |
+
"You are BrainNavigator-R2, the SZL second-brain retrieval planner. "
|
| 31 |
+
"Capability profile SZL-BrainNavigator-R2. Base Qwen/Qwen3.5-0.8B. "
|
| 32 |
+
"You see HANDLES ONLY, never node text. Emit one JSON object. "
|
| 33 |
+
"decision is NAVIGATE or ABSTAIN. groundedOnly is true. "
|
| 34 |
+
"citedNodeIds must be a subset of offered nodeId values. "
|
| 35 |
+
"If none of the offered handles support the query, ABSTAIN with empty steps. "
|
| 36 |
+
"capabilityProfile must be SZL-BrainNavigator-R2. contentAccess HANDLES_ONLY. "
|
| 37 |
+
"brainBinding.status is NOT_RESOLVED. You never execute retrieval."
|
| 38 |
+
)
|
| 39 |
+
JSON_RE = re.compile(r"\{.*\}", re.S)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _load(path: Path) -> list[dict[str, Any]]:
|
| 43 |
+
rows = []
|
| 44 |
+
for line in path.read_text(encoding="utf-8").splitlines():
|
| 45 |
+
if line.strip():
|
| 46 |
+
rows.append(json.loads(line))
|
| 47 |
+
return rows
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _parse_plan(text: str) -> dict[str, Any] | None:
|
| 51 |
+
raw = (text or "").strip()
|
| 52 |
+
if not raw:
|
| 53 |
+
return None
|
| 54 |
+
try:
|
| 55 |
+
return json.loads(raw)
|
| 56 |
+
except json.JSONDecodeError:
|
| 57 |
+
m = JSON_RE.search(raw)
|
| 58 |
+
if not m:
|
| 59 |
+
return None
|
| 60 |
+
try:
|
| 61 |
+
return json.loads(m.group(0))
|
| 62 |
+
except json.JSONDecodeError:
|
| 63 |
+
return None
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def software_bench(idx: SecondBrainIndex) -> dict[str, Any]:
|
| 67 |
+
retrieve = _load(RETRIEVE_GATE)
|
| 68 |
+
abstain = _load(ABSTAIN_GATE)
|
| 69 |
+
retrieve_cases = []
|
| 70 |
+
hit = 0
|
| 71 |
+
for row in retrieve:
|
| 72 |
+
q = row["query"]
|
| 73 |
+
expect = list(row.get("expect_cite") or [])
|
| 74 |
+
got = idx.search(q, k=5)
|
| 75 |
+
ids = [h["nodeId"] for h in got["handles"]]
|
| 76 |
+
ok = bool(expect) and expect[0] in ids
|
| 77 |
+
if ok:
|
| 78 |
+
hit += 1
|
| 79 |
+
plan = plan_from_handles(q, got["handles"])
|
| 80 |
+
retrieve_cases.append(
|
| 81 |
+
{
|
| 82 |
+
"id": row["id"],
|
| 83 |
+
"query": q,
|
| 84 |
+
"expect_cite": expect,
|
| 85 |
+
"got_ids": ids,
|
| 86 |
+
"hit": ok,
|
| 87 |
+
"plan_decision": plan["decision"],
|
| 88 |
+
"plan_cite": plan["citedNodeIds"],
|
| 89 |
+
}
|
| 90 |
+
)
|
| 91 |
+
abs_cases = []
|
| 92 |
+
abs_ok = 0
|
| 93 |
+
for row in abstain:
|
| 94 |
+
q = row["query"]
|
| 95 |
+
plan = plan_from_handles(q, row.get("handles") or [])
|
| 96 |
+
ok = plan["decision"] == "ABSTAIN" and not plan["citedNodeIds"]
|
| 97 |
+
if ok:
|
| 98 |
+
abs_ok += 1
|
| 99 |
+
abs_cases.append(
|
| 100 |
+
{
|
| 101 |
+
"id": row["id"],
|
| 102 |
+
"query": q,
|
| 103 |
+
"decision": plan["decision"],
|
| 104 |
+
"citedNodeIds": plan["citedNodeIds"],
|
| 105 |
+
"ok": ok,
|
| 106 |
+
}
|
| 107 |
+
)
|
| 108 |
+
return {
|
| 109 |
+
"kind": "SOFTWARE",
|
| 110 |
+
"label": "MEASURED",
|
| 111 |
+
"retrieve_hit": f"{hit}/{len(retrieve)}" if retrieve else "0/0",
|
| 112 |
+
"retrieve_hit_correct": hit,
|
| 113 |
+
"retrieve_hit_total": len(retrieve),
|
| 114 |
+
"abstain": f"{abs_ok}/{len(abstain)}" if abstain else "0/0",
|
| 115 |
+
"abstain_correct": abs_ok,
|
| 116 |
+
"abstain_total": len(abstain),
|
| 117 |
+
"retrieve_cases": retrieve_cases,
|
| 118 |
+
"abstain_cases": abs_cases,
|
| 119 |
+
"honesty": (
|
| 120 |
+
"Lexical rank over the PUBLIC 575-chunk projection. "
|
| 121 |
+
"Score is overlap, never correctness. Named-N gates."
|
| 122 |
+
),
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def generate_bench() -> dict[str, Any]:
|
| 127 |
+
if not (ADAPTER / "adapter_config.json").is_file():
|
| 128 |
+
return {
|
| 129 |
+
"kind": "GENERATE",
|
| 130 |
+
"label": "UNAVAILABLE",
|
| 131 |
+
"reason": "no local adapter; SOFTWARE navigator is the shipped planner",
|
| 132 |
+
"publication_eligible": False,
|
| 133 |
+
}
|
| 134 |
+
try:
|
| 135 |
+
import torch
|
| 136 |
+
from unsloth import FastLanguageModel
|
| 137 |
+
except Exception as exc: # noqa: BLE001
|
| 138 |
+
return {
|
| 139 |
+
"kind": "GENERATE",
|
| 140 |
+
"label": "UNAVAILABLE",
|
| 141 |
+
"reason": f"unsloth/torch import failed: {exc}",
|
| 142 |
+
"publication_eligible": False,
|
| 143 |
+
}
|
| 144 |
+
if not torch.cuda.is_available():
|
| 145 |
+
return {
|
| 146 |
+
"kind": "GENERATE",
|
| 147 |
+
"label": "UNAVAILABLE",
|
| 148 |
+
"reason": "CUDA UNAVAILABLE for generate",
|
| 149 |
+
"publication_eligible": False,
|
| 150 |
+
}
|
| 151 |
+
try:
|
| 152 |
+
model, tokenizer = FastLanguageModel.from_pretrained(
|
| 153 |
+
model_name=str(ADAPTER),
|
| 154 |
+
max_seq_length=2048,
|
| 155 |
+
load_in_4bit=False,
|
| 156 |
+
load_in_16bit=True,
|
| 157 |
+
)
|
| 158 |
+
FastLanguageModel.for_inference(model)
|
| 159 |
+
except Exception as exc: # noqa: BLE001
|
| 160 |
+
return {
|
| 161 |
+
"kind": "GENERATE",
|
| 162 |
+
"label": "UNAVAILABLE",
|
| 163 |
+
"reason": f"adapter load failed: {type(exc).__name__}: {exc}",
|
| 164 |
+
"publication_eligible": False,
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
def infer(query: str, handles: list[dict[str, Any]]) -> dict[str, Any] | None:
|
| 168 |
+
user = query + "\n\nCANDIDATE_HANDLES_JSON:\n" + json.dumps(handles)
|
| 169 |
+
messages = [
|
| 170 |
+
{"role": "system", "content": SYS},
|
| 171 |
+
{"role": "user", "content": user},
|
| 172 |
+
]
|
| 173 |
+
# Qwen3.5 ships a multimodal processor; tokenize text only.
|
| 174 |
+
try:
|
| 175 |
+
prompt = tokenizer.apply_chat_template(
|
| 176 |
+
messages,
|
| 177 |
+
tokenize=False,
|
| 178 |
+
add_generation_prompt=True,
|
| 179 |
+
enable_thinking=False,
|
| 180 |
+
)
|
| 181 |
+
except TypeError:
|
| 182 |
+
prompt = tokenizer.apply_chat_template(
|
| 183 |
+
messages, tokenize=False, add_generation_prompt=True
|
| 184 |
+
)
|
| 185 |
+
tok = getattr(tokenizer, "tokenizer", tokenizer)
|
| 186 |
+
encoded = tok(prompt, return_tensors="pt", add_special_tokens=False)
|
| 187 |
+
input_ids = encoded["input_ids"].to(model.device)
|
| 188 |
+
attn = encoded.get("attention_mask")
|
| 189 |
+
eos = getattr(tok, "eos_token_id", None)
|
| 190 |
+
gen_kw: dict[str, Any] = {
|
| 191 |
+
"input_ids": input_ids,
|
| 192 |
+
"max_new_tokens": 384,
|
| 193 |
+
"do_sample": False,
|
| 194 |
+
}
|
| 195 |
+
if attn is not None:
|
| 196 |
+
gen_kw["attention_mask"] = attn.to(model.device)
|
| 197 |
+
if eos is not None:
|
| 198 |
+
gen_kw["eos_token_id"] = eos
|
| 199 |
+
out = model.generate(**gen_kw)
|
| 200 |
+
text = tok.decode(out[0][input_ids.shape[-1] :], skip_special_tokens=True)
|
| 201 |
+
return _parse_plan(text)
|
| 202 |
+
|
| 203 |
+
retrieve = _load(RETRIEVE_GATE)
|
| 204 |
+
abstain = _load(ABSTAIN_GATE)
|
| 205 |
+
nav_ok = 0
|
| 206 |
+
abs_ok = 0
|
| 207 |
+
halluc = 0
|
| 208 |
+
cases: list[dict[str, Any]] = []
|
| 209 |
+
parse_fail = 0
|
| 210 |
+
try:
|
| 211 |
+
for row in retrieve:
|
| 212 |
+
plan = infer(row["query"], row["handles"])
|
| 213 |
+
if not plan:
|
| 214 |
+
parse_fail += 1
|
| 215 |
+
cases.append({"id": row["id"], "ok": False, "reason": "unparseable"})
|
| 216 |
+
print(f"[generate] {row['id']} unparseable")
|
| 217 |
+
continue
|
| 218 |
+
offered = {h["nodeId"] for h in row["handles"]}
|
| 219 |
+
cites = list(plan.get("citedNodeIds") or [])
|
| 220 |
+
if any(c not in offered for c in cites):
|
| 221 |
+
halluc += 1
|
| 222 |
+
expect = list(row.get("expect_cite") or [])
|
| 223 |
+
ok = (
|
| 224 |
+
plan.get("decision") == "NAVIGATE"
|
| 225 |
+
and bool(expect)
|
| 226 |
+
and expect[0] in cites
|
| 227 |
+
and all(c in offered for c in cites)
|
| 228 |
+
)
|
| 229 |
+
if ok:
|
| 230 |
+
nav_ok += 1
|
| 231 |
+
print(f"[generate] {row['id']} {plan.get('decision')} ok={ok}")
|
| 232 |
+
cases.append(
|
| 233 |
+
{
|
| 234 |
+
"id": row["id"],
|
| 235 |
+
"decision": plan.get("decision"),
|
| 236 |
+
"citedNodeIds": cites,
|
| 237 |
+
"ok": ok,
|
| 238 |
+
}
|
| 239 |
+
)
|
| 240 |
+
for row in abstain:
|
| 241 |
+
plan = infer(row["query"], row["handles"])
|
| 242 |
+
if not plan:
|
| 243 |
+
parse_fail += 1
|
| 244 |
+
cases.append({"id": row["id"], "ok": False, "reason": "unparseable"})
|
| 245 |
+
continue
|
| 246 |
+
offered = {h["nodeId"] for h in row["handles"]}
|
| 247 |
+
cites = list(plan.get("citedNodeIds") or [])
|
| 248 |
+
if any(c not in offered for c in cites):
|
| 249 |
+
halluc += 1
|
| 250 |
+
ok = plan.get("decision") == "ABSTAIN" and not cites
|
| 251 |
+
if ok:
|
| 252 |
+
abs_ok += 1
|
| 253 |
+
cases.append(
|
| 254 |
+
{
|
| 255 |
+
"id": row["id"],
|
| 256 |
+
"decision": plan.get("decision"),
|
| 257 |
+
"citedNodeIds": cites,
|
| 258 |
+
"ok": ok,
|
| 259 |
+
}
|
| 260 |
+
)
|
| 261 |
+
except Exception as exc: # noqa: BLE001
|
| 262 |
+
return {
|
| 263 |
+
"kind": "GENERATE",
|
| 264 |
+
"label": "UNAVAILABLE",
|
| 265 |
+
"reason": f"generate failed: {type(exc).__name__}: {exc}",
|
| 266 |
+
"publication_eligible": False,
|
| 267 |
+
}
|
| 268 |
+
return {
|
| 269 |
+
"kind": "GENERATE",
|
| 270 |
+
"label": "MEASURED",
|
| 271 |
+
"retrieve_hit": f"{nav_ok}/{len(retrieve)}" if retrieve else "0/0",
|
| 272 |
+
"retrieve_hit_correct": nav_ok,
|
| 273 |
+
"retrieve_hit_total": len(retrieve),
|
| 274 |
+
"abstain": f"{abs_ok}/{len(abstain)}" if abstain else "0/0",
|
| 275 |
+
"abstain_correct": abs_ok,
|
| 276 |
+
"abstain_total": len(abstain),
|
| 277 |
+
"hallucinated_citation_count": halluc,
|
| 278 |
+
"parse_fail": parse_fail,
|
| 279 |
+
"cases": cases,
|
| 280 |
+
"publication_eligible": False,
|
| 281 |
+
"honesty": (
|
| 282 |
+
"Owner-run named-N generate on local LoRA. Not a third-party bench. "
|
| 283 |
+
"Train loss is not this number. publication_eligible stays false."
|
| 284 |
+
),
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def main() -> int:
|
| 289 |
+
idx = SecondBrainIndex()
|
| 290 |
+
software = software_bench(idx)
|
| 291 |
+
generate = generate_bench()
|
| 292 |
+
report = {
|
| 293 |
+
"schema": "szl.brain-navigator-r2.eval/v1",
|
| 294 |
+
"artifact": "SZLHOLDINGS/brain-navigator-r2",
|
| 295 |
+
"does_not_overwrite": "SZLHOLDINGS/SZL-Khipu-1.5B-BrainNavigator",
|
| 296 |
+
"lambda": "Conjecture 1",
|
| 297 |
+
"doctrine": "v11 LOCKED",
|
| 298 |
+
"publication_eligible": False,
|
| 299 |
+
"maturity": "MEASURED_RESEARCH_ONLY",
|
| 300 |
+
"train_loss_is_eval": False,
|
| 301 |
+
"raw_graph_nodes_admitted_to_gradients": 0,
|
| 302 |
+
"corpus_n": idx.n,
|
| 303 |
+
"software": software,
|
| 304 |
+
"generate": generate,
|
| 305 |
+
"computed_at": datetime.now(timezone.utc).isoformat(),
|
| 306 |
+
"honesty": (
|
| 307 |
+
"Do not claim 5/5 unless MEASURED. Existing 1.5B BrainNavigator "
|
| 308 |
+
"abstain 2/6 is a different SKU and is not restated as this run."
|
| 309 |
+
),
|
| 310 |
+
}
|
| 311 |
+
REPORT.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
|
| 312 |
+
print(
|
| 313 |
+
"SOFTWARE retrieve-hit "
|
| 314 |
+
f"{software['retrieve_hit']} abstain {software['abstain']} "
|
| 315 |
+
f"GENERATE {generate['label']} "
|
| 316 |
+
f"{generate.get('retrieve_hit', 'n/a')} / {generate.get('abstain', 'n/a')}"
|
| 317 |
+
)
|
| 318 |
+
print(f"wrote {REPORT}")
|
| 319 |
+
return 0
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
if __name__ == "__main__":
|
| 323 |
+
raise SystemExit(main())
|
train/eval_report.json
CHANGED
|
@@ -163,10 +163,96 @@
|
|
| 163 |
},
|
| 164 |
"generate": {
|
| 165 |
"kind": "GENERATE",
|
| 166 |
-
"label": "
|
| 167 |
-
"
|
| 168 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
},
|
| 170 |
-
"computed_at": "2026-08-29T13:
|
| 171 |
"honesty": "Do not claim 5/5 unless MEASURED. Existing 1.5B BrainNavigator abstain 2/6 is a different SKU and is not restated as this run."
|
| 172 |
}
|
|
|
|
| 163 |
},
|
| 164 |
"generate": {
|
| 165 |
"kind": "GENERATE",
|
| 166 |
+
"label": "MEASURED",
|
| 167 |
+
"retrieve_hit": "5/5",
|
| 168 |
+
"retrieve_hit_correct": 5,
|
| 169 |
+
"retrieve_hit_total": 5,
|
| 170 |
+
"abstain": "6/6",
|
| 171 |
+
"abstain_correct": 6,
|
| 172 |
+
"abstain_total": 6,
|
| 173 |
+
"hallucinated_citation_count": 0,
|
| 174 |
+
"parse_fail": 0,
|
| 175 |
+
"cases": [
|
| 176 |
+
{
|
| 177 |
+
"id": "nav-00",
|
| 178 |
+
"decision": "NAVIGATE",
|
| 179 |
+
"citedNodeIds": [
|
| 180 |
+
"ingest:szl-formula-ledger:001"
|
| 181 |
+
],
|
| 182 |
+
"ok": true
|
| 183 |
+
},
|
| 184 |
+
{
|
| 185 |
+
"id": "nav-01",
|
| 186 |
+
"decision": "NAVIGATE",
|
| 187 |
+
"citedNodeIds": [
|
| 188 |
+
"formula:blk-d1507e347013"
|
| 189 |
+
],
|
| 190 |
+
"ok": true
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
"id": "nav-02",
|
| 194 |
+
"decision": "NAVIGATE",
|
| 195 |
+
"citedNodeIds": [
|
| 196 |
+
"ingest:radicle-heartwood:001"
|
| 197 |
+
],
|
| 198 |
+
"ok": true
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"id": "nav-03",
|
| 202 |
+
"decision": "NAVIGATE",
|
| 203 |
+
"citedNodeIds": [
|
| 204 |
+
"ingest:radicle-heartwood:000"
|
| 205 |
+
],
|
| 206 |
+
"ok": true
|
| 207 |
+
},
|
| 208 |
+
{
|
| 209 |
+
"id": "nav-04",
|
| 210 |
+
"decision": "NAVIGATE",
|
| 211 |
+
"citedNodeIds": [
|
| 212 |
+
"invariant:flywheel-lineage"
|
| 213 |
+
],
|
| 214 |
+
"ok": true
|
| 215 |
+
},
|
| 216 |
+
{
|
| 217 |
+
"id": "abs-00",
|
| 218 |
+
"decision": "ABSTAIN",
|
| 219 |
+
"citedNodeIds": [],
|
| 220 |
+
"ok": true
|
| 221 |
+
},
|
| 222 |
+
{
|
| 223 |
+
"id": "abs-01",
|
| 224 |
+
"decision": "ABSTAIN",
|
| 225 |
+
"citedNodeIds": [],
|
| 226 |
+
"ok": true
|
| 227 |
+
},
|
| 228 |
+
{
|
| 229 |
+
"id": "abs-02",
|
| 230 |
+
"decision": "ABSTAIN",
|
| 231 |
+
"citedNodeIds": [],
|
| 232 |
+
"ok": true
|
| 233 |
+
},
|
| 234 |
+
{
|
| 235 |
+
"id": "abs-03",
|
| 236 |
+
"decision": "ABSTAIN",
|
| 237 |
+
"citedNodeIds": [],
|
| 238 |
+
"ok": true
|
| 239 |
+
},
|
| 240 |
+
{
|
| 241 |
+
"id": "abs-04",
|
| 242 |
+
"decision": "ABSTAIN",
|
| 243 |
+
"citedNodeIds": [],
|
| 244 |
+
"ok": true
|
| 245 |
+
},
|
| 246 |
+
{
|
| 247 |
+
"id": "abs-05",
|
| 248 |
+
"decision": "ABSTAIN",
|
| 249 |
+
"citedNodeIds": [],
|
| 250 |
+
"ok": true
|
| 251 |
+
}
|
| 252 |
+
],
|
| 253 |
+
"publication_eligible": false,
|
| 254 |
+
"honesty": "Owner-run named-N generate on local LoRA. Not a third-party bench. Train loss is not this number. publication_eligible stays false."
|
| 255 |
},
|
| 256 |
+
"computed_at": "2026-08-29T13:36:22.511311+00:00",
|
| 257 |
"honesty": "Do not claim 5/5 unless MEASURED. Existing 1.5B BrainNavigator abstain 2/6 is a different SKU and is not restated as this run."
|
| 258 |
}
|