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