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 {
+4
View File
@@ -1181,6 +1181,10 @@
<string name="group_chat_settings_system_prompt">تعليمات / موجه الغرفة</string>
<string name="group_chat_settings_system_prompt_hint">قواعد مخصصة، أو أسلوب، أو سياق لهذه المجموعة (اختياري)…</string>
<string name="group_chat_settings_system_prompt_desc">إرشادات مخصصة لجميع البوتات في هذه الغرفة</string>
<string name="group_chat_settings_high_limits_warning_title">تحذير من الحدود المرتفعة</string>
<string name="group_chat_settings_high_limits_warning_desc">قد يؤدي تعيين حدود مرتفعة للرسائل (%1$d) أو التمريرات (%2$d) إلى استهلاك كبير للرموز (Tokens) وحدوث حلقات نقاش طويلة ومستمرة بين البوتات.</string>
<string name="group_chat_settings_high_limits_confirm">حفظ على أي حال</string>
<string name="group_chat_settings_high_limits_adjust">تعديل الحدود</string>
<string name="group_chat_action_settings">الإعدادات</string>
<string name="group_chat_stopped_by_user">تم إيقاف النقاش</string>
<string name="group_chat_action_stop">إيقاف التوليد</string>
+4
View File
@@ -863,6 +863,10 @@
<string name="group_chat_settings_system_prompt">방 지침 / 프롬프트</string>
<string name="group_chat_settings_system_prompt_hint">이 그룹을 위한 사용자 지정 규칙, 어조 또는 컨텍스트 (선택사항)…</string>
<string name="group_chat_settings_system_prompt_desc">이 방의 모든 봇에 적용되는 사용자 지정 가이드라인</string>
<string name="group_chat_settings_high_limits_warning_title">높은 제한 경고</string>
<string name="group_chat_settings_high_limits_warning_desc">메시지 수(%1$d) 또는 전달 횟수(%2$d)를 높게 설정하면 API 토큰이 대량 소비되고 봇 간의 긴 토론 루프가 발생할 수 있습니다.</string>
<string name="group_chat_settings_high_limits_confirm">그래도 저장</string>
<string name="group_chat_settings_high_limits_adjust">제한 조정</string>
<string name="group_chat_action_settings">설정</string>
<string name="subagent_agent_plan">에이전트 계획</string>
<string name="subagent_live_transcript">실시간 기록</string>
+4
View File
@@ -1164,5 +1164,9 @@
<string name="group_chat_settings_system_prompt">房间指令 / 提示词</string>
<string name="group_chat_settings_system_prompt_hint">此群聊的自定义规则、语气或上下文(可选)…</string>
<string name="group_chat_settings_system_prompt_desc">适用于此房间内所有 Bot 的自定义准则</string>
<string name="group_chat_settings_high_limits_warning_title">高上限警告</string>
<string name="group_chat_settings_high_limits_warning_desc">设置较高的消息数 (%1$d) 或交接次数 (%2$d) 可能会消耗大量 API 令牌,并导致机器人之间产生长时间的连续讨论循环。</string>
<string name="group_chat_settings_high_limits_confirm">仍要保存</string>
<string name="group_chat_settings_high_limits_adjust">调整限制</string>
<string name="group_chat_action_settings">设置</string>
</resources>
+4
View File
@@ -1295,6 +1295,10 @@
<string name="group_chat_settings_system_prompt">Room Instructions / Prompt</string>
<string name="group_chat_settings_system_prompt_hint">Custom rules, tone, or context for this group (optional)…</string>
<string name="group_chat_settings_system_prompt_desc">Custom guidelines for all bots in this room</string>
<string name="group_chat_settings_high_limits_warning_title">High Limits Warning</string>
<string name="group_chat_settings_high_limits_warning_desc">Setting high message (%1$d) or handoff (%2$d) limits can consume significant API tokens and lead to long continuous bot discussion loops.</string>
<string name="group_chat_settings_high_limits_confirm">Save Anyway</string>
<string name="group_chat_settings_high_limits_adjust">Adjust Limits</string>
<string name="group_chat_action_settings">Settings</string>
<string name="group_chat_stopped_by_user">Discussion stopped</string>
<string name="group_chat_action_stop">Stop generation</string>
@@ -420,6 +420,33 @@ class GroupChatViewModelTest {
assertEquals(15, viewModel.uiState.value.maxBotMessages)
assertEquals(4, viewModel.uiState.value.maxContinuationPasses)
assertEquals("Custom instructions for this group", viewModel.uiState.value.systemPrompt)
// Extended limits within new range
viewModel.updateGroupLimits(
maxMessages = 45,
maxPasses = 18,
)
testScheduler.runCurrent()
assertEquals(45, viewModel.uiState.value.maxBotMessages)
assertEquals(18, viewModel.uiState.value.maxContinuationPasses)
// Exceeding ceiling clamps to MAX_ALLOWED_BOT_MESSAGES (50) and MAX_ALLOWED_CONTINUATION_PASSES (20)
viewModel.updateGroupLimits(
maxMessages = 999,
maxPasses = 999,
)
testScheduler.runCurrent()
assertEquals(MAX_ALLOWED_BOT_MESSAGES, viewModel.uiState.value.maxBotMessages)
assertEquals(MAX_ALLOWED_CONTINUATION_PASSES, viewModel.uiState.value.maxContinuationPasses)
// Under floor clamps to 1 and 0
viewModel.updateGroupLimits(
maxMessages = -10,
maxPasses = -5,
)
testScheduler.runCurrent()
assertEquals(1, viewModel.uiState.value.maxBotMessages)
assertEquals(0, viewModel.uiState.value.maxContinuationPasses)
}
@Test