code wiki / _hdl_build / nx_wiki_search_page.nx

nx_wiki_search_page.nx source

↩ module page · 102 lines · 8223 B

1// nx_wiki_search_page.nx -- GENERATE the live "/wiki/search.html" instant-answer page (IMS Thrust B). 2// A walkable Diataxis REFERENCE page built through the reusable typed generator nx_ims_page (full walk 3// furniture: head/nav/breadcrumb/tree/prev-next/related/backlinks/infobox/toc/footer, nav model read from 4// wiki_links.tsv by slug=search). The page's OWN content (body_html) is: 5// 1. a search box (GET form -> /wiki/search.html?q=...) -- the same form the site nav already exposes, 6// 2. an INSTANT-ANSWER CARD section: for 3 representative queries we call the FIXED answer organ 7// (nx_wiki_answer.answer) AT BUILD TIME and embed the returned snippet + the CORRECT source link, 8// 3. a short "how it works" note (registry-driven BM25F + densest-cluster snippet + NO_ANSWER teeth). 9// 10// This demonstrates the instant-answer card end-to-end with REAL output (not hand-written): the snippet is 11// produced by nx_search_snippet_extract (already html-escaped + <mark>-highlighted, safe to embed) and the 12// source link is base_url+slug.html exactly as the answer organ resolves it. STATIC page (the answer is 13// rendered at build time for the example queries); a live daemon /wiki/search route would call answer() per 14// request -- noted in the page itself. ADDITIVE: writes web_assets/search.html only. 15// 16// REUSE (nothing reinvented): nx_ims_page ims_page (the typed walkable generator) + nx_wiki_answer answer/ 17// wa_new/WaAnswer (the fixed instant-answer engine). license_tier: ORIGINAL 18import "nx_ims_page.nx" 19import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 20import "nx_wiki_answer.nx" 21 22const WSP_BODY_CAP: i64 = 65536 // body_html scratch (>> the few KB this page needs) 23 24func wsp_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 25// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 26// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 27// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 28// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 29func wsp_num(v: i64) -> i64 { nxi_out(v); return 0 } 30 31// append NUL-terminated s into dst at off; return new off. 32func wsp_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != 0 as u8 { dst[off + i] = s[i]; i = i + 1 } return off + i } 33// append a counted (non-NUL-terminated) byte run. 34func wsp_catb(dst: *u8, off: i64, s: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { dst[off + i] = s[i]; i = i + 1 } return off + i } 35func wsp_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 36 37// render ONE instant-answer card for `query` into dst at off (calls the live answer organ). Returns new off. 38// On WA_OK: a card with the query, the (escaped+marked) snippet in a blockquote, and the CORRECT source link. 39// On WA_NO_ANSWER (e.g. the nonsense demo): a "refused" card -- proves the no-fabrication teeth, no fake link. 40func wsp_card(dst: *u8, off: i64, query: *u8) -> i64 { 41 let a: *WaAnswer = wa_new() 42 answer(query, a) 43 var o: i64 = off 44 o = wsp_cat(dst, o, "<div class=\"answer-card\"><p class=\"q\"><strong>Q:</strong> " as *u8) 45 o = wsp_cat(dst, o, query) 46 o = wsp_cat(dst, o, "</p>\n" as *u8) 47 if a.verdict == WA_OK { 48 // snippet is already html-escaped + <mark>-highlighted by nx_search_snippet_extract -> embed directly. 49 o = wsp_cat(dst, o, "<blockquote class=\"answer\">" as *u8) 50 o = wsp_catb(dst, o, a.snippet, wsp_slen(a.snippet)) 51 o = wsp_cat(dst, o, "</blockquote>\n" as *u8) 52 // CORRECT source link: base_url + slug + .html (e.g. https://nishifamily.com/wiki/access_wall.html). 53 // Use the on-site relative form /wiki/<slug>.html so the link stays inside the corpus (guard-safe). 54 o = wsp_cat(dst, o, "<p class=\"src\">Source: <a href=\"/wiki/" as *u8) 55 o = wsp_catb(dst, o, a.slug, wsp_slen(a.slug)) 56 o = wsp_cat(dst, o, ".html\"><code>" as *u8) 57 o = wsp_catb(dst, o, a.slug, wsp_slen(a.slug)) 58 o = wsp_cat(dst, o, ".html</code></a></p>\n" as *u8) 59 wsp_w(" card OK q='"); wsp_w(query); wsp_w("' -> /wiki/"); wsp_w(a.slug); wsp_w(".html\n") 60 } else { 61 o = wsp_cat(dst, o, "<p class=\"noanswer\"><em>No confident answer &mdash; the engine refuses rather than fabricate (BM25F below the registered floor).</em></p>\n" as *u8) 62 wsp_w(" card REFUSED q='"); wsp_w(query); wsp_w("' (no fabrication)\n") 63 } 64 o = wsp_cat(dst, o, "</div>\n" as *u8) 65 return o 66} 67 68func main() -> i64 { 69 let b: *u8 = sys_mmap(WSP_BODY_CAP) 70 var o: i64 = 0 71 72 // ---- lede + the search box (GET -> /wiki/search.html, matching the site nav form) ---- 73 o = wsp_cat(b, o, "<p class=\"lede\"><strong>Instant answers over the live wiki.</strong> Ask a question and get the passage that answers it &mdash; with its source page &mdash; in one step, no results list to click through. The ranker is sovereign BM25F over a clean per-page index; the answer is the densest cluster of your terms on the top page. If nothing scores above the confidence floor, the engine says so instead of inventing an answer.</p>\n" as *u8) 74 o = wsp_cat(b, o, "<form class=\"wiki-search\" action=\"/wiki/search.html\" method=\"get\"><input type=\"search\" name=\"q\" placeholder=\"Ask the wiki&hellip; (e.g. zero trust access)\" aria-label=\"Search the wiki\"><button type=\"submit\">Answer</button></form>\n" as *u8) 75 76 // ---- the instant-answer cards: REAL output from the fixed answer organ at build time ---- 77 o = wsp_cat(b, o, "<h3>Example instant answers</h3>\n" as *u8) 78 o = wsp_cat(b, o, "<p>Each card below was rendered by calling the answer organ (<code>nx_wiki_answer</code>) at build time over the live index &mdash; the snippet and source link are real, not hand-written.</p>\n" as *u8) 79 o = wsp_card(b, o, "zero trust access" as *u8) 80 o = wsp_card(b, o, "capitalism lab game" as *u8) 81 o = wsp_card(b, o, "genesis genealogy lineage" as *u8) 82 // a teeth demo: nonsense -> refusal (no fabricated source). 83 o = wsp_card(b, o, "xyzzy qwffz" as *u8) 84 85 // ---- how it works ---- 86 o = wsp_cat(b, o, "<h3 id=\"how\">How it works</h3>\n" as *u8) 87 o = wsp_cat(b, o, "<ol>\n" as *u8) 88 o = wsp_cat(b, o, "<li><strong>Clean index.</strong> Each of the 13 live pages is fetched, the nav/breadcrumb/footer chrome is stripped, and the visible prose is indexed &mdash; so common chrome words don't poison relevance.</li>\n" as *u8) 89 o = wsp_cat(b, o, "<li><strong>BM25F ranking.</strong> The fielded ranker boosts a term that appears in the page <em>title</em> (the page is about it), then picks the single best page.</li>\n" as *u8) 90 o = wsp_cat(b, o, "<li><strong>Densest-cluster snippet.</strong> The answer is the tightest window of your query terms on that page, highlighted.</li>\n" as *u8) 91 o = wsp_cat(b, o, "<li><strong>No-fabrication floor.</strong> If the top score is below the registered confidence threshold, the answer is withheld &mdash; the engine never makes one up.</li>\n" as *u8) 92 o = wsp_cat(b, o, "</ol>\n" as *u8) 93 o = wsp_cat(b, o, "<p class=\"why\">This page is <strong>static</strong>: the example cards are rendered at build time. A live <code>/wiki/search</code> daemon route would call the same <code>answer()</code> per request with your typed query &mdash; the engine and verdicts are identical; only the trigger differs.</p>\n" as *u8) 94 o = wsp_cat(b, o, "<p>Related: <a href=\"/wiki/board.html\">workstream board</a> &middot; <a href=\"/wiki/devlog.html\">devlog</a> &middot; <a href=\"/wiki/roadmap.html\">roadmap</a>.</p>\n" as *u8) 95 b[o] = 0 as u8 96 97 wsp_w("=== nx_wiki_search_page: rendering /wiki/search.html (reference, walkable) ===\n") 98 let bytes: i64 = ims_page("search" as *u8, "reference" as *u8, "Wiki Search &mdash; Instant Answers" as *u8, b) 99 if bytes < 0 { wsp_w("FATAL: ims_page failed rc="); wsp_num(bytes); wsp_w("\n"); sys_exit(2); return 2 } 100 wsp_w("=== wrote web_assets/search.html bytes="); wsp_num(bytes); wsp_w(" ===\n") 101 sys_exit(0); return 0 102}