nx_quad_companion_compose_test.nx source
↩ module page · 237 lines · 8754 B
1// nx_quad_companion_compose_test.nx -- 4-modality concurrent integration.
2//
3// Closes the parallel-companion north star: FOUR actors of DIFFERENT
4// modalities running interleaved through one scheduler + message bus +
5// multiplexer.
6//
7// NARRATOR (LLM, priority 80) -- nx_actor_role_llm
8// NPC (LLM, priority 60) -- nx_actor_role_llm
9// AUDIO_QC (audio grader, priority 50) -- nx_actor_role_audio_grader
10// GAME_LOGIC (tictactoe, priority 70) -- nx_actor_role_game_logic
11//
12// Replaces the image grader from the original quad attempt with the
13// lighter-deps game-logic actor per [[feedback-nxc2-module-const-ceiling]]
14// guidance. Each actor calls a DIFFERENT other-agent / sibling-arc
15// primitive:
16//
17// LLM -> nx_embedding_lookup + nx_blas_matmul + nx_sample_categorical
18// AUDIO -> nx_audio_scene_grade (5-axis Q14 verdict)
19// GAME -> nx_ttt_pick + nx_ttt_try_apply + nx_ttt_evaluate
20//
21// Proves the substrate handles arbitrary heterogeneous modalities
22// through one uniform actor + message + multiplexer pipeline.
23
24import "nx_syscalls.nx"
25import "nx_tier.nx"
26import "nx_tensor.nx"
27import "nx_actor.nx"
28import "nx_message.nx"
29import "nx_stream_multiplexer.nx"
30import "nx_actor_role_llm.nx"
31import "nx_actor_role_audio_grader.nx"
32import "nx_actor_role_game_logic.nx"
33
34func _fill_q10(t: *NxTensor, base: i64, stride: i64) -> i64 {
35 let p: *i64 = t.storage as *i64
36 var i: nx_int = 0
37 while i < t.numel {
38 p[i] = base + (i as i64) * stride
39 i = i + 1
40 }
41 return 0
42}
43
44func main() -> i64 {
45 let now: nx_size = 1000000
46
47 // ===== Stage 1: LLM weights (shared by NARRATOR + NPC) =====
48 let shape_buf: *u8 = sys_mmap(16)
49 let shape: *i64 = shape_buf as *i64
50 shape[0] = 4
51 shape[1] = 4
52 let err_buf: *u8 = sys_mmap(8)
53 let err_p: *i64 = err_buf as *i64
54 let embed_table: *NxTensor = nx_t_alloc(NX_DT_I64, shape, 2, err_p)
55 if err_p[0] != NX_T_OK { return 1 }
56 _fill_q10(embed_table, 100, 5)
57
58 let shape2_buf: *u8 = sys_mmap(16)
59 let shape2: *i64 = shape2_buf as *i64
60 shape2[0] = 4
61 shape2[1] = 4
62 let out_proj: *NxTensor = nx_t_alloc(NX_DT_I64, shape2, 2, err_p)
63 _fill_q10(out_proj, 40, 3)
64
65 // ===== Stage 2: audio inputs =====
66 let bins_buf: *u8 = sys_mmap(64)
67 let bins: *i64 = bins_buf as *i64
68 var b: nx_int = 0
69 while b < 8 {
70 bins[b] = 5000 + ((b * 600) as i64)
71 b = b + 1
72 }
73 let rms_buf: *u8 = sys_mmap(128)
74 let rms: *i64 = rms_buf as *i64
75 var f: nx_int = 0
76 while f < 16 {
77 rms[f] = 2200 + ((f * 180) as i64)
78 f = f + 1
79 }
80
81 // ===== Stage 3: shared substrate =====
82 let sched: *NxActorScheduler = nx_ac_sched_new(8, 1000000, 24, now)
83 let bus: *NxMessageBus = nx_ms_bus_new(8, 32, 32)
84 let mux: *NxStreamMultiplexer = nx_sm_new(64, 50000)
85
86 let NARR: nx_int = 9001
87 let NPC: nx_int = 9002
88 let AUD: nx_int = 9003
89 let GAME: nx_int = 9004
90 let LIS: nx_int = 9099
91
92 nx_ac_spawn(sched, NARR, 1, 80, 0, now)
93 nx_ac_spawn(sched, NPC, 1, 60, 0, now)
94 nx_ac_spawn(sched, AUD, 4, 50, 0, now)
95 nx_ac_spawn(sched, GAME, 5, 70, 0, now)
96 nx_ac_spawn(sched, LIS, 5, 30, 0, now)
97
98 nx_ms_register(bus, NARR)
99 nx_ms_register(bus, NPC)
100 nx_ms_register(bus, AUD)
101 nx_ms_register(bus, GAME)
102 nx_ms_register(bus, LIS)
103 nx_ms_subscribe(bus, LIS, NX_MS_KIND_LLM_TOKEN)
104 nx_ms_subscribe(bus, LIS, NX_MS_KIND_AUDIO_FRAME)
105 nx_ms_subscribe(bus, LIS, NX_MS_KIND_GAME_ACTION)
106
107 // ===== Stage 4: actor contexts =====
108 let narr_ctx: *NxLlmActorCtx = nx_lr_actor_new(embed_table, out_proj, 1, 4, 4, 1024, 2, 7)
109 let npc_ctx: *NxLlmActorCtx = nx_lr_actor_new(embed_table, out_proj, 2, 4, 4, 1024, 2, 13)
110 let aud_ctx: *NxAudioGraderCtx = nx_ag_actor_new(bins, 8, rms, 16, 4)
111 let game_ctx: *NxGameLogicCtx = nx_gl_actor_new(NX_TTT_X, NX_TTT_AI_PERFECT, NX_TTT_AI_PERFECT, 19)
112 if (narr_ctx as i64) == 0 { return 2 }
113 if (npc_ctx as i64) == 0 { return 3 }
114 if (aud_ctx as i64) == 0 { return 4 }
115 if (game_ctx as i64) == 0 { return 5 }
116
117 // ===== Stage 5: pick_next initial = NARRATOR (priority 80) =====
118 let first: *NxActor = nx_ac_pick_next(sched)
119 if first.actor_id != NARR { return 6 }
120
121 // ===== Stage 6: drive each actor to completion =====
122 // NARR + NPC: 5 phases each
123 // AUD: 3 phases
124 // GAME: up to 9 plies (1 step per ply, terminates when game ends)
125 var tick: nx_size = now + 100
126 var i: nx_int = 0
127 while i < 5 {
128 nx_lr_actor_step(narr_ctx, sched, bus, NARR, tick)
129 tick = tick + 50
130 i = i + 1
131 }
132 var j: nx_int = 0
133 while j < 5 {
134 nx_lr_actor_step(npc_ctx, sched, bus, NPC, tick)
135 tick = tick + 50
136 j = j + 1
137 }
138 var p: nx_int = 0
139 while p < 3 {
140 nx_ag_actor_step(aud_ctx, sched, bus, AUD, tick)
141 tick = tick + 50
142 p = p + 1
143 }
144 // Game: step until COMPLETED (PERFECT vs PERFECT -> DRAW in 9 plies)
145 var game_steps: nx_int = 0
146 var game_verdict: nx_int = NX_GL_V_STEPPED
147 while game_verdict == NX_GL_V_STEPPED {
148 game_verdict = nx_gl_actor_step(game_ctx, sched, bus, GAME, tick)
149 tick = tick + 50
150 game_steps = game_steps + 1
151 if game_steps > 12 { return 7 }
152 }
153
154 // ===== Stage 7: all four actors COMPLETED =====
155 let na: *NxActor = nx_ac_find(sched, NARR)
156 let nb: *NxActor = nx_ac_find(sched, NPC)
157 let nc: *NxActor = nx_ac_find(sched, AUD)
158 let nd: *NxActor = nx_ac_find(sched, GAME)
159 if na.state != NX_AC_STATE_COMPLETED { return 8 }
160 if nb.state != NX_AC_STATE_COMPLETED { return 9 }
161 if nc.state != NX_AC_STATE_COMPLETED { return 10 }
162 if nd.state != NX_AC_STATE_COMPLETED { return 11 }
163
164 // ===== Stage 8: game theory invariant -- PERFECT vs PERFECT must DRAW =====
165 if nx_gl_actor_outcome(game_ctx) != NX_TTT_DRAW { return 12 }
166 if nx_gl_actor_plies(game_ctx) != 9 { return 13 }
167
168 // ===== Stage 9: LISTENER mailbox totals =====
169 // 2 LLM_TOKEN (narr + npc) + 1 AUDIO_FRAME + 9 GAME_ACTION = 12
170 if nx_ms_pending(bus, LIS) != 12 { return 14 }
171
172 // ===== Stage 10: verdicts =====
173 let narr_tok: nx_int = nx_lr_actor_token(narr_ctx)
174 let npc_tok: nx_int = nx_lr_actor_token(npc_ctx)
175 if narr_tok < 0 { return 15 }
176 if narr_tok >= 4 { return 16 }
177 if npc_tok < 0 { return 17 }
178 if npc_tok >= 4 { return 18 }
179 if nx_ag_actor_score_q14(aud_ctx) <= 0 { return 19 }
180
181 // ===== Stage 11: drain LISTENER in FIFO order =====
182 // First 2 messages = LLM_TOKEN (narr, npc); then AUDIO_FRAME; then 9 GAME_ACTION
183 let m1: *NxMessage = nx_ms_receive(bus, LIS)
184 if m1.kind != NX_MS_KIND_LLM_TOKEN { return 20 }
185 if m1.sender_actor_id != NARR { return 21 }
186
187 let m2: *NxMessage = nx_ms_receive(bus, LIS)
188 if m2.kind != NX_MS_KIND_LLM_TOKEN { return 22 }
189 if m2.sender_actor_id != NPC { return 23 }
190
191 let m3: *NxMessage = nx_ms_receive(bus, LIS)
192 if m3.kind != NX_MS_KIND_AUDIO_FRAME { return 24 }
193
194 // Remaining 9 are GAME_ACTION
195 var ga_count: nx_int = 0
196 while ga_count < 9 {
197 let mg: *NxMessage = nx_ms_receive(bus, LIS)
198 if mg.kind != NX_MS_KIND_GAME_ACTION { return 30 + ga_count }
199 if mg.sender_actor_id != GAME { return 50 + ga_count }
200 ga_count = ga_count + 1
201 }
202
203 // ===== Stage 12: multiplexer priority ordering =====
204 // Push one of each class; drain. Priorities:
205 // LLM_TEXT = 80
206 // GAME_ACTION = 90 (game actions are highest non-system)
207 // AUDIO_TTS = 70
208 nx_sm_push(mux, NARR, NX_SM_CLASS_LLM_TEXT, 1, 8, narr_tok as nx_size, tick)
209 nx_sm_push(mux, AUD, NX_SM_CLASS_AUDIO_TTS, 2, 8, nx_ag_actor_score_q14(aud_ctx) as nx_size, tick)
210 nx_sm_push(mux, GAME, NX_SM_CLASS_GAME_ACTION, 3, 8, 0xCAFE, tick)
211 if nx_sm_pending_count(mux) != 3 { return 70 }
212
213 let oc_buf: *u8 = sys_mmap(24)
214 let oc: *i64 = oc_buf as *i64
215 let os: *i64 = (oc_buf + 8) as *i64
216 let oid: *i64 = (oc_buf + 16) as *i64
217
218 // First drained = GAME_ACTION (priority 90)
219 nx_sm_next(mux, oc, os, oid, tick + 100)
220 if oc[0] != (NX_SM_CLASS_GAME_ACTION as i64) { return 71 }
221
222 // Second = LLM_TEXT (priority 80)
223 nx_sm_next(mux, oc, os, oid, tick + 200)
224 if oc[0] != (NX_SM_CLASS_LLM_TEXT as i64) { return 72 }
225
226 // Third = AUDIO_TTS (priority 70)
227 nx_sm_next(mux, oc, os, oid, tick + 300)
228 if oc[0] != (NX_SM_CLASS_AUDIO_TTS as i64) { return 73 }
229
230 if nx_sm_pending_count(mux) != 0 { return 74 }
231
232 // ===== Stage 13: scheduler stats =====
233 if nx_ac_actor_count(sched) != 5 { return 75 } // NARR+NPC+AUD+GAME+LIS
234 if nx_ac_total_runtime_us(sched) <= 0 { return 76 }
235
236 return 0
237}