alith_client/workflows/reason/
one_round.rs

1use super::{
2    PrimitiveTrait, ReasonResult, ReasonTrait, SentencesPrimitive, decision::DecisionTrait,
3};
4use crate::components::{
5    InstructPromptTrait,
6    cascade::{CascadeFlow, step::StepConfig},
7    instruct_prompt::InstructPrompt,
8};
9use alith_interface::requests::{
10    completion::CompletionRequest,
11    req_components::{RequestConfig, RequestConfigTrait},
12};
13
14pub struct ReasonOneRound<P> {
15    pub reasoning_sentences: u8,
16    pub conclusion_sentences: u8,
17    pub result_can_be_none: bool,
18    pub primitive: P,
19    pub base_req: CompletionRequest,
20    pub instruct_prompt: InstructPrompt,
21}
22
23impl<P: PrimitiveTrait + ReasonTrait> ReasonOneRound<P> {
24    pub async fn return_primitive(&mut self) -> crate::Result<P::PrimitiveResult> {
25        let res = self.return_result().await?;
26        if let Some(primitive_result) =
27            self.primitive.result_index_to_primitive(res.result_index)?
28        {
29            Ok(primitive_result)
30        } else {
31            Err(anyhow::format_err!("No result returned."))
32        }
33    }
34
35    pub async fn return_optional_primitive(&mut self) -> crate::Result<Option<P::PrimitiveResult>> {
36        let res = self.return_optional_result().await?;
37        self.primitive.result_index_to_primitive(res.result_index)
38    }
39
40    pub async fn return_result(&mut self) -> crate::Result<ReasonResult> {
41        self.result_can_be_none = false;
42
43        let mut flow = match self.reason_one_round() {
44            Ok(flow) => flow,
45            Err(e) => {
46                crate::error!("Error creating reason one round flow: {}", e);
47                return Err(e);
48            }
49        };
50        flow.run_all_rounds(&mut self.base_req).await?;
51
52        ReasonResult::new(flow, &self.primitive, &self.base_req)
53    }
54
55    pub async fn return_optional_result(&mut self) -> crate::Result<ReasonResult> {
56        self.result_can_be_none = true;
57        let mut flow = match self.reason_one_round() {
58            Ok(flow) => flow,
59            Err(e) => {
60                crate::error!("Error creating reason one round flow: {}", e);
61                return Err(e);
62            }
63        };
64        flow.run_all_rounds(&mut self.base_req).await?;
65        ReasonResult::new(flow, &self.primitive, &self.base_req)
66    }
67
68    pub fn reasoning_sentences(&mut self, reasoning_sentences: u8) -> &mut Self {
69        self.reasoning_sentences = reasoning_sentences;
70        self
71    }
72
73    pub fn conclusion_sentences(&mut self, conclusion_sentences: u8) -> &mut Self {
74        self.conclusion_sentences = conclusion_sentences;
75        self
76    }
77
78    fn reason_one_round(&mut self) -> crate::Result<CascadeFlow> {
79        let mut flow = CascadeFlow::new("Reason One Round");
80
81        flow.new_round(
82        "A request will be provided. Think out loud about the request. State the arguments before arriving at a conclusion with, 'Therefore, we can conclude:...', and finish with a solution by saying, 'Thus, the solution...'. With no yapping.").add_guidance_step(
83        &StepConfig {
84            ..StepConfig::default()
85        },
86        "'no yapping' refers to a design principle or behavior where the AI model provides direct, concise responses without unnecessary verbosity or filler content. Therefore, we can conclude: The user would like to get straight to the point. Thus, the solution is to to resolve the request as efficiently as possible.",
87    );
88
89        let task = self.build_task()?;
90
91        // CoT reasoning
92        let step_config = StepConfig {
93            step_prefix: Some("Thinking out loud about the users request...".to_string()),
94            stop_word_done: "Therefore, we can conclude".to_string(),
95            cache_prompt: false, // Clears the cache on the initial request
96            grammar: SentencesPrimitive::default()
97                .min_count(1)
98                .max_count(self.reasoning_sentences)
99                .grammar(),
100            ..StepConfig::default()
101        };
102
103        flow.new_round(task).add_inference_step(&step_config);
104
105        // Conclusion
106        let step_config = StepConfig {
107            step_prefix: Some(format!(
108                "The user requested a conclusion of {}. Therefore, we can conclude:",
109                self.primitive.solution_description(self.result_can_be_none),
110            )),
111            stop_word_done: "Thus, the solution".to_string(),
112            grammar: SentencesPrimitive::default()
113                .min_count(1)
114                .max_count(self.conclusion_sentences)
115                .grammar(),
116
117            ..StepConfig::default()
118        };
119        flow.last_round()?.add_inference_step(&step_config);
120
121        // Instructions Restatement
122        if let Some(instructions) = self.instruct_prompt.build_instructions() {
123            let instructions_restatement =
124                format!("The user's original request was '{}'.", &instructions,);
125            let step_config = StepConfig {
126                step_prefix: None,
127                grammar: SentencesPrimitive::default().grammar(),
128                ..StepConfig::default()
129            };
130            flow.last_round()?
131                .add_guidance_step(&step_config, instructions_restatement);
132        };
133
134        // Solution
135        let solution = format!(
136            "Thus, the {} solution to the user's request is:",
137            self.primitive.type_description(self.result_can_be_none),
138        );
139        let step_config = StepConfig {
140            step_prefix: Some(solution),
141            stop_word_no_result: self
142                .primitive
143                .stop_word_result_is_none(self.result_can_be_none),
144            grammar: self.primitive.grammar(),
145            ..StepConfig::default()
146        };
147        flow.last_round()?.add_inference_step(&step_config);
148
149        Ok(flow)
150    }
151
152    fn build_task(&mut self) -> crate::Result<String> {
153        let instructions = self.instruct_prompt.build_instructions();
154        let supporting_material = self.instruct_prompt.build_supporting_material();
155
156        Ok(match (instructions, supporting_material) {
157            (Some(instructions), Some(supporting_material)) => {
158                format!(
159                    "The user provided some supporting material: {supporting_material}\n The user's request is: {instructions}",
160                )
161            }
162            (Some(instructions), None) => {
163                format!("The user's request is: {instructions}",)
164            }
165            (None, Some(supporting_material)) => {
166                format!("The user's request is: {supporting_material}",)
167            }
168            (None, None) => {
169                return Err(anyhow::format_err!(
170                    "No instructions or supporting material provided."
171                ));
172            }
173        })
174    }
175}
176
177impl<P: PrimitiveTrait> RequestConfigTrait for ReasonOneRound<P> {
178    fn config(&mut self) -> &mut RequestConfig {
179        &mut self.base_req.config
180    }
181
182    fn reset_request(&mut self) {
183        self.instruct_prompt.reset_instruct_prompt();
184        self.base_req.reset_completion_request();
185    }
186}
187
188impl<P: PrimitiveTrait + ReasonTrait> InstructPromptTrait for ReasonOneRound<P> {
189    fn instruct_prompt_mut(&mut self) -> &mut InstructPrompt {
190        &mut self.instruct_prompt
191    }
192}
193
194impl<P: PrimitiveTrait + ReasonTrait> DecisionTrait for ReasonOneRound<P> {
195    type ReasonPrimitive = P;
196    fn base_req(&self) -> &CompletionRequest {
197        &self.base_req
198    }
199
200    fn base_req_mut(&mut self) -> &mut CompletionRequest {
201        &mut self.base_req
202    }
203
204    fn primitive(&self) -> &Self::ReasonPrimitive {
205        &self.primitive
206    }
207
208    async fn return_reason_result(
209        &mut self,
210        result_can_be_none: bool,
211    ) -> crate::Result<ReasonResult> {
212        if result_can_be_none {
213            self.return_optional_result().await
214        } else {
215            self.return_result().await
216        }
217    }
218}