- Fix a bug where deleting a local model when there are >1 local models shows the wrong download status.

- Don't go back to model selection screen automatically when there is an error during model initialization, so that users have a chance to change model parameters (e.g. accelerator) to retry the initialization.
- Show error dialog properly in prompt lab screen.
This commit is contained in:
Jing Jin 2025-05-19 14:46:00 -07:00
parent e53acf6065
commit 7d567c3499
6 changed files with 92 additions and 43 deletions

View file

@ -30,7 +30,7 @@ android {
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 1 versionCode = 1
versionName = "0.9.3" versionName = "0.9.4"
// Needed for HuggingFace auth workflows. // Needed for HuggingFace auth workflows.
manifestPlaceholders["appAuthRedirectScheme"] = "com.google.aiedge.gallery.oauth" manifestPlaceholders["appAuthRedirectScheme"] = "com.google.aiedge.gallery.oauth"

View file

@ -0,0 +1,67 @@
/*
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.aiedge.gallery.ui.common
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@Composable
fun ErrorDialog(error: String, onDismiss: () -> Unit) {
Dialog(
onDismissRequest = onDismiss
) {
Card(modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp)) {
Column(
modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// Title
Text(
"Error",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 8.dp)
)
// Error
Text(
error,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error,
)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Button(
onClick = onDismiss
) {
Text("Close")
}
}
}
}
}
}

View file

@ -43,14 +43,11 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Timer import androidx.compose.material.icons.outlined.Timer
import androidx.compose.material.icons.rounded.Close import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.ContentCopy import androidx.compose.material.icons.rounded.ContentCopy
import androidx.compose.material.icons.rounded.Refresh import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@ -97,11 +94,11 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.google.aiedge.gallery.R import com.google.aiedge.gallery.R
import com.google.aiedge.gallery.data.Model import com.google.aiedge.gallery.data.Model
import com.google.aiedge.gallery.data.Task import com.google.aiedge.gallery.data.Task
import com.google.aiedge.gallery.data.TaskType import com.google.aiedge.gallery.data.TaskType
import com.google.aiedge.gallery.ui.common.ErrorDialog
import com.google.aiedge.gallery.ui.modelmanager.ModelInitializationStatusType import com.google.aiedge.gallery.ui.modelmanager.ModelInitializationStatusType
import com.google.aiedge.gallery.ui.modelmanager.ModelManagerViewModel import com.google.aiedge.gallery.ui.modelmanager.ModelManagerViewModel
import com.google.aiedge.gallery.ui.preview.PreviewChatModel import com.google.aiedge.gallery.ui.preview.PreviewChatModel
@ -581,44 +578,12 @@ fun ChatPanel(
// Error dialog. // Error dialog.
if (showErrorDialog) { if (showErrorDialog) {
Dialog( ErrorDialog(error = modelInitializationStatus?.error ?: "", onDismiss = {
onDismissRequest = {
showErrorDialog = false showErrorDialog = false
navigateUp() })
},
) {
Card(modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp)) {
Column(
modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// Title
Text(
"Error",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 8.dp)
)
// Error
Text(
modelInitializationStatus?.error ?: "",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error,
)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Button(onClick = {
showErrorDialog = false
navigateUp()
}) {
Text("Close")
}
}
}
}
}
} }
// Benchmark config dialog. // Benchmark config dialog.
if (showBenchmarkConfigsDialog) { if (showBenchmarkConfigsDialog) {
BenchmarkConfigDialog(onDismissed = { showBenchmarkConfigsDialog = false }, BenchmarkConfigDialog(onDismissed = { showBenchmarkConfigsDialog = false },
messageToBenchmark = benchmarkMessage.value, messageToBenchmark = benchmarkMessage.value,
@ -627,7 +592,7 @@ fun ChatPanel(
}) })
} }
// Sheet to show when a message is long-pressed. // Sheet to show when a message is long-pressed.
if (showMessageLongPressedSheet) { if (showMessageLongPressedSheet) {
val message = longPressedMessage.value val message = longPressedMessage.value
if (message != null && message is ChatMessageText) { if (message != null && message is ChatMessageText) {

View file

@ -43,8 +43,10 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.aiedge.gallery.data.ModelDownloadStatusType import com.google.aiedge.gallery.data.ModelDownloadStatusType
import com.google.aiedge.gallery.ui.ViewModelProvider import com.google.aiedge.gallery.ui.ViewModelProvider
import com.google.aiedge.gallery.ui.common.ErrorDialog
import com.google.aiedge.gallery.ui.common.ModelPageAppBar import com.google.aiedge.gallery.ui.common.ModelPageAppBar
import com.google.aiedge.gallery.ui.common.chat.ModelDownloadStatusInfoPanel import com.google.aiedge.gallery.ui.common.chat.ModelDownloadStatusInfoPanel
import com.google.aiedge.gallery.ui.modelmanager.ModelInitializationStatusType
import com.google.aiedge.gallery.ui.modelmanager.ModelManagerViewModel import com.google.aiedge.gallery.ui.modelmanager.ModelManagerViewModel
import com.google.aiedge.gallery.ui.preview.PreviewLlmSingleTurnViewModel import com.google.aiedge.gallery.ui.preview.PreviewLlmSingleTurnViewModel
import com.google.aiedge.gallery.ui.preview.PreviewModelManagerViewModel import com.google.aiedge.gallery.ui.preview.PreviewModelManagerViewModel
@ -78,6 +80,7 @@ fun LlmSingleTurnScreen(
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current val context = LocalContext.current
var navigatingUp by remember { mutableStateOf(false) } var navigatingUp by remember { mutableStateOf(false) }
var showErrorDialog by remember { mutableStateOf(false) }
val handleNavigateUp = { val handleNavigateUp = {
navigatingUp = true navigatingUp = true
@ -110,6 +113,11 @@ fun LlmSingleTurnScreen(
} }
} }
val modelInitializationStatus = modelManagerUiState.modelInitializationStatus[selectedModel.name]
LaunchedEffect(modelInitializationStatus) {
showErrorDialog = modelInitializationStatus?.status == ModelInitializationStatusType.ERROR
}
Scaffold(modifier = modifier, topBar = { Scaffold(modifier = modifier, topBar = {
ModelPageAppBar( ModelPageAppBar(
task = task, task = task,
@ -186,6 +194,12 @@ fun LlmSingleTurnScreen(
} }
}) })
} }
if (showErrorDialog) {
ErrorDialog(error = modelInitializationStatus?.error ?: "", onDismiss = {
showErrorDialog = false
})
}
} }
} }
} }

View file

@ -160,7 +160,7 @@ fun ModelList(
} }
// List of imported models within a task. // List of imported models within a task.
items(items = importedModels) { model -> items(items = importedModels, key = { it.name }) { model ->
Box { Box {
ModelItem( ModelItem(
model = model, model = model,

View file

@ -683,6 +683,9 @@ open class ModelManagerViewModel(
Log.d(TAG, "Allowlist: $modelAllowlist") Log.d(TAG, "Allowlist: $modelAllowlist")
// Convert models in the allowlist. // Convert models in the allowlist.
TASK_LLM_CHAT.models.clear()
TASK_LLM_PROMPT_LAB.models.clear()
TASK_LLM_ASK_IMAGE.models.clear()
for (allowedModel in modelAllowlist.models) { for (allowedModel in modelAllowlist.models) {
if (allowedModel.disabled == true) { if (allowedModel.disabled == true) {
continue continue