code wiki / _hdl_build / nx_gen_ingest.nx

nx_gen_ingest.nx source

↩ module page · 242 lines · 11987 B

1// nx_gen_ingest.nx -- ingest an ON-DISK PNG into the gallery: GENREC tEXt -> CID -> blob -> sidecar. 2// 3// WHY THIS EXISTS. The operator asked, this session: *"are these gens pointable so right now i can 4// see them in gallery"*. The measured answer was NO, and it stayed no while three sovereign images 5// were produced: `grep -c sovgen /volume1/ai/gen/cids.tsv` returned 0. The sovereign renderer wrote 6// PNGs to /volume1/ai/images/sovgen/ -- a directory the gallery does not read. Nothing was broken; 7// the images were simply never handed to the index. 8// ★★★★★ AN ARTEFACT THAT IS NOT IN THE INDEX DOES NOT EXIST TO THE PRODUCT, HOWEVER REAL THE FILE IS. 9// 10// nx_gen_pipeline already does exactly this ingest, but its entry point takes BASE64 because it was 11// written for a GPU worker that answers in JSON. The sovereign path has raw PNG bytes on disk, so 12// this organ reuses that file's blob-path and sidecar-append helpers rather than restating them -- 13// the gallery's contract must have ONE definition or the two writers will disagree about where an 14// image lives, which is a corruption nobody would see until a link 404s. 15// 16// Usage: 17// nx_gen_ingest <blobdir> <sidecar|-> <idx|-> <blob|-> <view|-> <model> <seed> <prompt> <png> ... 18// Production values (measured from the live store, not assumed): 19// blobdir /volume1/ai/gen/blob- 20// idx /volume1/ai/galx/knowledge/index/galx_cid.idx 21// blob /volume1/ai/galx/knowledge/index/galx_cid.blob 22// Pass `-` for the sidecar to skip the legacy TSV entirely -- which is the point: the gen->gallery 23// path now writes the SOVEREIGN store directly (nx_galx_cidput), so no 70-byte text append and no 24// 34MB recompile of 235,445 rows stands between a render and the gallery. 25// The gate points everything at /tmp -- ★ A TEST THAT WRITES TO THE PRODUCTION STORE IS NOT A TEST. 26// license_tier: ORIGINAL 27 28import "nx_syscalls.nx" 29import "nx_strconv.nx" 30import "nx_canon_cid.nx" 31import "nx_png_textw.nx" 32import "nx_store_ingest.nx" 33import "nx_gen_pipeline.nx" 34import "nx_galx_cidput.nx" 35import "nx_galx_viewadd_lib.nx" 36const GI_MAGIC_4096: i64 = 4096 37const GI_MAGIC_1024: i64 = 1024 38 39func gi_puts(s: *u8) -> i64 { 40 var n: i64 = 0 41 while s[n] != (0 as u8) { n = n + 1 } 42 return sys_write(1, s, n) 43} 44 45// Read a whole file into a fresh mapping. Returns byte count, or negative. 46// ⚠ Returns -2 on a SHORT read rather than a partial buffer: a truncated PNG would otherwise be 47// ingested under a CID that no future read of the same file could reproduce. 48func gi_slurp(path: *u8, out: *u8, cap: i64) -> i64 { 49 let fd: i64 = sys_openat_rd(path) 50 if fd < 0 { return 0 - 1 } 51 var n: i64 = 0 52 var go: i64 = 1 53 while go == 1 { 54 let r: i64 = sys_read(fd, ((out as i64) + n) as *u8, cap - n) 55 if r <= 0 { go = 0 } else { n = n + r } 56 if n >= cap { go = 0 } 57 } 58 sys_close(fd) 59 if n >= cap { return 0 - 2 } 60 return n 61} 62 63func gi_be32(b: *u8, off: i64) -> i64 { 64 return ((b[off] as i64) << 24) | ((b[off + 1] as i64) << 16) 65 | ((b[off + 2] as i64) << 8) | (b[off + 3] as i64) 66} 67 68// "<w>x<h>" from the IHDR. The size string is DERIVED from the file, never passed in -- a caller 69// that mislabels the dimensions would write a GENREC that disagrees with its own pixels. 70func gi_size(png: *u8, n: i64, out: *u8) -> i64 { 71 if n < 33 { return 0 - 1 } 72 if png[12] != (0x49 as u8) { return 0 - 1 } // 'I' of IHDR 73 var o: i64 = nx_strconv_format_i64(gi_be32(png, 16), out) 74 out[o] = 0x78 // 'x' 75 o = o + 1 76 o = o + nx_strconv_format_i64(gi_be32(png, 20), ((out as i64) + o) as *u8) 77 out[o] = 0 78 return o 79} 80 81// basename, so the report reads as images rather than as directories 82func gi_base(p: *u8) -> *u8 { 83 var n: i64 = 0 84 var last: i64 = 0 85 while p[n] != (0 as u8) { 86 if p[n] == (0x2F as u8) { last = n + 1 } 87 n = n + 1 88 } 89 return ((p as i64) + last) as *u8 90} 91 92const GI_CAP: i64 = 33554432 93 94func main(argc: i64, argv: *i64) -> i64 { 95 if argc < 10 { 96 gi_puts("usage: nx_gen_ingest <blobdir> <sidecar|-> <idx|-> <blob|-> <model> <seed> <prompt> <png> [<png> ...]\n" as *u8) 97 return 2 98 } 99 let blobdir: *u8 = argv[1] as *u8 100 let sidecar: *u8 = argv[2] as *u8 101 let cidx: *u8 = argv[3] as *u8 102 let cblob: *u8 = argv[4] as *u8 103 let cview: *u8 = argv[5] as *u8 104 let model: *u8 = argv[6] as *u8 105 let seed: *u8 = argv[7] as *u8 106 let prompt: *u8 = argv[8] as *u8 107 // ⚠THE ENUMERATOR IS A SEPARATE INDEX AND IT IS THE ONE THE USER SEES. Resolver-only ingest 108 // yields an image fetchable by cid and invisible in the grid -- exactly what shipped once. 109 var do_view: i64 = 1 110 if cview[0] == (0x2D as u8) { if cview[1] == (0 as u8) { do_view = 0 } } 111 // "-" disables a sink. The legacy TSV is opt-IN now, not opt-out. 112 var do_side: i64 = 1 113 if sidecar[0] == (0x2D as u8) { if sidecar[1] == (0 as u8) { do_side = 0 } } 114 var do_native: i64 = 1 115 if cidx[0] == (0x2D as u8) { if cidx[1] == (0 as u8) { do_native = 0 } } 116 117 let raw: *u8 = sys_mmap(GI_CAP + GI_MAGIC_4096) 118 let wt: *u8 = sys_mmap(GI_CAP + GI_MAGIC_4096) 119 let cid: *u8 = sys_mmap(128) 120 let path: *u8 = sys_mmap(GI_MAGIC_1024) 121 let size: *u8 = sys_mmap(64) 122 let chash: *u8 = sys_mmap(128) 123 let args: *u8 = sys_mmap(512) 124 125 var n_new: i64 = 0 126 var n_dup: i64 = 0 127 var n_err: i64 = 0 128 var n_idx: i64 = 0 129 var n_view: i64 = 0 130 131 var a: i64 = 9 132 while a < argc { 133 let png: *u8 = argv[a] as *u8 134 gi_puts(gi_base(png)) 135 gi_puts(" " as *u8) 136 let rn: i64 = gi_slurp(png, raw, GI_CAP) 137 if rn < 0 { 138 gi_puts("UNREADABLE (or larger than the 32MB cap)\n" as *u8) 139 n_err = n_err + 1 140 } else { 141 if gi_size(raw, rn, size) < 0 { 142 // Refuse rather than ingest something that is not a PNG: the gallery serves these 143 // bytes to a browser, and the CID would make a non-image permanent. 144 gi_puts("NOT A PNG (no IHDR)\n" as *u8) 145 n_err = n_err + 1 146 } else { 147 // ⚠⚠ THE STORE'S CID IS A HASH OF THE HARVESTED GENREC FACTORS, NOT OF THE PIXELS. 148 // That is sound for the GPU-worker path, which increments the seed per image, so 149 // its factors differ per image. It is NOT sound for ingesting arbitrary files: 150 // ingesting two DIFFERENT sovereign renders (69,600 B and 68,863 B) under one seed 151 // produced ONE CID, and the second image was silently dropped as a duplicate. 152 // ★★★★★★ CONTENT ADDRESSING THAT HASHES THE LABEL INSTEAD OF THE CONTENT SILENTLY 153 // DISCARDS EVERY COLLISION AS A "DUPLICATE" -- and the dedup counter reports it as 154 // a success. The dedup path is exactly where the loss becomes invisible. 155 // ⇒ fold the TRUE content hash into the factors, so the record identifies the bytes. 156 // ⚠PRECISELY WHAT THIS HASH COVERS: the source file AS INGESTED, before the GENREC 157 // chunk is inserted. The stored blob is those bytes PLUS the chunk, so re-hashing 158 // the served image does NOT reproduce it -- you must hash the original, or strip 159 // the injected tEXt first. Stating the wrong subject here would hand the next 160 // reader a "verification" that fails for a file that is perfectly intact. 161 cid_of(raw, rn, chash) 162 let pfx: *u8 = "sovereign-gen-path content=" as *u8 163 var ao: i64 = 0 164 while pfx[ao] != (0 as u8) { args[ao] = pfx[ao]; ao = ao + 1 } 165 var ci: i64 = 0 166 while chash[ci] != (0 as u8) { args[ao] = chash[ci]; ao = ao + 1; ci = ci + 1 } 167 args[ao] = 0 168 169 let wl: i64 = png_insert_genrec(raw, rn, seed, model, 170 "nishi-sovereign" as *u8, "8" as *u8, 171 "nishios" as *u8, prompt, size, 172 args, wt) 173 if wl < 0 { 174 gi_puts("GENREC INJECT FAILED\n" as *u8) 175 n_err = n_err + 1 176 } else { 177 let rc: i64 = nx_store_ingest_ingest_cid(wt, wl, "nxc1" as *u8, cid) 178 if rc < 0 { 179 gi_puts("STORE INGEST FAILED\n" as *u8) 180 n_err = n_err + 1 181 } else { 182 gp_blob_path(blobdir, cid, path) 183 let fd: i64 = sys_openat_wr(path, 0x1a4) 184 if fd >= 0 { sys_write(fd, wt, wl); sys_close(fd) } 185 // rc==1 is new, rc==0 is already in the store. Appending on a dup would 186 // grow the sidecar without bound and make /img/<cid> ambiguous. 187 if rc == 1 { 188 if do_side == 1 { gp_sidecar_append(sidecar, cid, path) } 189 n_new = n_new + 1 190 } else { n_dup = n_dup + 1 } 191 gi_puts(size) 192 gi_puts(" " as *u8) 193 gi_puts(cid) 194 if rc == 1 { gi_puts(" NEW" as *u8) } else { gi_puts(" dup" as *u8) } 195 196 // ---- the sovereign store, directly. This is what the gallery READS. ---- 197 // Done even for rc==0: a blob can exist in the factor store while the 198 // cid->path index has never heard of it, which is precisely the state that 199 // left 109 images 404ing. The put is idempotent, so re-asserting is free. 200 if do_native == 1 { 201 let pr: i64 = nx_galx_cidput(cidx, cblob, cid, path) 202 if pr == CP_INSERTED { gi_puts(" idx:INSERTED" as *u8); n_idx = n_idx + 1 } 203 else { 204 if pr == CP_PRESENT { gi_puts(" idx:present" as *u8) } 205 else { 206 gi_puts(" idx:FAILED" as *u8) 207 n_err = n_err + 1 208 } 209 } 210 } 211 // ---- THE ENUMERATOR. Separate index, and the only one the grid reads. ---- 212 if do_view == 1 { 213 let vr: i64 = nx_galx_view_prepend(cview, cid) 214 if vr == NXV_ADDED { gi_puts(" view:ADDED" as *u8); n_view = n_view + 1 } 215 else { 216 if vr == NXV_PRESENT { gi_puts(" view:present" as *u8) } 217 else { gi_puts(" view:FAILED" as *u8); n_err = n_err + 1 } 218 } 219 } 220 gi_puts("\n" as *u8) 221 } 222 } 223 } 224 } 225 a = a + 1 226 } 227 228 gi_puts("ingested_new=" as *u8) 229 let b: *u8 = sys_mmap(32) 230 sys_write(1, b, nx_strconv_format_i64(n_new, b)) 231 gi_puts(" duplicates=" as *u8) 232 sys_write(1, b, nx_strconv_format_i64(n_dup, b)) 233 gi_puts(" idx_inserted=" as *u8) 234 sys_write(1, b, nx_strconv_format_i64(n_idx, b)) 235 gi_puts(" view_added=" as *u8) 236 sys_write(1, b, nx_strconv_format_i64(n_view, b)) 237 gi_puts(" errors=" as *u8) 238 sys_write(1, b, nx_strconv_format_i64(n_err, b)) 239 gi_puts("\n" as *u8) 240 if n_err > 0 { return 1 } 241 return 0 242}