UI: migrate ConversationViewModel onto ModelLoadingViewModel; update & refine ConversationScreen

This commit is contained in:
Han Yin 2025-04-18 15:02:26 -07:00
parent cb508be782
commit 9f1d26ac95
2 changed files with 28 additions and 11 deletions

View File

@ -1,6 +1,7 @@
package com.example.llama.revamp.ui.screens package com.example.llama.revamp.ui.screens
import android.llama.cpp.InferenceEngine.State import android.llama.cpp.InferenceEngine.State
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloat
@ -55,7 +56,9 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.LocalLifecycleOwner
import com.example.llama.revamp.APP_NAME
import com.example.llama.revamp.ui.components.ModelCardWithSystemPrompt import com.example.llama.revamp.ui.components.ModelCardWithSystemPrompt
import com.example.llama.revamp.ui.components.ModelUnloadDialogHandler
import com.example.llama.revamp.viewmodel.ConversationViewModel import com.example.llama.revamp.viewmodel.ConversationViewModel
import com.example.llama.revamp.viewmodel.Message import com.example.llama.revamp.viewmodel.Message
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -65,13 +68,14 @@ import kotlinx.coroutines.launch
*/ */
@Composable @Composable
fun ConversationScreen( fun ConversationScreen(
onBackPressed: () -> Unit, onNavigateBack: () -> Unit,
viewModel: ConversationViewModel viewModel: ConversationViewModel
) { ) {
val engineState by viewModel.engineState.collectAsState() val engineState by viewModel.engineState.collectAsState()
val messages by viewModel.messages.collectAsState() val messages by viewModel.messages.collectAsState()
val systemPrompt by viewModel.systemPrompt.collectAsState() val systemPrompt by viewModel.systemPrompt.collectAsState()
val selectedModel by viewModel.selectedModel.collectAsState() val selectedModel by viewModel.selectedModel.collectAsState()
val unloadDialogState by viewModel.unloadModelState.collectAsState()
val isProcessing = engineState is State.ProcessingUserPrompt val isProcessing = engineState is State.ProcessingUserPrompt
val isGenerating = engineState is State.Generating val isGenerating = engineState is State.Generating
@ -111,6 +115,11 @@ fun ConversationScreen(
} }
} }
// Handle back navigation requests
BackHandler {
viewModel.onBackPressed(onNavigateBack)
}
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -145,10 +154,18 @@ fun ConversationScreen(
isEnabled = !isProcessing && !isGenerating isEnabled = !isProcessing && !isGenerating
) )
} }
// Unload confirmation dialog
ModelUnloadDialogHandler(
unloadModelState = unloadDialogState,
onUnloadConfirmed = { viewModel.onUnloadConfirmed(onNavigateBack) },
onUnloadDismissed = { viewModel.onUnloadDismissed() },
onNavigateBack = onNavigateBack,
)
} }
@Composable @Composable
fun ConversationMessageList( private fun ConversationMessageList(
messages: List<Message>, messages: List<Message>,
listState: LazyListState, listState: LazyListState,
) { ) {
@ -172,7 +189,7 @@ fun ConversationMessageList(
} }
@Composable @Composable
fun MessageBubble(message: Message) { private fun MessageBubble(message: Message) {
when (message) { when (message) {
is Message.User -> UserMessageBubble( is Message.User -> UserMessageBubble(
formattedTime = message.formattedTime, formattedTime = message.formattedTime,
@ -198,7 +215,7 @@ fun MessageBubble(message: Message) {
} }
@Composable @Composable
fun UserMessageBubble(content: String, formattedTime: String) { private fun UserMessageBubble(content: String, formattedTime: String) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -235,7 +252,7 @@ fun UserMessageBubble(content: String, formattedTime: String) {
} }
@Composable @Composable
fun AssistantMessageBubble( private fun AssistantMessageBubble(
formattedTime: String, formattedTime: String,
content: String, content: String,
isThinking: Boolean, isThinking: Boolean,
@ -323,7 +340,7 @@ fun AssistantMessageBubble(
} }
@Composable @Composable
fun PulsatingDots(small: Boolean = false) { private fun PulsatingDots(small: Boolean = false) {
val transition = rememberInfiniteTransition(label = "dots") val transition = rememberInfiniteTransition(label = "dots")
val animations = List(3) { index -> val animations = List(3) { index ->
@ -363,7 +380,7 @@ fun PulsatingDots(small: Boolean = false) {
} }
@Composable @Composable
fun ConversationInputField( private fun ConversationInputField(
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
onSendClick: () -> Unit, onSendClick: () -> Unit,

View File

@ -1,6 +1,5 @@
package com.example.llama.revamp.viewmodel package com.example.llama.revamp.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.example.llama.revamp.engine.ConversationService import com.example.llama.revamp.engine.ConversationService
import com.example.llama.revamp.engine.GenerationUpdate import com.example.llama.revamp.engine.GenerationUpdate
@ -22,9 +21,8 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class ConversationViewModel @Inject constructor( class ConversationViewModel @Inject constructor(
private val conversationService: ConversationService private val conversationService: ConversationService
) : ViewModel() { ) : ModelUnloadingViewModel(conversationService) {
val engineState = conversationService.engineState
val selectedModel = conversationService.currentSelectedModel val selectedModel = conversationService.currentSelectedModel
val systemPrompt = conversationService.systemPrompt val systemPrompt = conversationService.systemPrompt
@ -150,8 +148,10 @@ class ConversationViewModel @Inject constructor(
} }
} }
override suspend fun performCleanup() = clearConversation()
/** /**
* Clear conversation * Stop ongoing generation if any, then clean up all messages in the current conversation
*/ */
fun clearConversation() { fun clearConversation() {
stopGeneration() stopGeneration()