Skip to content

Commit df7fd9c

Browse files
Fix manual offline model detection for Qwen package
1 parent de9c847 commit df7fd9c

3 files changed

Lines changed: 23 additions & 15 deletions

File tree

app/src/main/kotlin/com/google/ai/sample/GenerativeAiViewModelFactory.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ enum class ModelOption(
3030
val supportsScreenshot: Boolean = true,
3131
val isOfflineModel: Boolean = false,
3232
val offlineModelFilename: String? = null,
33+
val offlineAlternateModelFilenames: List<String> = emptyList(),
3334
val offlineRequiredFilenames: List<String> = emptyList(),
3435
val additionalDownloadUrls: List<String> = emptyList(),
3536
val requiresVisionBackend: Boolean = false
@@ -77,6 +78,7 @@ enum class ModelOption(
7778
"6.3 GB",
7879
isOfflineModel = true,
7980
offlineModelFilename = "model_multimodal.litertlm",
81+
offlineAlternateModelFilenames = listOf("model_quantized.litertlm"),
8082
offlineRequiredFilenames = listOf(
8183
"model_multimodal.litertlm",
8284
"sentencepiece.model",

app/src/main/kotlin/com/google/ai/sample/feature/multimodal/ModelDownloadManager.kt

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ object ModelDownloadManager {
6666
)
6767

6868
fun isModelDownloaded(context: Context, model: ModelOption = GenerativeAiViewModelFactory.getCurrentModel()): Boolean {
69-
val required = getRequiredFiles(context, model)
70-
return required.isNotEmpty() && required.all { it.exists() && it.length() > 0 }
69+
val modelFile = getModelFile(context, model)
70+
return modelFile != null && modelFile.exists() && modelFile.length() > 0
7171
}
7272

7373
fun getModelFile(context: Context, model: ModelOption = GenerativeAiViewModelFactory.getCurrentModel()): File? {
74-
val modelFilename = model.offlineModelFilename ?: return null
74+
val modelFilename = resolveInstalledModelFilename(context, model) ?: model.offlineModelFilename ?: return null
7575
val externalFilesDir = context.getExternalFilesDir(null)
7676
return if (externalFilesDir != null) {
7777
File(externalFilesDir, modelFilename)
@@ -83,26 +83,30 @@ object ModelDownloadManager {
8383

8484
private fun getRequiredFiles(context: Context, model: ModelOption): List<File> {
8585
val externalFilesDir = context.getExternalFilesDir(null) ?: return emptyList()
86-
val requiredNames = if (model.offlineRequiredFilenames.isNotEmpty()) {
86+
val activeModelFilename = resolveInstalledModelFilename(context, model)
87+
val requiredNames = if (model == ModelOption.QWEN3_5_4B_OFFLINE && activeModelFilename == "model_quantized.litertlm") {
88+
listOf("model_quantized.litertlm", "sentencepiece.model")
89+
} else if (model.offlineRequiredFilenames.isNotEmpty()) {
8790
model.offlineRequiredFilenames
8891
} else {
8992
listOfNotNull(model.offlineModelFilename)
9093
}
9194
return requiredNames.map { File(externalFilesDir, it) }
9295
}
9396

94-
fun getMissingRequiredFiles(context: Context, model: ModelOption): List<String> {
95-
val externalFilesDir = context.getExternalFilesDir(null) ?: return model.offlineRequiredFilenames
96-
val requiredNames = if (model.offlineRequiredFilenames.isNotEmpty()) {
97-
model.offlineRequiredFilenames
98-
} else {
99-
listOfNotNull(model.offlineModelFilename)
100-
}
101-
return requiredNames.filter { name ->
97+
private fun resolveInstalledModelFilename(context: Context, model: ModelOption): String? {
98+
val externalFilesDir = context.getExternalFilesDir(null) ?: return null
99+
val candidates = listOfNotNull(model.offlineModelFilename) + model.offlineAlternateModelFilenames
100+
return candidates.firstOrNull { name ->
102101
val f = File(externalFilesDir, name)
103-
!f.exists() || f.length() <= 0
102+
f.exists() && f.length() > 0
104103
}
105104
}
105+
106+
fun getMissingRequiredFiles(context: Context, model: ModelOption): List<String> {
107+
val requiredFiles = getRequiredFiles(context, model)
108+
return requiredFiles.filter { !it.exists() || it.length() <= 0 }.map { it.name }
109+
}
106110

107111
private fun createNotificationChannel(context: Context) {
108112
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

app/src/main/kotlin/com/google/ai/sample/feature/multimodal/PhotoReasoningViewModel.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,9 @@ class PhotoReasoningViewModel(
350350
)
351351
if (liteRtEngine == null) {
352352
val preferredBackend = if (backend == InferenceBackend.GPU) Backend.GPU() else Backend.CPU()
353-
val preferredVisionBackend = if (currentModel.requiresVisionBackend) {
353+
val useVisionBackend = currentModel.requiresVisionBackend &&
354+
modelFile.name.contains("multimodal", ignoreCase = true)
355+
val preferredVisionBackend = if (useVisionBackend) {
354356
if (backend == InferenceBackend.GPU) Backend.GPU() else Backend.CPU()
355357
} else {
356358
null
@@ -413,7 +415,7 @@ class PhotoReasoningViewModel(
413415
if (msg.contains("litert_compiled_model", ignoreCase = true) ||
414416
msg.contains("litert_tensor_buffer", ignoreCase = true)
415417
) {
416-
return "Offline model could not be initialized: LiteRT cannot compile this model package on this device. Ensure the full multimodal package files are present and try CPU backend."
418+
return "Offline model could not be initialized: LiteRT cannot compile this model package on this device. Check model files and try CPU backend."
417419
}
418420
return if (msg.contains("memory", ignoreCase = true) || msg.contains("RAM", ignoreCase = true) || msg.contains("OOM", ignoreCase = true) || msg.contains("alloc", ignoreCase = true) || msg.contains("out of", ignoreCase = true)) {
419421
"Not enough RAM to load the model on GPU. Try switching to CPU."

0 commit comments

Comments
 (0)