UI: add a info button to explain token metrics
This commit is contained in:
parent
027c68db64
commit
5794d7ae6c
|
|
@ -1,6 +1,8 @@
|
||||||
package com.example.llama.ui.screens
|
package com.example.llama.ui.screens
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.llama.cpp.InferenceEngine.State
|
import android.llama.cpp.InferenceEngine.State
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.BackHandler
|
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
|
||||||
|
|
@ -28,12 +30,14 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||||
import androidx.compose.material.icons.filled.Timer
|
import androidx.compose.material.icons.filled.Timer
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TonalToggleButton
|
import androidx.compose.material3.TonalToggleButton
|
||||||
|
|
@ -52,9 +56,11 @@ import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.net.toUri
|
||||||
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
|
||||||
|
|
@ -279,6 +285,13 @@ private fun ConversationMessageList(
|
||||||
messages: List<Message>,
|
messages: List<Message>,
|
||||||
listState: LazyListState,
|
listState: LazyListState,
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val onInfoClick = {
|
||||||
|
Toast.makeText(context, "Please refer to this guide for more details on the metrics", Toast.LENGTH_SHORT).show()
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, "https://docs.nvidia.com/nim/benchmarking/llm/latest/metrics.html".toUri())
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
|
@ -289,18 +302,6 @@ private fun ConversationMessageList(
|
||||||
items = messages,
|
items = messages,
|
||||||
key = { "${it::class.simpleName}_${it.timestamp}" }
|
key = { "${it::class.simpleName}_${it.timestamp}" }
|
||||||
) { message ->
|
) { message ->
|
||||||
MessageBubble(message = message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add extra space at the bottom for better UX and a scroll target
|
|
||||||
item(key = "bottom-spacer") {
|
|
||||||
Spacer(modifier = Modifier.height(36.dp))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MessageBubble(message: Message) {
|
|
||||||
when (message) {
|
when (message) {
|
||||||
is Message.User -> UserMessageBubble(
|
is Message.User -> UserMessageBubble(
|
||||||
formattedTime = message.formattedTime,
|
formattedTime = message.formattedTime,
|
||||||
|
|
@ -312,7 +313,8 @@ private fun MessageBubble(message: Message) {
|
||||||
content = message.content,
|
content = message.content,
|
||||||
isThinking = message.content.isBlank(),
|
isThinking = message.content.isBlank(),
|
||||||
isGenerating = true,
|
isGenerating = true,
|
||||||
metrics = null
|
metrics = null,
|
||||||
|
onInfoClick = onInfoClick,
|
||||||
)
|
)
|
||||||
|
|
||||||
is Message.Assistant.Stopped -> AssistantMessageBubble(
|
is Message.Assistant.Stopped -> AssistantMessageBubble(
|
||||||
|
|
@ -320,9 +322,17 @@ private fun MessageBubble(message: Message) {
|
||||||
content = message.content,
|
content = message.content,
|
||||||
isThinking = false,
|
isThinking = false,
|
||||||
isGenerating = false,
|
isGenerating = false,
|
||||||
metrics = message.metrics
|
metrics = message.metrics,
|
||||||
|
onInfoClick = onInfoClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add extra space at the bottom for better UX and a scroll target
|
||||||
|
item(key = "bottom-spacer") {
|
||||||
|
Spacer(modifier = Modifier.height(36.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -367,9 +377,11 @@ private fun AssistantMessageBubble(
|
||||||
content: String,
|
content: String,
|
||||||
isThinking: Boolean,
|
isThinking: Boolean,
|
||||||
isGenerating: Boolean,
|
isGenerating: Boolean,
|
||||||
metrics: TokenMetrics? = null
|
metrics: TokenMetrics? = null,
|
||||||
|
onInfoClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.Top,
|
verticalAlignment = Alignment.Top,
|
||||||
) {
|
) {
|
||||||
// Assistant avatar
|
// Assistant avatar
|
||||||
|
|
@ -435,7 +447,7 @@ private fun AssistantMessageBubble(
|
||||||
} else {
|
} else {
|
||||||
// Show metrics when message is complete
|
// Show metrics when message is complete
|
||||||
metrics?.let {
|
metrics?.let {
|
||||||
ExpandableTokenMetricsBubble(metrics)
|
ExpandableTokenMetricsBubble(metrics, onInfoClick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -484,10 +496,13 @@ private fun PulsatingDots(small: Boolean = false) {
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ExpandableTokenMetricsBubble(metrics: TokenMetrics) {
|
private fun ExpandableTokenMetricsBubble(
|
||||||
|
metrics: TokenMetrics,
|
||||||
|
onInfoClick: () -> Unit
|
||||||
|
) {
|
||||||
var showMetrics by remember { mutableStateOf(false) }
|
var showMetrics by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Column {
|
Column(Modifier.fillMaxWidth(0.9f)) {
|
||||||
TonalToggleButton(
|
TonalToggleButton(
|
||||||
checked = showMetrics,
|
checked = showMetrics,
|
||||||
onCheckedChange = { showMetrics = !showMetrics }
|
onCheckedChange = { showMetrics = !showMetrics }
|
||||||
|
|
@ -512,7 +527,6 @@ private fun ExpandableTokenMetricsBubble(metrics: TokenMetrics) {
|
||||||
),
|
),
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(12.dp),
|
modifier = Modifier.padding(12.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(18.dp)
|
horizontalArrangement = Arrangement.spacedBy(18.dp)
|
||||||
|
|
@ -525,6 +539,15 @@ private fun ExpandableTokenMetricsBubble(metrics: TokenMetrics) {
|
||||||
|
|
||||||
val duration = formatMilliSecondstructured(metrics.duration)
|
val duration = formatMilliSecondstructured(metrics.duration)
|
||||||
TokenMetricSection("Duration", duration.value.toString(), duration.unit.toEnglishName())
|
TokenMetricSection("Duration", duration.value.toString(), duration.unit.toEnglishName())
|
||||||
|
|
||||||
|
IconButton(onClick = onInfoClick) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
imageVector = Icons.AutoMirrored.Outlined.HelpOutline,
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
contentDescription = "Information on token metrics"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue