Get up and running with the LEAP Android SDK in minutes. Install the SDK, load models, and start generating content.
Latest version: v0.9.7
The LEAP SDK is now a Kotlin Multiplatform library supporting Android, iOS, macOS, and JVM. While Android is well-tested and production-ready, other platforms are currently in testing.
An Android project created in Android Studio. You may create an empty project with the wizard. LEAP Android SDK is Kotlin-first. We recommend to work with the SDK only in Kotlin.
The LeapModelDownloader runs as a foreground service and displays notifications during downloads. You need to add the following permissions to your AndroidManifest.xml:
The POST_NOTIFICATIONS permission requires a runtime permission request on Android 13 (API 33) and above. See the code example in step 3 for how to request this permission.
Then perform a project sync in Android Studio to fetch the LeapSDK artifacts.
For Android, we recommend using the leap-model-downloader module which provides background downloads with WorkManager and notification support. Other platforms (iOS, macOS, JVM) should use the LeapDownloader class from the core leap-sdk module.
The SDK uses GGUF manifests for loading models (recommended for all new projects due to superior inference performance and better default generation parameters).
Legacy Executorch bundle support is available in the accordion below for existing projects.
The LEAP Edge SDK supports directly downloading LEAP models in GGUF format. Given the model name and quantization method (which you can find in the LEAP Model Library), the SDK will automatically download the necessary GGUF files along with generation parameters for optimal performance.
For Android, LeapModelDownloader provides the best experience with background downloads, WorkManager integration, and notification support. This function takes some time to finish as loading the model is a heavy I/O operation, but it is safe to call on the main thread. The function should be executed in a coroutine scope.ViewModel
Copy
Ask AI
import android.app.Applicationimport androidx.lifecycle.AndroidViewModelimport androidx.lifecycle.viewModelScopeimport ai.liquid.leap.Conversationimport ai.liquid.leap.ModelRunnerimport ai.liquid.leap.model_downloader.LeapModelDownloaderimport ai.liquid.leap.model_downloader.LeapModelDownloaderNotificationConfigimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.flow.MutableStateFlowimport kotlinx.coroutines.flow.StateFlowimport kotlinx.coroutines.flow.asStateFlowimport kotlinx.coroutines.launchimport kotlinx.coroutines.runBlockingclass ChatViewModel(application: Application) : AndroidViewModel(application) { private val modelDownloader = LeapModelDownloader( application, notificationConfig = LeapModelDownloaderNotificationConfig.build { notificationTitleDownloading = "Downloading AI model..." notificationTitleDownloaded = "Model ready!" notificationContentDownloading = "Please wait while the model downloads" } ) private var modelRunner: ModelRunner? = null private var conversation: Conversation? = null private val _isLoading = MutableStateFlow(false) val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow() private val _downloadProgress = MutableStateFlow(0f) val downloadProgress: StateFlow<Float> = _downloadProgress.asStateFlow() private val _errorMessage = MutableStateFlow<String?>(null) val errorMessage: StateFlow<String?> = _errorMessage.asStateFlow() fun loadModel() { viewModelScope.launch { _isLoading.value = true _errorMessage.value = null try { modelRunner = modelDownloader.loadModel( modelSlug = "LFM2-1.2B", quantizationSlug = "Q5_K_M", progress = { progressData -> _downloadProgress.value = progressData.progress } ) conversation = modelRunner?.createConversation() _isLoading.value = false } catch (e: Exception) { _errorMessage.value = "Failed to load model: ${e.message}" _isLoading.value = false } } } override fun onCleared() { super.onCleared() // Use runBlocking to ensure model is unloaded before ViewModel is destroyed // viewModelScope is cancelled during clearing, so we need a non-cancelled context runBlocking(Dispatchers.IO) { modelRunner?.unload() } }}
Activity
Copy
Ask AI
import android.os.Buildimport android.os.Bundleimport androidx.activity.result.contract.ActivityResultContractsimport androidx.activity.viewModelsimport androidx.appcompat.app.AppCompatActivityimport androidx.core.content.ContextCompatimport androidx.lifecycle.lifecycleScopeimport android.content.pm.PackageManagerimport kotlinx.coroutines.launchclass MainActivity : AppCompatActivity() { private val viewModel: ChatViewModel by viewModels() // Permission launcher for POST_NOTIFICATIONS private val requestPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission() ) { isGranted: Boolean -> if (isGranted) { // Permission granted viewModel.loadModel() } else { // Permission denied - downloads will work but without notifications viewModel.loadModel() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Observe loading state and download progress lifecycleScope.launch { viewModel.isLoading.collect { isLoading -> // Update UI loading indicator } } lifecycleScope.launch { viewModel.downloadProgress.collect { progress -> // Update download progress UI (0.0 to 1.0) } } lifecycleScope.launch { viewModel.errorMessage.collect { error -> error?.let { // Show error message to user } } } // Request notification permission and load model checkPermissionsAndLoadModel() } private fun checkPermissionsAndLoadModel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { when { ContextCompat.checkSelfPermission( this, android.Manifest.permission.POST_NOTIFICATIONS ) == PackageManager.PERMISSION_GRANTED -> { // Permission already granted viewModel.loadModel() } else -> { // Request permission requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS) } } } else { // No permission needed for Android 12 and below viewModel.loadModel() } }}
The SDK will automatically download the required GGUF files to the device’s cache and load the model with the appropriate generation parameters specified in the manifest.
Alternative: Using LeapDownloader (Cross-Platform)
For cross-platform projects or if you don’t need Android-specific features, you can use the LeapDownloader class from the core leap-sdk module:
Copy
Ask AI
import ai.liquid.leap.LeapDownloaderimport ai.liquid.leap.LeapDownloaderConfiglifecycleScope.launch { try { val baseDir = File(context.filesDir, "model_files").absolutePath val modelDownloader = LeapDownloader(config = LeapDownloaderConfig(saveDir = baseDir)) val modelRunner = modelDownloader.loadModel( modelSlug = "LFM2-1.2B", quantizationSlug = "Q5_K_M" ) } catch (e: LeapModelLoadingException) { Log.e(TAG, "Failed to load the model. Error message: ${e.message}") }}
This approach works on all platforms (Android, iOS, macOS, JVM) but doesn’t provide Android-specific features like background downloads or notifications.
Legacy: Executorch Bundles
Browse the Leap Model Library to find and download a model bundle that matches your needs.
Push the bundle file to the device using adb push. Assuming the downloaded model file is located at ~/Downloads/model.bundle, run the following commands:
The LeapClient.loadModel suspend function loads a model bundle file and returns a model runner instance for invoking the model. This function takes some time to finish as loading the model is a heavy I/O operation, but it is safe to call on the main thread. The function should be executed in a coroutine scope.
To generate content, use the conversation object created in the ViewModel. The Conversation.generateResponse function returns a Kotlin asynchronous flow of MessageResponse, which can be processed with Kotlin flow operators.Updated ViewModel with generation