Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ endif()
set(LLAMA_TEST_NAME test-mtmd-c-api)
llama_build_and_test(test-mtmd-c-api.c)
target_link_libraries(${LLAMA_TEST_NAME} PRIVATE mtmd)
target_include_directories(${LLAMA_TEST_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/tools/mtmd)

# dummy executable - not installed
get_filename_component(TEST_TARGET test-c.c NAME_WE)
Expand Down
31 changes: 31 additions & 0 deletions tools/mtmd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,37 @@ Built upon `clip.cpp` (similar to `llava.cpp`), `libmtmd` offers several advanta
- **Improved UX/DX:** Features a more intuitive API, inspired by the `Processor` class in the Hugging Face `transformers` library.
- **Flexibility:** Designed to support multiple input types (text, audio, images) while respecting the wide variety of chat templates used by different models.

## Logging Configuration

By default, `libmtmd` logs messages directly to stderr. To integrate `libmtmd` logging with your application's logging system, you can use the `mtmd_log_set_llama_callback()` function to redirect all mtmd/clip logs through llama's logging callback.

**Example usage:**

```c
#include "llama.h"
#include "mtmd.h"

// Your custom logging callback
void my_log_callback(ggml_log_level level, const char * text, void * user_data) {
// Your logging logic here
printf("[%d] %s", level, text);
}

int main() {
// Set up llama's logging
llama_log_set(my_log_callback, NULL);

// Redirect mtmd/clip logging to use the same callback
mtmd_log_set_llama_callback(my_log_callback, NULL);

// Now all mtmd and clip logs will use your custom callback
mtmd_context * ctx = mtmd_init_from_file(...);
// ...
}
```

This ensures that all logging from `libmtmd`, including the underlying `clip.cpp` vision encoder, is routed through your application's logging system consistently.

## How to obtain `mmproj`

Multimodal projector (`mmproj`) files are specific to each model architecture.
Expand Down
23 changes: 23 additions & 0 deletions tools/mtmd/clip-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,33 @@ struct clip_logger_state {
ggml_log_level verbosity_thold;
ggml_log_callback log_callback;
void * log_callback_user_data;
// Store the llama callback to forward logs to
ggml_log_callback llama_callback;
void * llama_callback_user_data;
};

extern struct clip_logger_state g_logger_state;

// Callback that redirects to llama's logging system
static void clip_log_callback_llama(enum ggml_log_level level, const char * text, void * user_data) {
(void) user_data;
// Forward to the stored llama callback if available
if (g_logger_state.llama_callback != nullptr) {
g_logger_state.llama_callback(level, text, g_logger_state.llama_callback_user_data);
} else {
// Fallback to default if no llama callback is set
clip_log_callback_default(level, text, user_data);
}
}

// Function to enable llama logging redirection
// This should be called after llama_log_set has been called to set up llama's logging
static inline void clip_log_use_llama_callback(ggml_log_callback llama_cb, void * llama_user_data) {
g_logger_state.llama_callback = llama_cb;
g_logger_state.llama_callback_user_data = llama_user_data;
g_logger_state.log_callback = clip_log_callback_llama;
}

static void clip_log_internal_v(enum ggml_log_level level, const char * format, va_list args) {
if (format == NULL) {
return;
Expand Down
8 changes: 7 additions & 1 deletion tools/mtmd/clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
#include <numeric>
#include <functional>

struct clip_logger_state g_logger_state = {GGML_LOG_LEVEL_CONT, clip_log_callback_default, NULL};
struct clip_logger_state g_logger_state = {
GGML_LOG_LEVEL_CONT, // verbosity_thold
clip_log_callback_default, // log_callback
NULL, // log_callback_user_data
nullptr, // llama_callback
nullptr // llama_callback_user_data
};

enum ffn_op_type {
FFN_GELU,
Expand Down
4 changes: 4 additions & 0 deletions tools/mtmd/mtmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,10 @@ void mtmd_free(mtmd_context * ctx) {
}
}

void mtmd_log_set_llama_callback(ggml_log_callback llama_cb, void * llama_user_data) {
clip_log_use_llama_callback(llama_cb, llama_user_data);
}

struct mtmd_tokenizer {
mtmd_context * ctx;
std::vector<const mtmd_bitmap *> bitmaps;
Expand Down
8 changes: 8 additions & 0 deletions tools/mtmd/mtmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ MTMD_API mtmd_context * mtmd_init_from_file(const char * mmproj_fname,

MTMD_API void mtmd_free(mtmd_context * ctx);

// Set up logging to use llama's logging callback
// This redirects all mtmd/clip logging through llama's logging system
// Call this after llama_log_set to ensure mtmd uses the same logging callback
// Example:
// llama_log_set(my_log_callback, my_user_data);
// mtmd_log_set_llama_callback(my_log_callback, my_user_data);
MTMD_API void mtmd_log_set_llama_callback(ggml_log_callback llama_cb, void * llama_user_data);

// whether we need to set non-causal mask before llama_decode
MTMD_API bool mtmd_decode_use_non_causal(mtmd_context * ctx);

Expand Down
1 change: 1 addition & 0 deletions tools/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ add_executable(${TARGET} ${TARGET_SRCS})
install(TARGETS ${TARGET} RUNTIME)

target_include_directories(${TARGET} PRIVATE ../llava)
target_include_directories(${TARGET} PRIVATE ../mtmd)
target_include_directories(${TARGET} PRIVATE ${CMAKE_SOURCE_DIR})
target_link_libraries(${TARGET} PRIVATE common mtmd ${CMAKE_THREAD_LIBS_INIT})

Expand Down
Loading