Kotlin API LiteRT-LM untuk Android dan JVM (Linux, MacOS, Windows) dengan fitur seperti akselerasi GPU dan NPU, multi-modalitas, dan penggunaan alat.
Pengantar
Berikut adalah contoh aplikasi chat terminal yang dibuat dengan Kotlin API:
import com.google.ai.edge.litertlm.*
suspend fun main() {
Engine.setNativeMinLogSeverity(LogSeverity.ERROR) // Hide log for TUI app
val engineConfig = EngineConfig(modelPath = "/path/to/model.litertlm")
Engine(engineConfig).use { engine ->
engine.initialize()
engine.createConversation().use { conversation ->
while (true) {
print("\n>>> ")
conversation.sendMessageAsync(readln()).collect { print(it) }
}
}
}
}

Untuk mencoba contoh di atas, clone repo dan jalankan dengan example/Main.kt:
bazel run -c opt //kotlin/java/com/google/ai/edge/litertlm/example:main -- <abs_model_path>
Model .litertlm yang tersedia ada di
HuggingFace LiteRT Community. Animasi di atas menggunakan
Gemma3-1B-IT.
Untuk contoh Android, lihat aplikasi Google AI Edge Gallery.
Mulai Menggunakan Gradle
Meskipun LiteRT-LM dikembangkan dengan Bazel, kami menyediakan paket Maven untuk pengguna Gradle/Maven.
1. Menambahkan dependensi Gradle
dependencies {
// For Android
implementation("com.google.ai.edge.litertlm:litertlm-android:latest.release")
// For JVM (Linux, MacOS, Windows)
implementation("com.google.ai.edge.litertlm:litertlm-jvm:latest.release")
}
Anda dapat menemukan versi yang tersedia di Google Maven di litertlm-android dan litertlm-jvm.
latest.release dapat digunakan untuk mendapatkan rilis terbaru.
2. Menginisialisasi Engine
Engine adalah titik entri ke API. Lakukan inisialisasi dengan jalur model
dan konfigurasi. Jangan lupa untuk menutup mesin untuk melepaskan resource.
Catatan: Metode engine.initialize() dapat memerlukan waktu yang cukup lama
(misalnya, hingga 10 detik) untuk memuat model. Sebaiknya panggil
ini di thread atau coroutine latar belakang untuk menghindari pemblokiran thread UI.
import com.google.ai.edge.litertlm.Backend
import com.google.ai.edge.litertlm.Engine
import com.google.ai.edge.litertlm.EngineConfig
val engineConfig = EngineConfig(
modelPath = "/path/to/your/model.litertlm", // Replace with your model path
backend = Backend.GPU(), // Or Backend.NPU(nativeLibraryDir = "...")
// Optional: Pick a writable dir. This can improve 2nd load time.
// cacheDir = "/tmp/" or context.cacheDir.path (for Android)
)
val engine = Engine(engineConfig)
engine.initialize()
// ... Use the engine to create a conversation ...
// Close the engine when done
engine.close()
Di Android, untuk menggunakan backend GPU, aplikasi perlu meminta library native yang bergantung secara eksplisit dengan menambahkan berikut ini ke AndroidManifest.xml di dalam tag <application>:
<application>
<uses-native-library android:name="libvndksupport.so" android:required="false"/>
<uses-native-library android:name="libOpenCL.so" android:required="false"/>
</application>
Untuk menggunakan backend NPU, Anda mungkin perlu menentukan direktori yang berisi
library NPU. Di Android, jika library digabungkan dengan aplikasi Anda, tetapkan
ke context.applicationInfo.nativeLibraryDir. Lihat LiteRT-LM
NPU untuk mengetahui detail selengkapnya
tentang library native NPU.
val engineConfig = EngineConfig(
modelPath = modelPath,
backend = Backend.NPU(nativeLibraryDir = context.applicationInfo.nativeLibraryDir)
)
3. Membuat Percakapan
Setelah mesin diinisialisasi, buat instance Conversation. Anda dapat
memberikan ConversationConfig untuk menyesuaikan perilakunya.
import com.google.ai.edge.litertlm.ConversationConfig
import com.google.ai.edge.litertlm.Message
import com.google.ai.edge.litertlm.SamplerConfig
// Optional: Configure the system instruction, initial messages, sampling
// parameters, etc.
val conversationConfig = ConversationConfig(
systemInstruction = Contents.of("You are a helpful assistant."),
initialMessages = listOf(
Message.user("What is the capital city of the United States?"),
Message.model("Washington, D.C."),
),
samplerConfig = SamplerConfig(topK = 10, topP = 0.95, temperature = 0.8),
)
val conversation = engine.createConversation(conversationConfig)
// Or with default config:
// val conversation = engine.createConversation()
// ... Use the conversation ...
// Close the conversation when done
conversation.close()
Conversation mengimplementasikan AutoCloseable, sehingga Anda dapat menggunakan blok use untuk
pengelolaan resource otomatis untuk percakapan sekali pakai atau singkat:
engine.createConversation(conversationConfig).use { conversation ->
// Interact with the conversation
}
4. Mengirim Pesan
Ada tiga cara untuk mengirim pesan:
sendMessage(contents): Message: Panggilan sinkron yang diblokir hingga model menampilkan respons lengkap. Hal ini lebih sederhana untuk interaksi permintaan/respons dasar.sendMessageAsync(contents, callback): Panggilan asinkron untuk streaming respons. Metode ini lebih baik untuk permintaan yang berjalan lama atau saat Anda ingin menampilkan respons saat sedang dibuat.sendMessageAsync(contents): Flow<Message>: Panggilan asinkron yang menampilkan Kotlin Flow untuk respons streaming. Ini adalah pendekatan yang direkomendasikan bagi pengguna Coroutine.
Contoh Sinkron:
import com.google.ai.edge.litertlm.Content
import com.google.ai.edge.litertlm.Message
print(conversation.sendMessage("What is the capital of France?"))
Contoh Asinkron dengan callback:
Gunakan sendMessageAsync untuk mengirim pesan ke model dan menerima respons
melalui callback.
import com.google.ai.edge.litertlm.Content
import com.google.ai.edge.litertlm.Message
import com.google.ai.edge.litertlm.MessageCallback
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
val callback = object : MessageCallback {
override fun onMessage(message: Message) {
print(message)
}
override fun onDone() {
// Streaming completed
}
override fun onError(throwable: Throwable) {
// Error during streaming
}
}
conversation.sendMessageAsync("What is the capital of France?", callback)
Contoh Asinkron dengan Alur:
Gunakan sendMessageAsync (tanpa argumen callback) untuk mengirim pesan ke model
dan menerima respons melalui Flow Kotlin.
import com.google.ai.edge.litertlm.Content
import com.google.ai.edge.litertlm.Message
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch
// Within a coroutine scope
conversation.sendMessageAsync("What is the capital of France?")
.catch { ... } // Error during streaming
.collect { print(it.toString()) }
5. Multi-Modalitas
Objek Message dapat berisi berbagai jenis Content, termasuk Text,
ImageBytes, ImageFile, dan AudioBytes, AudioFile.
// Initialize the `visionBackend` and/or the `audioBackend`
val engineConfig = EngineConfig(
modelPath = "/path/to/your/model.litertlm", // Replace with your model path
backend = Backend.CPU(), // Or Backend.GPU() or Backend.NPU(...)
visionBackend = Backend.GPU(), // Or Backend.NPU(...)
audioBackend = Backend.CPU(), // Or Backend.NPU(...)
)
// Sends a message with multi-modality.
// See the Content class for other variants.
conversation.sendMessage(Contents.of(
Content.ImageFile("/path/to/image"),
Content.AudioBytes(audioBytes), // ByteArray of the audio
Content.Text("Describe this image and audio."),
))
6. Menentukan dan Menggunakan Alat
Ada dua cara untuk menentukan alat:
- Dengan fungsi Kotlin (direkomendasikan untuk sebagian besar kasus)
- Dengan spesifikasi Open API (kontrol penuh atas spesifikasi dan eksekusi alat)
Menentukan Alat dengan Fungsi Kotlin
Anda dapat menentukan fungsi Kotlin kustom sebagai alat yang dapat dipanggil model untuk melakukan tindakan atau mengambil informasi.
Buat class yang mengimplementasikan ToolSet dan anotasikan metode dengan @Tool dan
parameter dengan @ToolParam.
import com.google.ai.edge.litertlm.Tool
import com.google.ai.edge.litertlm.ToolParam
class SampleToolSet: ToolSet {
@Tool(description = "Get the current weather for a city")
fun getCurrentWeather(
@ToolParam(description = "The city name, e.g., San Francisco") city: String,
@ToolParam(description = "Optional country code, e.g., US") country: String? = null,
@ToolParam(description = "Temperature unit (celsius or fahrenheit). Default: celsius") unit: String = "celsius"
): Map<String, Any> {
// In a real application, you would call a weather API here
return mapOf("temperature" to 25, "unit" to unit, "condition" to "Sunny")
}
@Tool(description = "Get the sum of a list of numbers.")
fun sum(
@ToolParam(description = "The numbers, could be floating point.") numbers: List<Double>,
): Double {
return numbers.sum()
}
}
Di balik layar, API memeriksa anotasi ini dan tanda tangan fungsi
untuk menghasilkan skema gaya OpenAPI. Skema ini menjelaskan fungsi alat, parameter (termasuk jenis dan deskripsinya dari @ToolParam), dan jenis nilai yang ditampilkan ke model bahasa.
Jenis Parameter
Jenis untuk parameter yang dianotasi dengan @ToolParam dapat berupa String, Int,
Boolean, Float, Double, atau List dari jenis ini (misalnya, List<String>).
Gunakan jenis nullable (misalnya, String?) untuk menunjukkan parameter yang dapat bernilai null. Tetapkan nilai default untuk menunjukkan bahwa parameter bersifat opsional, dan sebutkan nilai default dalam deskripsi di @ToolParam.
Jenis Nilai yang Ditampilkan
Jenis nilai yang ditampilkan fungsi alat Anda dapat berupa jenis Kotlin apa pun. Hasil akan dikonversi menjadi elemen JSON sebelum dikirim kembali ke model.
- Jenis
Listdikonversi menjadi array JSON. - Jenis
Mapdikonversi menjadi objek JSON. - Jenis primitif (
String,Number,Boolean) dikonversi ke primitif JSON yang sesuai. - Jenis lainnya dikonversi menjadi string dengan metode
toString().
Untuk data terstruktur, sebaiknya tampilkan Map atau class data yang akan dikonversi menjadi objek JSON.
Menentukan Alat dengan Spesifikasi OpenAPI
Atau, Anda dapat menentukan alat dengan menerapkan class OpenApiTool dan
memberikan deskripsi alat sebagai string JSON yang sesuai dengan spesifikasi
Open API. Metode ini berguna jika Anda sudah memiliki skema OpenAPI untuk
alat Anda atau jika Anda memerlukan kontrol terperinci atas definisi alat.
import com.google.ai.edge.litertlm.OpenApiTool
class SampleOpenApiTool : OpenApiTool {
override fun getToolDescriptionJsonString(): String {
return """
{
"name": "addition",
"description": "Add all numbers.",
"parameters": {
"type": "object",
"properties": {
"numbers": {
"type": "array",
"items": {
"type": "number"
}
},
"description": "The list of numbers to sum."
},
"required": [
"numbers"
]
}
}
""".trimIndent() // Tip: trim to save tokens
}
override fun execute(paramsJsonString: String): String {
// Parse paramsJsonString with your choice of parser/deserializer and
// execute the tool.
// Return the result as a JSON string
return """{"result": 1.4142}"""
}
}
Alat Pendaftaran
Sertakan contoh alat Anda dalam ConversationConfig.
val conversation = engine.createConversation(
ConversationConfig(
tools = listOf(
tool(SampleToolSet()),
tool(SampleOpenApiTool()),
),
// ... other configs
)
)
// Send messages that might trigger the tool
conversation.sendMessageAsync("What's the weather like in London?", callback)
Model akan memutuskan kapan harus memanggil alat berdasarkan percakapan. Hasil dari eksekusi alat akan otomatis dikirim kembali ke model untuk membuat respons akhir.
Panggilan Alat Manual
Secara default, panggilan alat yang dihasilkan oleh model akan otomatis dieksekusi oleh LiteRT-LM dan hasil dari eksekusi alat akan otomatis dikirim kembali ke model untuk menghasilkan respons berikutnya.
Jika Anda ingin menjalankan alat secara manual dan mengirimkan hasilnya kembali ke model, Anda
dapat menyetel automaticToolCalling di ConversationConfig ke false.
val conversation = engine.createConversation(
ConversationConfig(
tools = listOf(
tool(SampleOpenApiTool()),
),
automaticToolCalling = false,
)
)
Jika menonaktifkan panggilan alat otomatis, Anda harus menjalankan alat secara manual
dan mengirimkan hasilnya kembali ke model dalam kode aplikasi. Metode execute
dari OpenApiTool tidak akan dipanggil secara otomatis saat
automaticToolCalling disetel ke false.
// Send a message that triggers a tool call.
val responseMessage = conversation.sendMessage("What's the weather like in London?")
// The model returns a Message with `toolCalls` populated.
if (responseMessage.toolCalls.isNotEmpty()) {
val toolResponses = mutableListOf<Content.ToolResponse>()
// There can be multiple tool calls in a single response.
for (toolCall in responseMessage.toolCalls) {
println("Model wants to call: ${toolCall.name} with arguments: ${toolCall.arguments}")
// Execute the tool manually with your own logic. `executeTool` is just an example here.
val toolResponseJson = executeTool(toolCall.name, toolCall.arguments)
// Collect tool responses.
toolResponses.add(Content.ToolResponse(toolCall.name, toolResponseJson))
}
// Use Message.tool to create the tool response message.
val toolResponseMessage = Message.tool(Contents.of(toolResponses))
// Send the tool response message to the model.
val finalMessage = conversation.sendMessage(toolResponseMessage)
println("Final answer: ${finalMessage.text}") // e.g., "The weather in London is 25c."
}
Contoh
Untuk mencoba penggunaan alat, buat clone repo dan jalankan dengan example/ToolMain.kt:
bazel run -c opt //kotlin/java/com/google/ai/edge/litertlm/example:tool -- <abs_model_path>
Penanganan Error
Metode API dapat memunculkan LiteRtLmJniException untuk error dari lapisan native atau
pengecualian Kotlin standar seperti IllegalStateException untuk masalah siklus proses.
Selalu bungkus panggilan API dalam blok try-catch. Callback onError di
MessageCallback juga akan melaporkan error selama operasi asinkron.