feat(chat): add configurable message stats (#1134)
This commit is contained in:
20 files changed
+606
-44
No files matched your search
@@ -199,6 +199,14 @@ internal fun SettingsChatPage(
|
||||
onTypingEffectDelayMsChange = viewModel::onTypingEffectDelayMsChange,
|
||||
chatFontScale = state.chatFontScale,
|
||||
onChatFontScaleChange = viewModel::onChatFontScaleChange,
|
||||
messageStatsEnabled = state.messageStatsEnabled,
|
||||
onMessageStatsEnabledChange = viewModel::onMessageStatsEnabledChange,
|
||||
showUserMessageTokens = state.showUserMessageTokens,
|
||||
onUserMessageTokensChange = viewModel::onUserMessageTokensChange,
|
||||
showAssistantMessageTokens = state.showAssistantMessageTokens,
|
||||
onAssistantMessageTokensChange = viewModel::onAssistantMessageTokensChange,
|
||||
showTokensPerSecond = state.showTokensPerSecond,
|
||||
onTokensPerSecondChange = viewModel::onTokensPerSecondChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ data class SettingsUiState(
|
||||
val typingEffectEnabled: Boolean = false,
|
||||
val typingEffectDelayMs: Int = 30,
|
||||
val chatFontScale: Float = 1.0f,
|
||||
val messageStatsEnabled: Boolean = false,
|
||||
val showUserMessageTokens: Boolean = true,
|
||||
val showAssistantMessageTokens: Boolean = true,
|
||||
val showTokensPerSecond: Boolean = true,
|
||||
val profiles: List<ConnectionProfile> = emptyList(),
|
||||
val selectedProfileId: String? = null,
|
||||
val renameProfileName: String = "",
|
||||
@@ -80,6 +84,10 @@ class SettingsViewModel(
|
||||
val typingEffectEnabled = AuthManager.isTypingEffectEnabled()
|
||||
val typingEffectDelayMs = AuthManager.getTypingEffectDelayMs()
|
||||
val chatFontScale = AuthManager.getChatFontScale()
|
||||
val messageStatsEnabled = AuthManager.isMessageStatsEnabled()
|
||||
val showUserMessageTokens = AuthManager.isUserMessageTokensEnabled()
|
||||
val showAssistantMessageTokens = AuthManager.isAssistantMessageTokensEnabled()
|
||||
val showTokensPerSecond = AuthManager.isTokensPerSecondEnabled()
|
||||
val profiles = AuthManager.getConnectionProfiles()
|
||||
val appLanguage = AuthManager.getAppLanguage()
|
||||
val renameProfileName =
|
||||
@@ -101,6 +109,10 @@ class SettingsViewModel(
|
||||
typingEffectEnabled = typingEffectEnabled,
|
||||
typingEffectDelayMs = typingEffectDelayMs,
|
||||
chatFontScale = chatFontScale,
|
||||
messageStatsEnabled = messageStatsEnabled,
|
||||
showUserMessageTokens = showUserMessageTokens,
|
||||
showAssistantMessageTokens = showAssistantMessageTokens,
|
||||
showTokensPerSecond = showTokensPerSecond,
|
||||
profiles = profiles,
|
||||
selectedProfileId = selectedId,
|
||||
renameProfileName = renameProfileName,
|
||||
@@ -327,6 +339,26 @@ class SettingsViewModel(
|
||||
AuthManager.setChatFontScale(scale)
|
||||
}
|
||||
|
||||
fun onMessageStatsEnabledChange(enabled: Boolean) {
|
||||
_uiState.update { it.copy(messageStatsEnabled = enabled, isSaved = false) }
|
||||
AuthManager.setMessageStatsEnabled(enabled)
|
||||
}
|
||||
|
||||
fun onUserMessageTokensChange(enabled: Boolean) {
|
||||
_uiState.update { it.copy(showUserMessageTokens = enabled, isSaved = false) }
|
||||
AuthManager.setUserMessageTokensEnabled(enabled)
|
||||
}
|
||||
|
||||
fun onAssistantMessageTokensChange(enabled: Boolean) {
|
||||
_uiState.update { it.copy(showAssistantMessageTokens = enabled, isSaved = false) }
|
||||
AuthManager.setAssistantMessageTokensEnabled(enabled)
|
||||
}
|
||||
|
||||
fun onTokensPerSecondChange(enabled: Boolean) {
|
||||
_uiState.update { it.copy(showTokensPerSecond = enabled, isSaved = false) }
|
||||
AuthManager.setTokensPerSecondEnabled(enabled)
|
||||
}
|
||||
|
||||
/** Clear all auth credentials — logs out and returns to landing screen. */
|
||||
fun logout() {
|
||||
AuthManager.setToken(null)
|
||||
@@ -355,6 +387,10 @@ class SettingsViewModel(
|
||||
AuthManager.setThemePreset(state.themePreset)
|
||||
AuthManager.setTypingEffectEnabled(state.typingEffectEnabled)
|
||||
AuthManager.setTypingEffectDelayMs(state.typingEffectDelayMs)
|
||||
AuthManager.setMessageStatsEnabled(state.messageStatsEnabled)
|
||||
AuthManager.setUserMessageTokensEnabled(state.showUserMessageTokens)
|
||||
AuthManager.setAssistantMessageTokensEnabled(state.showAssistantMessageTokens)
|
||||
AuthManager.setTokensPerSecondEnabled(state.showTokensPerSecond)
|
||||
ApiClient.rebuild()
|
||||
|
||||
viewModelScope.launch(ioDispatcher) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.Density
|
||||
@@ -204,6 +205,14 @@ internal fun ChatSection(
|
||||
onTypingEffectDelayMsChange: (Int) -> Unit,
|
||||
chatFontScale: Float = 1.0f,
|
||||
onChatFontScaleChange: (Float) -> Unit = {},
|
||||
messageStatsEnabled: Boolean = false,
|
||||
onMessageStatsEnabledChange: (Boolean) -> Unit = {},
|
||||
showUserMessageTokens: Boolean = true,
|
||||
onUserMessageTokensChange: (Boolean) -> Unit = {},
|
||||
showAssistantMessageTokens: Boolean = true,
|
||||
onAssistantMessageTokensChange: (Boolean) -> Unit = {},
|
||||
showTokensPerSecond: Boolean = true,
|
||||
onTokensPerSecondChange: (Boolean) -> Unit = {},
|
||||
) {
|
||||
val fontScaleOptions = listOf(0.85f, 1.0f, 1.15f, 1.30f, 1.50f)
|
||||
val currentIndex =
|
||||
@@ -369,6 +378,53 @@ internal fun ChatSection(
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.settings_item_message_stats),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.settings_desc_message_stats),
|
||||
modifier = Modifier.weight(1f),
|
||||
style =
|
||||
MaterialTheme.typography.bodySmall.copy(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
Switch(
|
||||
checked = messageStatsEnabled,
|
||||
onCheckedChange = onMessageStatsEnabledChange,
|
||||
modifier = Modifier.testTag("settings_message_stats"),
|
||||
)
|
||||
}
|
||||
MessageStatsOption(
|
||||
label = stringResource(R.string.settings_item_user_message_tokens),
|
||||
checked = showUserMessageTokens,
|
||||
enabled = messageStatsEnabled,
|
||||
onCheckedChange = onUserMessageTokensChange,
|
||||
testTag = "settings_user_message_tokens",
|
||||
)
|
||||
MessageStatsOption(
|
||||
label = stringResource(R.string.settings_item_assistant_message_tokens),
|
||||
checked = showAssistantMessageTokens,
|
||||
enabled = messageStatsEnabled,
|
||||
onCheckedChange = onAssistantMessageTokensChange,
|
||||
testTag = "settings_assistant_message_tokens",
|
||||
)
|
||||
MessageStatsOption(
|
||||
label = stringResource(R.string.settings_item_tokens_per_second),
|
||||
checked = showTokensPerSecond,
|
||||
enabled = messageStatsEnabled,
|
||||
onCheckedChange = onTokensPerSecondChange,
|
||||
testTag = "settings_tps",
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
@@ -440,3 +496,37 @@ internal fun ChatSection(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MessageStatsOption(
|
||||
label: String,
|
||||
checked: Boolean,
|
||||
enabled: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
testTag: String,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(start = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
style =
|
||||
MaterialTheme.typography.bodyMedium.copy(
|
||||
color =
|
||||
if (enabled) {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
|
||||
},
|
||||
),
|
||||
)
|
||||
Switch(
|
||||
checked = checked,
|
||||
enabled = enabled,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = Modifier.testTag(testTag),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user