nx_companion_render.nx source
↩ module page · 117 lines · 7048 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 { m=0 }
27 if m==0 { t[0]=48 as u8; k=1 }
28 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
29 var w: i64=o; var i: i64=0; while i<k { d[w]=t[k-1-i]; w=w+1; i=i+1 }
30 return w
31}
32
33// build the OpenAI-compat generation body for a GIVEN step (deterministic; no state change). Returns length.
34// Turbo Z-Image defaults: 8 steps, cfg 1.0, euler (per the verified sd-cli flags). 512x512.
35func render_body_at_pfx(prefix: *u8, persona: *u8, step: i64, out: *u8, cap: i64) -> i64 {
36 let pbuf: *u8 = sys_mmap(16384)
37 let pl: i64 = varc_prompt_at_pfx(prefix, persona, step, pbuf, 16384)
38 if pl <= 0 { out[0]=0 as u8; return 0 }
39 var o: i64 = 0
40 o = tr_cat(out, o, "{\"prompt\":\"" as *u8)
41 o = rr_json_esc(out, o, pbuf, pl)
42 o = tr_cat(out, o, "\",\"n\":1,\"size\":\"512x512\",\"sample_steps\":8,\"cfg_scale\":1.0,\"sampling_method\":\"euler\"}" as *u8)
43 out[o] = 0 as u8
44 return o
45}
46
47// ADVANCE the persona's arc, then build the body at the new step. This is the per-render entry: every call
48// yields the same person in a NEW outfit/scene (the fix). Returns body length; *step_out = the new step.
49func render_request_pfx(prefix: *u8, persona: *u8, out: *u8, cap: i64, step_out: *i64) -> i64 {
50 let s: i64 = varc_advance_pfx(prefix, persona)
51 if (step_out as i64) != 0 { step_out[0] = s }
52 return render_body_at_pfx(prefix, persona, s, out, cap)
53}
54
55// wrap a body in a POST /v1/images/generations HTTP request for the sovereign HTTP client to send to the
56// hubbed generator (:7861). host = the generator host header. Returns request length.
57func render_http_at_pfx(prefix: *u8, persona: *u8, step: i64, host: *u8, out: *u8, cap: i64) -> i64 {
58 let body: *u8 = sys_mmap(16384)
59 let bl: i64 = render_body_at_pfx(prefix, persona, step, body, 16384)
60 if bl <= 0 { out[0]=0 as u8; return 0 }
61 var o: i64 = 0
62 o = tr_cat(out, o, "POST /v1/images/generations HTTP/1.1\r\nHost: " as *u8)
63 o = tr_cat(out, o, host)
64 o = tr_cat(out, o, "\r\nContent-Type: application/json\r\nContent-Length: " as *u8)
65 o = rr_catn(out, o, bl)
66 o = tr_cat(out, o, "\r\nConnection: close\r\n\r\n" as *u8)
67 var i: i64 = 0; while i < bl { out[o]=body[i]; o=o+1; i=i+1 }
68 out[o] = 0 as u8
69 return o
70}
71
72// --- integration with the EXISTING sovereign gen daemon (nx_gen_orchestrator, hubbed on the NAS) ---
73// The orchestrator exposes POST /gen/api/generate (opaque-gated behind nx_gen_gateway) with its OWN shape,
74// internally drives the GPU worker (/sdapi/v1/txt2img), then decodes/ingests/content-addresses each PNG and
75// serves it at /gen/img/<cid> through the gallery. Feeding the persona's advancing, grounded prompt to THIS
76// endpoint is the real integration -- the companion's images flow through the proven pipeline into her gallery,
77// same person, new scene every render. This targets the daemon's shape, not the raw worker's.
78func render_gen_body_at_pfx(prefix: *u8, persona: *u8, step: i64, out: *u8, cap: i64) -> i64 {
79 let pbuf: *u8 = sys_mmap(16384)
80 let pl: i64 = varc_prompt_at_pfx(prefix, persona, step, pbuf, 16384)
81 if pl <= 0 { out[0]=0 as u8; return 0 }
82 var o: i64 = 0
83 o = tr_cat(out, o, "{\"prompt\":\"" as *u8)
84 o = rr_json_esc(out, o, pbuf, pl)
85 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)
86 out[o] = 0 as u8
87 return o
88}
89// ADVANCE the arc, then build the orchestrator body at the new step (the per-render entry for the NAS pipeline)
90func render_gen_request_pfx(prefix: *u8, persona: *u8, out: *u8, cap: i64, step_out: *i64) -> i64 {
91 let s: i64 = varc_advance_pfx(prefix, persona)
92 if (step_out as i64) != 0 { step_out[0] = s }
93 return render_gen_body_at_pfx(prefix, persona, s, out, cap)
94}
95// wrap the orchestrator body in a POST /gen/api/generate request for the sovereign HTTP client
96func render_gen_http_at_pfx(prefix: *u8, persona: *u8, step: i64, host: *u8, out: *u8, cap: i64) -> i64 {
97 let body: *u8 = sys_mmap(16384)
98 let bl: i64 = render_gen_body_at_pfx(prefix, persona, step, body, 16384)
99 if bl <= 0 { out[0]=0 as u8; return 0 }
100 var o: i64 = 0
101 o = tr_cat(out, o, "POST /gen/api/generate HTTP/1.1\r\nHost: " as *u8)
102 o = tr_cat(out, o, host)
103 o = tr_cat(out, o, "\r\nContent-Type: application/json\r\nContent-Length: " as *u8)
104 o = rr_catn(out, o, bl)
105 o = tr_cat(out, o, "\r\nConnection: close\r\n\r\n" as *u8)
106 var i: i64 = 0; while i < bl { out[o]=body[i]; o=o+1; i=i+1 }
107 out[o] = 0 as u8
108 return o
109}
110func 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) }
111func 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) }
112func 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) }
113
114// production-prefix wrappers (the persona's real visual store)
115func render_body_at(persona: *u8, step: i64, out: *u8, cap: i64) -> i64 { return render_body_at_pfx(VID_PREFIX, persona, step, out, cap) }
116func render_request(persona: *u8, out: *u8, cap: i64, step_out: *i64) -> i64 { return render_request_pfx(VID_PREFIX, persona, out, cap, step_out) }
117func 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) }