@@ -71,10 +71,10 @@ class GemmaService : Service(), AgentPlatformCallbacks {
7171
7272 // REPLACE: private lateinit var engine: GemmaEngine
7373 // WITH:
74- private val engineRef = AtomicReference <GemmaEngine ?>(null )
74+ private val engineRef = AtomicReference <LlmBackend ?>(null )
7575 private val engineMutex = kotlinx.coroutines.sync.Mutex ()
7676
77- val engine: GemmaEngine ?
77+ val engine: LlmBackend ?
7878 get() = engineRef.get()
7979
8080 fun isGemmaLoaded (): Boolean = engineRef.get()?.let {
@@ -601,50 +601,71 @@ class GemmaService : Service(), AgentPlatformCallbacks {
601601 val modelNames = listOf (
602602 " gemma-3n-E4B-it-int4.litertlm" ,
603603 " gemma-3n-E2B-it-int4.litertlm" ,
604- " gemma.litertlm"
604+ " gemma.litertlm" ,
605+ " model.gguf" // Add support for catching GGUF models directly
605606 )
607+ // Let it find ANY .gguf file as a fallback if explicit name isn't matched
606608 val searchDirs = listOf (
607609 getExternalFilesDir(null ), // App storage (survives Downloads cleanup)
608610 downloadDir // Downloads folder
609611 )
610- val modelFile = searchDirs.flatMap { dir ->
612+
613+ // Modified: Search for explicit model names first, if not found, scan for any .gguf
614+ var modelFile = searchDirs.flatMap { dir ->
611615 modelNames.map { name -> File (dir, name) }
612616 }.firstOrNull { it.exists() }
617+
618+ if (modelFile == null ) {
619+ // Secondary heuristic: Pick any .gguf file available
620+ modelFile = searchDirs.mapNotNull { dir ->
621+ dir?.listFiles { _, name -> name.endsWith(" .gguf" ) || name.endsWith(" .nexa" ) }?.firstOrNull()
622+ }.firstOrNull()
623+ }
613624
614625 if (modelFile != null ) {
615626 val variant = when {
616627 modelFile.name.contains(" E4B" ) -> " E4B (full)"
617628 modelFile.name.contains(" E2B" ) -> " E2B (lite)"
629+ modelFile.name.endsWith(" .gguf" ) -> " GGUF Open Weights"
618630 else -> " unknown variant"
619631 }
620632 Timber .i(" 📦 Found model: ${modelFile.name} ($variant ) in ${modelFile.parent} " )
621633 }
622634
623635 if (modelFile == null ) {
624636 val searchedPaths = searchDirs.mapNotNull { it?.absolutePath }
625- Timber .e(" No model found! Searched: $searchedPaths for: $modelNames " )
626- updateNotification(" ERROR: No model found. Place .litertlm in app folder or Downloads" )
637+ Timber .e(" No model found! Searched: $searchedPaths " )
638+ updateNotification(" ERROR: No model found. Place .litertlm or .gguf in app folder/ Downloads" )
627639 return
628640 }
629641
630- updateNotification(" Loading Gemma..." )
631- val newEngine = GemmaEngine (applicationContext)
632- // Pass empty system prompt here — KoogAgent.initialize() sets the real one
633- // via softReset(buildSystemPrompt()) immediately after engine init.
634- // Previously BASE_SYSTEM_PROMPT was injected here AND buildSystemPrompt() was
635- // called on first flush, creating two competing identities on cold start.
636- val error = newEngine.initialize(modelFile.absolutePath, " " )
637- if (error != null ) {
638- val hint = when {
639- error.contains(" memory" , ignoreCase = true ) || error.contains(" OOM" , ignoreCase = true ) ->
640- " (Try E2B model on this device)"
641- error.contains(" GPU" , ignoreCase = true ) ->
642- " (GPU init failed — device may not support this model)"
643- else -> " "
642+ updateNotification(" Loading Model Core..." )
643+
644+ val newEngine: LlmBackend = if (modelFile.name.endsWith(" .gguf" ) || modelFile.name.endsWith(" .nexa" )) {
645+ NexaEngine (applicationContext).apply {
646+ val error = initialize(modelFile.absolutePath, " " )
647+ if (error != null ) {
648+ Timber .e(" NexaEngine load failed: $error " )
649+ updateNotification(" GGUF Load Error: ${error.take(80 )} " )
650+ return
651+ }
652+ }
653+ } else {
654+ GemmaEngine (applicationContext).apply {
655+ val error = initialize(modelFile.absolutePath, " " )
656+ if (error != null ) {
657+ val hint = when {
658+ error.contains(" memory" , ignoreCase = true ) || error.contains(" OOM" , ignoreCase = true ) ->
659+ " (Try E2B model on this device)"
660+ error.contains(" GPU" , ignoreCase = true ) ->
661+ " (GPU init failed — device may not support this model)"
662+ else -> " "
663+ }
664+ Timber .e(" GemmaEngine load failed: $error " )
665+ updateNotification(" LiteRT Load Error: ${error.take(80 )}$hint " )
666+ return
667+ }
644668 }
645- Timber .e(" Model load failed: $error " )
646- updateNotification(" Load Error: ${error.take(80 )}$hint " )
647- return
648669 }
649670
650671 // Atomic set (Kimi K2 Fix)
0 commit comments