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::{LLAMA_CPP_MAX_DEVICES, LlamaModelParams};
401
402 #[test]
403 fn device_capacity_is_not_smaller_than_the_upstream_limit() {
404 let upstream_limit = crate::max_devices();
405 assert!(
406 LLAMA_CPP_MAX_DEVICES >= upstream_limit,
407 "LLAMA_CPP_MAX_DEVICES ({LLAMA_CPP_MAX_DEVICES}) backs a fixed-size device array and is \
408 clamped against llama_max_devices() ({upstream_limit}); once upstream reports more \
409 devices than the array can hold, the extra devices are dropped without any error"
410 );
411 }
412
413 #[test]
414 fn default_params_have_expected_values() {
415 let params = LlamaModelParams::default();
416
417 assert_eq!(params.n_gpu_layers(), -1);
418 assert_eq!(params.main_gpu(), 0);
419 assert!(!params.vocab_only());
420 assert!(params.use_mmap());
421 assert!(!params.use_mlock());
422 assert_eq!(params.split_mode(), Ok(LlamaSplitMode::Layer));
423 assert!(params.devices().is_empty());
424 }
425
426 #[test]
427 fn with_n_gpu_layers_sets_the_offload_count() {
428 let params = LlamaModelParams::default().with_n_gpu_layers(999);
429
430 assert_eq!(params.n_gpu_layers(), 999);
431 }
432
433 #[test]
434 fn with_n_gpu_layers_sets_value() {
435 let params = LlamaModelParams::default().with_n_gpu_layers(32);
436
437 assert_eq!(params.n_gpu_layers(), 32);
438 }
439
440 #[test]
441 fn with_main_gpu_sets_value() {
442 let params = LlamaModelParams::default().with_main_gpu(2);
443
444 assert_eq!(params.main_gpu(), 2);
445 }
446
447 #[test]
448 fn with_split_mode_none() {
449 let params = LlamaModelParams::default().with_split_mode(LlamaSplitMode::None);
450
451 assert_eq!(params.split_mode(), Ok(LlamaSplitMode::None));
452 }
453
454 #[test]
455 fn with_split_mode_row() {
456 let params = LlamaModelParams::default().with_split_mode(LlamaSplitMode::Row);
457
458 assert_eq!(params.split_mode(), Ok(LlamaSplitMode::Row));
459 }
460
461 #[test]
462 fn with_vocab_only_enables() {
463 let params = LlamaModelParams::default().with_vocab_only(true);
464
465 assert!(params.vocab_only());
466 }
467
468 #[test]
469 fn with_vocab_only_disables() {
470 let params = LlamaModelParams::default().with_vocab_only(false);
471
472 assert!(!params.vocab_only());
473 }
474
475 #[test]
476 fn with_use_mmap_enables() {
477 let params = LlamaModelParams::default().with_use_mmap(true);
478
479 assert!(params.use_mmap());
480 }
481
482 #[test]
483 fn with_use_mmap_disables() {
484 let params = LlamaModelParams::default().with_use_mmap(false);
485
486 assert!(!params.use_mmap());
487 }
488
489 #[test]
490 fn with_no_alloc_enables() {
491 let params = LlamaModelParams::default().with_no_alloc(true);
492
493 assert!(params.no_alloc());
494 }
495
496 #[test]
497 fn with_no_alloc_disables() {
498 let params = LlamaModelParams::default().with_no_alloc(false);
499
500 assert!(!params.no_alloc());
501 }
502
503 #[test]
504 fn with_no_alloc_true_disables_mmap() {
505 let params = LlamaModelParams::default()
506 .with_use_mmap(true)
507 .with_no_alloc(true);
508
509 assert!(params.no_alloc());
510 assert!(!params.use_mmap());
511 }
512
513 #[test]
514 fn default_no_alloc_is_false() {
515 let params = LlamaModelParams::default();
516
517 assert!(!params.no_alloc());
518 }
519
520 #[test]
521 fn with_use_mlock_enables() {
522 let params = LlamaModelParams::default().with_use_mlock(true);
523
524 assert!(params.use_mlock());
525 }
526
527 #[test]
528 fn with_use_mlock_disables() {
529 let params = LlamaModelParams::default().with_use_mlock(false);
530
531 assert!(!params.use_mlock());
532 }
533
534 #[test]
535 fn debug_format_contains_field_names() {
536 let params = LlamaModelParams::default();
537 let debug_output = format!("{params:?}");
538
539 assert!(debug_output.contains("n_gpu_layers"));
540 assert!(debug_output.contains("main_gpu"));
541 assert!(debug_output.contains("vocab_only"));
542 assert!(debug_output.contains("use_mmap"));
543 assert!(debug_output.contains("use_mlock"));
544 assert!(debug_output.contains("split_mode"));
545 }
546
547 #[test]
548 fn builder_chaining_preserves_all_values() {
549 let params = LlamaModelParams::default()
550 .with_n_gpu_layers(10)
551 .with_main_gpu(1)
552 .with_split_mode(LlamaSplitMode::Row)
553 .with_vocab_only(true)
554 .with_use_mlock(true);
555
556 assert_eq!(params.n_gpu_layers(), 10);
557 assert_eq!(params.main_gpu(), 1);
558 assert_eq!(params.split_mode(), Ok(LlamaSplitMode::Row));
559 assert!(params.vocab_only());
560 assert!(params.use_mlock());
561 }
562
563 #[test]
564 fn with_devices_empty_list_succeeds() {
565 let params = LlamaModelParams::default().with_devices(&[]);
566
567 assert!(params.is_ok());
568 assert!(params.unwrap().devices().is_empty());
569 }
570
571 #[test]
572 fn with_devices_invalid_index_returns_error() {
573 let result = LlamaModelParams::default().with_devices(&[999_999]);
574
575 assert_eq!(
576 std::mem::discriminant(&result.unwrap_err()),
577 std::mem::discriminant(&crate::LlamaCppError::BackendDeviceNotFound(0)),
578 );
579 }
580
581 #[test]
582 fn add_cpu_buft_override_succeeds() {
583 let mut params = std::pin::pin!(LlamaModelParams::default());
584 let result = params.as_mut().add_cpu_buft_override(c"test_pattern");
585
586 assert!(result.is_ok());
587 }
588
589 #[test]
590 fn add_cpu_buft_override_twice_fails_with_slot_not_empty() {
591 let mut params = std::pin::pin!(LlamaModelParams::default());
592 params
593 .as_mut()
594 .add_cpu_buft_override(c"first_pattern")
595 .unwrap();
596 let result = params.as_mut().add_cpu_buft_override(c"second_pattern");
597
598 assert_eq!(
599 result.unwrap_err(),
600 crate::error::ModelParamsError::SlotNotEmpty
601 );
602 }
603
604 #[test]
605 fn add_cpu_moe_override_succeeds() {
606 let mut params = std::pin::pin!(LlamaModelParams::default());
607 let result = params.as_mut().add_cpu_moe_override();
608
609 assert!(result.is_ok());
610 }
611
612 #[test]
613 fn append_kv_override_twice_fails_with_slot_not_empty() {
614 use crate::model::params::param_override_value::ParamOverrideValue;
615 use std::ffi::CString;
616
617 let mut params = std::pin::pin!(LlamaModelParams::default());
618 let key = CString::new("first_key").unwrap();
619 params
620 .as_mut()
621 .append_kv_override(&key, ParamOverrideValue::Int(1))
622 .unwrap();
623
624 let key2 = CString::new("second_key").unwrap();
625 let result = params
626 .as_mut()
627 .append_kv_override(&key2, ParamOverrideValue::Int(2));
628
629 assert_eq!(
630 result.unwrap_err(),
631 crate::error::ModelParamsError::SlotNotEmpty
632 );
633 }
634
635 #[test]
636 fn with_devices_too_many_returns_max_exceeded() {
637 let too_many: Vec<usize> = (0..17).collect();
638 let result = LlamaModelParams::default().with_devices(&too_many);
639
640 assert!(
641 result
642 .unwrap_err()
643 .to_string()
644 .contains("Max devices exceeded")
645 );
646 }
647
648 #[test]
649 fn with_devices_sets_devices_when_available() {
650 #[cfg(feature = "dynamic-backends")]
651 crate::load_backends::load_backends().unwrap();
652
653 let dev_count = unsafe { llama_cpp_bindings_sys::ggml_backend_dev_count() };
654 assert!(dev_count > 0, "Test requires at least one backend device");
655
656 let params = LlamaModelParams::default().with_devices(&[0]).unwrap();
657
658 assert_eq!(params.devices().len(), 1);
659 assert_eq!(params.devices()[0], 0);
660 }
661
662 #[test]
663 fn with_devices_invalid_index_returns_not_found() {
664 let invalid_index = usize::MAX;
665 let result = LlamaModelParams::default().with_devices(&[invalid_index]);
666
667 assert!(result.unwrap_err().to_string().contains("Backend device"));
668 }
669
670 #[test]
671 #[cfg(not(target_os = "windows"))]
672 fn append_kv_override_with_high_byte_returns_invalid_character_error() {
673 use crate::model::params::param_override_value::ParamOverrideValue;
674
675 let key_bytes: &[u8] = b"\xff\0";
676 let key = std::ffi::CStr::from_bytes_with_nul(key_bytes).unwrap();
677 let mut params = std::pin::pin!(LlamaModelParams::default());
678 let result = params
679 .as_mut()
680 .append_kv_override(key, ParamOverrideValue::Int(1));
681
682 assert_eq!(
683 std::mem::discriminant(&result.unwrap_err()),
684 std::mem::discriminant(&crate::error::ModelParamsError::InvalidCharacterInKey {
685 byte: 0,
686 reason: String::new(),
687 }),
688 );
689 }
690
691 #[test]
692 #[cfg(not(target_os = "windows"))]
693 fn add_cpu_buft_override_with_high_byte_returns_invalid_character_error() {
694 let key_bytes: &[u8] = b"\xff\0";
695 let key = std::ffi::CStr::from_bytes_with_nul(key_bytes).unwrap();
696 let mut params = std::pin::pin!(LlamaModelParams::default());
697 let result = params.as_mut().add_cpu_buft_override(key);
698
699 assert_eq!(
700 std::mem::discriminant(&result.unwrap_err()),
701 std::mem::discriminant(&crate::error::ModelParamsError::InvalidCharacterInKey {
702 byte: 0,
703 reason: String::new(),
704 }),
705 );
706 }
707
708 #[test]
709 fn append_kv_override_with_empty_slot_vector_returns_no_available_slot() {
710 use crate::model::params::param_override_value::ParamOverrideValue;
711
712 let mut params = LlamaModelParams::default();
713 params.kv_overrides.clear();
714 let mut pinned = std::pin::pin!(params);
715
716 let result = pinned
717 .as_mut()
718 .append_kv_override(c"any_key", ParamOverrideValue::Int(1));
719
720 assert_eq!(
721 result.unwrap_err(),
722 crate::error::ModelParamsError::NoAvailableSlot
723 );
724 }
725
726 #[test]
727 fn add_cpu_buft_override_with_empty_slot_vector_returns_no_available_slot() {
728 let mut params = LlamaModelParams::default();
729 params.buft_overrides.clear();
730 let mut pinned = std::pin::pin!(params);
731
732 let result = pinned.as_mut().add_cpu_buft_override(c"any_pattern");
733
734 assert_eq!(
735 result.unwrap_err(),
736 crate::error::ModelParamsError::NoAvailableSlot
737 );
738 }
739
740 #[test]
741 #[serial_test::serial]
742 fn fit_params_invalid_model_path_returns_error() {
743 use crate::context::params::LlamaContextParams;
744 use crate::error::FitError;
745 use crate::llama_backend::LlamaBackend;
746
747 let _backend = LlamaBackend::init();
748 let mut params = std::pin::pin!(LlamaModelParams::default());
749 let mut context_params = LlamaContextParams::default();
750 let mut margins = vec![0usize; crate::max_devices()];
751
752 let bogus_path = c"/nonexistent/path/to/model.gguf";
753 let result = params.as_mut().fit_params(
754 bogus_path,
755 &mut context_params,
756 &mut margins,
757 512,
758 llama_cpp_bindings_sys::GGML_LOG_LEVEL_NONE,
759 );
760
761 assert!(
762 matches!(result, Err(FitError::Aborted | FitError::Reported { .. })),
763 "expected Aborted or Reported, got {result:?}"
764 );
765 }
766
767 #[test]
768 fn fit_params_status_ok_returns_ok() {
769 let result = super::fit_params_status_to_result(
770 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_OK,
771 0,
772 std::ptr::null_mut(),
773 );
774
775 assert_eq!(result, Ok(()));
776 }
777
778 #[test]
779 fn fit_params_status_reported_failure_returns_no_fitting_memory_layout() {
780 let result = super::fit_params_status_to_result(
781 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_REPORTED_FAILURE,
782 0,
783 std::ptr::null_mut(),
784 );
785
786 assert_eq!(result, Err(crate::error::FitError::NoFittingMemoryLayout));
787 }
788
789 #[test]
790 fn fit_params_status_reported_error_returns_aborted() {
791 let result = super::fit_params_status_to_result(
792 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_REPORTED_ERROR,
793 0,
794 std::ptr::null_mut(),
795 );
796
797 assert_eq!(result, Err(crate::error::FitError::Aborted));
798 }
799
800 #[test]
801 fn fit_params_status_unrecognized_code_returns_unknown_status() {
802 let result = super::fit_params_status_to_result(
803 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_RETURNED_UNRECOGNIZED_STATUS_CODE,
804 42,
805 std::ptr::null_mut(),
806 );
807
808 assert_eq!(
809 result,
810 Err(crate::error::FitError::UnknownStatus { code: 42 })
811 );
812 }
813
814 #[test]
815 fn fit_params_status_allocation_failed_returns_not_enough_memory() {
816 let result = super::fit_params_status_to_result(
817 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_ERROR_STRING_ALLOCATION_FAILED,
818 0,
819 std::ptr::null_mut(),
820 );
821
822 assert_eq!(result, Err(crate::error::FitError::NotEnoughMemory));
823 }
824
825 #[test]
826 fn fit_params_status_cxx_exception_returns_reported_with_unknown_error() {
827 let result = super::fit_params_status_to_result(
828 llama_cpp_bindings_sys::LLAMA_RS_FIT_PARAMS_VENDORED_THREW_CXX_EXCEPTION,
829 0,
830 std::ptr::null_mut(),
831 );
832
833 assert_eq!(
834 result,
835 Err(crate::error::FitError::Reported {
836 message: "unknown error".to_owned()
837 })
838 );
839 }
840
841 #[test]
842 #[should_panic(expected = "unrecognized wrapper status")]
843 fn fit_params_status_out_of_range_panics() {
844 let _ = super::fit_params_status_to_result(
845 llama_cpp_bindings_sys::llama_rs_fit_params_status::MAX,
846 0,
847 std::ptr::null_mut(),
848 );
849 }
850}