pub struct Reasoner<A> { /* private fields */ }Expand description
Holds candidate Actions and selects among them by utility.
Implementations§
Source§impl<A> Reasoner<A>
impl<A> Reasoner<A>
Sourcepub fn new() -> Reasoner<A>
pub fn new() -> Reasoner<A>
Creates an empty reasoner.
Examples found in repository?
9fn main() {
10 // Signals about the current request, each normalized to 0.0..=1.0.
11 let urgency = Score::from_ratio(80, 100);
12 let confidence = Score::from_ratio(40, 100); // how well we already understand it
13 let llm_available = true; // e.g. circuit breaker closed AND rate limiter has tokens
14
15 let mut brain = Reasoner::new();
16
17 // Answer from a template — strong when we are already confident.
18 brain.add(Action::new("answer_template").consider_labeled(
19 "confidence",
20 Curve::Linear,
21 confidence,
22 ));
23
24 // Escalate to the LLM — strong when urgent, but only if the LLM is available.
25 brain.add(
26 Action::new("call_llm")
27 .gate(llm_available) // constraint-aware: skipped entirely if the LLM is down/limited
28 .consider_labeled("urgency", Curve::Linear, urgency),
29 );
30
31 // An always-available, low-weight fallback so a decision still resolves.
32 brain.add(Action::new("defer").with_base(Score::from_ratio(1, 10)));
33
34 // Abstain if nothing clears the bar — the caller would escalate to a human.
35 let threshold = Score::from_ratio(5, 100);
36 match brain.decide_above(threshold) {
37 Some(decision) => {
38 println!(
39 "chose: {} (utility {}/10000)",
40 decision.id,
41 decision.utility.raw()
42 );
43 if let Some(why) = brain.explain() {
44 for c in why.contributions {
45 println!(
46 " {} : input {} -> {}",
47 c.label,
48 c.input.raw(),
49 c.output.raw()
50 );
51 }
52 }
53 }
54 None => println!("nothing good enough — escalate to a human"),
55 }
56}Sourcepub fn add(&mut self, action: Action<A>) -> &mut Self
pub fn add(&mut self, action: Action<A>) -> &mut Self
Adds a candidate action.
Examples found in repository?
9fn main() {
10 // Signals about the current request, each normalized to 0.0..=1.0.
11 let urgency = Score::from_ratio(80, 100);
12 let confidence = Score::from_ratio(40, 100); // how well we already understand it
13 let llm_available = true; // e.g. circuit breaker closed AND rate limiter has tokens
14
15 let mut brain = Reasoner::new();
16
17 // Answer from a template — strong when we are already confident.
18 brain.add(Action::new("answer_template").consider_labeled(
19 "confidence",
20 Curve::Linear,
21 confidence,
22 ));
23
24 // Escalate to the LLM — strong when urgent, but only if the LLM is available.
25 brain.add(
26 Action::new("call_llm")
27 .gate(llm_available) // constraint-aware: skipped entirely if the LLM is down/limited
28 .consider_labeled("urgency", Curve::Linear, urgency),
29 );
30
31 // An always-available, low-weight fallback so a decision still resolves.
32 brain.add(Action::new("defer").with_base(Score::from_ratio(1, 10)));
33
34 // Abstain if nothing clears the bar — the caller would escalate to a human.
35 let threshold = Score::from_ratio(5, 100);
36 match brain.decide_above(threshold) {
37 Some(decision) => {
38 println!(
39 "chose: {} (utility {}/10000)",
40 decision.id,
41 decision.utility.raw()
42 );
43 if let Some(why) = brain.explain() {
44 for c in why.contributions {
45 println!(
46 " {} : input {} -> {}",
47 c.label,
48 c.input.raw(),
49 c.output.raw()
50 );
51 }
52 }
53 }
54 None => println!("nothing good enough — escalate to a human"),
55 }
56}Source§impl<A: Clone> Reasoner<A>
impl<A: Clone> Reasoner<A>
Sourcepub fn decide(&self) -> Option<Decision<A>>
pub fn decide(&self) -> Option<Decision<A>>
Chooses the highest-utility action, or None if there are none.
Ties resolve deterministically in favor of the earlier-declared action, so the same candidates always yield the same decision.
Sourcepub fn decide_above(&self, threshold: Score) -> Option<Decision<A>>
pub fn decide_above(&self, threshold: Score) -> Option<Decision<A>>
Like decide, but abstains: returns the best
action only when its utility is strictly above threshold, otherwise
None.
Use it to say “nothing here is good enough” and fall back to a slower or
more capable path — for example, escalate to an LLM instead of forcing a
weak choice. A threshold of Score::ZERO abstains exactly when every
action is vetoed or gated off.
Examples found in repository?
9fn main() {
10 // Signals about the current request, each normalized to 0.0..=1.0.
11 let urgency = Score::from_ratio(80, 100);
12 let confidence = Score::from_ratio(40, 100); // how well we already understand it
13 let llm_available = true; // e.g. circuit breaker closed AND rate limiter has tokens
14
15 let mut brain = Reasoner::new();
16
17 // Answer from a template — strong when we are already confident.
18 brain.add(Action::new("answer_template").consider_labeled(
19 "confidence",
20 Curve::Linear,
21 confidence,
22 ));
23
24 // Escalate to the LLM — strong when urgent, but only if the LLM is available.
25 brain.add(
26 Action::new("call_llm")
27 .gate(llm_available) // constraint-aware: skipped entirely if the LLM is down/limited
28 .consider_labeled("urgency", Curve::Linear, urgency),
29 );
30
31 // An always-available, low-weight fallback so a decision still resolves.
32 brain.add(Action::new("defer").with_base(Score::from_ratio(1, 10)));
33
34 // Abstain if nothing clears the bar — the caller would escalate to a human.
35 let threshold = Score::from_ratio(5, 100);
36 match brain.decide_above(threshold) {
37 Some(decision) => {
38 println!(
39 "chose: {} (utility {}/10000)",
40 decision.id,
41 decision.utility.raw()
42 );
43 if let Some(why) = brain.explain() {
44 for c in why.contributions {
45 println!(
46 " {} : input {} -> {}",
47 c.label,
48 c.input.raw(),
49 c.output.raw()
50 );
51 }
52 }
53 }
54 None => println!("nothing good enough — escalate to a human"),
55 }
56}Sourcepub fn decide_weighted(&self, rand: u32) -> Option<Decision<A>>
pub fn decide_weighted(&self, rand: u32) -> Option<Decision<A>>
Chooses an action at random with probability proportional to its utility (roulette selection), so repeated decisions vary instead of always returning the single best.
rand is any uniformly-distributed u32 you supply (e.g. from rand or
getrandom), interpreted as the fraction rand / 2^32. The same rand
and candidates always yield the same choice — the engine never owns a
random source, so it stays deterministic and testable.
Returns None if there are no actions. If every utility is zero, the
earliest-declared action is returned.
Sourcepub fn explain(&self) -> Option<Explanation<A>>
pub fn explain(&self) -> Option<Explanation<A>>
Explains the winning decision: the chosen id, its utility, and the
per-consideration breakdown that produced it. None if there are no
actions. The winner matches decide.
Examples found in repository?
9fn main() {
10 // Signals about the current request, each normalized to 0.0..=1.0.
11 let urgency = Score::from_ratio(80, 100);
12 let confidence = Score::from_ratio(40, 100); // how well we already understand it
13 let llm_available = true; // e.g. circuit breaker closed AND rate limiter has tokens
14
15 let mut brain = Reasoner::new();
16
17 // Answer from a template — strong when we are already confident.
18 brain.add(Action::new("answer_template").consider_labeled(
19 "confidence",
20 Curve::Linear,
21 confidence,
22 ));
23
24 // Escalate to the LLM — strong when urgent, but only if the LLM is available.
25 brain.add(
26 Action::new("call_llm")
27 .gate(llm_available) // constraint-aware: skipped entirely if the LLM is down/limited
28 .consider_labeled("urgency", Curve::Linear, urgency),
29 );
30
31 // An always-available, low-weight fallback so a decision still resolves.
32 brain.add(Action::new("defer").with_base(Score::from_ratio(1, 10)));
33
34 // Abstain if nothing clears the bar — the caller would escalate to a human.
35 let threshold = Score::from_ratio(5, 100);
36 match brain.decide_above(threshold) {
37 Some(decision) => {
38 println!(
39 "chose: {} (utility {}/10000)",
40 decision.id,
41 decision.utility.raw()
42 );
43 if let Some(why) = brain.explain() {
44 for c in why.contributions {
45 println!(
46 " {} : input {} -> {}",
47 c.label,
48 c.input.raw(),
49 c.output.raw()
50 );
51 }
52 }
53 }
54 None => println!("nothing good enough — escalate to a human"),
55 }
56}