langchain_rust/language_models/
options.rs1use futures::Future;
2use std::{pin::Pin, sync::Arc};
3use tokio::sync::Mutex;
4
5use crate::schemas::{FunctionCallBehavior, FunctionDefinition, ResponseFormat};
6
7#[derive(Clone)]
8pub struct CallOptions {
9 pub candidate_count: Option<usize>,
10 pub max_tokens: Option<u32>,
11 pub temperature: Option<f32>,
12 pub stop_words: Option<Vec<String>>,
13 pub streaming_func: Option<
14 Arc<
15 Mutex<dyn FnMut(String) -> Pin<Box<dyn Future<Output = Result<(), ()>> + Send>> + Send>,
16 >,
17 >,
18 pub top_k: Option<usize>,
19 pub top_p: Option<f32>,
20 pub seed: Option<usize>,
21 pub min_length: Option<usize>,
22 pub max_length: Option<usize>,
23 pub n: Option<usize>,
24 pub repetition_penalty: Option<f32>,
25 pub frequency_penalty: Option<f32>,
26 pub presence_penalty: Option<f32>,
27 pub functions: Option<Vec<FunctionDefinition>>,
28 pub function_call_behavior: Option<FunctionCallBehavior>,
29 pub response_format: Option<ResponseFormat>,
30 pub stream_usage: Option<bool>,
31}
32
33impl Default for CallOptions {
34 fn default() -> Self {
35 CallOptions::new()
36 }
37}
38impl CallOptions {
39 pub fn new() -> Self {
40 CallOptions {
41 candidate_count: None,
42 max_tokens: None,
43 temperature: None,
44 stop_words: None,
45 streaming_func: None,
46 top_k: None,
47 top_p: None,
48 seed: None,
49 min_length: None,
50 max_length: None,
51 n: None,
52 repetition_penalty: None,
53 frequency_penalty: None,
54 presence_penalty: None,
55 functions: None,
56 function_call_behavior: None,
57 response_format: None,
58 stream_usage: None,
59 }
60 }
61
62 pub fn with_max_tokens(mut self, max_tokens: u32) -> Self {
64 self.max_tokens = Some(max_tokens);
65 self
66 }
67
68 pub fn with_candidate_count(mut self, candidate_count: usize) -> Self {
69 self.candidate_count = Some(candidate_count);
70 self
71 }
72
73 pub fn with_temperature(mut self, temperature: f32) -> Self {
74 self.temperature = Some(temperature);
75 self
76 }
77
78 pub fn with_stop_words(mut self, stop_words: Vec<String>) -> Self {
79 self.stop_words = Some(stop_words);
80 self
81 }
82
83 pub fn with_streaming_func<F, Fut>(mut self, mut func: F) -> Self
85 where
86 F: FnMut(String) -> Fut + Send + 'static,
87 Fut: Future<Output = Result<(), ()>> + Send + 'static,
88 {
89 let func = Arc::new(Mutex::new(
90 move |s: String| -> Pin<Box<dyn Future<Output = Result<(), ()>> + Send>> {
91 Box::pin(func(s))
92 },
93 ));
94
95 self.streaming_func = Some(func);
96 self
97 }
98
99 pub fn with_top_k(mut self, top_k: usize) -> Self {
100 self.top_k = Some(top_k);
101 self
102 }
103
104 pub fn with_top_p(mut self, top_p: f32) -> Self {
105 self.top_p = Some(top_p);
106 self
107 }
108
109 pub fn with_seed(mut self, seed: usize) -> Self {
110 self.seed = Some(seed);
111 self
112 }
113
114 pub fn with_min_length(mut self, min_length: usize) -> Self {
115 self.min_length = Some(min_length);
116 self
117 }
118
119 pub fn with_max_length(mut self, max_length: usize) -> Self {
120 self.max_length = Some(max_length);
121 self
122 }
123
124 pub fn with_n(mut self, n: usize) -> Self {
125 self.n = Some(n);
126 self
127 }
128
129 pub fn with_repetition_penalty(mut self, repetition_penalty: f32) -> Self {
130 self.repetition_penalty = Some(repetition_penalty);
131 self
132 }
133
134 pub fn with_frequency_penalty(mut self, frequency_penalty: f32) -> Self {
135 self.frequency_penalty = Some(frequency_penalty);
136 self
137 }
138
139 pub fn with_presence_penalty(mut self, presence_penalty: f32) -> Self {
140 self.presence_penalty = Some(presence_penalty);
141 self
142 }
143
144 pub fn with_functions(mut self, functions: Vec<FunctionDefinition>) -> Self {
145 self.functions = Some(functions);
146 self
147 }
148
149 pub fn with_function_call_behavior(mut self, behavior: FunctionCallBehavior) -> Self {
150 self.function_call_behavior = Some(behavior);
151 self
152 }
153
154 pub fn with_response_format(mut self, response_format: ResponseFormat) -> Self {
155 self.response_format = Some(response_format);
156 self
157 }
158
159 pub fn with_stream_usage(mut self, stream_usage: bool) -> Self {
160 self.stream_usage = Some(stream_usage);
161 self
162 }
163
164 pub fn merge_options(&mut self, incoming_options: CallOptions) {
165 self.candidate_count = incoming_options.candidate_count.or(self.candidate_count);
167 self.max_tokens = incoming_options.max_tokens.or(self.max_tokens);
168 self.temperature = incoming_options.temperature.or(self.temperature);
169 self.top_k = incoming_options.top_k.or(self.top_k);
170 self.top_p = incoming_options.top_p.or(self.top_p);
171 self.seed = incoming_options.seed.or(self.seed);
172 self.min_length = incoming_options.min_length.or(self.min_length);
173 self.max_length = incoming_options.max_length.or(self.max_length);
174 self.n = incoming_options.n.or(self.n);
175 self.repetition_penalty = incoming_options
176 .repetition_penalty
177 .or(self.repetition_penalty);
178 self.frequency_penalty = incoming_options
179 .frequency_penalty
180 .or(self.frequency_penalty);
181 self.presence_penalty = incoming_options.presence_penalty.or(self.presence_penalty);
182 self.function_call_behavior = incoming_options
183 .function_call_behavior
184 .or(self.function_call_behavior.clone());
185 self.response_format = incoming_options
186 .response_format
187 .or(self.response_format.clone());
188 self.stream_usage = incoming_options.stream_usage.or(self.stream_usage);
189
190 if let Some(mut new_stop_words) = incoming_options.stop_words {
192 if let Some(existing_stop_words) = &mut self.stop_words {
193 existing_stop_words.append(&mut new_stop_words);
194 } else {
195 self.stop_words = Some(new_stop_words);
196 }
197 }
198
199 if let Some(mut incoming_functions) = incoming_options.functions {
201 if let Some(existing_functions) = &mut self.functions {
202 existing_functions.append(&mut incoming_functions);
203 } else {
204 self.functions = Some(incoming_functions);
205 }
206 }
207
208 self.streaming_func = incoming_options
211 .streaming_func
212 .or_else(|| self.streaming_func.clone());
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use serde_json::json;
220
221 #[test]
222 fn merge_prefers_incoming_scalars_and_keeps_existing() {
223 let mut base = CallOptions::new()
224 .with_max_tokens(100)
225 .with_temperature(0.1)
226 .with_seed(7);
227 base.merge_options(CallOptions::new().with_temperature(0.9).with_top_p(0.5));
228
229 assert_eq!(base.max_tokens, Some(100), "kept when incoming is None");
230 assert_eq!(base.temperature, Some(0.9), "incoming wins");
231 assert_eq!(base.top_p, Some(0.5), "new field set");
232 assert_eq!(base.seed, Some(7));
233 }
234
235 #[test]
236 fn merge_appends_stop_words_and_functions() {
237 let f = |n: &str| FunctionDefinition::new(n, "d", json!({}));
238 let mut base = CallOptions::new()
239 .with_stop_words(vec!["a".into()])
240 .with_functions(vec![f("one")]);
241 base.merge_options(
242 CallOptions::new()
243 .with_stop_words(vec!["b".into()])
244 .with_functions(vec![f("two")]),
245 );
246 assert_eq!(base.stop_words, Some(vec!["a".into(), "b".into()]));
247 let names: Vec<_> = base
248 .functions
249 .unwrap()
250 .into_iter()
251 .map(|f| f.name)
252 .collect();
253 assert_eq!(names, vec!["one", "two"]);
254
255 let mut empty = CallOptions::new();
256 empty.merge_options(CallOptions::new().with_stop_words(vec!["x".into()]));
257 assert_eq!(empty.stop_words, Some(vec!["x".into()]));
258 }
259
260 #[test]
261 fn merge_streaming_func_and_behaviors() {
262 let mut base = CallOptions::new().with_streaming_func(|_s| async { Ok(()) });
263 base.merge_options(
264 CallOptions::new()
265 .with_function_call_behavior(FunctionCallBehavior::Auto)
266 .with_response_format(ResponseFormat::JsonObject)
267 .with_stream_usage(true),
268 );
269 assert!(base.streaming_func.is_some(), "existing func kept");
270 assert!(matches!(
271 base.function_call_behavior,
272 Some(FunctionCallBehavior::Auto)
273 ));
274 assert!(matches!(
275 base.response_format,
276 Some(ResponseFormat::JsonObject)
277 ));
278 assert_eq!(base.stream_usage, Some(true));
279 }
280
281 #[tokio::test]
282 async fn streaming_func_is_invoked() {
283 let seen = Arc::new(Mutex::new(Vec::<String>::new()));
284 let seen_clone = seen.clone();
285 let opts = CallOptions::new().with_streaming_func(move |s| {
286 let seen = seen_clone.clone();
287 async move {
288 seen.lock().await.push(s);
289 Ok(())
290 }
291 });
292 let func = opts.streaming_func.unwrap();
293 let mut f = func.lock().await;
294 f("chunk".to_string()).await.unwrap();
295 assert_eq!(*seen.lock().await, vec!["chunk".to_string()]);
296 }
297}