1pub mod guided;
27pub mod json_mode;
28pub mod schema_to_regex;
29pub mod schema_validation;
30pub mod structured_output;
31
32pub use ferrum_interfaces::sampler::{
34 GreedySampler, LogitsProcessor, LogitsProcessorChain, MinPProcessor, MultiSampler,
35 MultinomialSampler, PresenceFrequencyPenaltyProcessor, ProcessorPriority,
36 RepetitionPenaltyProcessor, Sampler, SamplingConfig, SamplingConfigBuilder, SamplingContext,
37 SamplingStats, TemperatureProcessor, TopKProcessor, TopPProcessor,
38};
39
40pub use ferrum_types::{Result, SamplingParams, TokenId};
42
43use rand::RngCore;
44use std::collections::HashMap;
45
46#[derive(Debug, Clone, Default)]
48pub struct DefaultSamplerFactory;
49
50impl DefaultSamplerFactory {
51 pub fn new() -> Self {
53 Self
54 }
55
56 pub fn build_config(&self, params: &SamplingParams) -> SamplingConfig {
58 SamplingConfig::from_params(params)
59 }
60
61 pub fn create_sampler(&self, params: &SamplingParams) -> Box<dyn Sampler + Send + Sync> {
65 if params.temperature == 0.0 {
66 Box::new(GreedySampler)
67 } else {
68 Box::new(MultinomialSampler)
69 }
70 }
71
72 pub fn build_pipeline(&self, params: &SamplingParams) -> SamplingPipeline {
74 let config = self.build_config(params);
75 SamplingPipeline { config }
76 }
77}
78
79pub struct SamplingPipeline {
84 config: SamplingConfig,
85}
86
87impl SamplingPipeline {
88 pub fn new(params: &SamplingParams) -> Self {
90 let config = SamplingConfig::from_params(params);
91 Self { config }
92 }
93
94 pub fn config(&self) -> &SamplingConfig {
96 &self.config
97 }
98
99 pub fn sample_next(
109 &self,
110 step: usize,
111 logits: &mut [f32],
112 previous_tokens: &[TokenId],
113 token_frequencies: &HashMap<TokenId, usize>,
114 sampling_params: &SamplingParams,
115 rng: &mut dyn RngCore,
116 ) -> Result<TokenId> {
117 let vocab_size = logits.len();
118 let ctx = SamplingContext::new(
119 step,
120 sampling_params,
121 logits,
122 previous_tokens,
123 token_frequencies,
124 vocab_size,
125 );
126 self.config.sample(ctx, rng)
127 }
128
129 pub fn sample_simple(&self, logits: &mut [f32], rng: &mut dyn RngCore) -> Result<TokenId> {
131 let params = SamplingParams::default();
132 let empty_tokens = Vec::new();
133 let empty_freqs = HashMap::new();
134 self.sample_next(0, logits, &empty_tokens, &empty_freqs, ¶ms, rng)
135 }
136}
137
138pub fn build_sampling_config(params: &SamplingParams) -> SamplingConfig {
146 SamplingConfig::from_params(params)
147}
148
149pub fn sampler_from_params(params: &SamplingParams) -> Box<dyn Sampler + Send + Sync> {
153 DefaultSamplerFactory::new().create_sampler(params)
154}
155
156pub fn pipeline_from_params(params: &SamplingParams) -> SamplingPipeline {
158 DefaultSamplerFactory::new().build_pipeline(params)
159}
160
161pub fn greedy_sampler() -> Box<dyn Sampler + Send + Sync> {
163 Box::new(GreedySampler)
164}
165
166pub fn multinomial_sampler() -> Box<dyn Sampler + Send + Sync> {
168 Box::new(MultinomialSampler)
169}
170
171#[cfg(test)]
176mod tests {
177 use super::*;
178 use rand::rngs::StdRng;
179 use rand::SeedableRng;
180
181 #[test]
182 fn test_factory_creates_greedy_for_zero_temp() {
183 let factory = DefaultSamplerFactory::new();
184 let params = SamplingParams {
185 temperature: 0.0,
186 ..Default::default()
187 };
188 let sampler = factory.create_sampler(¶ms);
189 assert!(sampler.is_deterministic());
190 }
191
192 #[test]
193 fn test_factory_creates_multinomial_for_nonzero_temp() {
194 let factory = DefaultSamplerFactory::new();
195 let params = SamplingParams {
196 temperature: 1.0,
197 ..Default::default()
198 };
199 let sampler = factory.create_sampler(¶ms);
200 assert!(!sampler.is_deterministic());
201 }
202
203 #[test]
204 fn test_build_sampling_config() {
205 let params = SamplingParams {
206 temperature: 0.8,
207 top_k: Some(50),
208 top_p: 0.95,
209 repetition_penalty: 1.1,
210 ..Default::default()
211 };
212 let config = build_sampling_config(¶ms);
213 assert_eq!(config.processor_chain.processor_names().len(), 4);
216 }
217
218 #[test]
219 fn test_pipeline_sample_simple() {
220 let params = SamplingParams::greedy();
221 let pipeline = pipeline_from_params(¶ms);
222 let mut rng = StdRng::seed_from_u64(42);
223
224 let mut logits = vec![1.0, 5.0, 2.0, 0.5];
225 let token = pipeline.sample_simple(&mut logits, &mut rng).unwrap();
226
227 assert_eq!(token.get(), 1);
229 }
230
231 #[test]
232 fn test_greedy_sampler_deterministic() {
233 let sampler = greedy_sampler();
234 assert!(sampler.is_deterministic());
235 assert_eq!(sampler.name(), "greedy");
236 }
237
238 #[test]
239 fn test_multinomial_sampler_stochastic() {
240 let sampler = multinomial_sampler();
241 assert!(!sampler.is_deterministic());
242 assert_eq!(sampler.name(), "multinomial");
243 }
244
245 #[test]
246 fn test_pipeline_with_context() {
247 let params = SamplingParams {
248 temperature: 1.0,
249 repetition_penalty: 1.2,
250 ..Default::default()
251 };
252 let pipeline = SamplingPipeline::new(¶ms);
253 let mut rng = StdRng::seed_from_u64(42);
254
255 let mut logits = vec![1.0, 2.0, 3.0, 2.0];
256 let previous_tokens = vec![TokenId::new(2)]; let mut freqs = HashMap::new();
258 freqs.insert(TokenId::new(2), 1);
259
260 let token = pipeline
261 .sample_next(0, &mut logits, &previous_tokens, &freqs, ¶ms, &mut rng)
262 .unwrap();
263
264 assert!(token.get() < 4);
266 }
267}