feat(bots): increase group chat limits to 50/20 with warning banner and confirmation dialog (#1049)

This commit is contained in:
M57 authored and GitHub committed 2026-09-03 15:41:22 +03:00
1 parent 78e104f432
commit fb80be04b4
7 files changed
+180 -13

No files matched your search

@@ -42,8 +42,10 @@ import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material.icons.filled.Terminal
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -549,6 +551,11 @@ private fun GroupChatSettingsDialog(
var maxMessages by remember(currentMaxMessages) { mutableStateOf(currentMaxMessages.toFloat()) }
var maxPasses by remember(currentMaxPasses) { mutableStateOf(currentMaxPasses.toFloat()) }
var systemPrompt by remember(currentSystemPrompt) { mutableStateOf(currentSystemPrompt.orEmpty()) }
var showHighLimitsConfirmation by remember { mutableStateOf(false) }
val isHighLimit =
maxMessages.roundToInt() > WARN_MAX_BOT_MESSAGES ||
maxPasses.roundToInt() > WARN_MAX_CONTINUATION_PASSES
AlertDialog(
onDismissRequest = onDismiss,
@@ -579,7 +586,12 @@ private fun GroupChatSettingsDialog(
Text(
text = "${maxMessages.roundToInt()}",
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
color =
if (maxMessages.roundToInt() > WARN_MAX_BOT_MESSAGES) {
MaterialTheme.colorScheme.error
} else {
MaterialTheme.colorScheme.primary
},
fontWeight = FontWeight.Bold,
)
}
@@ -595,8 +607,8 @@ private fun GroupChatSettingsDialog(
Slider(
value = maxMessages,
onValueChange = { maxMessages = it },
valueRange = 1f..20f,
steps = 18,
valueRange = 1f..MAX_ALLOWED_BOT_MESSAGES.toFloat(),
steps = MAX_ALLOWED_BOT_MESSAGES - 2,
modifier = Modifier.testTag("max_messages_slider"),
)
}
@@ -616,7 +628,12 @@ private fun GroupChatSettingsDialog(
Text(
text = "${maxPasses.roundToInt()}",
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
color =
if (maxPasses.roundToInt() > WARN_MAX_CONTINUATION_PASSES) {
MaterialTheme.colorScheme.error
} else {
MaterialTheme.colorScheme.primary
},
fontWeight = FontWeight.Bold,
)
}
@@ -632,12 +649,52 @@ private fun GroupChatSettingsDialog(
Slider(
value = maxPasses,
onValueChange = { maxPasses = it },
valueRange = 0f..6f,
steps = 5,
valueRange = 0f..MAX_ALLOWED_CONTINUATION_PASSES.toFloat(),
steps = MAX_ALLOWED_CONTINUATION_PASSES - 1,
modifier = Modifier.testTag("max_handoffs_slider"),
)
}
// Live In-Dialog Warning Banner (Option C)
AnimatedVisibility(
visible = isHighLimit,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
Surface(
color = MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.35f),
contentColor = MaterialTheme.colorScheme.onErrorContainer,
shape = RoundedCornerShape(12.dp),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.error.copy(alpha = 0.5f)),
modifier =
Modifier
.fillMaxWidth()
.testTag("high_limits_warning_banner"),
) {
Row(
modifier = Modifier.padding(12.dp),
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
Icon(
imageVector = Icons.Filled.Warning,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
modifier = Modifier.size(20.dp),
)
Text(
text =
stringResource(
R.string.group_chat_settings_high_limits_warning_desc,
maxMessages.roundToInt(),
maxPasses.roundToInt(),
),
style = MaterialTheme.typography.bodySmall,
)
}
}
}
// Room Instructions / System Prompt
Column {
Text(
@@ -674,11 +731,15 @@ private fun GroupChatSettingsDialog(
confirmButton = {
Button(
onClick = {
onSave(
maxMessages.roundToInt(),
maxPasses.roundToInt(),
systemPrompt.trim().ifBlank { null },
)
if (isHighLimit) {
showHighLimitsConfirmation = true
} else {
onSave(
maxMessages.roundToInt(),
maxPasses.roundToInt(),
systemPrompt.trim().ifBlank { null },
)
}
},
modifier = Modifier.testTag("save_settings_button"),
) {
@@ -702,6 +763,65 @@ private fun GroupChatSettingsDialog(
}
},
)
if (showHighLimitsConfirmation) {
AlertDialog(
onDismissRequest = { showHighLimitsConfirmation = false },
icon = {
Icon(
imageVector = Icons.Filled.Warning,
contentDescription = null,
tint = MaterialTheme.colorScheme.error,
)
},
title = {
Text(
text = stringResource(R.string.group_chat_settings_high_limits_warning_title),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
)
},
text = {
Text(
text =
stringResource(
R.string.group_chat_settings_high_limits_warning_desc,
maxMessages.roundToInt(),
maxPasses.roundToInt(),
),
style = MaterialTheme.typography.bodyMedium,
)
},
confirmButton = {
Button(
onClick = {
showHighLimitsConfirmation = false
onSave(
maxMessages.roundToInt(),
maxPasses.roundToInt(),
systemPrompt.trim().ifBlank { null },
)
},
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.error,
contentColor = MaterialTheme.colorScheme.onError,
),
modifier = Modifier.testTag("confirm_high_limits_button"),
) {
Text(stringResource(R.string.group_chat_settings_high_limits_confirm))
}
},
dismissButton = {
TextButton(
onClick = { showHighLimitsConfirmation = false },
modifier = Modifier.testTag("dismiss_high_limits_button"),
) {
Text(stringResource(R.string.group_chat_settings_high_limits_adjust))
}
},
)
}
}
@Composable
@@ -52,6 +52,10 @@ data class MemberSession(
const val DEFAULT_MAX_BOT_MESSAGES = 6
const val DEFAULT_MAX_CONTINUATION_PASSES = 2
const val MAX_ALLOWED_BOT_MESSAGES = 50
const val MAX_ALLOWED_CONTINUATION_PASSES = 20
const val WARN_MAX_BOT_MESSAGES = 20
const val WARN_MAX_CONTINUATION_PASSES = 6
private const val LEASE_STEP_TTL_MS = 45_000L
private const val TURN_TIMEOUT_MS = 45_000L
private const val HISTORY_LIMIT = 10
@@ -424,8 +428,8 @@ class GroupChatViewModel(
maxPasses: Int,
systemPrompt: String? = null,
) {
val clampedMessages = maxMessages.coerceIn(1, 20)
val clampedPasses = maxPasses.coerceIn(0, 10)
val clampedMessages = maxMessages.coerceIn(1, MAX_ALLOWED_BOT_MESSAGES)
val clampedPasses = maxPasses.coerceIn(0, MAX_ALLOWED_CONTINUATION_PASSES)
val cleanPrompt = systemPrompt?.trim()?.ifBlank { null }
_uiState.update {