Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
id("com.google.devtools.ksp")
kotlin("plugin.serialization") version "2.1.0"
kotlin("plugin.serialization") version "2.0.0"
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class DownloadModelActivity : ComponentActivity() {
route.modelInfo,
route.modelFiles,
onDownloadModel = { modelUrl ->
viewModel.downloadModelFromUrl(modelUrl)
viewModel.downloadModelFromUrl(modelUrl, onComplete = { openChatActivity() })
},
onBackClicked = { navController.navigateUp() },
)
Expand Down Expand Up @@ -200,7 +200,7 @@ class DownloadModelActivity : ComponentActivity() {
addNewModelStep = AddNewModelStep.ImportModel
},
onDownloadModelClick = { selectedPopularModelIndex ->
viewModel.downloadModelFromIndex(selectedPopularModelIndex)
viewModel.downloadModelFromIndex(selectedPopularModelIndex, onComplete = { openChatActivity() })
},
modifier = Modifier
.fillMaxSize()
Expand Down

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes good look 👍🏻

Original file line number Diff line number Diff line change
Expand Up @@ -51,39 +51,75 @@ import java.net.HttpURLConnection
import java.net.URL
import java.nio.file.Paths

import io.shubham0204.smollmandroid.ui.screens.manage_asr.DownloadService
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update

