UI: scaffold Models Management screen and view model
This commit is contained in:
parent
5b3b6126e4
commit
310771f6aa
|
|
@ -390,7 +390,7 @@ fun AppContent(
|
||||||
modelsManagementViewModel.toggleImportMenu(false)
|
modelsManagementViewModel.toggleImportMenu(false)
|
||||||
},
|
},
|
||||||
importFromHuggingFace = {
|
importFromHuggingFace = {
|
||||||
modelsManagementViewModel.importFromHuggingFace()
|
modelsManagementViewModel.queryModelsFromHuggingFace()
|
||||||
modelsManagementViewModel.toggleImportMenu(false)
|
modelsManagementViewModel.toggleImportMenu(false)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,8 @@ fun ModelsManagementScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
is ModelManagementState.Idle -> { /* Idle state, nothing to show */ }
|
is ModelManagementState.Idle -> { /* Idle state, nothing to show */ }
|
||||||
|
|
||||||
|
else -> TODO()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,15 @@ import com.example.llama.data.model.ModelInfo
|
||||||
import com.example.llama.data.model.ModelSortOrder
|
import com.example.llama.data.model.ModelSortOrder
|
||||||
import com.example.llama.data.model.filterBy
|
import com.example.llama.data.model.filterBy
|
||||||
import com.example.llama.data.model.sortByOrder
|
import com.example.llama.data.model.sortByOrder
|
||||||
|
import com.example.llama.data.remote.HuggingFaceDownloadInfo
|
||||||
import com.example.llama.data.remote.HuggingFaceModel
|
import com.example.llama.data.remote.HuggingFaceModel
|
||||||
import com.example.llama.data.repository.InsufficientStorageException
|
import com.example.llama.data.repository.InsufficientStorageException
|
||||||
import com.example.llama.data.repository.ModelRepository
|
import com.example.llama.data.repository.ModelRepository
|
||||||
|
import com.example.llama.util.formatFileByteSize
|
||||||
import com.example.llama.util.getFileNameFromUri
|
import com.example.llama.util.getFileNameFromUri
|
||||||
import com.example.llama.util.getFileSizeFromUri
|
import com.example.llama.util.getFileSizeFromUri
|
||||||
import com.example.llama.viewmodel.ModelManagementState.Deletion
|
import com.example.llama.viewmodel.ModelManagementState.Deletion
|
||||||
|
import com.example.llama.viewmodel.ModelManagementState.Download
|
||||||
import com.example.llama.viewmodel.ModelManagementState.Importation
|
import com.example.llama.viewmodel.ModelManagementState.Importation
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
|
@ -130,6 +133,10 @@ class ModelsManagementViewModel @Inject constructor(
|
||||||
_showImportModelMenu.value = show
|
_showImportModelMenu.value = show
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UI state: HuggingFace models query result
|
||||||
|
private val _huggingFaceModels = MutableSharedFlow<List<HuggingFaceModel>>()
|
||||||
|
val huggingFaceModels: SharedFlow<List<HuggingFaceModel>> = _huggingFaceModels
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
combine(
|
combine(
|
||||||
|
|
@ -152,13 +159,8 @@ class ModelsManagementViewModel @Inject constructor(
|
||||||
_managementState.value = ModelManagementState.Idle
|
_managementState.value = ModelManagementState.Idle
|
||||||
}
|
}
|
||||||
|
|
||||||
fun viewModelDetails(model: ModelInfo) {
|
|
||||||
// TODO-han.yin: print logs for now. Would navigate to model details screen or show dialog
|
|
||||||
Log.i(TAG, "${model.name} metadata: \n" + model.metadata.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First show confirmation instead of starting import immediately
|
* First show confirmation instead of starting import local file immediately
|
||||||
*/
|
*/
|
||||||
fun localModelFileSelected(uri: Uri) = viewModelScope.launch {
|
fun localModelFileSelected(uri: Uri) = viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
|
|
@ -216,18 +218,56 @@ class ModelsManagementViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO-han.yin: UI TO BE IMPLEMENTED
|
/**
|
||||||
private val _huggingFaceModels = MutableSharedFlow<List<HuggingFaceModel>>()
|
* Query models on HuggingFace available for download even without signing in
|
||||||
val huggingFaceModels: SharedFlow<List<HuggingFaceModel>> = _huggingFaceModels
|
*/
|
||||||
|
fun queryModelsFromHuggingFace() = viewModelScope.launch {
|
||||||
fun importFromHuggingFace() = viewModelScope.launch {
|
|
||||||
modelRepository.searchHuggingFaceModels().let { models ->
|
modelRepository.searchHuggingFaceModels().let { models ->
|
||||||
_huggingFaceModels.emit(models)
|
_huggingFaceModels.emit(models)
|
||||||
|
|
||||||
Log.d(TAG, "Fetched ${models.size} models from HuggingFace:")
|
Log.d(TAG, "Fetched ${models.size} models from HuggingFace:")
|
||||||
models.forEachIndexed { index, model ->
|
|
||||||
Log.d(TAG, "#$index: $model")
|
// TODO-han.yin: remove these logs
|
||||||
}
|
// models.forEachIndexed { index, model ->
|
||||||
|
// Log.d(TAG, "#$index: $model")
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First show confirmation instead of dispatch download immediately
|
||||||
|
*/
|
||||||
|
fun downloadHuggingFaceModelSelected(model: HuggingFaceModel) {
|
||||||
|
_managementState.value = Download.Confirming(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch download request to [DownloadManager] and update UI
|
||||||
|
*/
|
||||||
|
fun downloadHuggingFaceModel(model: HuggingFaceModel) = viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
require(!model.gated) { "Model is gated!" }
|
||||||
|
require(!model.private) { "Model is private!" }
|
||||||
|
val downloadInfo = model.toDownloadInfo()
|
||||||
|
requireNotNull(downloadInfo) { "Download URL is missing!" }
|
||||||
|
|
||||||
|
val actualSize = modelRepository.getHuggingFaceModelFileSize(downloadInfo)
|
||||||
|
requireNotNull(actualSize) { "Unknown model file size!" }
|
||||||
|
Log.d(TAG, "Model file size: ${formatFileByteSize(actualSize)}")
|
||||||
|
|
||||||
|
modelRepository.importHuggingFaceModel(downloadInfo, actualSize)
|
||||||
|
.onSuccess {
|
||||||
|
_managementState.value = Download.Dispatched(downloadInfo)
|
||||||
|
}
|
||||||
|
.onFailure { throw it }
|
||||||
|
|
||||||
|
} catch (e: InsufficientStorageException) {
|
||||||
|
_managementState.value = Download.Error(
|
||||||
|
message = e.message ?: "Insufficient storage space to download ${model.modelId}",
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_managementState.value = Download.Error(
|
||||||
|
message = e.message ?: "Unknown error downloading ${model.modelId}",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,6 +323,12 @@ sealed class ModelManagementState {
|
||||||
data class Error(val message: String) : Importation()
|
data class Error(val message: String) : Importation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class Download: ModelManagementState() {
|
||||||
|
data class Confirming(val model: HuggingFaceModel) : Download()
|
||||||
|
data class Dispatched(val downloadInfo: HuggingFaceDownloadInfo) : Download()
|
||||||
|
data class Error(val message: String) : Download()
|
||||||
|
}
|
||||||
|
|
||||||
sealed class Deletion : ModelManagementState() {
|
sealed class Deletion : ModelManagementState() {
|
||||||
data class Confirming(val models: Map<String, ModelInfo>): ModelManagementState()
|
data class Confirming(val models: Map<String, ModelInfo>): ModelManagementState()
|
||||||
data class Deleting(val progress: Float = 0f, val models: Map<String, ModelInfo>) : ModelManagementState()
|
data class Deleting(val progress: Float = 0f, val models: Map<String, ModelInfo>) : ModelManagementState()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue