deepstrike_core/scheduler/
policy.rs1pub const SCHEDULER_POLICY_VERSION: u32 = 1;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6#[serde(deny_unknown_fields)]
7pub struct SchedulerPolicyConfig {
8 pub version: u32,
9 pub critical_path_weight: i64,
10 pub fanout_weight: i64,
11 pub age_weight: i64,
12 pub token_cost_weight: i64,
13}
14
15impl Default for SchedulerPolicyConfig {
16 fn default() -> Self {
17 Self {
18 version: SCHEDULER_POLICY_VERSION,
19 critical_path_weight: 1_000_000,
20 fanout_weight: 10_000,
21 age_weight: 1_000,
22 token_cost_weight: 1,
23 }
24 }
25}
26
27impl SchedulerPolicyConfig {
28 pub fn validate(&self) -> Result<(), String> {
29 if self.version != SCHEDULER_POLICY_VERSION {
30 return Err(format!(
31 "scheduler_policy version must be {SCHEDULER_POLICY_VERSION}"
32 ));
33 }
34 for (name, weight) in [
35 ("critical_path_weight", self.critical_path_weight),
36 ("fanout_weight", self.fanout_weight),
37 ("age_weight", self.age_weight),
38 ("token_cost_weight", self.token_cost_weight),
39 ] {
40 if !(0..=1_000_000_000).contains(&weight) {
41 return Err(format!(
42 "scheduler_policy {name} must be between 0 and 1000000000"
43 ));
44 }
45 }
46 Ok(())
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
52pub struct SchedulerBudget {
53 pub max_tokens: u32,
55 pub max_turns: u32,
57 pub max_total_tokens: u64,
59 pub max_wall_ms: Option<u64>,
63}
64
65impl Default for SchedulerBudget {
66 fn default() -> Self {
67 Self {
68 max_tokens: 128_000,
69 max_turns: 25,
70 max_total_tokens: 1_000_000,
71 max_wall_ms: None,
72 }
73 }
74}
75
76impl SchedulerBudget {
77 pub fn should_terminate(
80 &self,
81 turns: u32,
82 total_tokens: u64,
83 now_ms: Option<u64>,
84 started_at_ms: Option<u64>,
85 ) -> Option<&'static str> {
86 if turns >= self.max_turns {
87 return Some("max_turns");
88 }
89 if total_tokens >= self.max_total_tokens {
90 return Some("token_budget");
91 }
92 if let (Some(limit), Some(now), Some(start)) = (self.max_wall_ms, now_ms, started_at_ms) {
93 if now.saturating_sub(start) >= limit {
94 return Some("wall_time");
95 }
96 }
97 None
98 }
99}