1use serde::Serialize;
2
3use crate::ast::{FieldValue, PgnPacket};
4use crate::registry::WorkflowRegistry;
5use crate::safety::SafetyResult;
6
7#[derive(Debug, Serialize)]
8pub struct RouteDecision {
9 pub recommended_executor: String,
10 pub fallback_executor: String,
11 pub reason: String,
12 pub human_required: bool,
13}
14
15pub fn route(
16 packet: &PgnPacket,
17 registry: &WorkflowRegistry,
18 safety: &SafetyResult,
19) -> RouteDecision {
20 let wf_name = packet
21 .fields
22 .get("wf")
23 .and_then(|v| match v {
24 FieldValue::Scalar(s) => Some(s.as_str()),
25 _ => None,
26 })
27 .unwrap_or("unknown");
28
29 let explicit_route = packet.fields.get("route").and_then(|v| match v {
30 FieldValue::Scalar(s) => Some(s.clone()),
31 _ => None,
32 });
33
34 let (recommended, fallback) = if let Some(route_val) = &explicit_route {
35 (route_val.clone(), registry.workflows.get(wf_name).map(|w| w.fallback_executor.clone()).unwrap_or_else(|| "unknown".to_string()))
36 } else if let Some(entry) = registry.workflows.get(wf_name) {
37 (entry.recommended_executor.clone(), entry.fallback_executor.clone())
38 } else {
39 ("unknown".to_string(), "unknown".to_string())
40 };
41
42 RouteDecision {
43 recommended_executor: recommended,
44 fallback_executor: fallback,
45 reason: if explicit_route.is_some() {
46 "explicit_route".to_string()
47 } else {
48 format!("workflow_{}", wf_name)
49 },
50 human_required: safety.human_required,
51 }
52}
53
54pub fn explain_route(decision: &RouteDecision) -> String {
55 format!(
56 "Route: {} (fallback: {}), human_required: {}, reason: {}",
57 decision.recommended_executor,
58 decision.fallback_executor,
59 decision.human_required,
60 decision.reason
61 )
62}