code wiki / (root) / nx_emit.nx

nx_emit.nx source

↩ module page · 138 lines · 5408 B

1// nx_emit.nx -- WRITING arc, rung W-ING-4: turn extracted scene DATA into the two 2// strings the downstream consumers actually want: 3// em_image_prompt -> a bare comma-tag prompt for IMAGE-GEN ("mira, garden, dusk") 4// em_companion_ctx -> a labeled context line for the COMPANION LLM 5// ("present: mira | desc: garden, dusk | dialogue: 1") 6// 7// Both are deterministic assemblies over (a) the roster names present in the scene 8// and (b) the descriptor-lexicon terms present in the scene -- reusing the 9// WHOLE-WORD counter from nx_ingest (DRY). CONTENT-AGNOSTIC: the operator's lane 10// supplies the real roster + lexicon (incl. outfit/MPL + register terms); the 11// engine emits the prompt/context identically for any register. It never invents 12// a tag -- only what is present in the text appears (proven by the empty-scene 13// neg-control in the gate). 14// 15// Pure integer, NO syscalls. Caller owns dst (>= a few hundred bytes is ample). 16// 17// license_tier: ORIGINAL 18// module: nishi-core.write.emit 19// depends: nishi-core.write.ingest 20// capability: WRITE_PROMPT_EMIT 21import "nx_ingest.nx" 22import "nx_ingest_scene.nx" // ig_* scene/entity extractor 23 24// append null-terminated src to dst at dlen; return new length (dst kept null-term) 25func em_append(dst: *u8, dlen: i64, src: *u8) -> i64 { 26 var d: i64 = dlen 27 var s: i64 = 0 28 while src[s] != (0 as u8) { dst[d] = src[s]; d = d + 1; s = s + 1 } 29 dst[d] = 0 as u8 30 return d 31} 32 33// append the null-terminated term at terms[off..]; return new length 34func em_append_at(dst: *u8, dlen: i64, terms: *u8, off: i64) -> i64 { 35 var d: i64 = dlen 36 var s: i64 = off 37 while terms[s] != (0 as u8) { dst[d] = terms[s]; d = d + 1; s = s + 1 } 38 dst[d] = 0 as u8 39 return d 40} 41 42// append a non-negative integer in decimal; return new length 43func em_append_num(dst: *u8, dlen: i64, v: i64) -> i64 { 44 var d: i64 = dlen 45 if v == 0 { dst[d] = 48 as u8; d = d + 1; dst[d] = 0 as u8; return d } 46 var m: i64 = v 47 if m < 0 { dst[d] = 45 as u8; d = d + 1; m = 0 - m } 48 var tmp: i64 = m 49 var ndig: i64 = 0 50 while tmp > 0 { ndig = ndig + 1; tmp = tmp / 10 } 51 let p: i64 = d + ndig 52 dst[p] = 0 as u8 53 var q: i64 = p 54 while m > 0 { q = q - 1; dst[q] = (48 + (m % 10)) as u8; m = m / 10 } 55 return p 56} 57 58// append each lexicon term that is present (whole-word) in t[a..b), comma-joined. 59// lead=1 prefixes ", " before the first emitted item (to chain onto prior content). 60func em_append_present(dst: *u8, dlen: i64, t: *u8, a: i64, b: i64, terms: *u8, nterms: i64, lead: i64) -> i64 { 61 var d: i64 = dlen 62 var first: i64 = 1 - lead 63 var k: i64 = 0 64 while k < nterms { 65 let off: i64 = ig_name_off(terms, k) 66 if ig_count_name_in(t, a, b, terms, off) > 0 { 67 if first == 0 { d = em_append(dst, d, ", \x00" as *u8) } 68 d = em_append_at(dst, d, terms, off) 69 first = 0 70 } 71 k = k + 1 72 } 73 return d 74} 75 76// IMAGE-GEN prompt: present roster names, then present descriptor terms, comma-joined 77func em_image_prompt(t: *u8, a: i64, b: i64, roster: *u8, nnames: i64, dterms: *u8, ndterms: i64, dst: *u8) -> i64 { 78 dst[0] = 0 as u8 79 let dn: i64 = em_append_present(dst, 0, t, a, b, roster, nnames, 0) 80 var lead2: i64 = 0 81 if dn > 0 { lead2 = 1 } 82 let d: i64 = em_append_present(dst, dn, t, a, b, dterms, ndterms, lead2) 83 return d 84} 85 86// IMAGE-GEN prompt, FOCUS-FIRST: the scene's subject character (focus, by mention 87// count -- from ig_extract) leads the prompt because image models weight early 88// tokens most; then the other present characters, then the descriptor tags. 89func em_image_prompt_focus(t: *u8, a: i64, b: i64, roster: *u8, nnames: i64, focus: i64, dterms: *u8, ndterms: i64, dst: *u8) -> i64 { 90 dst[0] = 0 as u8 91 var d: i64 = 0 92 if focus >= 0 { 93 if focus < nnames { 94 d = em_append_at(dst, d, roster, ig_name_off(roster, focus)) 95 } 96 } 97 var k: i64 = 0 98 while k < nnames { 99 if k != focus { 100 let off: i64 = ig_name_off(roster, k) 101 if ig_count_name_in(t, a, b, roster, off) > 0 { 102 if d > 0 { d = em_append(dst, d, ", \x00" as *u8) } 103 d = em_append_at(dst, d, roster, off) 104 } 105 } 106 k = k + 1 107 } 108 var lead: i64 = 0 109 if d > 0 { lead = 1 } 110 d = em_append_present(dst, d, t, a, b, dterms, ndterms, lead) 111 return d 112} 113 114// COMPANION context: "present: <names> | desc: <terms> | dialogue: <n>" 115func em_companion_ctx(t: *u8, a: i64, b: i64, roster: *u8, nnames: i64, dterms: *u8, ndterms: i64, dst: *u8) -> i64 { 116 dst[0] = 0 as u8 117 var d: i64 = em_append(dst, 0, "present: \x00" as *u8) 118 let after_names: i64 = d 119 d = em_append_present(dst, d, t, a, b, roster, nnames, 0) 120 if d == after_names { d = em_append(dst, d, "(none)\x00" as *u8) } 121 d = em_append(dst, d, " | desc: \x00" as *u8) 122 let after_desc: i64 = d 123 d = em_append_present(dst, d, t, a, b, dterms, ndterms, 0) 124 if d == after_desc { d = em_append(dst, d, "(none)\x00" as *u8) } 125 d = em_append(dst, d, " | dialogue: \x00" as *u8) 126 var dq: i64 = 0 127 var ins: i64 = 0 128 var p: i64 = a 129 while p < b { 130 if t[p] == (34 as u8) { 131 if ins == 0 { dq = dq + 1 } 132 ins = 1 - ins 133 } 134 p = p + 1 135 } 136 d = em_append_num(dst, d, dq) 137 return d 138}