Skip to content

Commit 7a1517c

Browse files
committed
Bug 2010698 - add concept-llm and a gemini nano lib implementation of it
1 parent 58f365b commit 7a1517c

File tree

11 files changed

+456
-64
lines changed

11 files changed

+456
-64
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ play-services-base = { group = "com.google.android.gms", name = "play-services-b
218218
play-services-fido = { group = "com.google.android.gms", name = "play-services-fido", version.ref = "play-services-fido" }
219219
protobuf-compiler = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }
220220
protobuf-javalite = { group = "com.google.protobuf", name = "protobuf-javalite", version.ref = "protobuf" }
221+
mlkit-prompt = { group = "com.google.mlkit", name = "genai-prompt", version = "1.0.0-alpha1" }
221222

222223
# Gradle plugins
223224
semanticdb-java = { group = "com.sourcegraph", name = "semanticdb-javac", version.ref = "semanticdb-javac" }

mobile/android/android-components/.buildconfig.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,6 +1733,8 @@ projects:
17331733
publish: true
17341734
upstream_dependencies:
17351735
- components:tooling-lint
1736+
- components:support-base
1737+
- components:concept-llm
17361738
components:lib-llm-mlpa:
17371739
description: A off-device implementation of the LLM concept that uses the MLPA
17381740
proxy server

mobile/android/android-components/components/concept/llm/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ android {
2222
}
2323

2424
dependencies {
25+
implementation libs.kotlinx.coroutines
26+
2527
testImplementation libs.androidx.test.junit
2628
}
2729

mobile/android/android-components/components/concept/llm/src/main/java/mozilla/components/concept/llm/Example.kt

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package mozilla.components.concept.llm
6+
7+
/**
8+
* A value type representing a prompt that can be delivered to a LLM.
9+
*/
10+
@JvmInline
11+
value class Prompt(val value: String)
12+
13+
/**
14+
* An abstract definition of a LLM that can receive prompts.
15+
*/
16+
interface Llm {
17+
/**
18+
* A prompt request deliver to the LLM for inference.
19+
*/
20+
suspend fun prompt(prompt: Prompt): Response
21+
22+
/**
23+
* Check the availability of a LLM. This may relate to local device or remote service capabilities,
24+
* for example.
25+
*/
26+
suspend fun checkAvailability(): AvailabilityStatus
27+
28+
/**
29+
* The response from prompting a LLM.
30+
*/
31+
sealed class Response {
32+
/**
33+
* A successful response from a LLM.
34+
*/
35+
data class Success(val reply: String) : Response()
36+
37+
/**
38+
* The LLM is already processing another prompt.
39+
*/
40+
data class InProgress(val reason: String) : Response()
41+
42+
/**
43+
* A failure response from a LLM.
44+
*/
45+
data class Failure(val reason: String) : Response()
46+
}
47+
48+
/**
49+
* Whether a LLM is available for processing prompts.
50+
*/
51+
enum class AvailabilityStatus {
52+
Available,
53+
Preparing,
54+
Unavailable,
55+
}
56+
}

mobile/android/android-components/components/concept/llm/src/test/java/mozilla/components/concept/llm/ExampleTest.kt

Lines changed: 0 additions & 15 deletions
This file was deleted.

mobile/android/android-components/components/lib/llm-gemininano/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ android {
2222
}
2323

2424
dependencies {
25+
api project(":components:concept-llm")
26+
implementation project(':components:support-base')
27+
implementation libs.mlkit.prompt
28+
29+
testImplementation project(':components:support-test')
2530
testImplementation libs.androidx.test.junit
31+
testImplementation libs.kotlinx.coroutines.test
2632
}
2733

2834
apply from: '../../../common-config.gradle'

mobile/android/android-components/components/lib/llm-gemininano/src/main/java/mozilla/components/lib/llm/gemini/nano/Example.kt

Lines changed: 0 additions & 17 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package mozilla.components.lib.llm.gemini.nano
6+
7+
import com.google.mlkit.genai.common.DownloadStatus
8+
import com.google.mlkit.genai.common.FeatureStatus
9+
import com.google.mlkit.genai.common.GenAiException
10+
import com.google.mlkit.genai.prompt.Generation
11+
import com.google.mlkit.genai.prompt.GenerativeModel
12+
import kotlinx.coroutines.flow.first
13+
import kotlinx.coroutines.flow.onEach
14+
import kotlinx.coroutines.yield
15+
import mozilla.components.concept.llm.Llm
16+
import mozilla.components.concept.llm.Prompt
17+
import mozilla.components.support.base.log.logger.Logger
18+
19+
/**
20+
* An instance of a LLM that uses local, on-device capabilities provided by Gemini Nano to handle
21+
* inference.
22+
*/
23+
class GeminiNanoLlm(
24+
private val buildModel: () -> GenerativeModel = { Generation.getClient() },
25+
private val logger: (String) -> Unit = { message -> Logger("mozac/GeminiNanoLlm").info(message) },
26+
) : Llm {
27+
28+
override suspend fun prompt(prompt: Prompt): Llm.Response {
29+
val model = buildModel()
30+
return when (model.checkStatus()) {
31+
FeatureStatus.AVAILABLE -> {
32+
model.getPromptResponse(prompt)
33+
}
34+
FeatureStatus.DOWNLOADING -> {
35+
Llm.Response.InProgress("already downloading")
36+
}
37+
FeatureStatus.DOWNLOADABLE -> {
38+
val result = model.download().onEach {
39+
logger("Download update: $it")
40+
yield()
41+
}.first { status ->
42+
status == DownloadStatus.DownloadCompleted || status is DownloadStatus.DownloadFailed
43+
}
44+
45+
if (result is DownloadStatus.DownloadFailed) {
46+
Llm.Response.Failure("download failed")
47+
} else {
48+
model.getPromptResponse(prompt)
49+
}
50+
}
51+
else -> Llm.Response.Failure("unavailable")
52+
}
53+
}
54+
55+
override suspend fun checkAvailability(): Llm.AvailabilityStatus = when (buildModel().checkStatus()) {
56+
FeatureStatus.AVAILABLE -> Llm.AvailabilityStatus.Available
57+
FeatureStatus.DOWNLOADING,
58+
FeatureStatus.DOWNLOADABLE,
59+
-> Llm.AvailabilityStatus.Preparing
60+
else -> Llm.AvailabilityStatus.Unavailable
61+
}
62+
63+
private suspend fun GenerativeModel.getPromptResponse(prompt: Prompt): Llm.Response = try {
64+
val response = generateContent(prompt.value)
65+
Llm.Response.Success(response.candidates[0].text)
66+
} catch (e: GenAiException) {
67+
Llm.Response.Failure("Gemini Nano inference failed: ${e.message}")
68+
}
69+
}

mobile/android/android-components/components/lib/llm-gemininano/src/test/java/mozilla/components/lib/llm/gemini/nano/ExampleTest.kt

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)