llama_cpp_bindings/model/
params.rs1use crate::LlamaCppError;
2use crate::context::params::LlamaContextParams;
3use crate::error::{FitError, ModelParamsError};
4use crate::model::llama_split_mode_parse_error::LlamaSplitModeParseError;
5use crate::model::params::fit_result::FitResult;
6use crate::model::params::kv_overrides::KvOverrides;
7use crate::model::split_mode::LlamaSplitMode;
8use std::ffi::{CStr, c_char};
9use std::fmt::{Debug, Formatter};
10use std::pin::Pin;
11use std::ptr::null;
12
13pub mod fit_result;
14pub mod kv_override_value_iterator;
15pub mod kv_overrides;
16pub mod param_override_value;
17pub mod unknown_kv_override_tag;
18
19pub const LLAMA_CPP_MAX_DEVICES: usize = 16;
20
21pub struct LlamaModelParams {
22 pub params: llama_cpp_bindings_sys::llama_model_params,
23 kv_overrides: Vec<llama_cpp_bindings_sys::llama_model_kv_override>,
24 buft_overrides: Vec<llama_cpp_bindings_sys::llama_model_tensor_buft_override>,
25 devices: Pin<Box<[llama_cpp_bindings_sys::ggml_backend_dev_t; LLAMA_CPP_MAX_DEVICES]>>,
26 tensor_split: Vec<f32>,
27}
28
29impl Debug for LlamaModelParams {
30 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31 f.debug_struct("LlamaModelParams")
32 .field("n_gpu_layers", &self.params.n_gpu_layers)
33 .field("main_gpu", &self.params.main_gpu)
34 .field("vocab_only", &self.params.vocab_only)
35 .field("use_mmap", &self.params.use_mmap)
36 .field("use_mlock", &self.params.use_mlock)
37 .field("split_mode", &self.split_mode())
38 .field("devices", &self.devices)
39 .field("kv_overrides", &"vec of kv_overrides")
40 .finish_non_exhaustive()
41 }
42}
43
44impl LlamaModelParams {
45 #[must_use]
46 pub const fn kv_overrides(&self) -> KvOverrides<'_> {
47 KvOverrides::new(self)
48 }
49
50 pub fn append_kv_override(
55 mut self: Pin<&mut Self>,
56 key: &CStr,
57 value: param_override_value::ParamOverrideValue,
58 ) -> Result<(), ModelParamsError> {
59 let kv_override = self
60 .kv_overrides
61 .get_mut(0)
62 .ok_or(ModelParamsError::NoAvailableSlot)?;
63
64 if kv_override.key[0] != 0 {
65 return Err(ModelParamsError::SlotNotEmpty);
66 }
67
68 for (i, &byte) in key.to_bytes_with_nul().iter().enumerate() {
69 kv_override.key[i] = c_char::try_from(byte).map_err(|convert_error| {
70 ModelParamsError::InvalidCharacterInKey {
71 byte,
72 reason: convert_error.to_string(),
73 }
74 })?;
75 }
76
77 kv_override.tag = value.tag();
78 kv_override.__bindgen_anon_1 = value.value();
79
80 self.push_kv_override_terminator();
81
82 Ok(())
83 }
84
85 fn push_kv_override_terminator(mut self: Pin<&mut Self>) {
86 self.params.kv_overrides = null();
87
88 self.kv_overrides
89 .push(llama_cpp_bindings_sys::llama_model_kv_override {
90 key: [0; 128],
91 tag: 0,
92 __bindgen_anon_1: llama_cpp_bindings_sys::llama_model_kv_override__bindgen_ty_1 {
93 val_i64: 0,
94 },
95 });
96
97 self.params.kv_overrides = self.kv_overrides.as_ptr();
98 }
99}
100
101impl LlamaModelParams {
102 pub fn add_cpu_moe_override(self: Pin<&mut Self>) -> Result<(), ModelParamsError> {
106 self.add_cpu_buft_override(c"\\.ffn_(up|down|gate)_(ch|)exps")
107 }
108
109 pub fn add_cpu_buft_override(
113 mut self: Pin<&mut Self>,
114 key: &CStr,
115 ) -> Result<(), ModelParamsError> {
116 let buft_override = self
117 .buft_overrides
118 .get_mut(0)
119 .ok_or(ModelParamsError::NoAvailableSlot)?;
120
121 if !buft_override.pattern.is_null() {
122 return Err(ModelParamsError::SlotNotEmpty);
123 }
124
125 for &byte in key.to_bytes_with_nul() {
126 c_char::try_from(byte).map_err(|convert_error| {
127 ModelParamsError::InvalidCharacterInKey {
128 byte,
129 reason: convert_error.to_string(),
130 }
131 })?;
132 }
133
134 buft_override.pattern = key.as_ptr();
135 buft_override.buft = unsafe { llama_cpp_bindings_sys::ggml_backend_cpu_buffer_type() };
136
137 self.push_buft_override_terminator();
138
139 Ok(())
140 }
141
142 fn push_buft_override_terminator(mut self: Pin<&mut Self>) {
143 self.params.tensor_buft_overrides = null();
144
145 self.buft_overrides
146 .push(llama_cpp_bindings_sys::llama_model_tensor_buft_override {
147 pattern: null(),
148 buft: std::ptr::null_mut(),
149 });
150
151 self.params.tensor_buft_overrides = self.buft_overrides.as_ptr();
152 }
153}
154
155impl LlamaModelParams {
156 #[must_use]
157 pub const fn n_gpu_layers(&self) -> i32 {
158 self.params.n_gpu_layers
159 }
160
161 #[must_use]
162 pub const fn main_gpu(&self) -> i32 {
163 self.params.main_gpu
164 }
165
166 #[must_use]
167 pub const fn vocab_only(&self) -> bool {
168 self.params.vocab_only
169 }
170
171 #[must_use]
172 pub const fn use_mmap(&self) -> bool {
173 self.params.use_mmap
174 }
175
176 #[must_use]
177 pub const fn use_mlock(&self) -> bool {
178 self.params.use_mlock
179 }
180
181 pub fn split_mode(&self) -> Result<LlamaSplitMode, LlamaSplitModeParseError> {
184 LlamaSplitMode::try_from(self.params.split_mode)
185 }
186
187 #[must_use]
188 pub fn devices(&self) -> Vec<usize> {
189 let mut backend_devices = Vec::new();
190 for i in 0..unsafe { llama_cpp_bindings_sys::ggml_backend_dev_count() } {
191 let dev = unsafe { llama_cpp_bindings_sys::ggml_backend_dev_get(i) };
192 backend_devices.push(dev);
193 }
194 let mut devices = Vec::new();
195 for &dev in self.devices.iter() {
196 if dev.is_null() {
197 break;
198 }
199 let matched_index = backend_devices
200 .iter()
201 .enumerate()
202 .find(|&(_i, &d)| d == dev)
203 .map(|(index, _)| index);
204
205 if let Some(index) = matched_index {
206 devices.push(index);
207 }
208 }
209 devices
210 }
211
212 #[must_use]
213 pub const fn with_n_gpu_layers(mut self, n_gpu_layers: i32) -> Self {
214 self.params.n_gpu_layers = n_gpu_layers;
215 self
216 }
217
218 #[must_use]
219 pub const fn with_main_gpu(mut self, main_gpu: i32) -> Self {
220 self.params.main_gpu = main_gpu;
221 self
222 }
223
224 #[must_use]
225 pub const fn with_vocab_only(mut self, vocab_only: bool) -> Self {
226 self.params.vocab_only = vocab_only;
227 self
228 }
229
230 #[must_use]
231 pub const fn with_use_mmap(mut self, use_mmap: bool) -> Self {
232 self.params.use_mmap = use_mmap;
233 self
234 }
235
236 #[must_use]
237 pub const fn no_alloc(&self) -> bool {
238 self.params.no_alloc
239 }
240
241 #[must_use]
242 pub const fn with_no_alloc(mut self, no_alloc: bool) -> Self {
243 self.params.no_alloc = no_alloc;
244 if no_alloc {
245 self.params.use_mmap = false;
246 }
247 self
248 }
249
250 #[must_use]
251 pub const fn with_use_mlock(mut self, use_mlock: bool) -> Self {
252 self.params.use_mlock = use_mlock;
253 self
254 }
255
256 #[must_use]
257 pub fn with_split_mode(mut self, split_mode: LlamaSplitMode) -> Self {
258 self.params.split_mode = split_mode.into();
259 self
260 }
261
262 pub fn with_devices(mut self, devices: &[usize]) -> Result<Self, LlamaCppError> {
265 for dev in self.devices.iter_mut() {
266 *dev = std::ptr::null_mut();
267 }
268 let max_devices = crate::max_devices().min(LLAMA_CPP_MAX_DEVICES);
269 if devices.len() > max_devices {
270 return Err(LlamaCppError::MaxDevicesExceeded(max_devices));
271 }
272 for (i, &dev) in devices.iter().enumerate() {
273 if dev >= unsafe { llama_cpp_bindings_sys::ggml_backend_dev_count() } {
274 return Err(LlamaCppError::BackendDeviceNotFound(dev));
275 }
276 let backend_dev = unsafe { llama_cpp_bindings_sys::ggml_backend_dev_get(dev) };
277 self.devices[i] = backend_dev;
278 }
279 self.params.devices = self.devices.as_mut_ptr();
280
281 Ok(self)
282 }
283}
284
285fn fit_params_status_to_result(
286 status: llama_cpp_bindings_sys::llama_rs_fit_params_status,
287 out_unrecognized_status_code: i32,
288 out_error: *mut c_char,
289) -> Result<(), FitError> {
290 match status {
291 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_OK => Ok(()),
292 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_REPORTED_FAILURE => {
293 Err(FitError::NoFittingMemoryLayout)
294 }
295 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_REPORTED_ERROR => {
296 Err(FitError::Aborted)
297 }
298 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_RETURNED_UNRECOGNIZED_STATUS_CODE => {
299 Err(FitError::UnknownStatus {
300 code: out_unrecognized_status_code,
301 })
302 }
303 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_ERROR_STRING_ALLOCATION_FAILED => {
304 Err(FitError::NotEnoughMemory)
305 }
306 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_THREW_CXX_EXCEPTION => {
307 let message = unsafe { crate::ffi_error_reader::read_and_free_cpp_error(out_error) };
308 Err(FitError::Reported { message })
309 }
310 other => unreachable!("llama_rs_fit_params returned unrecognized wrapper status: {other}"),
311 }
312}
313
314impl LlamaModelParams {
315 pub fn fit_params(
319 mut self: Pin<&mut Self>,
320 model_path: &CStr,
321 context_params: &mut LlamaContextParams,
322 margins: &mut [usize],
323 n_ctx_min: u32,
324 log_level: llama_cpp_bindings_sys::ggml_log_level,
325 ) -> Result<FitResult, FitError> {
326 let max_devices = unsafe { llama_cpp_bindings_sys::llama_max_devices() };
327 let max_buft = unsafe { llama_cpp_bindings_sys::llama_max_tensor_buft_overrides() };
328
329 self.tensor_split.clear();
330 self.tensor_split.resize(max_devices, 0.0);
331
332 self.buft_overrides.clear();
333 self.buft_overrides.resize(
334 max_buft + 1,
335 llama_cpp_bindings_sys::llama_model_tensor_buft_override {
336 pattern: null(),
337 buft: std::ptr::null_mut(),
338 },
339 );
340
341 self.params.tensor_split = null::<f32>();
342 self.params.tensor_buft_overrides = null();
343
344 let mut out_unrecognized_status_code: i32 = 0;
345 let mut out_error: *mut c_char = std::ptr::null_mut();
346
347 let status = unsafe {
348 llama_cpp_bindings_sys::llama_rs_fit_params(
349 model_path.as_ptr(),
350 &raw mut self.params,
351 &raw mut context_params.context_params,
352 self.tensor_split.as_mut_ptr(),
353 self.buft_overrides.as_mut_ptr(),
354 margins.as_mut_ptr(),
355 n_ctx_min,
356 log_level,
357 &raw mut out_unrecognized_status_code,
358 &raw mut out_error,
359 )
360 };
361
362 fit_params_status_to_result(status, out_unrecognized_status_code, out_error)?;
363
364 self.params.tensor_split = self.tensor_split.as_ptr();
365 self.params.tensor_buft_overrides = self.buft_overrides.as_ptr();
366
367 Ok(FitResult {
368 n_ctx: context_params.context_params.n_ctx,
369 })
370 }
371}
372
373impl Default for LlamaModelParams {
374 fn default() -> Self {
375 let default_params = unsafe { llama_cpp_bindings_sys::llama_model_default_params() };
376 Self {
377 params: default_params,
378 kv_overrides: vec![llama_cpp_bindings_sys::llama_model_kv_override {
379 key: [0; 128],
380 tag: 0,
381 __bindgen_anon_1: llama_cpp_bindings_sys::llama_model_kv_override__bindgen_ty_1 {
382 val_i64: 0,
383 },
384 }],
385 buft_overrides: vec![llama_cpp_bindings_sys::llama_model_tensor_buft_override {
386 pattern: null(),
387 buft: std::ptr::null_mut(),
388 }],
389 devices: Box::pin([std::ptr::null_mut(); 16]),
390 tensor_split: Vec::new(),
391 }
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use crate::model::split_mode::LlamaSplitMode;
398
399 use super::LlamaModelParams;
400
401 #[test]
402 fn default_params_have_expected_values() {
403 let params = LlamaModelParams::default();
404
405 assert_eq!(params.n_gpu_layers(), -1);
406 assert_eq!(params.main_gpu(), 0);
407 assert!(!params.vocab_only());
408 assert!(params.use_mmap());
409 assert!(!params.use_mlock());
410 assert_eq!(params.split_mode(), Ok(LlamaSplitMode::Layer));
411 assert!(params.devices().is_empty());
412 }
413
414 #[test]
415 fn with_n_gpu_layers_sets_the_offload_count() {
416 let params = LlamaModelParams::default().with_n_gpu_layers(999);
417
418 assert_eq!(params.n_gpu_layers(), 999);
419 }
420
421 #[test]
422 fn with_n_gpu_layers_sets_value() {
423 let params = LlamaModelParams::default().with_n_gpu_layers(32);
424
425 assert_eq!(params.n_gpu_layers(), 32);
426 }
427
428 #[test]
429 fn with_main_gpu_sets_value() {
430 let params = LlamaModelParams::default().with_main_gpu(2);
431
432 assert_eq!(params.main_gpu(), 2);
433 }
434
435 #[test]
436 fn with_split_mode_none() {
437 let params = LlamaModelParams::default().with_split_mode(LlamaSplitMode::None);
438
439 assert_eq!(params.split_mode(), Ok(LlamaSplitMode::None));
440 }
441
442 #[test]
443 fn with_split_mode_row() {
444 let params = LlamaModelParams::default().with_split_mode(LlamaSplitMode::Row);
445
446 assert_eq!(params.split_mode(), Ok(LlamaSplitMode::Row));
447 }
448
449 #[test]
450 fn with_vocab_only_enables() {
451 let params = LlamaModelParams::default().with_vocab_only(true);
452
453 assert!(params.vocab_only());
454 }
455
456 #[test]
457 fn with_vocab_only_disables() {
458 let params = LlamaModelParams::default().with_vocab_only(false);
459
460 assert!(!params.vocab_only());
461 }
462
463 #[test]
464 fn with_use_mmap_enables() {
465 let params = LlamaModelParams::default().with_use_mmap(true);
466
467 assert!(params.use_mmap());
468 }
469
470 #[test]
471 fn with_use_mmap_disables() {
472 let params = LlamaModelParams::default().with_use_mmap(false);
473
474 assert!(!params.use_mmap());
475 }
476
477 #[test]
478 fn with_no_alloc_enables() {
479 let params = LlamaModelParams::default().with_no_alloc(true);
480
481 assert!(params.no_alloc());
482 }
483
484 #[test]
485 fn with_no_alloc_disables() {
486 let params = LlamaModelParams::default().with_no_alloc(false);
487
488 assert!(!params.no_alloc());
489 }
490
491 #[test]
492 fn with_no_alloc_true_disables_mmap() {
493 let params = LlamaModelParams::default()
494 .with_use_mmap(true)
495 .with_no_alloc(true);
496
497 assert!(params.no_alloc());
498 assert!(!params.use_mmap());
499 }
500
501 #[test]
502 fn default_no_alloc_is_false() {
503 let params = LlamaModelParams::default();
504
505 assert!(!params.no_alloc());
506 }
507
508 #[test]
509 fn with_use_mlock_enables() {
510 let params = LlamaModelParams::default().with_use_mlock(true);
511
512 assert!(params.use_mlock());
513 }
514
515 #[test]
516 fn with_use_mlock_disables() {
517 let params = LlamaModelParams::default().with_use_mlock(false);
518
519 assert!(!params.use_mlock());
520 }
521
522 #[test]
523 fn debug_format_contains_field_names() {
524 let params = LlamaModelParams::default();
525 let debug_output = format!("{params:?}");
526
527 assert!(debug_output.contains("n_gpu_layers"));
528 assert!(debug_output.contains("main_gpu"));
529 assert!(debug_output.contains("vocab_only"));
530 assert!(debug_output.contains("use_mmap"));
531 assert!(debug_output.contains("use_mlock"));
532 assert!(debug_output.contains("split_mode"));
533 }
534
535 #[test]
536 fn builder_chaining_preserves_all_values() {
537 let params = LlamaModelParams::default()
538 .with_n_gpu_layers(10)
539 .with_main_gpu(1)
540 .with_split_mode(LlamaSplitMode::Row)
541 .with_vocab_only(true)
542 .with_use_mlock(true);
543
544 assert_eq!(params.n_gpu_layers(), 10);
545 assert_eq!(params.main_gpu(), 1);
546 assert_eq!(params.split_mode(), Ok(LlamaSplitMode::Row));
547 assert!(params.vocab_only());
548 assert!(params.use_mlock());
549 }
550
551 #[test]
552 fn with_devices_empty_list_succeeds() {
553 let params = LlamaModelParams::default().with_devices(&[]);
554
555 assert!(params.is_ok());
556 assert!(params.unwrap().devices().is_empty());
557 }
558
559 #[test]
560 fn with_devices_invalid_index_returns_error() {
561 let result = LlamaModelParams::default().with_devices(&[999_999]);
562
563 assert_eq!(
564 std::mem::discriminant(&result.unwrap_err()),
565 std::mem::discriminant(&crate::LlamaCppError::BackendDeviceNotFound(0)),
566 );
567 }
568
569 #[test]
570 fn add_cpu_buft_override_succeeds() {
571 let mut params = std::pin::pin!(LlamaModelParams::default());
572 let result = params.as_mut().add_cpu_buft_override(c"test_pattern");
573
574 assert!(result.is_ok());
575 }
576
577 #[test]
578 fn add_cpu_buft_override_twice_fails_with_slot_not_empty() {
579 let mut params = std::pin::pin!(LlamaModelParams::default());
580 params
581 .as_mut()
582 .add_cpu_buft_override(c"first_pattern")
583 .unwrap();
584 let result = params.as_mut().add_cpu_buft_override(c"second_pattern");
585
586 assert_eq!(
587 result.unwrap_err(),
588 crate::error::ModelParamsError::SlotNotEmpty
589 );
590 }
591
592 #[test]
593 fn add_cpu_moe_override_succeeds() {
594 let mut params = std::pin::pin!(LlamaModelParams::default());
595 let result = params.as_mut().add_cpu_moe_override();
596
597 assert!(result.is_ok());
598 }
599
600 #[test]
601 fn append_kv_override_twice_fails_with_slot_not_empty() {
602 use crate::model::params::param_override_value::ParamOverrideValue;
603 use std::ffi::CString;
604
605 let mut params = std::pin::pin!(LlamaModelParams::default());
606 let key = CString::new("first_key").unwrap();
607 params
608 .as_mut()
609 .append_kv_override(&key, ParamOverrideValue::Int(1))
610 .unwrap();
611
612 let key2 = CString::new("second_key").unwrap();
613 let result = params
614 .as_mut()
615 .append_kv_override(&key2, ParamOverrideValue::Int(2));
616
617 assert_eq!(
618 result.unwrap_err(),
619 crate::error::ModelParamsError::SlotNotEmpty
620 );
621 }
622
623 #[test]
624 fn with_devices_too_many_returns_max_exceeded() {
625 let too_many: Vec<usize> = (0..17).collect();
626 let result = LlamaModelParams::default().with_devices(&too_many);
627
628 assert!(
629 result
630 .unwrap_err()
631 .to_string()
632 .contains("Max devices exceeded")
633 );
634 }
635
636 #[test]
637 fn with_devices_sets_devices_when_available() {
638 #[cfg(feature = "dynamic-backends")]
639 crate::load_backends::load_backends().unwrap();
640
641 let dev_count = unsafe { llama_cpp_bindings_sys::ggml_backend_dev_count() };
642 assert!(dev_count > 0, "Test requires at least one backend device");
643
644 let params = LlamaModelParams::default().with_devices(&[0]).unwrap();
645
646 assert_eq!(params.devices().len(), 1);
647 assert_eq!(params.devices()[0], 0);
648 }
649
650 #[test]
651 fn with_devices_invalid_index_returns_not_found() {
652 let invalid_index = usize::MAX;
653 let result = LlamaModelParams::default().with_devices(&[invalid_index]);
654
655 assert!(result.unwrap_err().to_string().contains("Backend device"));
656 }
657
658 #[test]
659 #[cfg(not(target_os = "windows"))]
660 fn append_kv_override_with_high_byte_returns_invalid_character_error() {
661 use crate::model::params::param_override_value::ParamOverrideValue;
662
663 let key_bytes: &[u8] = b"\xff\0";
664 let key = std::ffi::CStr::from_bytes_with_nul(key_bytes).unwrap();
665 let mut params = std::pin::pin!(LlamaModelParams::default());
666 let result = params
667 .as_mut()
668 .append_kv_override(key, ParamOverrideValue::Int(1));
669
670 assert_eq!(
671 std::mem::discriminant(&result.unwrap_err()),
672 std::mem::discriminant(&crate::error::ModelParamsError::InvalidCharacterInKey {
673 byte: 0,
674 reason: String::new(),
675 }),
676 );
677 }
678
679 #[test]
680 #[cfg(not(target_os = "windows"))]
681 fn add_cpu_buft_override_with_high_byte_returns_invalid_character_error() {
682 let key_bytes: &[u8] = b"\xff\0";
683 let key = std::ffi::CStr::from_bytes_with_nul(key_bytes).unwrap();
684 let mut params = std::pin::pin!(LlamaModelParams::default());
685 let result = params.as_mut().add_cpu_buft_override(key);
686
687 assert_eq!(
688 std::mem::discriminant(&result.unwrap_err()),
689 std::mem::discriminant(&crate::error::ModelParamsError::InvalidCharacterInKey {
690 byte: 0,
691 reason: String::new(),
692 }),
693 );
694 }
695
696 #[test]
697 fn append_kv_override_with_empty_slot_vector_returns_no_available_slot() {
698 use crate::model::params::param_override_value::ParamOverrideValue;
699
700 let mut params = LlamaModelParams::default();
701 params.kv_overrides.clear();
702 let mut pinned = std::pin::pin!(params);
703
704 let result = pinned
705 .as_mut()
706 .append_kv_override(c"any_key", ParamOverrideValue::Int(1));
707
708 assert_eq!(
709 result.unwrap_err(),
710 crate::error::ModelParamsError::NoAvailableSlot
711 );
712 }
713
714 #[test]
715 fn add_cpu_buft_override_with_empty_slot_vector_returns_no_available_slot() {
716 let mut params = LlamaModelParams::default();
717 params.buft_overrides.clear();
718 let mut pinned = std::pin::pin!(params);
719
720 let result = pinned.as_mut().add_cpu_buft_override(c"any_pattern");
721
722 assert_eq!(
723 result.unwrap_err(),
724 crate::error::ModelParamsError::NoAvailableSlot
725 );
726 }
727
728 #[test]
729 #[serial_test::serial]
730 fn fit_params_invalid_model_path_returns_error() {
731 use crate::context::params::LlamaContextParams;
732 use crate::error::FitError;
733 use crate::llama_backend::LlamaBackend;
734
735 let _backend = LlamaBackend::init();
736 let mut params = std::pin::pin!(LlamaModelParams::default());
737 let mut context_params = LlamaContextParams::default();
738 let mut margins = vec![0usize; crate::max_devices()];
739
740 let bogus_path = c"/nonexistent/path/to/model.gguf";
741 let result = params.as_mut().fit_params(
742 bogus_path,
743 &mut context_params,
744 &mut margins,
745 512,
746 llama_cpp_bindings_sys::GGML_LOG_LEVEL_NONE,
747 );
748
749 assert!(
750 matches!(result, Err(FitError::Aborted | FitError::Reported { .. })),
751 "expected Aborted or Reported, got {result:?}"
752 );
753 }
754
755 #[test]
756 fn fit_params_status_ok_returns_ok() {
757 let result = super::fit_params_status_to_result(
758 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_OK,
759 0,
760 std::ptr::null_mut(),
761 );
762
763 assert_eq!(result, Ok(()));
764 }
765
766 #[test]
767 fn fit_params_status_reported_failure_returns_no_fitting_memory_layout() {
768 let result = super::fit_params_status_to_result(
769 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_REPORTED_FAILURE,
770 0,
771 std::ptr::null_mut(),
772 );
773
774 assert_eq!(result, Err(crate::error::FitError::NoFittingMemoryLayout));
775 }
776
777 #[test]
778 fn fit_params_status_reported_error_returns_aborted() {
779 let result = super::fit_params_status_to_result(
780 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_REPORTED_ERROR,
781 0,
782 std::ptr::null_mut(),
783 );
784
785 assert_eq!(result, Err(crate::error::FitError::Aborted));
786 }
787
788 #[test]
789 fn fit_params_status_unrecognized_code_returns_unknown_status() {
790 let result = super::fit_params_status_to_result(
791 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_RETURNED_UNRECOGNIZED_STATUS_CODE,
792 42,
793 std::ptr::null_mut(),
794 );
795
796 assert_eq!(
797 result,
798 Err(crate::error::FitError::UnknownStatus { code: 42 })
799 );
800 }
801
802 #[test]
803 fn fit_params_status_allocation_failed_returns_not_enough_memory() {
804 let result = super::fit_params_status_to_result(
805 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_ERROR_STRING_ALLOCATION_FAILED,
806 0,
807 std::ptr::null_mut(),
808 );
809
810 assert_eq!(result, Err(crate::error::FitError::NotEnoughMemory));
811 }
812
813 #[test]
814 fn fit_params_status_cxx_exception_returns_reported_with_unknown_error() {
815 let result = super::fit_params_status_to_result(
816 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_THREW_CXX_EXCEPTION,
817 0,
818 std::ptr::null_mut(),
819 );
820
821 assert_eq!(
822 result,
823 Err(crate::error::FitError::Reported {
824 message: "unknown error".to_owned()
825 })
826 );
827 }
828
829 #[test]
830 #[should_panic(expected = "unrecognized wrapper status")]
831 fn fit_params_status_out_of_range_panics() {
832 let _ = super::fit_params_status_to_result(
833 llama_cpp_bindings_sys::llama_rs_fit_params_status::MAX,
834 0,
835 std::ptr::null_mut(),
836 );
837 }
838}