nx_gen_zoo.nx source
↩ module page · 195 lines · 7056 B
1// nx_gen_zoo.nx -- walk a model directory, identify every file, and report which PIPELINES are
2// runnable. ComfyUI's capability without ComfyUI's interface.
3//
4// You should not have to know what you downloaded. Point this at a models tree and it answers the
5// only questions that matter: what is each file, and can I generate an image with what I have?
6//
7// It calls nx_genrole for every classification -- genuinely, not as a claim. This header used
8// to assert that reuse while zo_classify re-derived every probe, and the two copies had already
9// drifted apart. See nx_genrole.nx.
10//
11// Usage: nx_gen_zoo <dir> [dir2] [dir3] ...
12//
13// A pipeline is reported RUNNABLE only when every required role is present AND runnable for the
14// same family. Missing pieces are named, so the answer is actionable rather than a verdict.
15// license_tier: ORIGINAL
16
17import "nx_syscalls.nx"
18import "nx_le.nx"
19import "nx_f32.nx"
20import "nx_f32_div.nx"
21import "nx_f32_cvt.nx"
22import "nx_f16.nx"
23import "nx_strconv.nx"
24import "nx_genver.nx"
25import "nx_genweights.nx"
26import "nx_genarch.nx"
27import "nx_genrole.nx"
28const ZOO_MAGIC_65536: i64 = 65536
29const ZOO_MAGIC_1024: i64 = 1024
30
31const ZOO_MAX: i64 = 256
32
33func zo_puts(s: *u8) -> i64 {
34 var n: i64 = 0
35 while s[n] != (0 as u8) { n = n + 1 }
36 return sys_write(1, s, n)
37}
38func zo_len(s: *u8) -> i64 {
39 var n: i64 = 0
40 while s[n] != (0 as u8) { n = n + 1 }
41 return n
42}
43func zo_pad(s: *u8, w: i64) -> i64 {
44 let n: i64 = zo_len(s)
45 sys_write(1, s, n)
46 var i: i64 = n
47 while i < w { sys_write(1, " " as *u8, 1); i = i + 1 }
48 return 0
49}
50func zo_has(gw: *i64, name: *u8) -> i64 {
51 if nx_gw_find(gw, name, zo_len(name)) < 0 { return 0 }
52 return 1
53}
54func zo_join(out: *u8, dir: *u8, name: *u8) -> *u8 {
55 var o: i64 = 0
56 var i: i64 = 0
57 while dir[i] != (0 as u8) { out[o] = dir[i]; o = o + 1; i = i + 1 }
58 if o > 0 { if out[o - 1] != (0x2F as u8) { out[o] = 0x2F; o = o + 1 } }
59 i = 0
60 while name[i] != (0 as u8) { out[o] = name[i]; o = o + 1; i = i + 1 }
61 out[o] = 0
62 return out
63}
64
65static g_have_dit: i64
66static g_have_vae: i64
67static g_have_te: i64
68static g_n_adapter: i64
69static g_n_unknown: i64
70
71// Classification now lives in nx_genrole -- ONE copy, three consumers. It used to live here too,
72// and the two copies had ALREADY drifted: this one printed "adapter-lora(peft)" with no family
73// while nx_gen_modelid resolved the target family from the same bytes.
74// ★★★★★ A COMMENT ASSERTING DRY IS NOT DRY -- this function's header claimed it reused modelid's
75// probes while re-deriving every one of them.
76func zo_classify(path: *u8, label: *u8) -> i64 {
77 let gw: *i64 = nx_gw_open(path)
78 zo_pad(label, 44)
79 if (gw as i64) == 0 {
80 zo_puts("unreadable (not gguf/safetensors)\n" as *u8)
81 g_n_unknown = g_n_unknown + 1
82 return 0
83 }
84 let rl: *i64 = sys_mmap(RL_SLOTS * 8 + 64) as *i64
85 let role: i64 = nx_role_probe(gw, rl)
86 zo_pad(nx_family_name(rl[RL_FAMILY]), 10)
87 zo_pad(nx_role_name(role), 13)
88 if role == ROLE_ADAPTER { zo_pad(nx_adkind_name(rl[RL_ADKIND]), 13) }
89 else { zo_pad("" as *u8, 13) }
90
91 if rl[RL_RUNNABLE] == 1 {
92 if role == ROLE_DIT {
93 let arch: *i64 = sys_mmap(NX_ARCH_SLOTS * 8 + 64) as *i64
94 if nx_arch_probe(gw, arch) == 0 {
95 let b: *u8 = sys_mmap(32)
96 zo_puts("layers=" as *u8)
97 sys_write(1, b, nx_strconv_format_i64(arch[NX_ARCH_N_LAYERS], b))
98 zo_puts(" heads=" as *u8)
99 sys_write(1, b, nx_strconv_format_i64(arch[NX_ARCH_N_HEADS], b))
100 zo_puts(" " as *u8)
101 }
102 }
103 zo_puts("RUNNABLE\n" as *u8)
104 } else {
105 zo_puts("blocked: " as *u8)
106 zo_puts(nx_role_blocker(rl))
107 zo_puts("\n" as *u8)
108 }
109
110 if role == ROLE_ADAPTER { g_n_adapter = g_n_adapter + 1; return role }
111 if role == ROLE_UNKNOWN { g_n_unknown = g_n_unknown + 1; return role }
112 // Only a RUNNABLE part counts toward the pipeline verdict: an sd-vae we cannot decode must not
113 // make the zoo report that a vae decoder is present.
114 if rl[RL_RUNNABLE] == 1 {
115 if role == ROLE_DIT { g_have_dit = g_have_dit + 1 }
116 if role == ROLE_VAE_DEC { g_have_vae = g_have_vae + 1 }
117 if role == ROLE_TEXTENC { g_have_te = g_have_te + 1 }
118 }
119 return role
120}
121
122func zo_walk(dir: *u8) -> i64 {
123 let fd: i64 = sys_openat_rd(dir)
124 if fd < 0 { zo_puts(" (cannot open dir)\n" as *u8); return 0 - 1 }
125 let buf: *u8 = sys_mmap(ZOO_MAGIC_65536)
126 let path: *u8 = sys_mmap(ZOO_MAGIC_1024)
127 var go: i64 = 1
128 while go == 1 {
129 let n: i64 = sys_getdents64(fd, buf, ZOO_MAGIC_65536)
130 if n <= 0 { go = 0 }
131 else {
132 var off: i64 = 0
133 while off < n {
134 let ent: *u8 = ((buf as i64) + off) as *u8
135 let rl: i64 = dirent_reclen(ent)
136 let ty: i64 = dirent_type(ent)
137 let nm: *u8 = dirent_name(ent)
138 // 8 = DT_REG. Skip dot entries; a name starting '.' is never a model here.
139 if ty == 8 {
140 if nm[0] != (0x2E as u8) {
141 zo_classify(zo_join(path, dir, nm), nm)
142 }
143 }
144 if rl <= 0 { off = n } else { off = off + rl }
145 }
146 }
147 }
148 sys_close(fd)
149 return 0
150}
151
152func main(argc: i64, argv: *i64) -> i64 {
153 if argc < 2 {
154 zo_puts("usage: nx_gen_zoo <dir> [dir2 ...]\n" as *u8)
155 return 2
156 }
157 g_have_dit = 0
158 g_have_vae = 0
159 g_have_te = 0
160 g_n_adapter = 0
161 g_n_unknown = 0
162
163 zo_puts("=== NISHI GEN ZOO ===\n" as *u8)
164 var a: i64 = 1
165 while a < argc {
166 zo_puts("-- " as *u8)
167 zo_puts(argv[a] as *u8)
168 zo_puts("\n" as *u8)
169 zo_walk(argv[a] as *u8)
170 a = a + 1
171 }
172
173 zo_puts("=== PIPELINE ===\n" as *u8)
174 nx_genver_emit("runnable_dits" as *u8, g_have_dit)
175 nx_genver_emit("runnable_vae_decoders" as *u8, g_have_vae)
176 nx_genver_emit("text_encoders" as *u8, g_have_te)
177 nx_genver_emit("adapters" as *u8, g_n_adapter)
178 nx_genver_emit("unidentified" as *u8, g_n_unknown)
179
180 // Report what is MISSING by name, not just a verdict -- a bare NO is not actionable.
181 if g_have_dit > 0 {
182 if g_have_vae > 0 {
183 if g_have_te > 0 {
184 zo_puts("VERDICT: image generation RUNNABLE (dit + vae-decoder present; text encoder is third-party)\n" as *u8)
185 return 0
186 }
187 zo_puts("VERDICT: blocked -- no text encoder found\n" as *u8)
188 return 1
189 }
190 zo_puts("VERDICT: blocked -- no RUNNABLE vae decoder (taesd) found\n" as *u8)
191 return 1
192 }
193 zo_puts("VERDICT: blocked -- no runnable DiT found\n" as *u8)
194 return 1
195}