code wiki / (root) / nx_actor_role_llm_v2_real_autoreg_test.nx

nx_actor_role_llm_v2_real_autoreg_test.nx source

↩ module page · 120 lines · 5107 B

1// nx_actor_role_llm_v2_real_autoreg_test.nx -- multi-token REAL transformer 2// forward through the v2 LLM actor adapter. 3// 4// Composes nx_gguf_fixture_tiny (fixture builder, sibling primitive) 5// with nx_actor_role_llm_v2 (actor adapter) to drive THREE 6// autoregressive iterations through the FULL Llama-class runner. 7// Each iteration: 8// 1. INIT -> RUN -> EMIT through the actor 9// 2. RUN actually invokes nx_llm_generate_one_v2 (embedding + 10// RMSNorm + multi-head attention + SwiGLU FFN + output projection 11// + temperature + top_k + sample) 12// 3. EMIT pushes LLM_TOKEN through the bus to the listener 13// 4. reset_for_next_token rolls the adapter back to INIT, swaps the 14// prompt to the previous generated token's byte 15// 16// Verifies: 17// * 3 LLM_TOKEN messages reach the listener 18// * All 3 tokens are in [0, vocab) and match the runner's return 19// * Each iteration succeeds with NX_LR2_OK (no boundary-guard slip) 20// * Scheduler state cycles READY -> RUNNING -> COMPLETED -> READY 21// -> ... cleanly across iterations 22// 23// This closes the gap documented in nx_actor_role_llm_v2_autoreg_test.nx 24// (where the GGUF fixture was inline and hit the nxc2 preprocessor 25// edge). The fixture extraction puts the heavy primitive imports in 26// their own file, so this smoke's preprocessor scope stays clean. 27 28import "nx_syscalls.nx" 29import "nx_tier.nx" 30import "nx_gguf_fixture_tiny.nx" 31import "nx_actor.nx" 32import "nx_message.nx" 33import "nx_actor_role_llm_v2.nx" 34 35func main() -> i64 { 36 let now: nx_size = 1000000 37 let N_ITERS: nx_int = 3 38 39 // ===== Stage 1: one-call fixture ===== 40 let fix: *NxGgufFixtureBundle = nx_gft_build_tiny_llama(0xdeadbeef) 41 if nx_gft_is_built(fix) != 1 { return 1 } 42 43 // ===== Stage 2: initial prompt + adapter ===== 44 let prompt: *u8 = sys_mmap(1) 45 prompt[0] = 0x61 // 'a' 46 let ctx: *NxLlmV2ActorCtx = nx_lv_actor_new( 47 fix.spec, fix.gguf_buf, fix.hdr, fix.bpe, 48 prompt, 1, 49 1024, 4, fix.prng_state, 10000, 724) 50 if (ctx as i64) == 0 { return 2 } 51 52 // ===== Stage 3: scheduler + bus ===== 53 let sched: *NxActorScheduler = nx_ac_sched_new(4, 1000000, 16, now) 54 nx_ac_spawn(sched, 7001, 1, 80, 0, now) 55 let bus: *NxMessageBus = nx_ms_bus_new(4, 8, 8) 56 nx_ms_register(bus, 7001) 57 nx_ms_register(bus, 7002) 58 nx_ms_subscribe(bus, 7002, NX_MS_KIND_LLM_TOKEN) 59 60 // ===== Stage 4: drive 3 autoregressive iterations ===== 61 let history: *u8 = sys_mmap(8) 62 let hist: *i64 = history as *i64 63 var iter: nx_int = 0 64 var tick: nx_size = now + 100 65 while iter < N_ITERS { 66 // INIT -> RUN -> EMIT -> DONE 67 if nx_lv_actor_step(ctx, sched, bus, 7001, tick) != NX_LV_V_STEPPED { return 10 + iter } 68 tick = tick + 100 69 let v_run: nx_int = nx_lv_actor_step(ctx, sched, bus, 7001, tick) 70 if ctx.runner_result < 0 { return 20 + iter } 71 if ctx.runner_result >= fix.vocab_size { return 30 + iter } 72 if ctx.runner_verdict != NX_LR2_OK { return 40 + iter } 73 if v_run != NX_LV_V_STEPPED { return 50 + iter } 74 tick = tick + 100 75 let v_emit: nx_int = nx_lv_actor_step(ctx, sched, bus, 7001, tick) 76 if v_emit != NX_LV_V_COMPLETED { return 60 + iter } 77 if ctx.current_phase != NX_LV_PHASE_DONE { return 70 + iter } 78 let tok: nx_int = ctx.runner_result 79 hist[iter] = tok as i64 80 tick = tick + 100 81 82 // Reset for next iteration (unless this was the last) 83 if iter < (N_ITERS - 1) { 84 let new_byte: nx_int = nx_gft_vocab_byte(fix, tok) 85 if new_byte < 0 { return 80 + iter } 86 prompt[0] = new_byte as u8 87 let v_reset: nx_int = nx_lv_actor_reset_for_next_token(ctx, sched, 7001, prompt, 1) 88 if v_reset != NX_LV_V_STEPPED { return 90 + iter } 89 if ctx.current_phase != NX_LV_PHASE_INIT { return 100 + iter } 90 let a_after: *NxActor = nx_ac_find(sched, 7001) 91 if a_after.state != NX_AC_STATE_READY { return 110 + iter } 92 } 93 iter = iter + 1 94 } 95 96 // ===== Stage 5: final state ===== 97 let a_final: *NxActor = nx_ac_find(sched, 7001) 98 if a_final.state != NX_AC_STATE_COMPLETED { return 120 } 99 if ctx.current_phase != NX_LV_PHASE_DONE { return 121 } 100 if nx_lv_actor_is_done(ctx) != 1 { return 122 } 101 102 // Listener received N_ITERS LLM_TOKEN messages 103 if nx_ms_pending(bus, 7002) != N_ITERS { return 123 } 104 105 // ===== Stage 6: drain listener + verify each message ===== 106 var drained: nx_int = 0 107 while drained < N_ITERS { 108 let m: *NxMessage = nx_ms_receive(bus, 7002) 109 if (m as i64) == 0 { return 130 + drained } 110 if m.kind != NX_MS_KIND_LLM_TOKEN { return 140 + drained } 111 if m.sender_actor_id != 7001 { return 150 + drained } 112 let recv_tok: nx_int = m.payload_handle as nx_int 113 if recv_tok != (hist[drained] as nx_int) { return 160 + drained } 114 if recv_tok < 0 { return 170 + drained } 115 if recv_tok >= fix.vocab_size { return 180 + drained } 116 drained = drained + 1 117 } 118 119 return 0 120}