server: save processed mtmd chunks as placeholder (#27278)

This commit is contained in:
Xuan-Son Nguyen
2026-08-17 21:29:50 +02:00
committed by GitHub
parent ed1c3a20f5
commit 533b18257b
5 changed files with 58 additions and 15 deletions
+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;