Automatic Speech Recognition
MLX
ONNX
GGUF
Rust
English
Chinese
audio8
streaming-asr
quantized
experimental
Instructions to use Reza2kn/Audio8-ASR-Infinite-Compressed with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use Reza2kn/Audio8-ASR-Infinite-Compressed with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] hf download Reza2kn/Audio8-ASR-Infinite-Compressed --local-dir Audio8-ASR-Infinite-Compressed
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
Download sources/gguf-format2/audio8_model.cpp from Reza2kn/Audio8-ASR-Infinite-Compressed: direct link, hf CLI and curl.
- Browser
- Download file 28.7 kB
-
https://huggingface.co/Reza2kn/Audio8-ASR-Infinite-Compressed/resolve/main/sources/gguf-format2/audio8_model.cpp
- Command line
-
hf download hf://Reza2kn/Audio8-ASR-Infinite-Compressed/sources/gguf-format2/audio8_model.cpp
-
curl -L -o audio8_model.cpp https://huggingface.co/Reza2kn/Audio8-ASR-Infinite-Compressed/resolve/main/sources/gguf-format2/audio8_model.cpp
28.7 kB
| namespace audio8 { | |
| namespace { | |
| constexpr std::size_t kTensorCount = 938; | |
| constexpr std::uint64_t kMetadataLimit = 64ULL * 1024 * 1024; | |
| constexpr std::uint64_t kSourcePayloadBytes = 2313973312ULL; | |
| constexpr std::uint64_t kOriginalBytes = 21474880ULL; | |
| constexpr const char * kRevision = "b4413de154ed6bdef0a4011028b1ebd12aca8152"; | |
| constexpr const char * kGgmlRevision = "84e76d8a23162eca70490da131945ebec1f09bf4"; | |
| constexpr const char * kAlias = "language_model.lm_head.weight"; | |
| constexpr const char * kEmbedding = "language_model.model.embed_tokens.weight"; | |
| constexpr const char * kAliasesJson = | |
| "{\"language_model.lm_head.weight\":\"language_model.model.embed_tokens.weight\"}"; | |
| struct FileCloser { | |
| void operator()(FILE * file) const noexcept { | |
| if (file != nullptr) std::fclose(file); | |
| } | |
| }; | |
| void require(bool ok, const std::string & message) { | |
| if (!ok) throw std::runtime_error("Audio8 GGUF: " + message); | |
| } | |
| std::uint64_t add(std::uint64_t a, std::uint64_t b) { | |
| require(a <= std::numeric_limits<std::uint64_t>::max()-b, "size overflow"); | |
| return a+b; | |
| } | |
| std::uint64_t aligned(std::uint64_t n) { return add(n, 31)/32*32; } | |
| void read_exact(FILE * file, void * out, std::size_t bytes) { | |
| require(bytes == 0 || std::fread(out, 1, bytes, file) == bytes, "truncated file/read error"); | |
| } | |
| void seek(FILE * file, std::uint64_t offset) { | |
| require(offset <= static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()), "file offset overflow"); | |
| require(_fseeki64(file, static_cast<__int64>(offset), SEEK_SET) == 0, "seek failed"); | |
| static_assert(sizeof(off_t) >= 8, "Audio8 loader requires 64-bit file offsets"); | |
| require(fseeko(file, static_cast<off_t>(offset), SEEK_SET) == 0, "seek failed"); | |
| } | |
| std::uint64_t tell(FILE * file) { | |
| const auto result = _ftelli64(file); | |
| const auto result = ftello(file); | |
| require(result >= 0, "tell failed"); | |
| return static_cast<std::uint64_t>(result); | |
| } | |
| std::uint64_t file_size(FILE * file) { | |
| require(_fseeki64(file, 0, SEEK_END) == 0, "end seek failed"); | |
| require(fseeko(file, 0, SEEK_END) == 0, "end seek failed"); | |
| const auto result = tell(file); | |
| seek(file, 0); | |
| return result; | |
| } | |
| template<typename T> T scalar(FILE * file) { | |
| static_assert(std::is_unsigned_v<T>); | |
| std::array<std::uint8_t, sizeof(T)> bytes{}; | |
| read_exact(file, bytes.data(), bytes.size()); | |
| T value = 0; | |
| for (std::size_t i = 0; i < bytes.size(); ++i) value |= static_cast<T>(bytes[i]) << (i*8); | |
| return value; | |
| } | |
| void skip(FILE * file, std::uint64_t count, std::uint64_t file_bytes) { | |
| const auto end = add(tell(file), count); | |
| require(end <= file_bytes && end <= kMetadataLimit, "metadata length exceeds bound"); | |
| seek(file, end); | |
| } | |
| void skip_string(FILE * file, std::uint64_t maximum, std::uint64_t file_bytes) { | |
| const auto n = scalar<std::uint64_t>(file); | |
| require(n <= maximum, "oversized metadata string"); | |
| require(add(tell(file),n)<=file_bytes && add(tell(file),n)<=kMetadataLimit,"string extends beyond metadata"); | |
| std::array<std::uint8_t,4096> block{}; | |
| for (std::uint64_t remaining=n; remaining;) { | |
| const auto count=static_cast<std::size_t>(std::min<std::uint64_t>(remaining,block.size())); | |
| read_exact(file,block.data(),count); | |
| require(std::all_of(block.begin(),block.begin()+count,[](std::uint8_t c){return c!=0;}),"NUL in metadata string"); | |
| remaining-=count; | |
| } | |
| } | |
| // Bounds are checked before calling the general GGUF parser, which may allocate | |
| // metadata based on lengths. This scanner never allocates a declared string. | |
| std::uint64_t preflight(FILE * file, std::uint64_t file_bytes) { | |
| std::array<char,4> magic{}; read_exact(file, magic.data(), magic.size()); | |
| require(std::memcmp(magic.data(), "GGUF", 4) == 0 && scalar<std::uint32_t>(file) == 3, "magic/version"); | |
| require(scalar<std::uint64_t>(file) == kTensorCount, "expected all 938 source tensors"); | |
| const auto keys = scalar<std::uint64_t>(file); | |
| require(keys == 64 || keys == 72, "metadata key count for custom formats v1/v2"); | |
| std::set<std::string> seen_keys; | |
| for (std::uint64_t i = 0; i < keys; ++i) { | |
| const auto key_size = scalar<std::uint64_t>(file); | |
| require(key_size > 0 && key_size <= 512, "metadata key length"); | |
| std::string name(key_size,'\0'); read_exact(file,name.data(),name.size()); | |
| require(std::all_of(name.begin(),name.end(),[](unsigned char c){return c>=32 && c<127;}) | |
| && seen_keys.insert(name).second,"invalid or duplicate metadata key"); | |
| switch (scalar<std::uint32_t>(file)) { | |
| case GGUF_TYPE_UINT32: skip(file, 4, file_bytes); break; | |
| case GGUF_TYPE_BOOL: require(scalar<std::uint8_t>(file) <= 1, "invalid metadata bool"); break; | |
| case GGUF_TYPE_STRING: skip_string(file, 32*1024*1024, file_bytes); break; | |
| case GGUF_TYPE_ARRAY: { | |
| require(scalar<std::uint32_t>(file) == GGUF_TYPE_UINT8, "unsupported metadata array type"); | |
| const auto size = scalar<std::uint64_t>(file); | |
| require(size <= 4*1024*1024, "metadata byte array exceeds bound"); | |
| skip(file, size, file_bytes); break; | |
| } | |
| default: throw std::runtime_error("Audio8 GGUF: unsupported emitted metadata type"); | |
| } | |
| } | |
| std::uint64_t cursor = 0; | |
| for (std::size_t i = 0; i < kTensorCount; ++i) { | |
| const auto name_size = scalar<std::uint64_t>(file); | |
| require(name_size > 0 && name_size < GGML_MAX_NAME, "tensor name length"); | |
| std::string name(name_size,'\0'); read_exact(file,name.data(),name.size()); | |
| require(std::all_of(name.begin(),name.end(),[](unsigned char c){return c>=32 && c<127;}),"invalid tensor name bytes"); | |
| const auto rank = scalar<std::uint32_t>(file); | |
| require(rank >= 1 && rank <= 4, "tensor rank"); | |
| std::uint64_t elements = 1, first = 0; | |
| for (std::uint32_t dim = 0; dim < rank; ++dim) { | |
| const auto n = scalar<std::uint64_t>(file); | |
| require(n > 0 && n <= 1024*1024 && elements <= (1ULL<<32)/n, "tensor dimension/product bound"); | |
| if (dim == 0) first = n; | |
| elements *= n; | |
| } | |
| const auto type = scalar<std::uint32_t>(file); | |
| require(type == GGML_TYPE_Q4_0 || type == GGML_TYPE_BF16, "source tensor dtype"); | |
| require(type != GGML_TYPE_Q4_0 || (rank == 2 && first % 64 == 0), "source Q4 group64 row shape"); | |
| const auto bytes = type == GGML_TYPE_Q4_0 ? elements/32*18 : elements*2; | |
| require(scalar<std::uint64_t>(file) == cursor, "noncanonical or overlapping tensor offsets"); | |
| cursor = aligned(add(cursor, bytes)); | |
| require(cursor <= file_bytes && tell(file) <= kMetadataLimit, "tensor directory bound"); | |
| } | |
| const auto unpadded=tell(file); | |
| const auto data_start = aligned(unpadded); | |
| require(data_start <= kMetadataLimit && add(data_start,cursor) == file_bytes, "file coverage/trailing bytes"); | |
| std::array<std::uint8_t,32> pad{}; | |
| const auto count=static_cast<std::size_t>(data_start-unpadded); | |
| read_exact(file,pad.data(),count); | |
| require(std::all_of(pad.begin(),pad.begin()+count,[](std::uint8_t c){return c==0;}),"nonzero metadata padding"); | |
| return data_start; | |
| } | |
| bool valid_hash(std::string_view text) { | |
| return text.size() == 64 && std::all_of(text.begin(), text.end(), [](char x) { | |
| return (x >= '0' && x <= '9') || (x >= 'a' && x <= 'f'); | |
| }); | |
| } | |
| std::string hex(const std::array<std::uint8_t,32> & bytes) { | |
| constexpr char digits[] = "0123456789abcdef"; | |
| std::string result; result.reserve(64); | |
| for (const auto byte : bytes) { result.push_back(digits[byte>>4]); result.push_back(digits[byte&15]); } | |
| return result; | |
| } | |
| std::unique_ptr<Sha256> hasher(const Sha256Factory & factory) { | |
| require(static_cast<bool>(factory), "missing SHA256 factory"); | |
| auto result = factory(); require(result != nullptr, "null SHA256 instance"); return result; | |
| } | |
| std::string digest(const Sha256Factory & factory, std::span<const std::uint8_t> bytes) { | |
| auto h = hasher(factory); h->update(bytes); return hex(h->finish()); | |
| } | |
| std::span<const std::uint8_t> as_bytes(std::string_view text) { | |
| return {reinterpret_cast<const std::uint8_t *>(text.data()), text.size()}; | |
| } | |
| int64_t key(const gguf_context * meta, std::string_view name, gguf_type type) { | |
| const auto index = gguf_find_key(meta, std::string(name).c_str()); | |
| require(index >= 0 && gguf_get_kv_type(meta,index) == type, "missing/wrong metadata type: " + std::string(name)); | |
| return index; | |
| } | |
| std::string_view text(const gguf_context * meta, std::string_view name) { | |
| const char * value = gguf_get_val_str(meta,key(meta,name,GGUF_TYPE_STRING)); | |
| require(value != nullptr, "null metadata string"); | |
| return value; | |
| } | |
| std::span<const std::uint8_t> array(const gguf_context * meta, std::string_view name) { | |
| const auto index = key(meta,name,GGUF_TYPE_ARRAY); | |
| require(gguf_get_arr_type(meta,index) == GGUF_TYPE_UINT8, "auxiliary array dtype"); | |
| return {static_cast<const std::uint8_t *>(gguf_get_arr_data(meta,index)), gguf_get_arr_n(meta,index)}; | |
| } | |
| struct Shape { | |
| ggml_type type; | |
| std::array<int64_t,4> ne = {1,1,1,1}; | |
| int rank; | |
| }; | |
| std::map<std::string,Shape,std::less<>> schema() { | |
| std::map<std::string,Shape,std::less<>> shapes; | |
| const auto put = [&](std::string name, ggml_type type, std::initializer_list<int64_t> dims) { | |
| Shape s{type,{1,1,1,1},static_cast<int>(dims.size())}; | |
| std::copy(dims.begin(),dims.end(),s.ne.begin()); | |
| require(shapes.emplace(std::move(name),s).second,"duplicate internal schema name"); | |
| }; | |
| const auto b = GGML_TYPE_BF16, q = GGML_TYPE_Q4_0; | |
| put("audio_tower.embedder.conv1.weight",b,{3,128,1280}); | |
| put("audio_tower.embedder.conv1.bias",b,{1280}); | |
| put("audio_tower.embedder.conv2.weight",b,{3,1280,1280}); | |
| put("audio_tower.embedder.conv2.bias",b,{1280}); | |
| put("audio_tower.norm.weight",b,{1280}); | |
| for (int i=0; i<32; ++i) { | |
| const auto p="audio_tower.layers."+std::to_string(i)+"."; | |
| put(p+"final_layer_norm.weight",b,{1280}); | |
| put(p+"self_attn_layer_norm.weight",b,{1280}); | |
| put(p+"mlp.down_proj.weight",q,{5120,1280}); | |
| put(p+"mlp.down_proj.bias",b,{1280}); | |
| put(p+"mlp.gate_proj.weight",q,{1280,5120}); | |
| put(p+"mlp.up_proj.weight",q,{1280,5120}); | |
| for (const auto * projection : {"q_proj","k_proj","v_proj"}) | |
| put(p+"self_attn."+projection+".weight",q,{1280,2048}); | |
| put(p+"self_attn.q_proj.bias",b,{2048}); | |
| put(p+"self_attn.v_proj.bias",b,{2048}); | |
| put(p+"self_attn.o_proj.weight",q,{2048,1280}); | |
| put(p+"self_attn.o_proj.bias",b,{1280}); | |
| } | |
| put("frame_len_embedding.weight",b,{2048,3}); | |
| put(kEmbedding,q,{2048,151936}); | |
| put("language_model.model.norm.weight",b,{2048}); | |
| put("multi_modal_projector.linear_1.weight",q,{10240,2048}); | |
| put("multi_modal_projector.linear_2.weight",q,{2048,2048}); | |
| for (int i=0; i<36; ++i) { | |
| const auto p="language_model.model.layers."+std::to_string(i)+"."; | |
| put(p+"ada_rms_norm.linear1.weight",b,{2048,32}); | |
| put(p+"ada_rms_norm.linear2.weight",b,{32,2048}); | |
| put(p+"input_layernorm.weight",b,{2048}); | |
| put(p+"post_attention_layernorm.weight",b,{2048}); | |
| put(p+"mlp.down_proj.weight",q,{11008,2048}); | |
| put(p+"mlp.gate_proj.weight",q,{2048,11008}); | |
| put(p+"mlp.up_proj.weight",q,{2048,11008}); | |
| put(p+"self_attn.q_proj.weight",q,{2048,2048}); | |
| put(p+"self_attn.q_proj.bias",b,{2048}); | |
| put(p+"self_attn.o_proj.weight",q,{2048,2048}); | |
| for (const auto * projection : {"k_proj","v_proj"}) { | |
| put(p+"self_attn."+projection+".weight",q,{2048,256}); | |
| put(p+"self_attn."+projection+".bias",b,{256}); | |
| } | |
| } | |
| for (int i=0; i<4; ++i) { | |
| const auto p="semantic_vad_heads."+std::to_string(i)+"."; | |
| put(p+"weight",b,{2048,8}); put(p+"bias",b,{8}); | |
| } | |
| require(shapes.size()==kTensorCount,"internal schema count"); | |
| return shapes; | |
| } | |
| void verify_metadata(const gguf_context * meta, const Sha256Factory & hash) { | |
| require(gguf_get_version(meta)==3 && gguf_get_alignment(meta)==32,"GGUF format/alignment"); | |
| require(text(meta,"general.architecture")=="audio8", "architecture must be audio8"); | |
| require(text(meta,"general.license")=="apache-2.0", "model license metadata"); | |
| const auto format=gguf_get_val_u32(meta,key(meta,"audio8.format_version",GGUF_TYPE_UINT32)); | |
| require(format==1 || format==2,"supported Audio8 format version"); | |
| require(gguf_get_n_kv(meta)==(format==1 ? 64 : 72),"format-specific metadata key count"); | |
| for (const auto &[name,value] : std::initializer_list<std::pair<const char *,std::uint32_t>>{ | |
| {"general.quantization_version",2}, | |
| {"audio8.mapping.source_group_size",64},{"audio8.mapping.destination_group_size",32}}) | |
| require(gguf_get_val_u32(meta,key(meta,name,GGUF_TYPE_UINT32))==value,"format/mapping integer metadata"); | |
| require(!gguf_get_val_bool(meta,key(meta,"audio8.backend_verified",GGUF_TYPE_BOOL)) && | |
| !gguf_get_val_bool(meta,key(meta,"audio8.mapping.refit",GGUF_TYPE_BOOL)),"unexpected artifact claim/refit"); | |
| require(text(meta,"audio8.mapping")== (format==1 ? "q4g64-zp7-to-q4_0g32-zp8-v1" : "q3fullg64-zp4-and-q4g64-zp7-to-q4_0g32-zp8-v2"), "mapping version"); | |
| if (format==2) { | |
| require(text(meta,"audio8.source.bundle_manifest_sha256")=="f132d8c4dd0af2eaf49cef779635e83678e3169f002015e9a0a35c9f6f685656", "Q3 native manifest identity"); | |
| require(text(meta,"audio8.source.weights_sha256")=="e38b5a14c8636e1d9a586cd725538d17164554c8ef52f60f7fd293791acaa6c4", "Q3 native weights identity"); | |
| require(text(meta,"audio8.aux.provenance/q3-overlay-manifest.json.sha256")=="0e77d69bf604f6d4815a3397206efa9b34f13cc0edb4999efd8e0cabae4c320e", "Q3 fitted overlay identity"); | |
| require(text(meta,"audio8.aux.provenance/q3-native-manifest.json.sha256")==text(meta,"audio8.source.bundle_manifest_sha256"), "Q3 provenance manifest identity"); | |
| } | |
| require(text(meta,"audio8.source.model_id")=="Edge0/Audio8-ASR-Infinite" && | |
| text(meta,"audio8.source.revision")==kRevision && | |
| text(meta,"audio8.ggml_source_revision")==kGgmlRevision,"upstream identity"); | |
| require(text(meta,"audio8.tensor.aliases")==kAliasesJson,"single tied-head alias required"); | |
| const auto manifest_hash=text(meta,"audio8.source.bundle_manifest_sha256"); | |
| require(valid_hash(manifest_hash) && digest(hash,as_bytes(text(meta,"audio8.source.bundle_manifest")))==manifest_hash, | |
| "embedded source manifest hash"); | |
| require(valid_hash(text(meta,"audio8.source.weights_sha256")),"source weight identity"); | |
| const std::pair<const char *,const char *> assets[] = { | |
| {"chat_template.jinja","cd8e9439f0570856fd70470bf8889ebd8b5d1107207f67a5efb46e342330527f"}, | |
| {"config.json","744baed356a8c86a9791fc76cecb73219ed4aff795147c6cdc082337a13f019c"}, | |
| {"generation_config.json","d3f96050ed55e02136d3cea2d1c03c0163d8e93631cdfb87bf0e82945ed2db64"}, | |
| {"model.safetensors.index.json","18307b2e6abe5e41a867b27158c65c4097e6ee1b16a87369006dc2d4df1546fe"}, | |
| {"preprocessor_config.json","ee0dac67c684faa527db5d26a75109e168723fdae38b49e0c1bb471a953f6c45"}, | |
| {"tokenizer.json","72f8b7e878ee6606c309881f68201e277f9dae50175a387074a5181d771b9599"}, | |
| {"tokenizer_config.json","ceca11f549d6a8550430c7272e9aa40582bffd633f8998335883a428f4d77fb9"}, | |
| }; | |
| for (const auto &[name,expected] : assets) { | |
| const std::string k=std::string("audio8.asset.")+name; | |
| require(text(meta,k+".sha256")==expected && digest(hash,as_bytes(text(meta,k)))==expected, | |
| "source config/tokenizer asset hash: "+k); | |
| } | |
| const std::pair<const char *,const char *> auxiliary[] = { | |
| {"frontend.window_f32le","c692d19f0b2bd745f0dfc967261c3e3d21a2473e485a677d219e9b4a73e0218e"}, | |
| {"frontend.filters_f32le","c342ed6d06c90b42990bf9e4a3a07ca54f3f1422f7bc3c66b3177a774395df5a"}, | |
| {"tokenizer.a8tok","7fdc1e903297ea1fa438b77503db993c50a67f8b325b4e55754f08b8579c5410"}, | |
| {"source_model_card_utf8","791e07ad5efa25a1ae4dde9f795d3d041bd06487eef9b39b07ddef1ad0a6e5e2"}, | |
| }; | |
| for (const auto &[name,expected] : auxiliary) { | |
| const std::string k=std::string("audio8.aux.")+name; | |
| require(text(meta,k+".sha256")==expected && digest(hash,array(meta,k))==expected, | |
| "frontend/tokenizer/model-card hash: "+k); | |
| } | |
| require(array(meta,"audio8.aux.frontend.window_f32le").size()==400*4 && | |
| array(meta,"audio8.aux.frontend.filters_f32le").size()==128*201*4,"frontend dimensions"); | |
| const auto tok=array(meta,"audio8.aux.tokenizer.a8tok"); | |
| require(tok.size()==1734837 && std::memcmp(tok.data(),"A8TOK001",8)==0,"native tokenizer format"); | |
| // Preserve and check every embedded provenance sidecar; never execute it. | |
| std::size_t sidecars=0; | |
| for (int64_t i=0; i<gguf_get_n_kv(meta); ++i) { | |
| const std::string_view name=gguf_get_key(meta,i); | |
| if (name.starts_with("audio8.aux.provenance/") && !name.ends_with(".sha256")) { | |
| const auto expected=text(meta,std::string(name)+".sha256"); | |
| require(valid_hash(expected) && digest(hash,array(meta,name))==expected,"provenance sidecar hash"); | |
| ++sidecars; | |
| } | |
| } | |
| require(sidecars==(format==1 ? 11U : 14U),"incomplete quantization provenance sidecars"); | |
| } | |
| void validate_q4(std::span<const std::uint8_t> bytes) { | |
| require(bytes.size()%36==0,"Q4 group64 staging alignment"); | |
| for (std::size_t i=0; i<bytes.size(); i+=36) { | |
| const std::uint16_t scale=bytes[i] | (std::uint16_t(bytes[i+1])<<8); | |
| // Keep the success path allocation-free while inspecting every packed | |
| // byte. Constructing std::string errors per code is expensive at 4B weights. | |
| if ((scale&0x7c00)==0x7c00 || ((scale&0x8000)!=0 && scale!=0x8000)) | |
| require(false,"invalid Q4 scale"); | |
| if (bytes[i]!=bytes[i+18] || bytes[i+1]!=bytes[i+19]) | |
| require(false,"nonduplicated group64 scale"); | |
| for (const auto start : {i+2,i+20}) | |
| for (std::size_t j=0; j<16; ++j) | |
| if ((bytes[start+j]&15)==0 || (bytes[start+j]>>4)==0) | |
| require(false,"unused -8 code in exact Q4 mapping"); | |
| } | |
| } | |
| void validate_bf16(std::span<const std::uint8_t> bytes) { | |
| require(bytes.size()%2==0,"BF16 staging alignment"); | |
| for (std::size_t i=0; i<bytes.size(); i+=2) { | |
| const std::uint16_t bits=bytes[i] | (std::uint16_t(bytes[i+1])<<8); | |
| if ((bits&0x7f80)==0x7f80) require(false,"nonfinite original BF16 weight"); | |
| } | |
| } | |
| } // namespace | |
| struct Model::Impl { | |
| gguf_context * metadata=nullptr; | |
| ggml_context * context=nullptr; | |
| ggml_backend_buffer_t buffer=nullptr; | |
| ggml_backend_t backend=nullptr; // Borrowed, never freed here. | |
| std::map<std::string,ggml_tensor *,std::less<>> tensors; | |
| Audio8Config config; | |
| OriginalWeightPolicy original_weights=OriginalWeightPolicy::PreserveBf16; | |
| std::uint64_t source_bytes=0, resident_bytes=0, allocated_bytes=0; | |
| std::string file_hash, manifest_hash; | |
| ~Impl() { | |
| if (buffer) ggml_backend_buffer_free(buffer); | |
| if (context) ggml_free(context); | |
| if (metadata) gguf_free(metadata); | |
| } | |
| }; | |
| Model::Model(std::unique_ptr<Impl> impl) : impl_(std::move(impl)) {} | |
| Model::~Model() = default; | |
| std::unique_ptr<Model> Model::load(const std::string & path, ggml_backend_t backend, | |
| const LoadOptions & options) { | |
| static_assert(sizeof(std::size_t)>=8,"Audio8 requires a 64-bit host"); | |
| require(std::endian::native==std::endian::little,"big-endian upload is not implemented"); | |
| require(backend!=nullptr && static_cast<bool>(options.sha256),"backend and SHA256 factory required"); | |
| require(valid_hash(options.expected_gguf_sha256),"external full-file SHA256 pin required"); | |
| require(options.staging_bytes>=4096 && options.staging_bytes<=4*1024*1024,"staging bound"); | |
| require(options.maximum_file_bytes>=kSourcePayloadBytes && options.maximum_file_bytes<=8ULL*1024*1024*1024, | |
| "maximum file size bound"); | |
| require(options.original_weights==OriginalWeightPolicy::PreserveBf16 || | |
| options.original_weights==OriginalWeightPolicy::PromoteBf16ToF32,"unsupported original weight conversion"); | |
| std::unique_ptr<FILE,FileCloser> file(std::fopen(path.c_str(),"rb")); | |
| require(file!=nullptr,"cannot open input"); | |
| const auto bytes=file_size(file.get()); | |
| require(bytes>=24 && bytes<=options.maximum_file_bytes,"input file size bound"); | |
| const auto data_start=preflight(file.get(),bytes); | |
| seek(file.get(),0); | |
| auto result=std::make_unique<Impl>(); | |
| result->backend=backend; result->original_weights=options.original_weights; | |
| result->metadata=gguf_init_from_file_ptr(file.get(),gguf_init_params{true,nullptr}); | |
| require(result->metadata!=nullptr,"GGUF parse failed"); | |
| require(gguf_get_data_offset(result->metadata)==data_start && gguf_get_n_tensors(result->metadata)==kTensorCount, | |
| "GGUF parser disagrees with bounded preflight"); | |
| verify_metadata(result->metadata,options.sha256); | |
| result->manifest_hash=text(result->metadata,"audio8.source.bundle_manifest_sha256"); | |
| const auto expected=schema(); | |
| const auto context_bytes=ggml_tensor_overhead()*(kTensorCount+8); | |
| result->context=ggml_init(ggml_init_params{context_bytes,nullptr,true}); | |
| require(result->context!=nullptr,"weight tensor metadata allocation failed"); | |
| std::size_t index=0; | |
| for (const auto &[name,shape] : expected) { | |
| require(name==gguf_get_tensor_name(result->metadata,index),"missing/extra/reordered tensor: "+name); | |
| require(gguf_get_tensor_type(result->metadata,index)==shape.type,"source tensor dtype: "+name); | |
| const auto * dims=gguf_get_tensor_ne(result->metadata,index); | |
| require(std::equal(shape.ne.begin(),shape.ne.end(),dims),"source tensor shape: "+name); | |
| const auto target_type=shape.type==GGML_TYPE_BF16 && options.original_weights==OriginalWeightPolicy::PromoteBf16ToF32 | |
| ? GGML_TYPE_F32 : shape.type; | |
| auto * tensor=ggml_new_tensor(result->context,target_type,shape.rank,shape.ne.data()); | |
| require(tensor!=nullptr,"weight tensor creation failed"); | |
| ggml_set_name(tensor,name.c_str()); | |
| result->tensors.emplace(name,tensor); | |
| result->source_bytes+=gguf_get_tensor_size(result->metadata,index); | |
| result->resident_bytes+=ggml_nbytes(tensor); | |
| ++index; | |
| } | |
| require(result->source_bytes==kSourcePayloadBytes,"source weight payload count"); | |
| require(result->resident_bytes==kSourcePayloadBytes+(options.original_weights==OriginalWeightPolicy::PromoteBf16ToF32?kOriginalBytes:0), | |
| "resident weight payload count"); | |
| result->buffer=ggml_backend_alloc_ctx_tensors(result->context,backend); | |
| require(result->buffer!=nullptr,"backend weight allocation failed"); | |
| if (options.require_device_buffer) | |
| require(!ggml_backend_buft_is_host(ggml_backend_buffer_get_type(result->buffer)),"requested device weights landed in host buffer"); | |
| ggml_backend_buffer_set_usage(result->buffer,GGML_BACKEND_BUFFER_USAGE_WEIGHTS); | |
| result->allocated_bytes=ggml_backend_buffer_get_size(result->buffer); | |
| // Hash precisely the bytes uploaded, including header, metadata and padding. | |
| // Never retain/mmap the entire packed file next to backend-resident weights. | |
| auto whole=hasher(options.sha256); | |
| std::vector<std::uint8_t> staging(options.staging_bytes); | |
| std::vector<std::uint32_t> widened; | |
| if (options.original_weights==OriginalWeightPolicy::PromoteBf16ToF32) widened.resize(options.staging_bytes/2); | |
| seek(file.get(),0); | |
| for (std::uint64_t remaining=data_start; remaining;) { | |
| const auto count=static_cast<std::size_t>(std::min<std::uint64_t>(remaining,staging.size())); | |
| read_exact(file.get(),staging.data(),count); whole->update({staging.data(),count}); remaining-=count; | |
| } | |
| index=0; | |
| for (const auto &[name,shape] : expected) { | |
| auto * tensor=result->tensors.at(name); | |
| const auto source_size=gguf_get_tensor_size(result->metadata,index); | |
| require(tell(file.get())==data_start+gguf_get_tensor_offset(result->metadata,index),"source stream position"); | |
| const std::size_t unit=shape.type==GGML_TYPE_Q4_0?36:2; | |
| const std::size_t chunk=staging.size()/unit*unit; | |
| std::size_t uploaded=0; | |
| for (std::size_t remaining=source_size; remaining;) { | |
| const auto count=std::min(remaining,chunk); | |
| read_exact(file.get(),staging.data(),count); | |
| const std::span<const std::uint8_t> block(staging.data(),count); | |
| whole->update(block); | |
| if (shape.type==GGML_TYPE_Q4_0) validate_q4(block); else validate_bf16(block); | |
| if (tensor->type==GGML_TYPE_F32 && shape.type==GGML_TYPE_BF16) { | |
| for (std::size_t i=0; i<count/2; ++i) | |
| widened[i]=(std::uint32_t(staging[2*i]) | (std::uint32_t(staging[2*i+1])<<8))<<16; | |
| ggml_backend_tensor_set(tensor,widened.data(),uploaded,count*2); | |
| uploaded+=count*2; | |
| } else { | |
| ggml_backend_tensor_set(tensor,block.data(),uploaded,count); | |
| uploaded+=count; | |
| } | |
| remaining-=count; | |
| } | |
| require(uploaded==ggml_nbytes(tensor),"backend tensor upload coverage"); | |
| const auto padding=static_cast<std::size_t>(aligned(tell(file.get()))-tell(file.get())); | |
| read_exact(file.get(),staging.data(),padding); | |
| require(std::all_of(staging.begin(),staging.begin()+padding,[](std::uint8_t x){return x==0;}),"nonzero tensor padding"); | |
| whole->update({staging.data(),padding}); | |
| ++index; | |
| } | |
| require(tell(file.get())==bytes && std::fgetc(file.get())==EOF && !std::ferror(file.get()),"trailing data/read error"); | |
| result->file_hash=hex(whole->finish()); | |
| require(result->file_hash==options.expected_gguf_sha256,"full-file SHA256 mismatch; model discarded"); | |
| ggml_backend_synchronize(backend); | |
| // staging, widened and FILE are destroyed before the model is returned. | |
| return std::unique_ptr<Model>(new Model(std::move(result))); | |
| } | |
| ggml_tensor * Model::tensor(std::string_view source_name) const { | |
| if (source_name==kAlias) source_name=kEmbedding; | |
| const auto found=impl_->tensors.find(source_name); | |
| require(found!=impl_->tensors.end(),"unknown tensor: "+std::string(source_name)); | |
| return found->second; | |
| } | |
| const Audio8Config & Model::config() const noexcept { return impl_->config; } | |
| ggml_backend_t Model::backend() const noexcept { return impl_->backend; } | |
| OriginalWeightPolicy Model::original_weight_policy() const noexcept { return impl_->original_weights; } | |
| std::uint64_t Model::source_weight_payload_bytes() const noexcept { return impl_->source_bytes; } | |
| std::uint64_t Model::resident_weight_payload_bytes() const noexcept { return impl_->resident_bytes; } | |
| std::uint64_t Model::allocated_weight_buffer_bytes() const noexcept { return impl_->allocated_bytes; } | |
| std::size_t Model::tensor_count() const noexcept { return impl_->tensors.size(); } | |
| const std::string & Model::file_sha256() const noexcept { return impl_->file_hash; } | |
| const std::string & Model::source_manifest_sha256() const noexcept { return impl_->manifest_hash; } | |
| std::string_view Model::metadata_text(std::string_view name) const { return text(impl_->metadata,name); } | |
| std::span<const std::uint8_t> Model::auxiliary(std::string_view name) const { | |
| return array(impl_->metadata,std::string("audio8.aux.")+std::string(name)); | |
| } | |
| std::span<const std::uint8_t> Model::frontend_window_f32le() const { return auxiliary("frontend.window_f32le"); } | |
| std::span<const std::uint8_t> Model::frontend_filters_f32le() const { return auxiliary("frontend.filters_f32le"); } | |
| std::span<const std::uint8_t> Model::tokenizer_bytes() const { return auxiliary("tokenizer.a8tok"); } | |
| } // namespace audio8 | |