ferrin_core/generate_text/
prepare_step.rs1use std::fmt;
4use std::sync::Arc;
5
6use ferrin_message::Message;
7use ferrin_spec::BoxFuture;
8use ferrin_spec::DynLanguageModel;
9use ferrin_spec::JsonValue;
10use ferrin_spec::LanguageModelRef;
11use ferrin_spec::ToolChoice;
12use ferrin_spec::ToolName;
13
14use super::StepResult;
15use crate::error::Error;
16use crate::prompt::CallSettings;
17use crate::prompt::Instructions;
18
19pub struct PrepareStepContext<'a> {
21 pub steps: &'a [StepResult],
23 pub step_number: u32,
25 pub model: &'a Arc<dyn DynLanguageModel>,
27 pub instructions: Option<&'a Instructions>,
29 pub initial_instructions: Option<&'a Instructions>,
31 pub messages: &'a [Message],
33 pub initial_messages: &'a [Message],
35 pub response_messages: &'a [Message],
37 pub tools_context: Option<&'a JsonValue>,
39 pub runtime_context: Option<&'a JsonValue>,
41 #[cfg(feature = "sandbox")]
43 pub sandbox: Option<&'a Arc<dyn ferrin_tool::Sandbox>>,
44}
45
46impl fmt::Debug for PrepareStepContext<'_> {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 f.debug_struct("PrepareStepContext")
49 .field("step_number", &self.step_number)
50 .field("provider", self.model.provider())
51 .field("model_id", self.model.model_id())
52 .field("steps", &self.steps.len())
53 .field("messages", &self.messages.len())
54 .finish_non_exhaustive()
55 }
56}
57
58#[derive(Default)]
61pub struct StepOverrides {
62 pub model: Option<LanguageModelRef>,
64 pub tool_choice: Option<ToolChoice>,
66 pub active_tools: Option<Vec<ToolName>>,
68 pub tool_order: Option<Vec<ToolName>>,
70 pub instructions: Option<Instructions>,
72 pub messages: Option<Vec<Message>>,
74 pub tools_context: Option<JsonValue>,
76 pub runtime_context: Option<JsonValue>,
78 #[cfg(feature = "sandbox")]
80 pub sandbox: Option<Arc<dyn ferrin_tool::Sandbox>>,
81 pub settings: Option<CallSettings>,
83}
84
85impl fmt::Debug for StepOverrides {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 f.debug_struct("StepOverrides")
88 .field("model", &self.model)
89 .field("tool_choice", &self.tool_choice)
90 .field("active_tools", &self.active_tools)
91 .field("tool_order", &self.tool_order)
92 .field("instructions", &self.instructions)
93 .field("messages", &self.messages)
94 .field("has_tools_context", &self.tools_context.is_some())
95 .field("has_runtime_context", &self.runtime_context.is_some())
96 .field("settings", &self.settings)
97 .finish_non_exhaustive()
98 }
99}
100
101impl StepOverrides {
102 #[must_use]
104 pub fn none() -> Self {
105 Self::default()
106 }
107
108 #[must_use]
110 pub fn with_model(mut self, model: impl Into<LanguageModelRef>) -> Self {
111 self.model = Some(model.into());
112 self
113 }
114
115 #[must_use]
117 pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self {
118 self.tool_choice = Some(tool_choice);
119 self
120 }
121
122 #[must_use]
124 pub fn with_active_tools(
125 mut self,
126 names: impl IntoIterator<Item = impl Into<ToolName>>,
127 ) -> Self {
128 self.active_tools = Some(names.into_iter().map(Into::into).collect());
129 self
130 }
131
132 #[must_use]
134 pub fn with_tool_order(mut self, names: impl IntoIterator<Item = impl Into<ToolName>>) -> Self {
135 self.tool_order = Some(names.into_iter().map(Into::into).collect());
136 self
137 }
138
139 #[must_use]
141 pub fn with_instructions(mut self, instructions: impl Into<Instructions>) -> Self {
142 self.instructions = Some(instructions.into());
143 self
144 }
145
146 #[must_use]
148 pub fn with_messages(mut self, messages: impl IntoIterator<Item = Message>) -> Self {
149 self.messages = Some(messages.into_iter().collect());
150 self
151 }
152
153 #[must_use]
155 pub fn with_tools_context(mut self, context: JsonValue) -> Self {
156 self.tools_context = Some(context);
157 self
158 }
159
160 #[must_use]
162 pub fn with_runtime_context(mut self, context: JsonValue) -> Self {
163 self.runtime_context = Some(context);
164 self
165 }
166
167 #[cfg(feature = "sandbox")]
169 #[must_use]
170 pub fn with_sandbox(mut self, sandbox: Arc<dyn ferrin_tool::Sandbox>) -> Self {
171 self.sandbox = Some(sandbox);
172 self
173 }
174
175 #[must_use]
177 pub fn with_settings(mut self, settings: CallSettings) -> Self {
178 self.settings = Some(settings);
179 self
180 }
181}
182
183pub trait PrepareStep: Send + Sync {
188 fn prepare_step<'a>(
190 &'a self,
191 ctx: PrepareStepContext<'a>,
192 ) -> BoxFuture<'a, Result<StepOverrides, Error>>;
193}
194
195impl<F> PrepareStep for F
196where
197 F: Fn(&PrepareStepContext<'_>) -> StepOverrides + Send + Sync,
198{
199 fn prepare_step<'a>(
200 &'a self,
201 ctx: PrepareStepContext<'a>,
202 ) -> BoxFuture<'a, Result<StepOverrides, Error>> {
203 let overrides = self(&ctx);
204 Box::pin(async move { Ok(overrides) })
205 }
206}