fix(build): isolate ML Kit GenAI to the Google flavor (fix F-Droid rb-check) (#5824)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
James Rich
2026-06-16 20:58:46 -05:00
committed by GitHub
parent c3488d4f5d
commit f2769a3b29
7 changed files with 30 additions and 8 deletions

View File

@@ -291,6 +291,7 @@ dependencies {
googleImplementation(libs.firebase.ai)
googleImplementation(libs.firebase.ai.ondevice)
googleImplementation(libs.mlkit.translate)
googleImplementation(libs.mlkit.genai.prompt)
googleImplementation(libs.androidx.appfunctions)
googleImplementation(libs.androidx.appfunctions.service)

View File

@@ -18,6 +18,8 @@ package org.meshtastic.app.di
import org.koin.core.annotation.Module
import org.koin.core.annotation.Single
import org.meshtastic.feature.discovery.ai.AlgorithmicSummaryProvider
import org.meshtastic.feature.discovery.ai.DiscoverySummaryAiProvider
import org.meshtastic.feature.docs.ai.AIDocAssistant
import org.meshtastic.feature.docs.ai.KeywordFallbackAssistant
import org.meshtastic.feature.docs.translation.DocTranslationService
@@ -28,5 +30,7 @@ import org.meshtastic.feature.docs.translation.NoOpDocTranslator
class FdroidAiModule {
@Single fun aiDocAssistant(fallback: KeywordFallbackAssistant): AIDocAssistant = fallback
@Single fun discoverySummaryAiProvider(fallback: AlgorithmicSummaryProvider): DiscoverySummaryAiProvider = fallback
@Single fun docTranslationService(): DocTranslationService = NoOpDocTranslator()
}

View File

@@ -22,8 +22,11 @@ import okio.Path.Companion.toOkioPath
import org.koin.core.annotation.Module
import org.koin.core.annotation.Single
import org.meshtastic.app.ai.GeminiNanoDocAssistant
import org.meshtastic.app.discovery.GeminiNanoSummaryProvider
import org.meshtastic.app.translation.MlKitDocTranslator
import org.meshtastic.core.repository.NodeRepository
import org.meshtastic.feature.discovery.DiscoverySummaryGenerator
import org.meshtastic.feature.discovery.ai.DiscoverySummaryAiProvider
import org.meshtastic.feature.docs.ai.AIDocAssistant
import org.meshtastic.feature.docs.data.DocBundleLoader
import org.meshtastic.feature.docs.data.KeywordSearchEngine
@@ -44,6 +47,10 @@ class GoogleAiModule {
nodeRepository: NodeRepository,
): AIDocAssistant = GeminiNanoDocAssistant(searchEngine, bundleLoader, nodeRepository)
@Single
fun discoverySummaryAiProvider(generator: DiscoverySummaryGenerator): DiscoverySummaryAiProvider =
GeminiNanoSummaryProvider(generator)
@Single
fun docTranslationCache(context: Context): DocTranslationCache =
DocTranslationCache(cacheDir = context.cacheDir.toOkioPath(), fileSystem = FileSystem.SYSTEM)

View File

@@ -14,26 +14,29 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.meshtastic.feature.discovery.ai
package org.meshtastic.app.discovery
import co.touchlab.kermit.Logger
import com.google.mlkit.genai.prompt.Generation
import com.google.mlkit.genai.prompt.GenerativeModel
import com.google.mlkit.genai.prompt.TextPart
import com.google.mlkit.genai.prompt.generateContentRequest
import org.koin.core.annotation.Single
import org.meshtastic.core.database.entity.DiscoveryPresetResultEntity
import org.meshtastic.core.database.entity.DiscoverySessionEntity
import org.meshtastic.feature.discovery.DiscoverySummaryGenerator
import org.meshtastic.feature.discovery.ai.DiscoverySummaryAiProvider
/**
* Android provider that uses Gemini Nano via ML Kit GenAI Prompt API for on-device AI summaries.
* Google-flavor provider that uses Gemini Nano via the ML Kit GenAI Prompt API for on-device AI summaries.
*
* Lives in the Google flavor source set (not the shared `:feature:discovery` module) so the proprietary ML Kit GenAI
* dependency never reaches the F-Droid build. The F-Droid flavor binds
* [org.meshtastic.feature.discovery.ai. AlgorithmicSummaryProvider] instead.
*
* Falls back to [DiscoverySummaryGenerator] when:
* - The on-device model is unavailable (unsupported hardware or not downloaded)
* - Generation fails for any reason
*/
@Single(binds = [DiscoverySummaryAiProvider::class])
class GeminiNanoSummaryProvider(private val generator: DiscoverySummaryGenerator) : DiscoverySummaryAiProvider {
private val log = Logger.withTag("GeminiNanoSummary")

View File

@@ -87,6 +87,8 @@ import org.meshtastic.desktop.stub.NoopMeshLocationManager
import org.meshtastic.desktop.stub.NoopMeshWorkerManager
import org.meshtastic.desktop.stub.NoopPhoneLocationProvider
import org.meshtastic.desktop.stub.NoopPlatformAnalytics
import org.meshtastic.feature.discovery.ai.AlgorithmicSummaryProvider
import org.meshtastic.feature.discovery.ai.DiscoverySummaryAiProvider
import org.meshtastic.feature.docs.ai.AIDocAssistant
import org.meshtastic.feature.docs.ai.KeywordFallbackAssistant
import org.meshtastic.feature.docs.translation.DocTranslationService
@@ -227,6 +229,7 @@ private fun desktopPlatformStubsModule() = module {
// AI assistant: keyword-only fallback on desktop (no on-device model)
single<AIDocAssistant> { get<KeywordFallbackAssistant>() }
single<DiscoverySummaryAiProvider> { get<AlgorithmicSummaryProvider>() }
single<DocTranslationService> { NoOpDocTranslator() }
// Desktop uses the real ApiService implementation (no flavor stub needed)

View File

@@ -51,7 +51,5 @@ kotlin {
}
commonTest.dependencies { implementation(projects.core.testing) }
androidMain.dependencies { implementation(libs.mlkit.genai.prompt) }
}
}

View File

@@ -21,8 +21,14 @@ import org.meshtastic.core.database.entity.DiscoveryPresetResultEntity
import org.meshtastic.core.database.entity.DiscoverySessionEntity
import org.meshtastic.feature.discovery.DiscoverySummaryGenerator
/** JVM/Desktop fallback that delegates to the algorithmic [DiscoverySummaryGenerator]. */
@Single(binds = [DiscoverySummaryAiProvider::class])
/**
* Algorithmic [DiscoverySummaryAiProvider] that delegates to the deterministic [DiscoverySummaryGenerator].
*
* Used wherever no on-device AI model is available: Desktop, iOS, and the Android F-Droid flavor. Registered with
* `binds = []` so it is injectable by concrete type but is not auto-bound to [DiscoverySummaryAiProvider]; each
* platform binds the interface explicitly (the Google flavor binds the Gemini Nano provider instead).
*/
@Single(binds = [])
class AlgorithmicSummaryProvider(private val generator: DiscoverySummaryGenerator) : DiscoverySummaryAiProvider {
override val isAvailable: Boolean = true