1pub use a3s_lane::MetricsSnapshot;
21use anyhow::Result;
22use async_trait::async_trait;
23use serde::{Deserialize, Serialize};
24use std::collections::HashMap;
25use std::time::{Duration, Instant};
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub enum SessionLane {
34 Control,
36 Query,
38 Execute,
40 Generate,
42}
43
44impl SessionLane {
45 pub fn priority(&self) -> u8 {
47 match self {
48 SessionLane::Control => 0,
49 SessionLane::Query => 1,
50 SessionLane::Execute => 2,
51 SessionLane::Generate => 3,
52 }
53 }
54
55 pub fn from_tool_name(tool_name: &str) -> Self {
57 match tool_name {
58 "read" | "glob" | "ls" | "grep" | "list_files" | "search" | "web_fetch"
59 | "web_search" | "code_symbols" | "code_navigation" | "code_diagnostics" => {
60 SessionLane::Query
61 }
62 "bash" | "write" | "edit" | "delete" | "move" | "copy" | "execute" => {
63 SessionLane::Execute
64 }
65 _ => SessionLane::Execute,
66 }
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
76pub enum TaskHandlerMode {
77 #[default]
79 Internal,
80 External,
82 Hybrid,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct LaneHandlerConfig {
89 pub mode: TaskHandlerMode,
91 pub timeout_ms: u64,
93}
94
95impl Default for LaneHandlerConfig {
96 fn default() -> Self {
97 Self {
98 mode: TaskHandlerMode::Internal,
99 timeout_ms: 60_000,
100 }
101 }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ExternalTask {
111 pub task_id: String,
113 pub session_id: String,
115 pub lane: SessionLane,
117 pub command_type: String,
119 pub payload: serde_json::Value,
121 pub timeout_ms: u64,
123 #[serde(skip)]
125 pub created_at: Option<Instant>,
126}
127
128impl ExternalTask {
129 pub fn is_timed_out(&self) -> bool {
131 self.created_at
132 .map(|t| t.elapsed() > Duration::from_millis(self.timeout_ms))
133 .unwrap_or(false)
134 }
135
136 pub fn remaining_ms(&self) -> u64 {
138 self.created_at
139 .map(|t| {
140 let elapsed = t.elapsed().as_millis() as u64;
141 self.timeout_ms.saturating_sub(elapsed)
142 })
143 .unwrap_or(self.timeout_ms)
144 }
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct ExternalTaskResult {
150 pub success: bool,
152 pub result: serde_json::Value,
154 pub error: Option<String>,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
164#[serde(rename_all = "camelCase")]
165pub struct SessionQueueConfig {
166 #[serde(default = "default_control_concurrency")]
168 pub control_max_concurrency: usize,
169 #[serde(default = "default_query_concurrency")]
171 pub query_max_concurrency: usize,
172 #[serde(default = "default_execute_concurrency")]
174 pub execute_max_concurrency: usize,
175 #[serde(default = "default_generate_concurrency")]
177 pub generate_max_concurrency: usize,
178 #[serde(default)]
180 pub lane_handlers: HashMap<SessionLane, LaneHandlerConfig>,
181
182 #[serde(default)]
187 pub enable_dlq: bool,
188 #[serde(default)]
190 pub dlq_max_size: Option<usize>,
191 #[serde(default)]
193 pub enable_metrics: bool,
194 #[serde(default)]
196 pub enable_alerts: bool,
197 #[serde(default)]
199 pub default_timeout_ms: Option<u64>,
200 #[serde(default)]
202 pub storage_path: Option<std::path::PathBuf>,
203
204 #[serde(default)]
209 pub retry_policy: Option<RetryPolicyConfig>,
210 #[serde(default)]
212 pub rate_limit: Option<RateLimitConfig>,
213 #[serde(default)]
215 pub priority_boost: Option<PriorityBoostConfig>,
216 #[serde(default)]
218 pub pressure_threshold: Option<usize>,
219 #[serde(default)]
221 pub lane_timeouts: HashMap<SessionLane, u64>,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226#[serde(rename_all = "camelCase")]
227pub struct RetryPolicyConfig {
228 pub strategy: String,
230 #[serde(default = "default_max_retries")]
232 pub max_retries: u32,
233 #[serde(default = "default_initial_delay_ms")]
235 pub initial_delay_ms: u64,
236 #[serde(default)]
238 pub fixed_delay_ms: Option<u64>,
239}
240
241fn default_max_retries() -> u32 {
242 3
243}
244
245fn default_initial_delay_ms() -> u64 {
246 100
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize)]
251#[serde(rename_all = "camelCase")]
252pub struct RateLimitConfig {
253 pub limit_type: String,
255 #[serde(default)]
257 pub max_operations: Option<u64>,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
262#[serde(rename_all = "camelCase")]
263pub struct PriorityBoostConfig {
264 pub strategy: String,
266 #[serde(default)]
268 pub deadline_ms: Option<u64>,
269}
270
271fn default_control_concurrency() -> usize {
272 4
273}
274
275fn default_query_concurrency() -> usize {
276 12 }
278
279fn default_execute_concurrency() -> usize {
280 4
281}
282
283fn default_generate_concurrency() -> usize {
284 2
285}
286
287impl Default for SessionQueueConfig {
288 fn default() -> Self {
289 Self {
290 control_max_concurrency: 2,
291 query_max_concurrency: 4,
292 execute_max_concurrency: 2,
293 generate_max_concurrency: 1,
294 lane_handlers: HashMap::new(),
295 enable_dlq: false,
296 dlq_max_size: None,
297 enable_metrics: false,
298 enable_alerts: false,
299 default_timeout_ms: None,
300 storage_path: None,
301 retry_policy: None,
302 rate_limit: None,
303 priority_boost: None,
304 pressure_threshold: None,
305 lane_timeouts: HashMap::new(),
306 }
307 }
308}
309
310impl SessionQueueConfig {
311 pub fn max_concurrency(&self, lane: SessionLane) -> usize {
313 match lane {
314 SessionLane::Control => self.control_max_concurrency,
315 SessionLane::Query => self.query_max_concurrency,
316 SessionLane::Execute => self.execute_max_concurrency,
317 SessionLane::Generate => self.generate_max_concurrency,
318 }
319 }
320
321 pub fn handler_config(&self, lane: SessionLane) -> LaneHandlerConfig {
323 self.lane_handlers.get(&lane).cloned().unwrap_or_default()
324 }
325
326 pub fn with_dlq(mut self, max_size: Option<usize>) -> Self {
328 self.enable_dlq = true;
329 self.dlq_max_size = max_size;
330 self
331 }
332
333 pub fn with_metrics(mut self) -> Self {
335 self.enable_metrics = true;
336 self
337 }
338
339 pub fn with_alerts(mut self) -> Self {
341 self.enable_alerts = true;
342 self
343 }
344
345 pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
347 self.default_timeout_ms = Some(timeout_ms);
348 self
349 }
350
351 pub fn with_storage(mut self, path: impl Into<std::path::PathBuf>) -> Self {
353 self.storage_path = Some(path.into());
354 self
355 }
356
357 pub fn with_lane_features(mut self) -> Self {
359 self.enable_dlq = true;
360 self.dlq_max_size = Some(1000);
361 self.enable_metrics = true;
362 self.enable_alerts = true;
363 self.default_timeout_ms = Some(60_000);
364 self
365 }
366}
367
368#[async_trait]
374pub trait SessionCommand: Send + Sync {
375 async fn execute(&self) -> Result<serde_json::Value>;
377
378 fn command_type(&self) -> &str;
380
381 fn payload(&self) -> serde_json::Value {
383 serde_json::json!({})
384 }
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct LaneStatus {
394 pub lane: SessionLane,
395 pub pending: usize,
396 pub active: usize,
397 pub max_concurrency: usize,
398 pub handler_mode: TaskHandlerMode,
399}
400
401#[derive(Debug, Clone, Default, Serialize, Deserialize)]
403pub struct SessionQueueStats {
404 pub total_pending: usize,
405 pub total_active: usize,
406 pub external_pending: usize,
407 pub lanes: HashMap<String, LaneStatus>,
408}
409
410#[cfg(test)]
415mod tests {
416 use super::*;
417
418 #[test]
419 fn test_task_handler_mode_default() {
420 let mode = TaskHandlerMode::default();
421 assert_eq!(mode, TaskHandlerMode::Internal);
422 }
423
424 #[test]
425 fn test_lane_handler_config_default() {
426 let config = LaneHandlerConfig::default();
427 assert_eq!(config.mode, TaskHandlerMode::Internal);
428 assert_eq!(config.timeout_ms, 60_000);
429 }
430
431 #[test]
432 fn test_external_task_timeout() {
433 let task = ExternalTask {
434 task_id: "test".to_string(),
435 session_id: "session".to_string(),
436 lane: SessionLane::Query,
437 command_type: "read".to_string(),
438 payload: serde_json::json!({}),
439 timeout_ms: 100,
440 created_at: Some(Instant::now()),
441 };
442
443 assert!(!task.is_timed_out());
444 assert!(task.remaining_ms() <= 100);
445 }
446
447 #[test]
448 fn test_session_queue_config_default() {
449 let config = SessionQueueConfig::default();
450 assert_eq!(config.control_max_concurrency, 2);
451 assert_eq!(config.query_max_concurrency, 4);
452 assert_eq!(config.execute_max_concurrency, 2);
453 assert_eq!(config.generate_max_concurrency, 1);
454 assert!(!config.enable_dlq);
455 assert!(!config.enable_metrics);
456 assert!(!config.enable_alerts);
457 }
458
459 #[test]
460 fn test_session_queue_config_max_concurrency() {
461 let config = SessionQueueConfig::default();
462 assert_eq!(config.max_concurrency(SessionLane::Control), 2);
463 assert_eq!(config.max_concurrency(SessionLane::Query), 4);
464 assert_eq!(config.max_concurrency(SessionLane::Execute), 2);
465 assert_eq!(config.max_concurrency(SessionLane::Generate), 1);
466 }
467
468 #[test]
469 fn test_session_queue_config_handler_config() {
470 let config = SessionQueueConfig::default();
471 let handler = config.handler_config(SessionLane::Execute);
472 assert_eq!(handler.mode, TaskHandlerMode::Internal);
473 assert_eq!(handler.timeout_ms, 60_000);
474 }
475
476 #[test]
477 fn test_session_queue_config_builders() {
478 let config = SessionQueueConfig::default()
479 .with_dlq(Some(500))
480 .with_metrics()
481 .with_alerts()
482 .with_timeout(30_000);
483
484 assert!(config.enable_dlq);
485 assert_eq!(config.dlq_max_size, Some(500));
486 assert!(config.enable_metrics);
487 assert!(config.enable_alerts);
488 assert_eq!(config.default_timeout_ms, Some(30_000));
489 }
490
491 #[test]
492 fn test_session_queue_config_with_lane_features() {
493 let config = SessionQueueConfig::default().with_lane_features();
494
495 assert!(config.enable_dlq);
496 assert_eq!(config.dlq_max_size, Some(1000));
497 assert!(config.enable_metrics);
498 assert!(config.enable_alerts);
499 assert_eq!(config.default_timeout_ms, Some(60_000));
500 }
501
502 #[test]
503 fn test_external_task_result() {
504 let result = ExternalTaskResult {
505 success: true,
506 result: serde_json::json!({"output": "hello"}),
507 error: None,
508 };
509 assert!(result.success);
510 assert!(result.error.is_none());
511 }
512}