Merge commit '533b18257b7da879a5de39f5d6437041e0e42c39' into concedo_experimental

# Conflicts:
#	CMakeLists.txt
#	examples/gguf-hash/CMakeLists.txt
#	examples/gguf-hash/gguf-hash.cpp
#	scripts/sync_vendor.py
#	tools/mtmd/CMakeLists.txt
#	vendor/hash/rotate-bits/rotate-bits.h
#	vendor/hash/sha1/sha1.c
#	vendor/hash/sha1/sha1.h
#	vendor/hash/sha256/sha256.c
#	vendor/hash/sha256/sha256.h
#	vendor/hash/xxhash/xxhash.c
#	vendor/hash/xxhash/xxhash.h
This commit is contained in:
Concedo
2026-08-20 18:42:18 +08:00
28 changed files with 8000 additions and 40 deletions
+4 -13
View File
@@ -12,6 +12,8 @@
#include "mtmd-helper-common.h"
#include "llama.h"
#include "vendor/hash/hash.h"
#include <algorithm>
#include <cinttypes>
#include <vector>
@@ -357,25 +359,14 @@ static bool decode_audio_from_buf(const unsigned char * buf_in, size_t len, int
} // namespace audio_helpers
// Computes FNV-1a hash of the data
static std::string fnv_hash(const uint8_t * data, size_t len) {
const uint64_t fnv_prime = 0x100000001b3ULL;
uint64_t hash = 0xcbf29ce484222325ULL;
for (size_t i = 0; i < len; ++i) {
hash ^= data[i];
hash *= fnv_prime;
}
return std::to_string(hash);
}
mtmd_helper_bitmap_wrapper mtmd_helper_bitmap_init_from_buf(mtmd_context * ctx, const unsigned char * buf, size_t len, bool placeholder) {
// calculate the hash if needed
std::string id;
mtmd_bitmap * result = nullptr;
if (!placeholder) {
id = fnv_hash(buf, len);
// use sha256 to prevent cache poisoning
id = hash_sha256_hex(buf, len);
}
if (audio_helpers::is_audio_file((const char *)buf, len)) {
+1 -1
View File
@@ -49,7 +49,7 @@ MTMD_API struct mtmd_helper_bitmap_wrapper mtmd_helper_bitmap_init_from_file(mtm
// note:
// - for now, video input is only supported via C++ helper functions
// - audio files will be auto-detected based on magic bytes
// - output bitmap will have FNV hash as the ID
// - output bitmap will have SHA-256 hash (hex string) as the ID
// returns nullptr on failure
// this function is thread-safe
MTMD_API struct mtmd_helper_bitmap_wrapper mtmd_helper_bitmap_init_from_buf(mtmd_context * ctx, const unsigned char * buf, size_t len, bool placeholder);
+14
View File
@@ -999,6 +999,20 @@ mtmd_image_preprocessor_llava_uhd::slice_instructions mtmd_image_preprocessor_mi
// mtmd_image_preprocessor_lfm2
//
mtmd_image_preproc_out mtmd_image_preprocessor_lfm2::preprocess(const clip_image_u8 & img) {
auto const inst = get_slice_instructions(img.get_size());
if (!inst.slices.empty()) {
return mtmd_image_preprocessor_llava_uhd::preprocess(img);
}
// single tile: no thumbnail
// note: not using output.overview here because it will emit <|img_thumbnail|> token, which we don't want in this case
auto sliced = slice_image(img, inst);
mtmd_image_preproc_out output;
output.append(hparams, sliced.overview, true);
return output;
}
mtmd_image_preprocessor_llava_uhd::slice_instructions mtmd_image_preprocessor_lfm2::get_slice_instructions(const clip_image_size & original_size) {
mtmd_image_preprocessor_llava_uhd::slice_instructions inst;
const int align_size = hparams.patch_size * hparams.n_merge;
+1
View File
@@ -145,6 +145,7 @@ struct mtmd_image_preprocessor_lfm2 : mtmd_image_preprocessor_llava_uhd {
static constexpr int tile_size = 512;
using mtmd_image_preprocessor_llava_uhd::mtmd_image_preprocessor_llava_uhd;
mtmd_image_preproc_out preprocess(const clip_image_u8 & img) override;
slice_instructions get_slice_instructions(const clip_image_size & original_size) override;
private:
+32 -14
View File
@@ -2322,23 +2322,12 @@ void mtmd_input_chunk_free(mtmd_input_chunk * chunk) {
}
}
int32_t mtmd_input_chunk_save(const mtmd_input_chunk * chunk, char * out_buf, size_t out_len, size_t * expected_out_len) {
// returns 0 on success
static int32_t mtmd_input_chunk_save_impl(const mtmd_input_chunk * chunk, std::vector<char> & out_buf) {
try {
mtmd_serialization ser(MTMD_SERIALIZATION_VERSION);
chunk->serialize(ser);
if (expected_out_len) {
*expected_out_len = ser.data.size();
}
if (!out_buf) {
// caller is only querying the required size
return 0;
}
if (out_len < ser.data.size()) {
LOG_ERR("%s: out_buf is too small, need %zu bytes, got %zu\n", __func__, ser.data.size(), out_len);
return -1;
}
std::memcpy(out_buf, ser.data.data(), ser.data.size());
out_buf = std::move(ser.data);
return 0;
} catch (const std::exception & e) {
LOG_ERR("%s: %s\n", __func__, e.what());
@@ -2346,6 +2335,35 @@ int32_t mtmd_input_chunk_save(const mtmd_input_chunk * chunk, char * out_buf, si
}
}
mtmd_input_chunk * mtmd_input_chunk_get_placeholder(const mtmd_input_chunk * chunk) {
// this is hacky, but still faster than copy the whole batch data
std::vector<char> buf;
if (mtmd_input_chunk_save_impl(chunk, buf) != 0) {
return nullptr;
}
return mtmd_input_chunk_load(buf.data(), buf.size());
}
int32_t mtmd_input_chunk_save(const mtmd_input_chunk * chunk, char * out_buf, size_t out_len, size_t * expected_out_len) {
std::vector<char> buf;
if (mtmd_input_chunk_save_impl(chunk, buf) != 0) {
return -1;
}
if (expected_out_len) {
*expected_out_len = buf.size();
}
if (!out_buf) {
// caller is only querying the required size
return 0;
}
if (out_len < buf.size()) {
LOG_ERR("%s: out_buf is too small, need %zu bytes, got %zu\n", __func__, buf.size(), out_len);
return -1;
}
std::memcpy(out_buf, buf.data(), buf.size());
return 0;
}
mtmd_input_chunk * mtmd_input_chunk_load(const char * buf, size_t len) {
try {
mtmd_serialization ser(MTMD_SERIALIZATION_VERSION, buf, len);
+3
View File
@@ -233,6 +233,9 @@ MTMD_API llama_pos mtmd_input_chunk_get_n_pos (const mtmd
MTMD_API mtmd_input_chunk * mtmd_input_chunk_copy(const mtmd_input_chunk * chunk);
MTMD_API void mtmd_input_chunk_free(mtmd_input_chunk * chunk);
// similar to mtmd_input_chunk_copy, but returns a placeholder chunk
MTMD_API mtmd_input_chunk * mtmd_input_chunk_get_placeholder(const mtmd_input_chunk * chunk);
// save/load an input chunk to/from a buffer (useful for KV save/load)
// important: only chunk's metadata will be saved, the actual image/audio data will not be saved
// the loaded chunk will always be a placeholder, cannot be used for mtmd_encode() or mtmd_batch_encode()
+17
View File
@@ -507,6 +507,23 @@ void server_tokens::push_back(const mtmd_input_chunk * chunk) {
}
}
void server_tokens::push_back_placeholder(const mtmd_input_chunk * chunk) {
auto type = mtmd_input_chunk_get_type(chunk);
if (type == MTMD_INPUT_CHUNK_TYPE_IMAGE || type == MTMD_INPUT_CHUNK_TYPE_AUDIO) {
GGML_ASSERT(has_mtmd);
mtmd::input_chunk_ptr new_chunk(mtmd_input_chunk_get_placeholder(chunk));
GGML_ASSERT(new_chunk != nullptr && "failed to create placeholder chunk");
const size_t n_tokens = mtmd_input_chunk_get_n_tokens(chunk);
size_t start_idx = tokens.size();
for (size_t i = 0; i < n_tokens; ++i) {
tokens.emplace_back(LLAMA_TOKEN_NULL);
}
map_idx_to_media[start_idx] = std::move(new_chunk);
} else {
push_back(chunk);
}
}
void server_tokens::push_back(server_tokens & tokens) {
size_t start_idx = size();
for (size_t i = 0; i < tokens.size(); i++) {
+4
View File
@@ -195,6 +195,10 @@ public:
// will create a copy of the chunk if it contains non-text data
void push_back(const mtmd_input_chunk * chunk);
// same as push_back, but media chunks are stored as placeholders (no image/audio data)
// only use this if the chunk will never be encoded again (e.g. it is already in the KV cache)
void push_back_placeholder(const mtmd_input_chunk * chunk);
// appends server tokens, updates the media map. copies media chunks.
void push_back(server_tokens & tokens);
+2 -1
View File
@@ -3416,7 +3416,8 @@ private:
// add the mtmd chunk to cache
{
const auto & chunk = input_tokens.find_chunk(cur_token_idx);
slot.prompt.tokens.push_back(chunk.get()); // copy
// the chunk is already in the KV cache at this point, so we don't need to keep its data around
slot.prompt.tokens.push_back_placeholder(chunk.get());
}
has_mtmd = true;
+2
View File
@@ -259,6 +259,8 @@ int main(int argc, char ** argv) {
}
cpp += fmt("static const unsigned char asset_%d_data[] = {", i);
append_bytes_hex(cpp, bytes);
// note: this is a simple hash for cache busting, not a cryptographic hash; fnv is enough here
const auto hash = fnv_hash(bytes.data(), bytes.size());
cpp += fmt("};\nstatic const std::size_t asset_%d_size = %zu;\n",