Android'de LiteRT-LM'yi kullanmaya başlama

Android ve JVM (Linux, MacOS, Windows) için LiteRT-LM'nin Kotlin API'si. GPU ve NPU hızlandırma, çok formatlılık ve araç kullanımı gibi özellikler içerir.

Giriş

Kotlin API ile oluşturulmuş örnek bir terminal sohbet uygulaması:

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) }
      }
    }
  }
}

Kotlin örnek kodu için demo

Yukarıdaki örneği denemek için depoyu klonlayın ve example/Main.kt ile çalıştırın:

bazel run -c opt //kotlin/java/com/google/ai/edge/litertlm/example:main -- <abs_model_path>

Kullanılabilir .litertlm modelleri HuggingFace LiteRT Community'de yer alır. Yukarıdaki animasyonda Gemma3-1B-IT kullanıldı.

Android örneği için Google AI Edge Gallery uygulamasına göz atın.

Gradle'ı kullanmaya başlama

LiteRT-LM, Bazel ile geliştirilmiş olsa da Gradle/Maven kullanıcıları için Maven paketleri sunuyoruz.

1. Gradle bağımlılığını ekleyin

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")
}

Kullanılabilir sürümleri Google Maven'de litertlm-android ve litertlm-jvm altında bulabilirsiniz.

latest.release, en son sürümü edinmek için kullanılabilir.

2. Engine'i başlatma

Engine, API'nin giriş noktasıdır. Model yolu ve yapılandırmayla başlatın. Kaynakları serbest bırakmak için motoru kapatmayı unutmayın.

Not: engine.initialize() yönteminin modeli yüklemesi önemli bir süre (ör. 10 saniyeye kadar) alabilir. Kullanıcı arayüzü iş parçacığının engellenmesini önlemek için bu işlevin arka plan iş parçacığında veya eş yordamda çağrılması önemle tavsiye edilir.

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()

Android'de GPU arka ucunu kullanmak için uygulamanın, <application> etiketi içindeki AndroidManifest.xml bölümüne aşağıdakileri ekleyerek bağımlı yerel kitaplıkları açıkça istemesi gerekir:

  <application>
    <uses-native-library android:name="libvndksupport.so" android:required="false"/>
    <uses-native-library android:name="libOpenCL.so" android:required="false"/>
  </application>

NPU arka ucunu kullanmak için NPU kitaplıklarını içeren dizini belirtmeniz gerekebilir. Android'de, kitaplıklar uygulamanızla birlikte paketlenmişse bunu context.applicationInfo.nativeLibraryDir olarak ayarlayın. NPU yerel kitaplıkları hakkında daha fazla bilgi için LiteRT-LM NPU başlıklı makaleyi inceleyin.

val engineConfig = EngineConfig(
    modelPath = modelPath,
    backend = Backend.NPU(nativeLibraryDir = context.applicationInfo.nativeLibraryDir)
)

3. Sohbet oluşturma

Motor başlatıldıktan sonra Conversation örneği oluşturun. Davranışını özelleştirmek için ConversationConfig sağlayabilirsiniz.

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, AutoCloseable'yi uyguladığından tek seferlik veya kısa süreli görüşmelerde otomatik kaynak yönetimi için use bloğunu kullanabilirsiniz:

engine.createConversation(conversationConfig).use { conversation ->
    // Interact with the conversation
}

4. Mesaj gönderme

Üç şekilde mesaj gönderebilirsiniz:

  • sendMessage(contents): Message: Model tam bir yanıt döndürene kadar engellenen senkronize çağrı. Bu, temel istek/yanıt etkileşimleri için daha basittir.
  • sendMessageAsync(contents, callback): Yayın yanıtları için eşzamansız çağrı. Bu yöntem, uzun süren istekler veya yanıt oluşturulurken gösterilmesini istediğiniz durumlarda daha uygundur.
  • sendMessageAsync(contents): Flow<Message>: Yanıtları yayınlamak için Kotlin Akışı döndüren eşzamansız çağrı. Bu, Coroutine kullanıcıları için önerilen yaklaşımdır.

Zaman uyumlu örnek:

import com.google.ai.edge.litertlm.Content
import com.google.ai.edge.litertlm.Message

print(conversation.sendMessage("What is the capital of France?"))

Geri çağırma işlevi içeren eşzamansız örnek:

Modele mesaj göndermek ve geri arama yoluyla yanıt almak için sendMessageAsync özelliğini kullanın.

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)

Akış içeren eşzamansız örnek:

Modele mesaj göndermek ve Kotlin Flow aracılığıyla yanıt almak için sendMessageAsync işlevini (geri çağırma bağımsız değişkeni olmadan) kullanın.

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. Çok Formatlılık

