code wiki / _hdl_build / nx_gen.nx

nx_gen.nx source

↩ module page · 177 lines · 11139 B

1// nx_gen.nx -- the sovereign GEN MCP tool: a thin NAS-side client for the gen orchestrator daemon (loopback :18795). 2// Runs ON THE NAS (invoked by nx_tools_api over /mcp), so it can reach the plain-HTTP loopback daemon that nx_https_get 3// (HTTPS-only) and the login-gated /gen edge cannot -- and returns the daemon's JSON straight through the MCP channel 4// (no root-owned files, no namespace/harness games). Verbs: 5// anatomy <prompt> -> POST /api/anatomy {prompt} = instant deterministic QC (risk/factors/wetfix) 6// recent [n] -> GET /api/recent?n=n = latest gallery cids 7// chat <message> -> POST /api/chat {message} = companion reply (photo msgs also gen) 8// generate <prompt> [count] [WxH] [seed] -> POST /api/generate = render on the swarm GPU + ingest -> {count,ms,cids} 9// img2img <cid> <prompt> [denoise] -> POST /api/img2img = edit an EXISTING gallery cid -> new cid 10// batch <prompt> [count] [WxH] [seed] -> POST /api/batch = async render (edge-timeout-proof) -> job id 11// batchget <jid> -> GET /api/batch/<jid> = poll the async job ({"done":0} while rendering) 12// (2026-08-04: generate/img2img/batch/batchget close the "expose the gen daemon as a full MCP tool" debt -- 13// agents/workflows drive the WHOLE gen loop over MCP; the daemon resolves its GPU worker from the swarm SSOT.) 14// license_tier: ORIGINAL expect_exit: 0 15import "nx_syscalls.nx" 16import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 17const K_MAGIC_4194304: i64 = 4194304 18const K_MAGIC_131072: i64 = 131072 19const K_MAGIC_65536: i64 = 65536 20const K_MAGIC_18795: i64 = 18795 21const G_TMO_FAST: i64 = 60 22const G_TMO_RENDER: i64 = 300 23 24func g_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 25func g_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){dst[o]=s[i]; o=o+1; i=i+1} return o } 26func g_catn(dst: *u8, off: i64, s: *u8, n: i64) -> i64 { var o: i64=off; var i: i64=0; while i<n {dst[o]=s[i]; o=o+1; i=i+1} return o } 27func g_itoa(dst: *u8, off: i64, v: i64) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var o: i64=off; var q: i64=k-1; while q>=0{dst[o]=t[q];o=o+1;q=q-1} return o } 28func g_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8) { if (a[i]&0xff)!=(b[i]&0xff) { return 0 } i=i+1 } if b[i]!=(0 as u8) { return 0 } return 1 } 29// parse a non-negative integer out of s (ignores non-digits); returns dflt when none found 30func g_atoi(s: *u8, dflt: i64) -> i64 { var v: i64=0; var seen: i64=0; var i: i64=0; while s[i]!=(0 as u8) { let c: i64=s[i]&0xff; if c>=48 { if c<=57 { v=v*10+(c-48); seen=1 } } i=i+1 } if seen==0 { return dflt } return v } 31// JSON-escape s into dst at off (escapes ", \, and control chars -> space) 32func g_jesc(dst: *u8, off: i64, s: *u8) -> i64 { 33 var o: i64=off; var i: i64=0 34 while s[i]!=(0 as u8) { 35 let c: i64 = s[i]&0xff 36 if c==34 { dst[o]=92 as u8; o=o+1; dst[o]=34 as u8; o=o+1 } 37 else { if c==92 { dst[o]=92 as u8; o=o+1; dst[o]=92 as u8; o=o+1 } 38 else { if c<32 { dst[o]=32 as u8; o=o+1 } else { dst[o]=s[i]; o=o+1 } } } 39 i=i+1 40 } 41 return o 42} 43// is s a plain decimal number (digits with at most one dot)? 1=yes -- safe to emit as a raw JSON number 44func g_is_decimal(s: *u8) -> i64 { 45 var i: i64=0; var dots: i64=0; var digits: i64=0 46 while s[i]!=(0 as u8) { 47 let c: i64=s[i]&0xff 48 if c==46 { dots=dots+1 } else { if c<48 { return 0 } if c>57 { return 0 } digits=digits+1 } 49 i=i+1 50 } 51 if digits==0 { return 0 } 52 if dots>1 { return 0 } 53 return 1 54} 55// connect loopback:port, send req[0..reqlen], stream the response BODY (after \r\n\r\n) to stdout. returns 0 ok. 56func g_http(port: i64, req: *u8, reqlen: i64, tmo: i64) -> i64 { 57 let fd: i64 = sys_socket(2, 1, 0) 58 if fd < 0 { sys_write(2, "nx_gen: socket-fail\n" as *u8, 20); return 1 } 59 sys_set_socket_timeout(fd, tmo) 60 let a: *u8 = sys_mmap(16) 61 a[0]=2 as u8; a[1]=0 as u8; a[2]=((port>>8)&0xff) as u8; a[3]=(port&0xff) as u8 62 a[4]=127 as u8; a[5]=0 as u8; a[6]=0 as u8; a[7]=1 as u8 63 var zi: i64=8; while zi<16 { a[zi]=0 as u8; zi=zi+1 } 64 if nx_connect_bounded(fd, a, 16, NX_CONN_DEFAULT_MS) != 0 { sys_close(fd); sys_write(2, "nx_gen: daemon unreachable on 127.0.0.1:18795 -- is nx_gen_orchestrator_daemon up?\n" as *u8, 81); return 2 } 65 sys_write(fd, req, reqlen) 66 let resp: *u8 = sys_mmap(K_MAGIC_4194304); var total: i64 = 0; var go: i64 = 1 67 while go == 1 { let r: i64 = sys_read(fd, (resp as i64 + total) as *u8, K_MAGIC_4194304 - total); if r <= 0 { go=0 } else { total=total+r; if total>=K_MAGIC_4194304 { go=0 } } } 68 sys_close(fd) 69 var bs: i64 = 0; var i: i64 = 0 70 while i + 4 <= total { if (resp[i]&0xff)==13 { if (resp[i+1]&0xff)==10 { if (resp[i+2]&0xff)==13 { if (resp[i+3]&0xff)==10 { bs=i+4; i=total } } } } i=i+1 } 71 if bs == 0 { sys_write(1, resp, total) } else { sys_write(1, (resp as i64 + bs) as *u8, total - bs) } 72 sys_write(1, "\n" as *u8, 1) 73 return 0 74} 75// build the {"prompt":..,"count":N,"width":W,"height":H[,"seed":S]} body shared by generate + batch. 76// argv layout: argv[2]=prompt argv[3]=count argv[4]=WxH argv[5]=seed (all optional past prompt). 77func g_render_body(body: *u8, argc: i64, argv: *i64) -> i64 { 78 var prompt: *u8 = "" as *u8 79 if argc >= 3 { prompt = argv[2] as *u8 } 80 var count: i64 = 1 81 if argc >= 4 { count = g_atoi(argv[3] as *u8, 1) } 82 var w: i64 = 512 83 var h: i64 = 512 84 if argc >= 5 { 85 let sz: *u8 = argv[4] as *u8 86 var wi: i64 = 0; var xat: i64 = 0 - 1; var i: i64 = 0 87 while sz[i] != (0 as u8) { if (sz[i]&0xff)==120 { xat=i } i=i+1 } 88 if xat > 0 { 89 var wv: i64=0; wi=0 90 while wi < xat { let c: i64=sz[wi]&0xff; if c>=48 { if c<=57 { wv=wv*10+(c-48) } } wi=wi+1 } 91 let hv: i64 = g_atoi((sz as i64 + xat + 1) as *u8, 0) 92 if wv > 0 { if hv > 0 { w=wv; h=hv } } 93 } 94 } 95 var bo: i64 = g_cat(body, 0, "{\"prompt\":\"" as *u8); bo = g_jesc(body, bo, prompt) 96 bo = g_cat(body, bo, "\",\"count\":" as *u8); bo = g_itoa(body, bo, count) 97 bo = g_cat(body, bo, ",\"width\":" as *u8); bo = g_itoa(body, bo, w) 98 bo = g_cat(body, bo, ",\"height\":" as *u8); bo = g_itoa(body, bo, h) 99 if argc >= 6 { let sv: i64 = g_atoi(argv[5] as *u8, 0 - 1); if sv >= 0 { bo = g_cat(body, bo, ",\"seed\":" as *u8); bo = g_itoa(body, bo, sv) } } 100 bo = g_cat(body, bo, "}" as *u8) 101 return bo 102} 103// assemble a loopback POST with a JSON body into req; returns request length 104func g_post(req: *u8, path: *u8, body: *u8, bo: i64) -> i64 { 105 var o: i64 = g_cat(req, 0, "POST " as *u8) 106 o = g_cat(req, o, path) 107 o = g_cat(req, o, " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\nContent-Type: application/json\r\nContent-Length: " as *u8) 108 o = g_itoa(req, o, bo); o = g_cat(req, o, "\r\n\r\n" as *u8); o = g_catn(req, o, body, bo) 109 return o 110} 111 112func main(argc: i64, argv: *i64) -> i64 { 113 let usage: *u8 = "usage: nx_gen <anatomy <prompt> | recent [n] | chat <message> | generate <prompt> [count] [WxH] [seed] | img2img <cid> <prompt> [denoise] | batch <prompt> [count] [WxH] [seed] | batchget <jid>>\n" as *u8 114 if argc < 2 { sys_write(1, usage, g_slen(usage)); return 1 } 115 let verb: *u8 = argv[1] as *u8 116 let req: *u8 = sys_mmap(K_MAGIC_131072) 117 118 if g_streq(verb, "anatomy" as *u8)==1 { 119 var prompt: *u8 = "" as *u8 120 if argc >= 3 { prompt = argv[2] as *u8 } 121 let body: *u8 = sys_mmap(K_MAGIC_65536); var bo: i64 = g_cat(body, 0, "{\"prompt\":\"" as *u8); bo = g_jesc(body, bo, prompt); bo = g_cat(body, bo, "\"}" as *u8) 122 let o: i64 = g_post(req, "/api/anatomy" as *u8, body, bo) 123 return g_http(K_MAGIC_18795, req, o, G_TMO_FAST) 124 } 125 if g_streq(verb, "chat" as *u8)==1 { 126 var msg: *u8 = "" as *u8 127 if argc >= 3 { msg = argv[2] as *u8 } 128 let body: *u8 = sys_mmap(K_MAGIC_65536); var bo: i64 = g_cat(body, 0, "{\"message\":\"" as *u8); bo = g_jesc(body, bo, msg); bo = g_cat(body, bo, "\"}" as *u8) 129 let o: i64 = g_post(req, "/api/chat" as *u8, body, bo) 130 return g_http(K_MAGIC_18795, req, o, G_TMO_RENDER) 131 } 132 if g_streq(verb, "recent" as *u8)==1 { 133 var n: i64 = 12 134 if argc >= 3 { n = g_atoi(argv[2] as *u8, 12) } 135 var o: i64 = g_cat(req, 0, "GET /api/recent?n=" as *u8); o = g_itoa(req, o, n) 136 o = g_cat(req, o, " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n" as *u8) 137 return g_http(K_MAGIC_18795, req, o, G_TMO_FAST) 138 } 139 if g_streq(verb, "generate" as *u8)==1 { 140 if argc < 3 { sys_write(1, "nx_gen generate needs a prompt: generate <prompt> [count] [WxH] [seed]\n" as *u8, 71); return 1 } 141 let body: *u8 = sys_mmap(K_MAGIC_65536) 142 let bo: i64 = g_render_body(body, argc, argv) 143 let o: i64 = g_post(req, "/api/generate" as *u8, body, bo) 144 return g_http(K_MAGIC_18795, req, o, G_TMO_RENDER) 145 } 146 if g_streq(verb, "batch" as *u8)==1 { 147 if argc < 3 { sys_write(1, "nx_gen batch needs a prompt: batch <prompt> [count] [WxH] [seed]\n" as *u8, 65); return 1 } 148 let body: *u8 = sys_mmap(K_MAGIC_65536) 149 let bo: i64 = g_render_body(body, argc, argv) 150 let o: i64 = g_post(req, "/api/batch" as *u8, body, bo) 151 return g_http(K_MAGIC_18795, req, o, G_TMO_FAST) 152 } 153 if g_streq(verb, "batchget" as *u8)==1 { 154 if argc < 3 { sys_write(1, "nx_gen batchget needs a job id from batch\n" as *u8, 42); return 1 } 155 var o: i64 = g_cat(req, 0, "GET /api/batch/" as *u8) 156 let jid: *u8 = argv[2] as *u8 157 var ji: i64 = 0 158 while jid[ji] != (0 as u8) { let c: i64=jid[ji]&0xff; if c>=48 { if c<=57 { req[o]=jid[ji]; o=o+1 } } ji=ji+1 } 159 o = g_cat(req, o, " HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n" as *u8) 160 return g_http(K_MAGIC_18795, req, o, G_TMO_FAST) 161 } 162 if g_streq(verb, "img2img" as *u8)==1 { 163 if argc < 4 { sys_write(1, "nx_gen img2img needs <cid> <prompt> [denoise 0.0-1.0, default 0.6] -- list cids via recent\n" as *u8, 91); return 1 } 164 let body: *u8 = sys_mmap(K_MAGIC_65536) 165 var bo: i64 = g_cat(body, 0, "{\"cid\":\"" as *u8); bo = g_jesc(body, bo, argv[2] as *u8) 166 bo = g_cat(body, bo, "\",\"prompt\":\"" as *u8); bo = g_jesc(body, bo, argv[3] as *u8) 167 bo = g_cat(body, bo, "\",\"denoise\":" as *u8) 168 var dn: *u8 = "0.6" as *u8 169 if argc >= 5 { if g_is_decimal(argv[4] as *u8)==1 { dn = argv[4] as *u8 } } 170 bo = g_cat(body, bo, dn) 171 bo = g_cat(body, bo, "}" as *u8) 172 let o: i64 = g_post(req, "/api/img2img" as *u8, body, bo) 173 return g_http(K_MAGIC_18795, req, o, G_TMO_RENDER) 174 } 175 let unk: *u8 = "nx_gen: unknown verb (use anatomy|recent|chat|generate|img2img|batch|batchget)\n" as *u8 176 sys_write(1, unk, g_slen(unk)); return 1 177}