fix(channel): clamp configurable timeout to minimum 30s

This commit is contained in:
Chummy 2026-02-19 14:15:39 +08:00
parent 41a6ed30dd
commit d548caa5f3
2 changed files with 22 additions and 1 deletions

View file

@ -65,6 +65,7 @@ Notes:
- Default `300s` is optimized for on-device LLMs (Ollama) which are slower than cloud APIs.
- If using cloud APIs (OpenAI, Anthropic, etc.), you can reduce this to `60` or lower.
- Values below `30` are clamped to `30` to avoid immediate timeout churn.
- When a timeout occurs, users receive: `⚠️ Request timed out while waiting for the model. Please try again.`
See detailed channel matrix and allowlist behavior in [channels-reference.md](channels-reference.md).

View file

@ -60,6 +60,7 @@ const BOOTSTRAP_MAX_CHARS: usize = 20_000;
const DEFAULT_CHANNEL_INITIAL_BACKOFF_SECS: u64 = 2;
const DEFAULT_CHANNEL_MAX_BACKOFF_SECS: u64 = 60;
const MIN_CHANNEL_MESSAGE_TIMEOUT_SECS: u64 = 30;
/// Default timeout for processing a single channel message (LLM + tools).
/// Used as fallback when not configured in channels_config.message_timeout_secs.
const CHANNEL_MESSAGE_TIMEOUT_SECS: u64 = 300;
@ -73,6 +74,10 @@ const MODEL_CACHE_PREVIEW_LIMIT: usize = 10;
type ProviderCacheMap = Arc<Mutex<HashMap<String, Arc<dyn Provider>>>>;
type RouteSelectionMap = Arc<Mutex<HashMap<String, ChannelRouteSelection>>>;
fn effective_channel_message_timeout_secs(configured: u64) -> u64 {
configured.max(MIN_CHANNEL_MESSAGE_TIMEOUT_SECS)
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct ChannelRouteSelection {
provider: String,
@ -1811,6 +1816,8 @@ pub async fn start_channels(config: Config) -> Result<()> {
let mut provider_cache_seed: HashMap<String, Arc<dyn Provider>> = HashMap::new();
provider_cache_seed.insert(provider_name.clone(), Arc::clone(&provider));
let message_timeout_secs =
effective_channel_message_timeout_secs(config.channels_config.message_timeout_secs);
let runtime_ctx = Arc::new(ChannelRuntimeContext {
channels_by_name,
@ -1833,7 +1840,7 @@ pub async fn start_channels(config: Config) -> Result<()> {
reliability: Arc::new(config.reliability.clone()),
provider_runtime_options,
workspace_dir: Arc::new(config.workspace_dir.clone()),
message_timeout_secs: config.channels_config.message_timeout_secs,
message_timeout_secs,
});
run_message_dispatch_loop(rx, runtime_ctx, max_in_flight_messages).await;
@ -1879,6 +1886,19 @@ mod tests {
tmp
}
#[test]
fn effective_channel_message_timeout_secs_clamps_to_minimum() {
assert_eq!(
effective_channel_message_timeout_secs(0),
MIN_CHANNEL_MESSAGE_TIMEOUT_SECS
);
assert_eq!(
effective_channel_message_timeout_secs(15),
MIN_CHANNEL_MESSAGE_TIMEOUT_SECS
);
assert_eq!(effective_channel_message_timeout_secs(300), 300);
}
#[derive(Default)]
struct RecordingChannel {
sent_messages: tokio::sync::Mutex<Vec<String>>,