Message nesneleri, Text, ImageBytes, ImageFile, AudioBytes ve AudioFile dahil olmak üzere farklı türlerde Content içerebilir.

// 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. Araçları Tanımlama ve Kullanma

Araçları tanımlamanın iki yolu vardır:

  1. Kotlin işlevleriyle (çoğu durumda önerilir)
  2. Open API spesifikasyonuyla (araç spesifikasyonu ve yürütme üzerinde tam kontrol)

Kotlin işlevleriyle araç tanımlama

Modelin işlem yapmak veya bilgi getirmek için çağırabileceği araçlar olarak özel Kotlin işlevleri tanımlayabilirsiniz.

ToolSet uygulayan bir sınıf oluşturun ve yöntemleri @Tool ile, parametreleri ise @ToolParam ile ek açıklama olarak belirtin.

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()
    }
}

API, arka planda OpenAPI tarzı bir şema oluşturmak için bu ek açıklamaları ve işlev imzasını inceler. Bu şema, aracın işlevini, parametrelerini (@ToolParam'daki türleri ve açıklamaları dahil) ve dil modeline döndürülen türünü açıklar.

Parametre Türleri

@ToolParam ile açıklama eklenen parametrelerin türleri String, Int, Boolean, Float, Double veya bu türlerin List olabilir (ör. List<String>). Boş değer atanabilir türler kullanın (ör. String?) ile gösterilir. Parametrenin isteğe bağlı olduğunu belirtmek için varsayılan bir değer ayarlayın ve açıklamada varsayılan değeri @ToolParam içinde belirtin.

Dönüş Türü

Araç işlevinizin dönüş türü herhangi bir Kotlin türü olabilir. Sonuç, modele geri gönderilmeden önce JSON öğesine dönüştürülür.

  • List türleri JSON dizilerine dönüştürülür.
  • Map türleri JSON nesnelerine dönüştürülür.
  • Temel türler (String, Number, Boolean) ilgili JSON temel türüne dönüştürülür.
  • Diğer türler toString() yöntemiyle dizelere dönüştürülür.

Yapılandırılmış veriler için Map veya JSON nesnesine dönüştürülecek bir veri sınıfı döndürmeniz önerilir.

OpenAPI Specification ile Araçları Tanımlama

Alternatif olarak, OpenApiTool sınıfını uygulayarak ve aracın açıklamasını Open API spesifikasyonuna uygun bir JSON dizesi olarak sağlayarak bir araç tanımlayabilirsiniz. Bu yöntem, aracınız için zaten bir OpenAPI şemanız varsa veya aracın tanımı üzerinde ayrıntılı kontrol sağlamanız gerekiyorsa yararlıdır.

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}"""
    }
}

Kayıt Araçları

Araçlarınızın örneklerini ConversationConfig'e ekleyin.

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, görüşmeye göre aracı ne zaman çağıracağına karar verir. Araç yürütme sonuçları, nihai yanıtı oluşturmak için otomatik olarak modele geri gönderilir.

Manuel Araç Çağırma

Varsayılan olarak, model tarafından oluşturulan araç çağrıları LiteRT-LM tarafından otomatik olarak yürütülür ve araç yürütme sonuçları, bir sonraki yanıtı oluşturmak için modele otomatik olarak geri gönderilir.

Araçları manuel olarak çalıştırmak ve sonuçları modele geri göndermek istiyorsanız ConversationConfig içindeki automaticToolCalling değerini false olarak ayarlayabilirsiniz.

val conversation = engine.createConversation(
    ConversationConfig(
        tools = listOf(
            tool(SampleOpenApiTool()),
        ),
        automaticToolCalling = false,
    )
)

Otomatik araç çağırma özelliğini devre dışı bırakırsanız araçları manuel olarak yürütmeniz ve sonuçları uygulama kodunuzdaki modele geri göndermeniz gerekir. execute OpenApiTool yöntemi, automaticToolCalling false olarak ayarlandığında otomatik olarak çağrılmaz.

// 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."
}

Örnek

Araç kullanımını denemek için depoyu klonlayın ve example/ToolMain.kt ile çalıştırın:

bazel run -c opt //kotlin/java/com/google/ai/edge/litertlm/example:tool -- <abs_model_path>

Hata İşleme

API yöntemleri, yerel katmandaki hatalar için LiteRtLmJniException veya yaşam döngüsü sorunları için IllegalStateException gibi standart Kotlin istisnaları oluşturabilir. API çağrılarını her zaman try-catch bloklarına sarmalayın. onError içindeki MessageCallback geri çağırma işlevi, eşzamansız işlemler sırasında da hataları bildirir.