1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6use crate::{FerrumError, Result, TokenId};
7
8pub const DEFAULT_CHAT_REPETITION_PENALTY: f32 = 1.1;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct SamplingParams {
18 pub max_tokens: usize,
20 pub temperature: f32,
22 pub top_p: f32,
24 pub top_k: Option<usize>,
26 pub repetition_penalty: f32,
28 pub presence_penalty: f32,
30 pub frequency_penalty: f32,
32 pub stop_sequences: Vec<String>,
34 pub seed: Option<u64>,
36 pub min_p: Option<f32>,
38 pub tfs: Option<f32>,
40 pub typical_p: Option<f32>,
42 pub mirostat: Option<MirostatParams>,
44 #[serde(default)]
46 pub response_format: ResponseFormat,
47 #[serde(default)]
51 pub structured_output_start: StructuredOutputStart,
52 #[serde(default)]
60 pub response_completion_boundary: ResponseCompletionBoundary,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
65#[serde(tag = "mode", content = "delimiter", rename_all = "snake_case")]
66pub enum StructuredOutputStart {
67 #[default]
69 Immediate,
70 AfterDelimiter(String),
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
80pub struct ResponseCompletionEnvelope {
81 pub open_token_text: String,
82 pub close_token_text: String,
83 pub max_envelopes: usize,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
88#[serde(tag = "mode", rename_all = "snake_case")]
89pub enum ResponseCompletionBoundary {
90 #[default]
92 Immediate,
93 AfterDelimiterAndPayload {
97 delimiter: String,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 alternate_envelope: Option<ResponseCompletionEnvelope>,
100 },
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
113#[serde(tag = "type", content = "schema")]
114#[derive(Default)]
115pub enum ResponseFormat {
116 #[default]
118 Text,
119 JsonObject,
121 JsonSchema(String),
123}
124
125impl Default for SamplingParams {
126 fn default() -> Self {
127 Self {
128 max_tokens: 512,
129 temperature: 1.0,
130 top_p: 1.0,
131 top_k: None,
132 repetition_penalty: 1.0,
133 presence_penalty: 0.0,
134 frequency_penalty: 0.0,
135 stop_sequences: vec![],
136 seed: None,
137 min_p: None,
138 tfs: None,
139 typical_p: None,
140 mirostat: None,
141 response_format: ResponseFormat::default(),
142 structured_output_start: StructuredOutputStart::default(),
143 response_completion_boundary: ResponseCompletionBoundary::default(),
144 }
145 }
146}
147
148impl SamplingParams {
149 pub fn greedy() -> Self {
151 Self {
152 temperature: 0.0,
153 top_p: 1.0,
154 top_k: None,
155 ..Default::default()
156 }
157 }
158
159 pub fn with_temperature(temperature: f32) -> Self {
161 Self {
162 temperature,
163 ..Default::default()
164 }
165 }
166
167 pub fn validate(&self) -> Result<()> {
169 if !self.temperature.is_finite() || self.temperature < 0.0 {
170 return Err(FerrumError::invalid_request(
171 "temperature must be finite and non-negative".to_string(),
172 ));
173 }
174 if !self.top_p.is_finite() || self.top_p <= 0.0 || self.top_p > 1.0 {
175 return Err(FerrumError::invalid_request(
176 "top_p must be in range (0, 1]".to_string(),
177 ));
178 }
179 if let Some(top_k) = self.top_k {
180 if top_k == 0 {
181 return Err(FerrumError::invalid_request(
182 "top_k must be positive".to_string(),
183 ));
184 }
185 }
186 if !self.repetition_penalty.is_finite() || self.repetition_penalty <= 0.0 {
187 return Err(FerrumError::invalid_request(
188 "repetition_penalty must be finite and positive".to_string(),
189 ));
190 }
191 if !self.presence_penalty.is_finite() || !(-2.0..=2.0).contains(&self.presence_penalty) {
192 return Err(FerrumError::invalid_request(
193 "presence_penalty must be in range [-2, 2]".to_string(),
194 ));
195 }
196 if !self.frequency_penalty.is_finite() || !(-2.0..=2.0).contains(&self.frequency_penalty) {
197 return Err(FerrumError::invalid_request(
198 "frequency_penalty must be in range [-2, 2]".to_string(),
199 ));
200 }
201 if let Some(min_p) = self.min_p {
202 if !min_p.is_finite() || min_p <= 0.0 || min_p > 1.0 {
203 return Err(FerrumError::invalid_request(
204 "min_p must be in range (0, 1]".to_string(),
205 ));
206 }
207 }
208 if let Some(tfs) = self.tfs {
209 if !tfs.is_finite() || tfs <= 0.0 || tfs > 1.0 {
210 return Err(FerrumError::invalid_request(
211 "tfs must be in range (0, 1]".to_string(),
212 ));
213 }
214 }
215 if let Some(typical_p) = self.typical_p {
216 if !typical_p.is_finite() || typical_p <= 0.0 || typical_p > 1.0 {
217 return Err(FerrumError::invalid_request(
218 "typical_p must be in range (0, 1]".to_string(),
219 ));
220 }
221 }
222 if let ResponseCompletionBoundary::AfterDelimiterAndPayload {
223 delimiter,
224 alternate_envelope,
225 } = &self.response_completion_boundary
226 {
227 if delimiter.is_empty() {
228 return Err(FerrumError::invalid_request(
229 "response completion delimiter must not be empty".to_string(),
230 ));
231 }
232 if let Some(envelope) = alternate_envelope {
233 if envelope.open_token_text.is_empty() || envelope.close_token_text.is_empty() {
234 return Err(FerrumError::invalid_request(
235 "response completion envelope tokens must not be empty".to_string(),
236 ));
237 }
238 if envelope.max_envelopes == 0 {
239 return Err(FerrumError::invalid_request(
240 "response completion envelope limit must be greater than zero".to_string(),
241 ));
242 }
243 }
244 }
245 Ok(())
246 }
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct MirostatParams {
252 pub mode: u8,
254 pub tau: f32,
256 pub eta: f32,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct SamplingPresets {
263 pub presets: HashMap<String, SamplingParams>,
264}
265
266impl Default for SamplingPresets {
267 fn default() -> Self {
268 let mut presets = HashMap::new();
269 presets.insert("greedy".to_string(), SamplingParams::greedy());
270 presets.insert(
271 "creative".to_string(),
272 SamplingParams {
273 temperature: 1.2,
274 top_p: 0.9,
275 top_k: Some(50),
276 repetition_penalty: 1.1,
277 ..Default::default()
278 },
279 );
280 presets.insert(
281 "precise".to_string(),
282 SamplingParams {
283 temperature: 0.3,
284 top_p: 0.95,
285 top_k: Some(20),
286 repetition_penalty: 1.05,
287 ..Default::default()
288 },
289 );
290 Self { presets }
291 }
292}
293
294#[derive(
296 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default,
297)]
298pub enum Priority {
299 Low = 0,
300 #[default]
301 Normal = 1,
302 High = 2,
303 Critical = 3,
304}
305
306#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
308pub enum FinishReason {
309 Length,
311 Stop,
313 EOS,
315 Cancelled,
317 Error,
319 ContentFilter,
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize, Default)]
325pub struct SpecialTokens {
326 pub bos_token: Option<TokenId>,
328 pub eos_token: Option<TokenId>,
330 pub unk_token: Option<TokenId>,
332 pub pad_token: Option<TokenId>,
334 pub sep_token: Option<TokenId>,
336 pub cls_token: Option<TokenId>,
338 pub mask_token: Option<TokenId>,
340 #[serde(default)]
344 pub extra_eos_tokens: Vec<TokenId>,
345}