nx_companion_render.nx source
↩ module page · 118 lines · 7103 B
1// nx_companion_render.nx -- ties the visual arc to the LIVE image-gen API. The sovereign generator already
2// exists and works: nx_pe_container_sdserver (a Nishi container PE, no Docker) Job-governs sd-server, which
3// serves an OpenAI-compatible endpoint POST /v1/images/generations on :7861 and returns a real PNG. What was
4// missing (the "clothes stayed the same, arcs never shifted" defect) is HERE: this composes the generation
5// request from the persona's ADVANCING visual arc, so each render is the same person in a NEW outfit/scene/pose.
6// render_request advances the arc and builds the JSON body; render_http wraps it in a POST the sovereign HTTP
7// client sends to the hubbed generator. The prompt is the load-bearing field (identity anchor + varying state,
8// from nx_visual_arc) -- projected verbatim, no censor (rule 25). license_tier: ORIGINAL
9import "nx_visual_arc.nx"
10
11// JSON-escape s[0..slen) into d at o (escape '"' and '\\'); returns new offset
12func rr_json_esc(d: *u8, o: i64, s: *u8, slen: i64) -> i64 {
13 var i: i64 = 0
14 var w: i64 = o
15 while i < slen {
16 let c: u8 = s[i]
17 if c == (0x22 as u8) { d[w]=0x5C as u8; w=w+1; d[w]=0x22 as u8; w=w+1 }
18 else { if c == (0x5C as u8) { d[w]=0x5C as u8; w=w+1; d[w]=0x5C as u8; w=w+1 }
19 else { d[w]=c; w=w+1 } }
20 i = i + 1
21 }
22 return w
23}
24func rr_catn(d: *u8, o: i64, v: i64) -> i64 {
25 let t: *u8=sys_mmap(24); var m: i64=v; var k: i64=0
26 if m < 0 { d[o] = 45 as u8; o = o + 1; m = 0 - m }
27 if m<0 { m=0 }
28 if m==0 { t[0]=48 as u8; k=1 }
29 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
30 var w: i64=o; var i: i64=0; while i<k { d[w]=t[k-1-i]; w=w+1; i=i+1 }
31 return w
32}
33
34// build the OpenAI-compat generation body for a GIVEN step (deterministic; no state change). Returns length.
35// Turbo Z-Image defaults: 8 steps, cfg 1.0, euler (per the verified sd-cli flags). 512x512.
36func render_body_at_pfx(prefix: *u8, persona: *u8, step: i64, out: *u8, cap: i64) -> i64 {
37 let pbuf: *u8 = sys_mmap(16384)
38 let pl: i64 = varc_prompt_at_pfx(prefix, persona, step, pbuf, 16384)
39 if pl <= 0 { out[0]=0 as u8; return 0 }
40 var o: i64 = 0
41 o = tr_cat(out, o, "{\"prompt\":\"" as *u8)
42 o = rr_json_esc(out, o, pbuf, pl)
43 o = tr_cat(out, o, "\",\"n\":1,\"size\":\"512x512\",\"sample_steps\":8,\"cfg_scale\":1.0,\"sampling_method\":\"euler\"}" as *u8)
44 out[o] = 0 as u8
45 return o
46}
47
48// ADVANCE the persona's arc, then build the body at the new step. This is the per-render entry: every call
49// yields the same person in a NEW outfit/scene (the fix). Returns body length; *step_out = the new step.
50func render_request_pfx(prefix: *u8, persona: *u8, out: *u8, cap: i64, step_out: *i64) -> i64 {
51 let s: i64 = varc_advance_pfx(prefix, persona)
52 if (step_out as i64) != 0 { step_out[0] = s }
53 return render_body_at_pfx(prefix, persona, s, out, cap)
54}
55
56// wrap a body in a POST /v1/images/generations HTTP request for the sovereign HTTP client to send to the
57// hubbed generator (:7861). host = the generator host header. Returns request length.
58func render_http_at_pfx(prefix: *u8, persona: *u8, step: i64, host: *u8, out: *u8, cap: i64) -> i64 {
59 let body: *u8 = sys_mmap(16384)
60 let bl: i64 = render_body_at_pfx(prefix, persona, step, body, 16384)
61 if bl <= 0 { out[0]=0 as u8; return 0 }
62 var o: i64 = 0
63 o = tr_cat(out, o, "POST /v1/images/generations HTTP/1.1\r\nHost: " as *u8)
64 o = tr_cat(out, o, host)
65 o = tr_cat(out, o, "\r\nContent-Type: application/json\r\nContent-Length: " as *u8)
66 o = rr_catn(out, o, bl)
67 o = tr_cat(out, o, "\r\nConnection: close\r\n\r\n" as *u8)
68 var i: i64 = 0; while i < bl { out[o]=body[i]; o=o+1; i=i+1 }
69 out[o] = 0 as u8
70 return o
71}
72
73// --- integration with the EXISTING sovereign gen daemon (nx_gen_orchestrator, hubbed on the NAS) ---
74// The orchestrator exposes POST /gen/api/generate (opaque-gated behind nx_gen_gateway) with its OWN shape,
75// internally drives the GPU worker (/sdapi/v1/txt2img), then decodes/ingests/content-addresses each PNG and
76// serves it at /gen/img/<cid> through the gallery. Feeding the persona's advancing, grounded prompt to THIS
77// endpoint is the real integration -- the companion's images flow through the proven pipeline into her gallery,
78// same person, new scene every render. This targets the daemon's shape, not the raw worker's.
79func render_gen_body_at_pfx(prefix: *u8, persona: *u8, step: i64, out: *u8, cap: i64) -> i64 {
80 let pbuf: *u8 = sys_mmap(16384)
81 let pl: i64 = varc_prompt_at_pfx(prefix, persona, step, pbuf, 16384)
82 if pl <= 0 { out[0]=0 as u8; return 0 }
83 var o: i64 = 0
84 o = tr_cat(out, o, "{\"prompt\":\"" as *u8)
85 o = rr_json_esc(out, o, pbuf, pl)
86 o = tr_cat(out, o, "\",\"negative\":\"blurry, lowres, deformed, extra limbs\",\"width\":512,\"height\":512,\"steps\":8,\"cfg\":\"1.0\",\"seed\":-1,\"count\":1,\"sampler\":\"euler\"}" as *u8)
87 out[o] = 0 as u8
88 return o
89}
90// ADVANCE the arc, then build the orchestrator body at the new step (the per-render entry for the NAS pipeline)
91func render_gen_request_pfx(prefix: *u8, persona: *u8, out: *u8, cap: i64, step_out: *i64) -> i64 {
92 let s: i64 = varc_advance_pfx(prefix, persona)
93 if (step_out as i64) != 0 { step_out[0] = s }
94 return render_gen_body_at_pfx(prefix, persona, s, out, cap)
95}
96// wrap the orchestrator body in a POST /gen/api/generate request for the sovereign HTTP client
97func render_gen_http_at_pfx(prefix: *u8, persona: *u8, step: i64, host: *u8, out: *u8, cap: i64) -> i64 {
98 let body: *u8 = sys_mmap(16384)
99 let bl: i64 = render_gen_body_at_pfx(prefix, persona, step, body, 16384)
100 if bl <= 0 { out[0]=0 as u8; return 0 }
101 var o: i64 = 0
102 o = tr_cat(out, o, "POST /gen/api/generate HTTP/1.1\r\nHost: " as *u8)
103 o = tr_cat(out, o, host)
104 o = tr_cat(out, o, "\r\nContent-Type: application/json\r\nContent-Length: " as *u8)
105 o = rr_catn(out, o, bl)
106 o = tr_cat(out, o, "\r\nConnection: close\r\n\r\n" as *u8)
107 var i: i64 = 0; while i < bl { out[o]=body[i]; o=o+1; i=i+1 }
108 out[o] = 0 as u8
109 return o
110}
111func render_gen_body_at(persona: *u8, step: i64, out: *u8, cap: i64) -> i64 { return render_gen_body_at_pfx(VID_PREFIX, persona, step, out, cap) }
112func render_gen_request(persona: *u8, out: *u8, cap: i64, step_out: *i64) -> i64 { return render_gen_request_pfx(VID_PREFIX, persona, out, cap, step_out) }
113func render_gen_http_at(persona: *u8, step: i64, host: *u8, out: *u8, cap: i64) -> i64 { return render_gen_http_at_pfx(VID_PREFIX, persona, step, host, out, cap) }
114
115// production-prefix wrappers (the persona's real visual store)
116func render_body_at(persona: *u8, step: i64, out: *u8, cap: i64) -> i64 { return render_body_at_pfx(VID_PREFIX, persona, step, out, cap) }
117func render_request(persona: *u8, out: *u8, cap: i64, step_out: *i64) -> i64 { return render_request_pfx(VID_PREFIX, persona, out, cap, step_out) }
118func render_http_at(persona: *u8, step: i64, host: *u8, out: *u8, cap: i64) -> i64 { return render_http_at_pfx(VID_PREFIX, persona, step, host, out, cap) }