capability_grower_configuration/
capstone_generation_configuration.rs

1// ---------------- [ File: capability-grower-configuration/src/capstone_generation_configuration.rs ]
2crate::ix!();
3
4/// In the context of our tree-generation system, a capstone node is a special type of leaf node
5/// that represents a culminating skill, concept, or endpoint of a hierarchical branch. 
6///
7/// Capstone nodes stand out clearly as a final, specialized mastery points—often signifying an advanced,
8/// important, or unique skill within a skill-tree.
9///
10/// Capstone nodes act as distinctive markers, clearly communicating to users that this particular node is either:
11/// - Essential: representing the core or pinnacle achievement of a skill branch.
12/// - Prestigious: indicating a high-level mastery or rare skill.
13/// - Strategic: highlighting a concept that offers substantial practical or conceptual leverage.
14///
15/// This struct merges the chosen capstone mode with its relevant probability (if any).
16///
17#[derive(
18    SaveLoad,
19    Debug, 
20    Clone, 
21    PartialEq, 
22    Getters, 
23    Builder, 
24    Default, 
25    Serialize, 
26    Deserialize, 
27    AiJsonTemplate,
28    AiJsonTemplateWithJustification,
29)]
30#[builder(pattern="owned", setter(into))]
31#[getset(get="pub")]
32pub struct CapstoneGenerationConfiguration {
33    /// This field should be one of Off, Single, or Probabilistic.
34    mode: CapstoneMode,
35
36    /// This fraction [0..1] is only meaningful if `mode=Probabilistic`, specifying
37    /// which fraction of leaves become highlight/capstones.
38    probability: f32,
39}
40
41impl CapstoneGenerationConfiguration {
42    pub fn validate(&self) -> Result<(), GrowerTreeConfigurationError> {
43        if self.mode == CapstoneMode::Probabilistic {
44            if !(0.0..=1.0).contains(&self.probability) {
45                return Err(GrowerTreeConfigurationError::CapstoneProbabilityOutOfRange);
46            }
47        }
48        Ok(())
49    }
50}
51
52impl FuzzyFromJsonValue for JustifiedCapstoneGenerationConfiguration {
53    fn fuzzy_from_json_value(value: &serde_json::Value) -> Result<Self, FuzzyFromJsonValueError> {
54        trace!("(JustifiedCapstoneGenerationConfiguration) Entering fuzzy_from_json_value");
55
56        // 1) If null => default
57        if value.is_null() {
58            debug!("(JustifiedCapstoneGenerationConfiguration) Null => returning default with mode=Off, probability=0");
59            return Ok(Self {
60                mode: JustifiedCapstoneMode::Off {
61                    variant_confidence: 0.0,
62                    variant_justification: String::new(),
63                },
64                mode_confidence: 0.0,
65                mode_justification: String::new(),
66                probability: 0.0,
67                probability_confidence: 0.0,
68                probability_justification: String::new(),
69            });
70        }
71
72        // 2) Must be an object => parse
73        let mut obj = match value.as_object() {
74            Some(m) => {
75                trace!("(JustifiedCapstoneGenerationConfiguration) Found object => flattening if needed.");
76                let mut cloned = m.clone();
77                flatten_all_fields(&mut cloned);
78                cloned
79            }
80            None => {
81                error!("(JustifiedCapstoneGenerationConfiguration) Not an object => fail!");
82                return Err(FuzzyFromJsonValueError::NotAnObject {
83                    target_type: "JustifiedCapstoneGenerationConfiguration",
84                    actual: value.clone(),
85                });
86            }
87        };
88
89        // 3) We parse the "mode" field (which is required). Then leftover fields 
90        //    like "mode_confidence" or "probability" belong to the parent struct.
91        trace!("(JustifiedCapstoneGenerationConfiguration) Parsing 'mode' => sub-enum fuzzy");
92        let raw_mode_val = match obj.remove("mode") {
93            Some(sub) => sub,
94            None => {
95                error!("(JustifiedCapstoneGenerationConfiguration) Missing 'mode' => cannot parse!");
96                return Err(FuzzyFromJsonValueError::MissingField {
97                    field_name: "mode",
98                    target_type: "JustifiedCapstoneGenerationConfiguration",
99                });
100            }
101        };
102
103        // We'll parse the child's (JustifiedCapstoneMode) logic here
104        let mode_val: JustifiedCapstoneMode = match raw_mode_val.as_object() {
105            Some(mode_map) => {
106                // We do a single-key unify if there's exactly one recognized variant (Off, Single, Probabilistic)
107                let mut mode_obj = mode_map.clone();
108                flatten_all_fields(&mut mode_obj);
109
110                let recognized_variants = &["Off", "Single", "Probabilistic"];
111                let mut found_variant_keys = Vec::new();
112                for k in mode_obj.keys() {
113                    if recognized_variants
114                        .iter()
115                        .any(|known| known.eq_ignore_ascii_case(k))
116                    {
117                        found_variant_keys.push(k.clone());
118                    }
119                }
120
121                if found_variant_keys.len() == 1 {
122                    // unify that recognized variant into "variant_name"
123                    let variant_key = found_variant_keys[0].clone();
124                    trace!(
125                        "(JustifiedCapstoneGenerationConfiguration) Found single variant='{}' in 'mode' object => unifying",
126                        variant_key
127                    );
128
129                    let variant_val = mode_obj.remove(&variant_key).unwrap_or(serde_json::Value::Null);
130
131                    let mut new_map = serde_json::Map::new();
132                    new_map.insert("variant_name".to_string(), serde_json::Value::String(variant_key));
133
134                    if let Some(nested_obj) = variant_val.as_object() {
135                        // merge subfields
136                        for (kk, vv) in nested_obj {
137                            new_map.insert(kk.clone(), vv.clone());
138                        }
139                    }
140                    // Now parse the recognized capstone variant
141                    let wrapped = serde_json::Value::Object(new_map);
142                    JustifiedCapstoneMode::fuzzy_from_json_value(&wrapped)
143                        .map_err(|err| {
144                            error!("(JustifiedCapstoneGenerationConfiguration) error parsing single variant key => {:?}", err);
145                            err
146                        })?
147                } else {
148                    // fallback => maybe "variant_name" or some other shape
149                    let reconstructed = serde_json::Value::Object(mode_obj);
150                    JustifiedCapstoneMode::fuzzy_from_json_value(&reconstructed)
151                        .map_err(|err| {
152                            error!("(JustifiedCapstoneGenerationConfiguration) error parsing 'mode' => {:?}", err);
153                            err
154                        })?
155                }
156            }
157            None => {
158                // If the user wrote `"mode": "Off"` or similar => direct parse:
159                JustifiedCapstoneMode::fuzzy_from_json_value(&raw_mode_val)
160                    .map_err(|err| {
161                        error!("(JustifiedCapstoneGenerationConfiguration) 'mode' parse error => {:?}", err);
162                        err
163                    })?
164            }
165        };
166
167        // Now parse leftover fields belonging to this parent struct:
168        // e.g. "mode_confidence","mode_justification","probability", etc.
169        let mode_conf = get_f64_field(&obj, "mode_confidence", "JustifiedCapstoneGenerationConfiguration")
170            .unwrap_or(0.0);
171        let mode_just = get_string_field(&obj, "mode_justification", "JustifiedCapstoneGenerationConfiguration")
172            .unwrap_or_default();
173
174        // 5) probability => f32
175        trace!("(JustifiedCapstoneGenerationConfiguration) Parsing 'probability' => f32");
176        let prob_f64 = get_f64_field(&obj, "probability", "JustifiedCapstoneGenerationConfiguration")
177            .unwrap_or(0.0);
178        let probability = prob_f64 as f32;
179
180        let prob_conf = get_f64_field(&obj, "probability_confidence", "JustifiedCapstoneGenerationConfiguration")
181            .unwrap_or(0.0);
182        let prob_just = get_string_field(&obj, "probability_justification", "JustifiedCapstoneGenerationConfiguration")
183            .unwrap_or_default();
184
185        trace!("(JustifiedCapstoneGenerationConfiguration) Successfully parsed => returning final struct.");
186        Ok(Self {
187            mode: mode_val,
188            mode_confidence: mode_conf,
189            mode_justification: mode_just,
190            probability,
191            probability_confidence: prob_conf,
192            probability_justification: prob_just,
193        })
194    }
195}