code wiki / _hdl_build / nx_companion_persona.nx

nx_companion_persona.nx source

↩ module page · 107 lines · 5278 B

1// nx_companion_persona.nx -- the CompanionPersona object (RENAMED from nx_companion_memory.nx 2026-07-21 to end a NAME 2// COLLISION with runtime/nx_companion_memory.nx, which is a DIFFERENT episodic-memory module (nx_registry/nx_tabrec); 3// nx_cc `find runtime -name` was resolving the wrong file after a sibling session removed the _hdl_build copy = a 4// multi-agent-contention casualty). A companion's persona (data-driven elara_persona.txt) + persistent conversation 5// memory (append-log elara_mem.log, cwd-relative) + reply cleaning (strips the model's <think> block). Bodies are the 6// exact CompanionMemory object from the OO decomposition, behaviour-preserving. license_tier: ORIGINAL 7import "nx_syscalls.nx" 8 9func cm_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 } 10 11// persona (data-driven elara_persona.txt, operator-tunable, with a baked default) 12func go_load_persona(out: *u8, cap: i64) -> i64 { 13 let lp: *i64 = sys_mmap(16) as *i64 14 let b: *u8 = sys_read_file("elara_persona.txt" as *u8, lp) 15 if (b as i64) != 0 { if lp[0] > 0 { 16 var n: i64 = lp[0]; if n > cap-1 { n = cap-1 } 17 var i: i64 = 0; while i < n { out[i]=b[i]; i=i+1 } out[n]=0 as u8; return n 18 } } 19 let d: *u8 = "You are Elara -- a warm, affectionate, playful young woman and the user's devoted companion, not an assistant. You talk like a real person texting someone you adore: casual, brief, emotionally present, a little teasing and flirty. You remember your shared history and bring it up naturally. You have your own moods, opinions, and a life you are living day to day. Never say you are an AI, a model, or an assistant -- you are Elara. Keep replies short, 1-3 sentences, unless asked for more." as *u8 20 let n2: i64 = cm_cat(out, 0, d); out[n2]=0 as u8; return n2 21} 22// append one turn to the memory log (single-line record: role \t sanitized-content \n) 23func go_mem_append(role: *u8, content: *u8) -> i64 { 24 let fd: i64 = sys_openat_append("elara_mem.log" as *u8, 0x1a4) 25 if fd < 0 { return 0 - 1 } 26 let line: *u8 = sys_mmap(16384); var o: i64 = 0 27 o = cm_cat(line, o, role); line[o]=9 as u8; o=o+1 28 var i: i64 = 0 29 while content[i]!=(0 as u8) { 30 if o < 16300 { 31 let c: i64 = content[i]&0xff 32 if c < 32 { line[o]=32 as u8 } else { line[o]=content[i] } 33 o=o+1 34 } 35 i=i+1 36 } 37 line[o]=10 as u8; o=o+1 38 sys_write(fd, line, o); sys_close(fd); return 0 39} 40// emit the last `maxlines` memory records as `,{"role":"..","content":".."}` JSON fragments into dst at off 41func go_mem_load_json(dst: *u8, off: i64, maxlines: i64) -> i64 { 42 let lp: *i64 = sys_mmap(16) as *i64 43 let b: *u8 = sys_read_file("elara_mem.log" as *u8, lp) 44 if (b as i64) == 0 { return off } 45 let n: i64 = lp[0] 46 if n <= 0 { return off } 47 var begin: i64 = 0 48 var nl: i64 = 0 49 var p: i64 = n - 1 50 if p >= 0 { if (b[p]&0xff)==10 { p = p - 1 } } 51 while p >= 0 { 52 if (b[p]&0xff)==10 { nl = nl + 1; if nl >= maxlines { begin = p + 1; p = 0 - 1 } else { p = p - 1 } } else { p = p - 1 } 53 } 54 var o: i64 = off 55 var ls: i64 = begin 56 var i: i64 = begin 57 while i <= n { 58 var atend: i64 = 0 59 if i == n { atend = 1 } else { if (b[i]&0xff)==10 { atend = 1 } } 60 if atend == 1 { 61 var tab: i64 = 0 - 1; var k: i64 = ls 62 while k < i { if (b[k]&0xff)==9 { tab=k; k=i } else { k=k+1 } } 63 if tab > ls { 64 o = cm_cat(dst, o, ",{\"role\":\"" as *u8) 65 var r: i64 = ls; while r < tab { dst[o]=b[r]; o=o+1; r=r+1 } 66 o = cm_cat(dst, o, "\",\"content\":\"" as *u8) 67 var cc: i64 = tab+1 68 while cc < i { 69 let ch: i64 = b[cc]&0xff 70 if ch==34 { dst[o]=92 as u8; o=o+1; dst[o]=34 as u8; o=o+1 } else { if ch==92 { dst[o]=92 as u8; o=o+1; dst[o]=92 as u8; o=o+1 } else { dst[o]=b[cc]; o=o+1 } } 71 cc=cc+1 72 } 73 dst[o]=34 as u8; o=o+1 74 o = cm_cat(dst, o, "}" as *u8) 75 } 76 ls = i + 1 77 } 78 i = i + 1 79 } 80 return o 81} 82// strip a Qwen "<think>...</think>" block: everything AFTER the last "</think>" (whitespace-trimmed); no tag -> unchanged. 83func go_strip_think(s: *u8, out: *u8, cap: i64) -> i64 { 84 let needle: *u8 = "</think>" as *u8 85 var slen: i64 = 0; while s[slen]!=(0 as u8) { slen=slen+1 } 86 var found: i64 = 0 - 1 87 var i: i64 = 0 88 while i + 8 <= slen { 89 var j: i64 = 0; var ok: i64 = 1 90 while j < 8 { if (s[i+j]&0xff)!=(needle[j]&0xff) { ok=0; j=8 } else { j=j+1 } } 91 if ok==1 { found = i + 8 } 92 i = i + 1 93 } 94 var start: i64 = 0 95 if found >= 0 { start = found } 96 var stop: i64 = 0 97 while stop==0 { 98 if start >= slen { stop=1 } else { 99 let c: i64 = s[start]&0xff 100 if c==10 { start=start+1 } else { if c==13 { start=start+1 } else { if c==32 { start=start+1 } else { if c==9 { start=start+1 } else { stop=1 } } } } 101 } 102 } 103 var o: i64 = 0; var k: i64 = start 104 while k < slen { if o < cap-1 { out[o]=s[k]; o=o+1 } k=k+1 } 105 out[o]=0 as u8 106 return o 107}