@Single
class DownloadModelsViewModel(
val context: Context,
val appDB: AppDB,
val hfModelsAPI: HFModelsAPI,
val downloadService: DownloadService,
) : ViewModel() {
private val downloadManager =
context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

fun downloadModelFromIndex(selectedPopularModelIndex: Int) {
// Downloading files in Android with the DownloadManager API
// Ref: https://youtu.be/4t8EevQSYK4?feature=shared
private val _downloadProgress = MutableStateFlow<Int?>(null)
val downloadProgress = _downloadProgress.asStateFlow()

fun downloadModelFromIndex(selectedPopularModelIndex: Int, onComplete: () -> Unit) {
val modelUrl = getPopularModel(selectedPopularModelIndex)!!.url
downloadModelFromUrl(modelUrl)
downloadModelFromUrl(modelUrl, onComplete)
}

fun downloadModelFromUrl(modelUrl: String) {
fun downloadModelFromUrl(modelUrl: String, onComplete: () -> Unit) {
val fileName = modelUrl.substring(modelUrl.lastIndexOf('/') + 1)
val request =
DownloadManager.Request(modelUrl.toUri())
.setTitle(fileName)
.setDescription(
"The GGUF model will be downloaded on your device for use with SmolChat."
)
.setMimeType("application/octet-stream")
.setAllowedNetworkTypes(
DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE
)
.setNotificationVisibility(
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED
)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
downloadManager.enqueue(request)
val destDir = context.filesDir.absolutePath

downloadService.startDownload(
url = modelUrl,
destDir = destDir,
destFileName = fileName,
onStart = {
Toast.makeText(context, "Starting direct download...", Toast.LENGTH_SHORT).show()
setProgressDialogTitle("Downloading Model")
setProgressDialogText("Connecting...")
showProgressDialog()
_downloadProgress.update { 0 }
},
onProgress = { progress ->
_downloadProgress.update { progress }
setProgressDialogText("Downloading: $progress%")
},
onSuccess = {
_downloadProgress.update { null }
setProgressDialogTitle("Registering Model")
setProgressDialogText("Analyzing GGUF metadata...")
CoroutineScope(Dispatchers.IO).launch {
val ggufReader = GGUFReader()
ggufReader.load(File(destDir, fileName).absolutePath)
val contextSize = ggufReader.getContextSize() ?: SmolLM.DefaultInferenceParams.contextSize
val chatTemplate = ggufReader.getChatTemplate() ?: SmolLM.DefaultInferenceParams.chatTemplate
appDB.addModel(
fileName,
"",
Paths.get(destDir, fileName).toString(),
contextSize.toInt(),
chatTemplate,
)
withContext(Dispatchers.Main) {
hideProgressDialog()
Toast.makeText(context, "Model ready!", Toast.LENGTH_SHORT).show()
onComplete()
}
}
},
onFailure = { error ->
_downloadProgress.update { null }
hideProgressDialog()
Toast.makeText(context, "Download failed: $error", Toast.LENGTH_LONG).show()
}
)
}

fun getModels(query: String): Flow<PagingData<HFModelSearch.ModelSearchResult>> =
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ plugins {
alias(libs.plugins.android.library) apply false
id("com.google.devtools.ksp") version "2.0.0-1.0.24" apply false
alias(libs.plugins.jetbrains.kotlin.jvm) apply false
kotlin("plugin.serialization") version "2.1.0" apply false
kotlin("plugin.serialization") version "2.0.0" apply false
}
2 changes: 1 addition & 1 deletion hf-model-hub-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
plugins {
id("java-library")
alias(libs.plugins.jetbrains.kotlin.jvm)
kotlin("plugin.serialization") version "2.1.0"
kotlin("plugin.serialization") version "2.0.0"
}

val ktorVersion = "3.0.2"
Expand Down
5 changes: 5 additions & 0 deletions smollm/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,17 @@ android {
arguments += "-DLLAMA_BUILD_COMMON=ON"
arguments += "-DLLAMA_CURL=OFF"
arguments += "-DGGML_LLAMAFILE=OFF"
arguments += "-DGGML_CPU_KLEIDIAI=ON"
Comment thread
afalefesifiofo marked this conversation as resolved.
arguments += "-DLLAMA_RUNNER_BACKEND=llama.cpp"
// (debugging) uncomment the following line to enable debug builds
// and attach hardware-assisted address sanitizer
// arguments += "-DCMAKE_BUILD_TYPE=Debug"
// arguments += listOf("-DANDROID_SANITIZE=hwaddress")
}
}
ndk {
abiFilters.add("arm64-v8a")

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break support for armeabi-v7a devices (32-bit Android).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know. From my research it would affect only 7-5 % of the active devices, and that's going to shrink year by year. We can add that again for the moment tho

}
}

buildTypes {
Expand Down
167 changes: 61 additions & 106 deletions smollm/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
cmake_minimum_required(VERSION 3.22.1)
project("smollm")

add_subdirectory(../../../../llama.cpp llama.cpp)
# Configure KleidiAI and Arm architecture for unified single-target build
set(GGML_SYSTEM_ARCH "ARM" CACHE STRING "" FORCE)
set(GGML_CPU_KLEIDIAI ON CACHE BOOL "" FORCE)
set(LLAMA_RUNNER_BACKEND "llama.cpp" CACHE STRING "" FORCE)

set(LLAMA_DIR_RELATIVE "../../../../llama.cpp")
get_filename_component(LLAMA_DIR ${LLAMA_DIR_RELATIVE} ABSOLUTE)


add_subdirectory(../../../../llama.cpp llama.cpp)

set(GGML_DIR ${LLAMA_DIR}/ggml)
set(COMMON_DIR ${LLAMA_DIR}/common)
set(VENDOR_DIR ${LLAMA_DIR}/vendor)

# -fvisibility=hidden: hide all symbols by default
# -fvisibility-inlines-hidden: hide all inline symbols by default
# Compile options for llama
target_compile_options(
llama
PUBLIC
-fvisibility=hidden -fvisibility-inlines-hidden
)
# -ffunction-sections: place each function in its own section
# -fdata-sections: place each data member in its own section
target_compile_options(
llama
PUBLIC
-ffunction-sections -fdata-sections
)
target_link_options(
Expand All @@ -30,109 +30,64 @@ target_link_options(
-Wl,--exclude-libs,ALL
)

# compiling for different CPU extensions for Arm64 (aarch64)
# See docs/build_arm_flags.md for more details

function(build_library target_name)
add_library(
${target_name}
SHARED
LLMInference.cpp
smollm.cpp
)
target_include_directories(
${target_name}
PUBLIC
${COMMON_DIR}
${GGML_DIR}/include
${GGML_DIR}/src
${GGML_DIR}/src/ggml-cpu
${LLAMA_DIR}/include
${VENDOR_DIR}
)
# -fvisibility=hidden: hide all symbols by default
# -fvisibility-inlines-hidden: hide all inline symbols by default
target_compile_options(
${target_name}
PUBLIC
-fvisibility=hidden -fvisibility-inlines-hidden
)
# -ffunction-sections: place each function in its own section
# -fdata-sections: place each data member in its own section
target_compile_options(
${target_name}
PUBLIC
-ffunction-sections -fdata-sections
)
target_link_libraries(
${target_name}
android log llama common vulkan
)
# -Wl,--gc-sections: remove unused sections (garbage collection)
# -flto: link-time optimization
# -Wl,--exclude-libs,ALL: exclude all libraries
target_link_options(
${target_name}
PRIVATE
-Wl,--gc-sections -flto
-Wl,--exclude-libs,ALL
)
endfunction()

# Unified single shared library target using Arm KleidiAI dynamic dispatching
add_library(
smollm
SHARED
LLMRunner.cpp
LLMInference.cpp
smollm.cpp
)

function(build_library_arm64 target_name cpu_flags)
build_library(${target_name})
set(GGML_SYSTEM_ARCH "ARM")
set(GGML_CPU_KLEIDIAI ON)
set(GGML_OPENMP ON)
target_compile_definitions(${target_name} PRIVATE
GGML_SYSTEM_ARCH=${GGML_SYSTEM_ARCH}
GGML_CPU_KLEIDIAI=$<BOOL:${GGML_CPU_KLEIDIAI}>
GGML_OPENMP=$<BOOL:${GGML_OPENMP}>
)
target_compile_options(
${target_name}
PUBLIC
-DGGML_USE_CPU -DGGML_USE_CPU_AARCH64 ${cpu_flags} -O3
)
endfunction()
target_include_directories(
smollm
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${COMMON_DIR}
${GGML_DIR}/include
${GGML_DIR}/src
${GGML_DIR}/src/ggml-cpu
${LLAMA_DIR}/include
${VENDOR_DIR}
)

function(build_library_armv7a target_name cpu_flags fpu fpu_abi)
build_library(${target_name})
target_compile_options(
${target_name}
PUBLIC
-DGGML_USE_CPU ${cpu_flags} ${fpu} ${fpu_abi} -O3
)
endfunction()
target_compile_definitions(
smollm
PRIVATE
GGML_SYSTEM_ARCH=${GGML_SYSTEM_ARCH}
GGML_CPU_KLEIDIAI=$<BOOL:${GGML_CPU_KLEIDIAI}>
GGML_USE_CPU
GGML_USE_CPU_AARCH64
)

function(build_library_universal target_name)
build_library(${target_name})
target_compile_options(
${target_name}
PUBLIC
-DGGML_USE_CPU -O3
)
endfunction()
target_compile_options(
smollm
PUBLIC
-march=armv8-a
-O3
-fvisibility=hidden
-fvisibility-inlines-hidden
-ffunction-sections
-fdata-sections
)

build_library_universal("smollm")
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
build_library_armv7a("smollm_v7a" "-march=armv7-a" "-mfpu=neon-vfpv4" "-mfloat-abi=softfp")
endif()
if (${ANDROID_ABI} STREQUAL "arm64-v8a")
build_library_arm64("smollm_v8" "-march=armv8-a")
# Targets for Arm-v8.2a
build_library_arm64("smollm_v8_2_fp16" "-march=armv8.2-a+fp16")
build_library_arm64("smollm_v8_2_fp16_dotprod" "-march=armv8.2-a+fp16+dotprod")
target_link_libraries(
smollm
android
log
llama
common
vulkan
)

# Targets for Arm-v8.4a
Comment thread
afalefesifiofo marked this conversation as resolved.
build_library_arm64("smollm_v8_4_fp16_dotprod" "-march=armv8.4-a+fp16+dotprod")
build_library_arm64("smollm_v8_4_fp16_dotprod_sve" "-march=armv8.4-a+fp16+dotprod+sve")
build_library_arm64("smollm_v8_4_fp16_dotprod_i8mm" "-march=armv8.4-a+fp16+dotprod+i8mm")
build_library_arm64("smollm_v8_4_fp16_dotprod_i8mm_sve" "-march=armv8.4-a+fp16+dotprod+i8mm+sve")
endif()
target_link_options(
smollm
PRIVATE
-Wl,--gc-sections -flto
-Wl,--exclude-libs,ALL
)

# library target for GGUFReader
# Library target for GGUFReader
set(TARGET_NAME_GGUF_READER ggufreader)
add_library(${TARGET_NAME_GGUF_READER} SHARED GGUFReader.cpp)
target_include_directories(
Expand Down
Loading