Skip to content

Commit ac022b3

Browse files
committed
Add mmjproj projector sidecar support
- Recognize mmjproj as a projector marker alongside mmproj, vision-adapter, projector - Score mmjproj candidates in sidecar auto-download selection - Broaden user-facing projector readiness message to mmproj/mmjproj - Add unit tests for mmjproj filtering and case-insensitive detection
1 parent 495c295 commit ac022b3

4 files changed

Lines changed: 12 additions & 5 deletions

File tree

app/src/main/java/com/dark/tool_neuron/repo/ModelStoreRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ModelStoreRepository(private val context: Context) {
3333
private val GGUF_SUFFIX_REGEX = Regex("\\.gguf$", RegexOption.IGNORE_CASE)
3434
private val QUANTIZATION_MATCH_REGEX = Regex("""(?:^|[.-])((?:I?Q)\d+(?:_[A-Z0-9]+)*)""")
3535
private val TRAILING_QUANTIZATION_REGEX = Regex("""([.-])(?:I?Q)\d+(?:_[A-Z0-9]+)*(?:-[A-Z0-9]+)*$""")
36-
private val PROJECTOR_MARKERS = listOf("mmproj", "vision-adapter", "projector")
36+
private val PROJECTOR_MARKERS = listOf("mmproj", "mmjproj", "vision-adapter", "projector")
3737

3838
internal fun isProjectorGgufFile(path: String): Boolean {
3939
return path.endsWith(".gguf", ignoreCase = true) &&

app/src/main/java/com/dark/tool_neuron/service/ModelDownloadService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ class ModelDownloadService : Service() {
413413
if (modelFamilyKey.isNotBlank() && lowerPath.contains(modelFamilyKey)) {
414414
score += 3
415415
}
416-
if (lowerPath.contains("mmproj")) {
416+
if (lowerPath.contains("mmproj") || lowerPath.contains("mmjproj")) {
417417
score += 1
418418
}
419419
file to score

app/src/main/java/com/dark/tool_neuron/viewmodel/ChatViewModel.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ class ChatViewModel @Inject constructor(
455455
fun sendTextMessage(prompt: String) = sendChat(prompt)
456456

457457
/**
458-
* Send a message with images (VLM). Requires a VLM projector to be loaded.
458+
* Send a message with projector-backed media. Requires a compatible projector to be loaded.
459459
* @param prompt User's text prompt
460460
* @param imageData List of raw image file bytes (JPEG/PNG)
461461
*/
@@ -557,7 +557,7 @@ class ChatViewModel @Inject constructor(
557557
val maxTokens = getCurrentModelMaxTokens()
558558
val isNewChat = isNewConversation
559559

560-
// Insert image marker into prompt for VLM
560+
// Insert the default media marker into the prompt for projector-backed generation.
561561
val marker = LlmModelWorker.getVlmDefaultMarker()
562562
val vlmPrompt = if (prompt.contains(marker)) prompt
563563
else marker.repeat(mediaData.size) + "\n" + prompt
@@ -636,7 +636,7 @@ class ChatViewModel @Inject constructor(
636636
return "Please load a text generation model first"
637637
}
638638
if (!LlmModelWorker.isVlmLoaded.value) {
639-
return "Please load a compatible projector (mmproj) first"
639+
return "Please load a compatible projector sidecar (mmproj/mmjproj) first"
640640
}
641641
if (_isGenerating.value) {
642642
return "Please wait for the current generation to finish"

app/src/test/java/com/dark/tool_neuron/repo/ModelStoreRepositoryTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ class ModelStoreRepositoryTest {
2121
@Test
2222
fun supportedGgufFileRejectsProjectionArtifacts() {
2323
assertFalse(ModelStoreRepository.isSupportedGgufFile("models/whisper-mmproj.Q4_K_M.GGUF"))
24+
assertFalse(ModelStoreRepository.isSupportedGgufFile("models/whisper-mmjproj.Q4_K_M.GGUF"))
2425
assertFalse(ModelStoreRepository.isSupportedGgufFile("models/whisper-vision-adapter.gguf"))
2526
assertFalse(ModelStoreRepository.isSupportedGgufFile("models/whisper-projector.gguf"))
2627
}
2728

29+
@Test
30+
fun projectorGgufFileAcceptsMmjprojAliasCaseInsensitively() {
31+
assertTrue(ModelStoreRepository.isProjectorGgufFile("models/whisper-mmjproj.q4_k_m.gguf"))
32+
assertTrue(ModelStoreRepository.isProjectorGgufFile("models/Whisper-MMJPROJ.Q4_K_M.GGUF"))
33+
}
34+
2835
@Test
2936
fun stripGgufSuffixRemovesExtensionCaseInsensitively() {
3037
assertEquals(

0 commit comments

Comments
 (0)