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