code wiki / (root) / nx_gguf_meta.nx

nx_gguf_meta.nx source

↩ module page · 238 lines · 8371 B

1// nx_gguf_meta.nx -- GGUF metadata key-value walker + typed readers. 2// 3// Extends nx_gguf.nx by parsing the metadata section that nx_gguf_parse 4// currently SKIPS. Unblocks reading model architecture parameters 5// (n_layers / hidden_dim / rope_base / n_heads / vocab_size) + the 6// BPE tokenizer vocab from real Llama / Mistral / Qwen GGUF files, 7// rather than requiring the caller to know the spec a priori. 8// 9// Canonical Llama metadata keys (public spec, ggml docs): 10// "general.architecture" STRING -> "llama" 11// "llama.attention.head_count" UINT32 -> n_heads 12// "llama.attention.head_count_kv" UINT32 -> n_kv_heads 13// "llama.embedding_length" UINT32 -> hidden_dim 14// "llama.block_count" UINT32 -> n_layers 15// "llama.feed_forward_length" UINT32 -> ffn_dim 16// "llama.context_length" UINT32 -> max_seq_len 17// "llama.rope.freq_base" FLOAT32 -> rope_base 18// "llama.attention.layer_norm_rms_epsilon" FLOAT32 -> rms eps 19// "tokenizer.ggml.tokens" ARRAY of STRING -> BPE vocab 20// "tokenizer.ggml.scores" ARRAY of FLOAT32 -> BPE merge scores 21// 22// Bits-up composition: 23// nx_gguf.nx -- header + skip-value canonical helper 24// nx_gguf_load.nx -- _gguf_f32_to_q10 (composed for f32 read) 25// nx_le.nx -- LE binary readers 26// 27// genealogy_id: gguf_v3_metadata_spec_gerganov_2024 28// lineage_id: substrate_gguf_meta_v1_walker 29 30// nx_safety_envelope: 31// intended_use: "Find a metadata entry by key + read its 32// value as a typed scalar; v1 supports 33// U8/I8/U16/I16/U32/I32/U64/I64/F32/BOOL/STRING. 34// Arrays expose len + start-offset for caller 35// to iterate." 36// sil_target: SIL2 37// asil_target: QM 38// dal_target: DAL C 39// evidence: [gguf_v3_spec_public, clean_room, 40// composes_nx_gguf_skip_value] 41// hazard_register: [bug-tape-malformed-key-len, 42// bug-tape-truncated-buf-during-walk, 43// bug-tape-nested-arrays-rejected] 44// residual_risk: "Caller must pre-parse header so 45// hdr.metadata_count is trustworthy" 46// verdict: NOT_YET_EVALUATED 47 48import "nx_syscalls.nx" 49import "nx_tier.nx" 50import "nx_loop.nx" 51import "nx_le.nx" 52import "nx_gguf.nx" 53import "nx_gguf_load.nx" 54const NX_MAGIC_32768: i64 = 32768 55const NX_MAGIC_65536: i64 = 65536 56const NX_MAGIC_2147483648: i64 = 2147483648 57const NX_MAGIC_4294967296: i64 = 4294967296 58 59// ===== Sealed-enum: GgufMetaVerdict =============================== 60 61const NX_GMETA_OK: nx_int = 0 62const NX_GMETA_NOT_FOUND: nx_int = 1 63const NX_GMETA_ERR_OOB: nx_int = 2 64const NX_GMETA_ERR_BAD_TYPE: nx_int = 3 65const NX_GMETA_ERR_TRUNCATED: nx_int = 4 66const NX_GMETA_N_VERDICTS: nx_int = 5 67 68func nx_gmeta_verdict_is_valid(v: nx_int) -> nx_int { 69 if v < 0 { return 0 } 70 if v >= NX_GMETA_N_VERDICTS { return 0 } 71 return 1 72} 73 74// Byte-equal helper (private to this file). 75func _gmeta_bytes_equal(a: *u8, b: *u8, n: nx_int) -> nx_int { 76 var i: nx_int = 0 77 while i < n { 78 if a[i] != b[i] { return 0 } 79 i = i + 1 80 } 81 return 1 82} 83 84// ===== Public: find metadata entry by key ========================= 85// 86// buf file bytes 87// len file length 88// hdr parsed header (hdr.metadata_count is the upper bound) 89// key caller-supplied key bytes 90// key_len length of key 91// out_value_off *i64 written on success: byte offset of value-body 92// out_type *i64 written on success: GGUF type tag (NX_GGUF_TYPE_*) 93// 94// Returns NX_GMETA_OK on hit; NX_GMETA_NOT_FOUND on miss; OOB/ 95// TRUNCATED on file corruption. 96 97func nx_gguf_meta_find(buf: *u8, len: i64, hdr: *NxGgufHeader, 98 key: *u8, key_len: nx_int, 99 out_value_off: *i64, out_type: *i64) -> nx_int { 100 // Metadata section starts at byte 24 (after magic+ver+tens+meta). 101 var off: i64 = 24 102 var i: nx_int = 0 103 var iter: nx_int = 0 104 var verdict: nx_int = NX_LOOP_RUNNING 105 let BUDGET: nx_int = hdr.metadata_count 106 while verdict == NX_LOOP_RUNNING && iter < BUDGET { 107 if off + 8 > len { return NX_GMETA_ERR_OOB } 108 let cur_key_len: i64 = nx_le_read_u64(buf, off) 109 off = off + 8 110 if off + cur_key_len > len { return NX_GMETA_ERR_OOB } 111 let key_off: i64 = off 112 off = off + cur_key_len 113 if off + 4 > len { return NX_GMETA_ERR_OOB } 114 let cur_type: i64 = nx_le_read_u32(buf, off) 115 off = off + 4 116 117 // Compare key. 118 if cur_key_len == key_len { 119 if _gmeta_bytes_equal((buf as i64 + key_off) as *u8, key, key_len) == 1 { 120 out_value_off[0] = off 121 out_type[0] = cur_type 122 return NX_GMETA_OK 123 } 124 } 125 126 // Skip the value to advance to next entry. 127 off = nx_gguf_skip_value(buf, len, off, cur_type) 128 if off < 0 { return NX_GMETA_ERR_TRUNCATED } 129 130 i = i + 1 131 iter = iter + 1 132 } 133 return NX_GMETA_NOT_FOUND 134} 135 136// ===== Typed readers ============================================= 137 138func nx_gguf_meta_read_u8(buf: *u8, off: i64) -> i64 { 139 return nx_le_read_u8(buf, off) 140} 141 142func nx_gguf_meta_read_u16(buf: *u8, off: i64) -> i64 { 143 return nx_le_read_u16(buf, off) 144} 145 146func nx_gguf_meta_read_u32(buf: *u8, off: i64) -> i64 { 147 return nx_le_read_u32(buf, off) 148} 149 150func nx_gguf_meta_read_u64(buf: *u8, off: i64) -> i64 { 151 return nx_le_read_u64(buf, off) 152} 153 154// Signed variants -- explicit sign extension since the LE readers 155// return unsigned. 156 157func nx_gguf_meta_read_i8(buf: *u8, off: i64) -> i64 { 158 let v: i64 = nx_le_read_u8(buf, off) 159 if v >= 128 { return v - 256 } 160 return v 161} 162 163func nx_gguf_meta_read_i16(buf: *u8, off: i64) -> i64 { 164 let v: i64 = nx_le_read_u16(buf, off) 165 if v >= NX_MAGIC_32768 { return v - NX_MAGIC_65536 } 166 return v 167} 168 169func nx_gguf_meta_read_i32(buf: *u8, off: i64) -> i64 { 170 let v: i64 = nx_le_read_u32(buf, off) 171 if v >= NX_MAGIC_2147483648 { return v - NX_MAGIC_4294967296 } 172 return v 173} 174 175// F32 → Q10 i64; composes the clean-room IEEE-754 decoder from 176// nx_gguf_load. 177 178func nx_gguf_meta_read_f32_q10(buf: *u8, off: i64) -> i64 { 179 let raw: i64 = nx_le_read_u32(buf, off) 180 return _gguf_f32_to_q10(raw) 181} 182 183// Bool: 1-byte unsigned 0/1. 184 185func nx_gguf_meta_read_bool(buf: *u8, off: i64) -> i64 { 186 return nx_le_read_u8(buf, off) 187} 188 189// String: 8-byte LE length followed by raw UTF-8 bytes. 190// 191// nx_gguf_meta_read_string_len returns the byte length of the string. 192// nx_gguf_meta_read_string_ptr returns a pointer to the string bytes 193// (no copy; lives inside the file buf). 194 195func nx_gguf_meta_read_string_len(buf: *u8, off: i64) -> i64 { 196 return nx_le_read_u64(buf, off) 197} 198 199func nx_gguf_meta_read_string_ptr(buf: *u8, off: i64) -> *u8 { 200 return (buf as i64 + off + 8) as *u8 201} 202 203// ===== Array helpers ============================================= 204// 205// Array value-body layout: 206// u32 inner_type 207// u64 element_count 208// <elements packed; layout depends on inner_type> 209// 210// We expose: 211// nx_gguf_meta_array_inner_type(buf, off) -> i64 212// nx_gguf_meta_array_count(buf, off) -> i64 213// nx_gguf_meta_array_first_elt_off(buf, off) -> i64 214// 215// Caller iterates elements themselves by composing inner_type with 216// the typed readers above. For STRING arrays each element is 217// length-prefixed; caller advances by 8 + slen per step. 218 219func nx_gguf_meta_array_inner_type(buf: *u8, off: i64) -> i64 { 220 return nx_le_read_u32(buf, off) 221} 222 223func nx_gguf_meta_array_count(buf: *u8, off: i64) -> i64 { 224 return nx_le_read_u64(buf, off + 4) 225} 226 227func nx_gguf_meta_array_first_elt_off(buf: *u8, off: i64) -> i64 { 228 return off + 12 229} 230 231// Convenience: advance past one element of `inner_type` from byte 232// offset `off`. Returns the offset AFTER the element. Caller is 233// expected to chain this in a loop with bounds checking. 234 235func nx_gguf_meta_array_advance(buf: *u8, len: i64, off: i64, 236 inner_type: i64) -> i64 { 237 return nx_gguf_skip_value(buf, len, off, inner_type) 238}