Skip to content
Merged
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
12 changes: 12 additions & 0 deletions .github/workflows/validate_wrapper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,22 @@ jobs:
fi

wrapper-contract:
name: ${{ matrix.upstream == 'pinned' && 'wrapper-contract' || 'wrapper-contract (v0.4.0)' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
upstream: [pinned, v0.4.0]
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
persist-credentials: false
- name: Select the candidate upstream release
if: matrix.upstream == 'v0.4.0'
run: |
git -C third_party/llama.cpp fetch --depth=1 origin tag v0.4.0
git -C third_party/llama.cpp checkout --detach 5266f24da75dc449bd56cbed7addb9c8e4a6a73e
- name: Configure wrapper contract tests
run: >-
cmake -S . -B build/wrapper-contract -G Ninja
Expand All @@ -96,6 +107,7 @@ jobs:
cmake --build build/wrapper-contract --target
llamadart_speculative_api_test
llamadart_tts_api_test
llamadart_mtmd_compat_test
llamadart_tts_smoke
- name: Run wrapper contract tests
run: ctest --test-dir build/wrapper-contract --output-on-failure
Expand Down
25 changes: 25 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,21 @@ if (APPLE)
endif()

add_subdirectory(third_party/llama.cpp)

# Probe declarations without calling upstream symbols or running target code.
# Recheck after switching upstream revisions in an existing build.
include(CheckCXXSourceCompiles)
include(CMakePushCheckState)
cmake_push_check_state(RESET)
set(CMAKE_REQUIRED_INCLUDES
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/llama.cpp/include"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/llama.cpp/ggml/include"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/llama.cpp/tools/mtmd")
unset(LLAMADART_MTMD_HELPER_HAS_OPTIONS CACHE)
check_cxx_source_compiles("#include <mtmd-helper.h>
int main() { return sizeof(decltype(mtmd_helper_init_opt_default())) == 0; }"
LLAMADART_MTMD_HELPER_HAS_OPTIONS)
cmake_pop_check_state()
if (TARGET ggml-vulkan AND LLAMADART_SPIRV_HEADERS_INCLUDE)
target_include_directories(ggml-vulkan SYSTEM PRIVATE "${LLAMADART_SPIRV_HEADERS_INCLUDE}")
endif()
Expand All @@ -158,6 +173,8 @@ endif()
llamadart_attach_android_cpu_backend_score(ggml-cpu)

add_library(llamadart_lib SHARED "src/llama_dart_wrapper.cpp")
target_compile_definitions(llamadart_lib PRIVATE
LLAMADART_MTMD_HELPER_HAS_OPTIONS=$<BOOL:${LLAMADART_MTMD_HELPER_HAS_OPTIONS}>)

if (MINGW)
target_link_options(llamadart_lib PRIVATE -Wl,--export-all-symbols)
Expand Down Expand Up @@ -203,6 +220,14 @@ if (LLAMADART_BUILD_TESTS)
target_link_libraries(llamadart_tts_api_test PRIVATE llamadart_lib)
target_include_directories(llamadart_tts_api_test PRIVATE src)
add_test(NAME llamadart_tts_api_test COMMAND llamadart_tts_api_test)

add_executable(llamadart_mtmd_compat_test tests/mtmd_compat_test.cpp)
target_compile_features(llamadart_mtmd_compat_test PRIVATE cxx_std_17)
target_include_directories(llamadart_mtmd_compat_test PRIVATE src)
target_compile_definitions(llamadart_mtmd_compat_test PRIVATE
LLAMADART_MTMD_HELPER_HAS_OPTIONS=$<BOOL:${LLAMADART_MTMD_HELPER_HAS_OPTIONS}>)
target_link_libraries(llamadart_mtmd_compat_test PRIVATE mtmd)
add_test(NAME llamadart_mtmd_compat_test COMMAND llamadart_mtmd_compat_test)
endif()

if (LLAMADART_BUILD_TTS_SMOKE)
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ cmake -S . -B build/tts-smoke -G Ninja \
-DLLAMADART_BUILD_TESTS=ON \
-DLLAMADART_BUILD_TTS_SMOKE=ON
cmake --build build/tts-smoke --target \
llamadart_speculative_api_test llamadart_tts_api_test llamadart_tts_smoke
llamadart_speculative_api_test llamadart_tts_api_test \
llamadart_mtmd_compat_test llamadart_tts_smoke
ctest --test-dir build/tts-smoke --output-on-failure
build/tts-smoke/llamadart_tts_smoke \
/path/to/Qwen3-TTS-model.gguf \
Expand Down
15 changes: 15 additions & 0 deletions src/llama_dart_mtmd_compat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include "mtmd-helper.h"

// v0.4.0 added helper options; retain builds against the published v0.3.0
// headers and use upstream's defaults when the options API is available.
static inline mtmd_helper_bitmap_wrapper llama_dart_bitmap_from_buffer(
mtmd_context *ctx, const unsigned char *buffer, size_t size) {
#if LLAMADART_MTMD_HELPER_HAS_OPTIONS
return mtmd_helper_bitmap_init_from_buf(
ctx, buffer, size, false, mtmd_helper_init_opt_default());
#else
return mtmd_helper_bitmap_init_from_buf(ctx, buffer, size, false);
#endif
}
5 changes: 3 additions & 2 deletions src/llama_dart_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "llama_dart_wrapper.h"
#include "llama_dart_mtp_internal.h"
#include "llama_dart_mtmd_compat.h"
#include "llama_dart_speculative_compat.h"

#include "common.h"
Expand Down Expand Up @@ -837,8 +838,8 @@ LLAMADART_API enum llama_dart_tts_status llama_dart_tts_start(
llama_memory_seq_rm(llama_get_memory(tts->llama), tts->sequence_id, 0, -1);
tts->owns_sequence = true;
if (request->speaker_audio_length > 0) {
mtmd_helper_bitmap_wrapper wrapper = mtmd_helper_bitmap_init_from_buf(
tts->mtmd, request->speaker_audio, request->speaker_audio_length, false);
mtmd_helper_bitmap_wrapper wrapper = llama_dart_bitmap_from_buffer(
tts->mtmd, request->speaker_audio, request->speaker_audio_length);
if (wrapper.bitmap == nullptr || !mtmd_bitmap_is_audio(wrapper.bitmap)) {
if (wrapper.bitmap != nullptr) {
mtmd_bitmap_free(wrapper.bitmap);
Expand Down
20 changes: 20 additions & 0 deletions tests/mtmd_compat_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "llama_dart_mtmd_compat.h"

#include <cstdio>

int main() {
// A real one-pixel PPM exercises buffer forwarding and non-placeholder
// decoding without a model. TTS rejects this non-audio result at its call site.
const unsigned char image[] = "P6\n1 1\n255\n\xff\x00\x00";
auto result = llama_dart_bitmap_from_buffer(nullptr, image, sizeof(image) - 1);
if (!result.bitmap || result.video_ctx || mtmd_bitmap_is_audio(result.bitmap) ||
mtmd_bitmap_get_nx(result.bitmap) != 1 ||
mtmd_bitmap_get_ny(result.bitmap) != 1 ||
!mtmd_bitmap_get_data(result.bitmap) ||
mtmd_bitmap_get_data(result.bitmap)[0] != 255) {
std::fprintf(stderr, "media helper did not preserve decoded image bytes\n");
return 1;
}
mtmd_bitmap_free(result.bitmap);
return 0;
}