1use std::collections::HashMap;
30
31use std::sync::{Arc, Mutex, RwLock};
32
33use std::time::{SystemTime, UNIX_EPOCH};
34
35#[derive(Debug, Clone, PartialEq)]
36
37pub enum GoalStatus {
38 Active,
39 InProgress,
40 Completed,
41 Failed(String),
42 Blocked(String),
43 Suspended,
44}
45
46#[derive(Debug, Clone, PartialEq)]
47
48pub enum PlanStatus {
49 Draft,
50 InProgress,
51 Completed,
52 Failed(String),
53 Blocked(String),
55 Replanning,
56}
57
58#[derive(Debug, Clone)]
59
60pub struct Goal {
61 pub id: u64,
62 pub name: String,
63 pub description: String,
64
65 pub priority: u8,
66 pub deadline_ms: Option<u64>,
67
68 pub status: GoalStatus,
69 pub parent_id: Option<u64>,
70
71 pub subgoals: Vec<u64>,
72 pub progress: f64,
73 pub created_ms: u64,
74}
75
76#[derive(Debug, Clone)]
77
78pub struct Action {
79 pub id: u64,
80 pub name: String,
81 pub module: String,
82
83 pub estimated_cost: f64,
84 pub estimated_duration_ms: u64,
85
86 pub dependencies: Vec<u64>,
87 pub preconditions: Vec<String>,
88
89 pub effects: Vec<String>,
90 pub risk_score: f64,
91}
92
93#[derive(Debug, Clone)]
94
95pub struct Plan {
96 pub id: u64,
97 pub name: String,
98 pub goal_id: u64,
99
100 pub actions: Vec<Action>,
101 pub status: PlanStatus,
102
103 pub total_cost: f64,
104 pub total_duration_ms: u64,
105
106 pub risk_score: f64,
107 pub alternatives: Vec<u64>,
108
109 pub created_ms: u64,
110 pub executed_step: usize,
111}
112
113#[derive(Debug, Clone)]
114
115pub struct PlanningConfig {
116 pub max_depth: u32,
117 pub min_confidence: f64,
118
119 pub max_alternatives: usize,
120 pub auto_replan: bool,
121
122 pub risk_tolerance: f64,
123}
124
125impl Default for PlanningConfig {
126 fn default() -> Self {
127 Self {
128 max_depth: 5,
129 min_confidence: 0.3,
130 max_alternatives: 3,
131 auto_replan: true,
132 risk_tolerance: 0.7,
133 }
134 }
135}
136
137pub struct Planner {
138 goals: Arc<RwLock<HashMap<u64, Goal>>>,
139
140 plans: Arc<RwLock<HashMap<u64, Plan>>>,
141
142 config: Arc<RwLock<PlanningConfig>>,
143
144 next_id: Arc<RwLock<u64>>,
145
146 enforcement: Arc<Mutex<crate::enforcement::EnforcementEngine>>,
149}
150
151impl Default for Planner {
152 fn default() -> Self {
153 Self::new()
154 }
155}
156
157impl Planner {
158 pub fn new() -> Self {
159 Self {
160 goals: Arc::new(RwLock::new(HashMap::new())),
161
162 plans: Arc::new(RwLock::new(HashMap::new())),
163
164 config: Arc::new(RwLock::new(PlanningConfig::default())),
165
166 next_id: Arc::new(RwLock::new(1)),
167
168 enforcement: Arc::new(Mutex::new(crate::enforcement::EnforcementEngine::new())),
169 }
170 }
171
172 pub fn set_enforcement(
175 &mut self,
176 enforcement: Arc<Mutex<crate::enforcement::EnforcementEngine>>,
177 ) {
178 self.enforcement = enforcement;
179 }
180
181 pub fn enforcement(&self) -> Arc<Mutex<crate::enforcement::EnforcementEngine>> {
183 Arc::clone(&self.enforcement)
184 }
185
186 fn nid(&self) -> u64 {
187 let mut id = crate::sync_guard::write_lock(&self.next_id);
188 let n = *id;
189 *id += 1;
190 n
191 }
192
193 fn now(&self) -> u64 {
194 SystemTime::now()
195 .duration_since(UNIX_EPOCH)
196 .unwrap_or_default()
197 .as_millis() as u64
198 }
199
200 pub fn add_goal(&self, name: &str, desc: &str, priority: u8, parent: Option<u64>) -> u64 {
201 let id = self.nid();
202
203 let goal = Goal {
204 id,
205 name: name.to_string(),
206 description: desc.to_string(),
207
208 priority,
209 deadline_ms: None,
210 status: GoalStatus::Active,
211
212 parent_id: parent,
213 subgoals: Vec::new(),
214 progress: 0.0,
215 created_ms: self.now(),
216 };
217
218 if let Some(pid) = parent {
219 if let Some(p) = crate::sync_guard::write_lock(&self.goals).get_mut(&pid) {
220 p.subgoals.push(id);
221 }
222 }
223
224 crate::sync_guard::write_lock(&self.goals).insert(id, goal);
225
226 id
227 }
228
229 pub fn get_goal(&self, id: u64) -> Option<Goal> {
230 crate::sync_guard::read_lock(&self.goals).get(&id).cloned()
231 }
232
233 pub fn list_goals(&self, status: Option<GoalStatus>) -> Vec<Goal> {
234 crate::sync_guard::read_lock(&self.goals)
235 .values()
236 .filter(|g| status.as_ref().is_none_or(|s| g.status == *s))
237 .cloned()
238 .collect()
239 }
240
241 pub fn decompose_goal(&self, goal_id: u64) -> Vec<u64> {
243 let goal = match crate::sync_guard::read_lock(&self.goals).get(&goal_id) {
244 Some(g) => g.clone(),
245 None => return vec![],
246 };
247
248 let _config = crate::sync_guard::read_lock(&self.config).clone();
249
250 let mut sub_ids = Vec::new();
251
252 if goal.priority > 200 {
253 return sub_ids;
254 } let subgoals: Vec<(String, String, u8)> = match goal.name.to_lowercase().as_str() {
259 "learn" | "tanulás" => vec![
260 (
261 "collect_data".to_string(),
262 "Adatok gyűjtése".to_string(),
263 goal.priority + 10,
264 ),
265 (
266 "analyze".to_string(),
267 "Elemzés és minták keresése".to_string(),
268 goal.priority + 20,
269 ),
270 (
271 "store".to_string(),
272 "Eredmények tárolása".to_string(),
273 goal.priority + 30,
274 ),
275 (
276 "verify".to_string(),
277 "Ellenőrzés és validálás".to_string(),
278 goal.priority + 40,
279 ),
280 ],
281
282 "optimize" | "optimalizálás" => vec![
283 (
284 "measure".to_string(),
285 "Jelenlegi állapot mérése".to_string(),
286 goal.priority + 10,
287 ),
288 (
289 "identify".to_string(),
290 "Szűk keresztmetszetek azonosítása".to_string(),
291 goal.priority + 20,
292 ),
293 (
294 "apply".to_string(),
295 "Optimalizáció alkalmazása".to_string(),
296 goal.priority + 30,
297 ),
298 (
299 "verify".to_string(),
300 "Javulás ellenőrzése".to_string(),
301 goal.priority + 40,
302 ),
303 ],
304
305 "explore" | "exploráció" => vec![
306 (
307 "scan".to_string(),
308 "Felület szkenelése".to_string(),
309 goal.priority + 10,
310 ),
311 (
312 "sample".to_string(),
313 "Mintavételezés".to_string(),
314 goal.priority + 20,
315 ),
316 (
317 "map".to_string(),
318 "Térkép készítése".to_string(),
319 goal.priority + 30,
320 ),
321 (
322 "report".to_string(),
323 "Eredmények jelentése".to_string(),
324 goal.priority + 40,
325 ),
326 ],
327
328 _ => vec![
329 (
330 "analyze".to_string(),
331 format!("{} elemzése", goal.name),
332 goal.priority + 10,
333 ),
334 (
335 "plan".to_string(),
336 format!("Terv készítése: {}", goal.name),
337 goal.priority + 20,
338 ),
339 (
340 "execute".to_string(),
341 format!("Végrehajtás: {}", goal.name),
342 goal.priority + 30,
343 ),
344 (
345 "verify".to_string(),
346 format!("Eredmény ellenőrzése: {}", goal.name),
347 goal.priority + 40,
348 ),
349 ],
350 };
351
352 for (name, desc, prio) in subgoals {
353 let sid = self.add_goal(&name, &desc, prio, Some(goal_id));
354
355 sub_ids.push(sid);
356 }
357
358 sub_ids
359 }
360
361 pub fn create_plan(&self, goal_id: u64) -> Plan {
363 let goal = match crate::sync_guard::read_lock(&self.goals).get(&goal_id) {
364 Some(g) => g.clone(),
365 None => return self.empty_plan(),
366 };
367
368 let _config = crate::sync_guard::read_lock(&self.config).clone();
369
370 let now = self.now();
371
372 let plan_id = self.nid();
373
374 let actions = match goal.name.to_lowercase().as_str() {
375 "collect_data" => self.make_collect_actions(),
376
377 "analyze" => self.make_analyze_actions(),
378
379 "execute" => self.make_execute_actions(&goal),
380
381 _ => self.make_generic_actions(&goal),
382 };
383
384 let total_cost: f64 = actions.iter().map(|a| a.estimated_cost).sum();
385
386 let total_dur: u64 = actions.iter().map(|a| a.estimated_duration_ms).sum();
387
388 let risk: f64 =
389 actions.iter().map(|a| a.risk_score).sum::<f64>() / actions.len().max(1) as f64;
390
391 let plan = Plan {
392 id: plan_id,
393 name: format!("plan_for_{}", goal.name),
394 goal_id,
395
396 actions,
397 status: PlanStatus::Draft,
398 total_cost,
399 total_duration_ms: total_dur,
400
401 risk_score: risk,
402 alternatives: Vec::new(),
403 created_ms: now,
404 executed_step: 0,
405 };
406
407 if let Some(g) = crate::sync_guard::write_lock(&self.goals).get_mut(&goal_id) {
408 g.status = GoalStatus::InProgress;
409 }
410
411 crate::sync_guard::write_lock(&self.plans).insert(plan_id, plan.clone());
412
413 plan
414 }
415
416 fn gated_step(
420 &self,
421 plans: &mut std::sync::RwLockWriteGuard<'_, std::collections::HashMap<u64, Plan>>,
422 plan_id: u64,
423 justification: Option<&str>,
424 ) -> Result<Option<Action>, crate::enforcement::EnforcementError> {
425 let plan = match plans.get_mut(&plan_id) {
426 Some(p) => p,
427 None => return Ok(None),
428 };
429
430 if plan.executed_step >= plan.actions.len() {
431 plan.status = PlanStatus::Completed;
432 if let Some(g) = crate::sync_guard::write_lock(&self.goals).get_mut(&plan.goal_id) {
433 g.status = GoalStatus::Completed;
434 g.progress = 1.0;
435 }
436 return Ok(None);
437 }
438
439 let action = plan.actions[plan.executed_step].clone();
440 let event = crate::enforcement::ActionEvent {
441 actor: "planner".to_string(),
442 action: action.name.clone(),
443 content: plan.name.clone(),
444 ts_ms: self.now(),
445 scope: format!("plan:{}:goal:{}", plan.id, plan.goal_id),
446 provenance: "planner/execute_step".to_string(),
447 };
448
449 let mut gate = crate::sync_guard::mutex_lock(&self.enforcement);
450 match gate.decide(&event, justification) {
451 crate::enforcement::Decision::Allowed { .. }
452 | crate::enforcement::Decision::Overridden { .. } => {
453 plan.executed_step += 1;
456 plan.status = PlanStatus::InProgress;
457 Ok(Some(action))
458 }
459 crate::enforcement::Decision::Blocked { action, reason, .. } => {
460 plan.status = PlanStatus::Blocked(reason.clone());
461 Err(crate::enforcement::EnforcementError::Blocked { action, reason })
462 }
463 crate::enforcement::Decision::AttributionError { reason } => {
464 plan.status = PlanStatus::Failed(reason.clone());
465 Err(crate::enforcement::EnforcementError::FaultyAttribution { reason })
466 }
467 }
468 }
469
470 pub fn execute_step(
472 &self,
473 plan_id: u64,
474 ) -> Result<Option<Action>, crate::enforcement::EnforcementError> {
475 let mut plans = crate::sync_guard::write_lock(&self.plans);
476 self.gated_step(&mut plans, plan_id, None)
477 }
478
479 pub fn execute_step_with_override(
482 &self,
483 plan_id: u64,
484 justification: &str,
485 ) -> Result<Option<Action>, crate::enforcement::EnforcementError> {
486 let mut plans = crate::sync_guard::write_lock(&self.plans);
487 self.gated_step(&mut plans, plan_id, Some(justification))
488 }
489
490 pub fn fail_step(&self, plan_id: u64, reason: &str) -> bool {
492 let mut plans = crate::sync_guard::write_lock(&self.plans);
493
494 let plan = match plans.get_mut(&plan_id) {
495 Some(p) => p,
496
497 None => return false,
498 };
499
500 if plan.executed_step == 0 {
501 return false;
502 }
503
504 plan.executed_step -= 1;
505
506 plan.status = PlanStatus::Failed(format!("step {} failed: {}", plan.executed_step, reason));
507
508 true
509 }
510
511 pub fn replan(&self, plan_id: u64, reason: &str) -> Option<Plan> {
513 let plans = crate::sync_guard::write_lock(&self.plans);
514
515 let old = plans.get(&plan_id)?.clone();
516
517 let _goal = crate::sync_guard::read_lock(&self.goals)
518 .get(&old.goal_id)?
519 .clone();
520
521 drop(plans);
522
523 if let Some(g) = crate::sync_guard::write_lock(&self.goals).get_mut(&old.goal_id) {
526 g.status = GoalStatus::Active;
527 }
528
529 let mut new_plan = self.create_plan(old.goal_id);
530
531 new_plan.name = format!("replan_{}_{}", old.name, reason);
532
533 new_plan.status = PlanStatus::Replanning;
534
535 new_plan.alternatives.push(old.id);
536
537 Some(new_plan)
538 }
539
540 pub fn plans_for_goal(&self, goal_id: u64) -> Vec<Plan> {
541 crate::sync_guard::read_lock(&self.plans)
542 .values()
543 .filter(|p| p.goal_id == goal_id)
544 .cloned()
545 .collect()
546 }
547
548 pub fn get_plan(&self, id: u64) -> Option<Plan> {
549 crate::sync_guard::read_lock(&self.plans).get(&id).cloned()
550 }
551
552 pub fn stats(&self) -> (usize, usize, usize) {
553 let g = crate::sync_guard::read_lock(&self.goals);
554
555 let p = crate::sync_guard::read_lock(&self.plans);
556
557 (
558 g.len(),
559 p.len(),
560 g.values()
561 .filter(|g| g.status == GoalStatus::Completed)
562 .count(),
563 )
564 }
565
566 fn empty_plan(&self) -> Plan {
569 Plan {
570 id: 0,
571 name: "empty".to_string(),
572 goal_id: 0,
573 actions: vec![],
574
575 status: PlanStatus::Draft,
576 total_cost: 0.0,
577 total_duration_ms: 0,
578
579 risk_score: 0.0,
580 alternatives: vec![],
581 created_ms: self.now(),
582 executed_step: 0,
583 }
584 }
585
586 fn make_collect_actions(&self) -> Vec<Action> {
587 let id = self.nid();
588
589 vec![
590 Action {
591 id: id + 1,
592 name: "query_source".to_string(),
593 module: "reader".to_string(),
594
595 estimated_cost: 0.3,
596 estimated_duration_ms: 100,
597 dependencies: vec![],
598
599 preconditions: vec!["config_ready".to_string()],
600 effects: vec!["data_collected".to_string()],
601 risk_score: 0.1,
602 },
603 Action {
604 id: id + 2,
605 name: "filter_relevant".to_string(),
606 module: "attention".to_string(),
607
608 estimated_cost: 0.2,
609 estimated_duration_ms: 50,
610 dependencies: vec![id + 1],
611
612 preconditions: vec!["data_collected".to_string()],
613 effects: vec!["data_filtered".to_string()],
614 risk_score: 0.1,
615 },
616 Action {
617 id: id + 3,
618 name: "store_results".to_string(),
619 module: "hippocampus".to_string(),
620
621 estimated_cost: 0.1,
622 estimated_duration_ms: 30,
623 dependencies: vec![id + 2],
624
625 preconditions: vec!["data_filtered".to_string()],
626 effects: vec!["data_stored".to_string()],
627 risk_score: 0.05,
628 },
629 ]
630 }
631
632 fn make_analyze_actions(&self) -> Vec<Action> {
633 let id = self.nid();
634
635 vec![
636 Action {
637 id: id + 1,
638 name: "load_data".to_string(),
639 module: "reader".to_string(),
640
641 estimated_cost: 0.1,
642 estimated_duration_ms: 20,
643 dependencies: vec![],
644
645 preconditions: vec!["data_available".to_string()],
646 effects: vec!["data_loaded".to_string()],
647 risk_score: 0.05,
648 },
649 Action {
650 id: id + 2,
651 name: "find_patterns".to_string(),
652 module: "pattern_recognition".to_string(),
653
654 estimated_cost: 0.5,
655 estimated_duration_ms: 200,
656 dependencies: vec![id + 1],
657
658 preconditions: vec!["data_loaded".to_string()],
659 effects: vec!["patterns_found".to_string()],
660 risk_score: 0.2,
661 },
662 Action {
663 id: id + 3,
664 name: "store_insights".to_string(),
665 module: "knowledge_base".to_string(),
666
667 estimated_cost: 0.1,
668 estimated_duration_ms: 30,
669 dependencies: vec![id + 2],
670
671 preconditions: vec!["patterns_found".to_string()],
672 effects: vec!["insights_stored".to_string()],
673 risk_score: 0.05,
674 },
675 ]
676 }
677
678 fn make_execute_actions(&self, goal: &Goal) -> Vec<Action> {
679 let id = self.nid();
680
681 vec![
682 Action {
683 id: id + 1,
684 name: format!("prepare_{}", goal.name),
685 module: "executive".to_string(),
686
687 estimated_cost: 0.2,
688 estimated_duration_ms: 50,
689 dependencies: vec![],
690
691 preconditions: vec![],
692 effects: vec!["ready".to_string()],
693 risk_score: 0.1,
694 },
695 Action {
696 id: id + 2,
697 name: format!("run_{}", goal.name),
698 module: "executive".to_string(),
699
700 estimated_cost: 0.8,
701 estimated_duration_ms: 500,
702 dependencies: vec![id + 1],
703
704 preconditions: vec!["ready".to_string()],
705 effects: vec!["executed".to_string()],
706 risk_score: 0.3,
707 },
708 Action {
709 id: id + 3,
710 name: "verify_result".to_string(),
711 module: "meta_supervision".to_string(),
712
713 estimated_cost: 0.1,
714 estimated_duration_ms: 30,
715 dependencies: vec![id + 2],
716
717 preconditions: vec!["executed".to_string()],
718 effects: vec!["verified".to_string()],
719 risk_score: 0.1,
720 },
721 ]
722 }
723
724 fn make_generic_actions(&self, goal: &Goal) -> Vec<Action> {
725 let id = self.nid();
726
727 vec![
728 Action {
729 id: id + 1,
730 name: format!("init_{}", goal.name),
731 module: "executive".to_string(),
732
733 estimated_cost: 0.2,
734 estimated_duration_ms: 50,
735 dependencies: vec![],
736
737 preconditions: vec![],
738 effects: vec!["initialized".to_string()],
739 risk_score: 0.1,
740 },
741 Action {
742 id: id + 2,
743 name: format!("process_{}", goal.name),
744 module: "executive".to_string(),
745
746 estimated_cost: 0.5,
747 estimated_duration_ms: 300,
748 dependencies: vec![id + 1],
749
750 preconditions: vec!["initialized".to_string()],
751 effects: vec!["processed".to_string()],
752 risk_score: 0.2,
753 },
754 Action {
755 id: id + 3,
756 name: "finalize".to_string(),
757 module: "executive".to_string(),
758
759 estimated_cost: 0.1,
760 estimated_duration_ms: 30,
761 dependencies: vec![id + 2],
762
763 preconditions: vec!["processed".to_string()],
764 effects: vec!["done".to_string()],
765 risk_score: 0.05,
766 },
767 ]
768 }
769}