1pub const ABI_VERSION_MAJOR: u32 = 0;
2pub const ABI_VERSION_MINOR: u32 = 1;
3pub const ABI_VERSION_PATCH: u32 = 35;
4pub const FEATURE_BACKEND_DEVICES: u64 = 1 << 23;
5pub const FEATURE_RUNTIME_EVENTS: u64 = 1 << 24;
6pub const FEATURE_NATIVE_MTP_N1: u64 = 1 << 25;
7pub const FEATURE_NGRAM_CACHE_DRAFT: u64 = 1 << 26;
8pub const FEATURE_INKLING_MTP_MM: u64 = 1 << 27;
9
10#[cfg(feature = "dynamic-runtime")]
11mod dynamic_library;
12
13#[repr(C)]
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct AbiVersion {
16 pub major: u32,
17 pub minor: u32,
18 pub patch: u32,
19}
20
21pub const fn runtime_abi_supported(version: AbiVersion) -> bool {
25 version.major == ABI_VERSION_MAJOR
26 && version.minor == ABI_VERSION_MINOR
27 && version.patch == ABI_VERSION_PATCH
28}
29
30use std::ffi::{c_char, c_int, c_void};
31
32pub type LlamaLogCallback =
33 Option<unsafe extern "C" fn(level: c_int, text: *const c_char, user_data: *mut c_void)>;
34pub type MtmdProgressCallback =
35 Option<unsafe extern "C" fn(progress: f32, user_data: *mut c_void) -> bool>;
36pub type SkippyRuntimeEventCallback =
37 Option<unsafe extern "C" fn(event: *const SkippyRuntimeEventV1, user_data: *mut c_void)>;
38
39#[repr(transparent)]
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
41pub struct SkippyRuntimeEventCategory(pub u32);
42
43impl SkippyRuntimeEventCategory {
44 pub const MODEL_OPEN: Self = Self(1);
45 pub const BACKEND: Self = Self(2);
46 pub const SESSION: Self = Self(3);
47 pub const KV: Self = Self(4);
48 pub const WARNING: Self = Self(5);
49}
50
51#[repr(transparent)]
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
53pub struct SkippyRuntimeEventKind(pub u32);
54
55impl SkippyRuntimeEventKind {
56 pub const MODEL_OPEN_STARTED: Self = Self(1);
57 pub const MODEL_OPEN_PROGRESS: Self = Self(2);
58 pub const BACKEND_DEVICE_SELECTED: Self = Self(3);
59 pub const MODEL_OPEN_FINISHED: Self = Self(4);
60 pub const MODEL_OPEN_FAILED_HANDLED: Self = Self(5);
61}
62
63#[repr(transparent)]
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
65pub struct SkippyRuntimeEventEmitterKind(pub u32);
66
67impl SkippyRuntimeEventEmitterKind {
68 pub const UNKNOWN: Self = Self(0);
69 pub const OPEN_THREAD: Self = Self(1);
70 pub const WORKER_THREAD: Self = Self(2);
71}
72
73#[repr(transparent)]
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
75pub struct SkippyRuntimeEventProgressUnit(pub u32);
76
77impl SkippyRuntimeEventProgressUnit {
78 pub const NONE: Self = Self(0);
79 pub const BYTES: Self = Self(1);
80 pub const ITEMS: Self = Self(2);
81 pub const TENSORS: Self = Self(3);
82 pub const STEPS: Self = Self(4);
83}
84
85#[repr(transparent)]
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
87pub struct SkippyRuntimeEventFailureCode(pub u32);
88
89impl SkippyRuntimeEventFailureCode {
90 pub const NONE: Self = Self(0);
91 pub const INVALID_ARGUMENT: Self = Self(1);
92 pub const IO_ERROR: Self = Self(2);
93 pub const MODEL_ERROR: Self = Self(3);
94 pub const RUNTIME_ERROR: Self = Self(4);
95 pub const BACKEND_ERROR: Self = Self(5);
96 pub const CANCELLED: Self = Self(6);
97 pub const INTERNAL_ERROR: Self = Self(7);
98}
99
100#[repr(C)]
101#[derive(Debug, Clone, Copy)]
102pub struct SkippyRuntimeEventV1 {
103 pub abi_version: u32,
104 pub struct_size: u32,
105 pub category: SkippyRuntimeEventCategory,
106 pub kind: SkippyRuntimeEventKind,
107 pub emitter: SkippyRuntimeEventEmitterKind,
108 pub reserved0: u32,
109 pub sequence: u64,
110 pub timestamp_mono_ns: u64,
111 pub model_id: u64,
112 pub stage_id: u64,
113 pub session_id: u64,
114 pub progress_current: u64,
115 pub progress_total: u64,
116 pub progress_unit: SkippyRuntimeEventProgressUnit,
117 pub failure_code: SkippyRuntimeEventFailureCode,
118 pub status: Status,
119 pub reserved1: u32,
120 pub detail_ptr: *const c_char,
121 pub detail_len: u64,
122}
123
124#[repr(C)]
125#[derive(Debug, Clone, Copy)]
126pub struct SkippyRuntimeEventReporterV1 {
127 pub abi_version: u32,
128 pub struct_size: u32,
129 pub callback: SkippyRuntimeEventCallback,
130 pub user_data: *mut c_void,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134#[repr(i32)]
135pub enum Status {
136 Ok = 0,
137 Error = 1,
138 InvalidArgument = 2,
139 Unsupported = 3,
140 BufferTooSmall = 4,
141 IoError = 5,
142 ModelError = 6,
143 RuntimeError = 7,
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147#[repr(i32)]
148pub enum LoadMode {
149 RuntimeSlice = 0,
150 LayerPackage = 1,
151 ArtifactSlice = 2,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155#[repr(i32)]
156pub enum TensorRole {
157 Unknown = 0,
158 Metadata = 1,
159 Tokenizer = 2,
160 Embedding = 3,
161 Layer = 4,
162 FinalNorm = 5,
163 Output = 6,
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167#[repr(i32)]
168pub enum ActivationDType {
169 Unknown = 0,
170 F32 = 1,
171 F16 = 2,
172 Bf16 = 3,
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176#[repr(i32)]
177pub enum ActivationLayout {
178 Opaque = 0,
179 TokenMajor = 1,
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183#[repr(i32)]
184pub enum BackendDeviceType {
185 Cpu = 0,
186 Gpu = 1,
187 IGpu = 2,
188 Accel = 3,
189 Meta = 4,
190}
191
192pub const BACKEND_DEVICE_CAP_ASYNC: u64 = 1 << 0;
193pub const BACKEND_DEVICE_CAP_HOST_BUFFER: u64 = 1 << 1;
194pub const BACKEND_DEVICE_CAP_BUFFER_FROM_HOST_PTR: u64 = 1 << 2;
195pub const BACKEND_DEVICE_CAP_EVENTS: u64 = 1 << 3;
196
197#[repr(C)]
198pub struct Error {
199 pub status: Status,
200 pub message: *const c_char,
201}
202
203#[repr(C)]
204#[derive(Debug, Clone, Copy)]
205pub struct RuntimeConfig {
206 pub stage_index: i32,
207 pub layer_start: i32,
208 pub layer_end: i32,
209 pub ctx_size: i32,
210 pub lane_count: i32,
211 pub n_batch: i32,
212 pub n_ubatch: i32,
213 pub n_threads: i32,
214 pub n_threads_batch: i32,
215 pub n_gpu_layers: i32,
216 pub has_mmap_override: bool,
217 pub use_mmap: bool,
218 pub use_mlock: bool,
219 pub cache_type_k: i32,
220 pub cache_type_v: i32,
221 pub flash_attn_type: i32,
222 pub load_mode: LoadMode,
223 pub disable_repack: bool,
224 pub use_mmap_prefetch: bool,
225 pub use_mmap_buffer: bool,
226 pub filter_tensors_on_load: bool,
227 pub include_embeddings: bool,
228 pub include_output: bool,
229 pub selected_backend_device: *const c_char,
230 pub glm_dsa_policy_profile: i32,
231 pub glm_dsa_policy_flags: u32,
232 pub glm_dsa_short_prefill_max_tokens: i32,
233 pub glm_dsa_direct_sparse_decode_max_top_k: i32,
234 pub glm_dsa_dense_sparse_mask_max_bytes: u64,
235 pub glm_dsa_compact_flash_min_kv: i32,
236}
237
238#[repr(C)]
239#[derive(Debug, Clone, Copy)]
240pub struct BackendDevice {
241 pub version: u32,
242 pub name: *const c_char,
243 pub description: *const c_char,
244 pub device_id: *const c_char,
245 pub memory_free: u64,
246 pub memory_total: u64,
247 pub device_type: BackendDeviceType,
248 pub caps: u64,
249}
250
251#[repr(C)]
252pub struct Model {
253 _private: [u8; 0],
254}
255
256#[repr(C)]
257pub struct NgramCache {
258 _private: [u8; 0],
259}
260
261#[repr(C)]
262pub struct Session {
263 _private: [u8; 0],
264}
265
266#[repr(C)]
267pub struct ModelInfo {
268 _private: [u8; 0],
269}
270
271pub type SkippyModelAttachMtpDraftModelFn = unsafe extern "C" fn(
272 target_model: *mut Model,
273 path: *const c_char,
274 config: *const RuntimeConfig,
275 out_error: *mut *mut Error,
276) -> Status;
277
278pub type SkippyDecodeStepSampledMtpFn = unsafe extern "C" fn(
279 session: *mut Session,
280 token_id: i32,
281 sampling: *const SamplingConfig,
282 out_predicted_token: *mut i32,
283 max_draft_tokens: usize,
284 out_mtp_draft: *mut NativeMtpDraft,
285 out_error: *mut *mut Error,
286) -> Status;
287
288#[repr(C)]
289#[derive(Debug, Clone, Copy)]
290pub struct ChatMessage {
291 pub role: *const c_char,
292 pub content: *const c_char,
293}
294
295#[repr(C)]
296pub struct SlicePlan {
297 _private: [u8; 0],
298}
299
300#[repr(C)]
301pub struct MtmdContext {
302 _private: [u8; 0],
303}
304
305#[repr(C)]
306pub struct MtmdBitmap {
307 _private: [u8; 0],
308}
309
310#[repr(C)]
311pub struct MtmdInputChunks {
312 _private: [u8; 0],
313}
314
315#[repr(C)]
316#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub enum MtmdInputChunkType {
318 Text = 0,
319 Image = 1,
320 Audio = 2,
321}
322
323#[repr(C)]
324#[derive(Debug, Clone, Copy)]
325pub struct MtmdDecoderPos {
326 pub t: u32,
327 pub x: u32,
328 pub y: u32,
329 pub z: u32,
330}
331
332#[repr(C)]
333#[derive(Debug, Clone, Copy)]
334pub struct MtmdInputText {
335 pub text: *const c_char,
336 pub add_special: bool,
337 pub parse_special: bool,
338}
339
340#[repr(C)]
341#[derive(Debug, Clone, Copy)]
342pub struct MtmdContextParams {
343 pub use_gpu: bool,
344 pub print_timings: bool,
345 pub n_threads: c_int,
346 pub image_marker: *const c_char,
347 pub media_marker: *const c_char,
348 pub flash_attn_type: c_int,
349 pub warmup: bool,
350 pub image_min_tokens: c_int,
351 pub image_max_tokens: c_int,
352 pub cb_eval: *mut c_void,
353 pub cb_eval_user_data: *mut c_void,
354 pub batch_max_tokens: c_int,
355 pub progress_callback: MtmdProgressCallback,
356 pub progress_callback_user_data: *mut c_void,
357}
358
359#[repr(C)]
360#[derive(Debug, Clone, Copy)]
361pub struct TensorInfo {
362 pub name: *const c_char,
363 pub layer_index: i32,
364 pub role: TensorRole,
365 pub ggml_type: u32,
366 pub byte_size: u64,
367 pub element_count: u64,
368}
369
370#[repr(C)]
371#[derive(Debug, Clone, Copy)]
372pub struct ActivationDesc {
373 pub version: u32,
374 pub dtype: ActivationDType,
375 pub layout: ActivationLayout,
376 pub producer_stage_index: i32,
377 pub layer_start: i32,
378 pub layer_end: i32,
379 pub token_count: u32,
380 pub sequence_count: u32,
381 pub payload_bytes: u64,
382 pub flags: u64,
383}
384
385pub const ACTIVATION_FLAG_INKLING_MTP_EMBD: u64 = 1 << 2;
386
387#[repr(C)]
388#[derive(Debug, Clone, Copy)]
389pub struct LogitBias {
390 pub token_id: i32,
391 pub bias: f32,
392}
393
394#[derive(Debug, Clone, Copy, PartialEq, Eq)]
395#[repr(i32)]
396pub enum LlamaFileType {
397 AllF32 = 0,
398 MostlyF16 = 1,
399 MostlyQ4_0 = 2,
400 MostlyQ4_1 = 3,
401 MostlyQ8_0 = 7,
402 MostlyQ5_0 = 8,
403 MostlyQ5_1 = 9,
404 MostlyQ2K = 10,
405 MostlyQ3KS = 11,
406 MostlyQ3KM = 12,
407 MostlyQ3KL = 13,
408 MostlyQ4KS = 14,
409 MostlyQ4KM = 15,
410 MostlyQ5KS = 16,
411 MostlyQ5KM = 17,
412 MostlyQ6K = 18,
413 MostlyIQ2XXS = 19,
414 MostlyIQ2XS = 20,
415 MostlyQ2KS = 21,
416 MostlyIQ3XS = 22,
417 MostlyIQ3XXS = 23,
418 MostlyIQ1S = 24,
419 MostlyIQ4NL = 25,
420 MostlyIQ3S = 26,
421 MostlyIQ3M = 27,
422 MostlyIQ2S = 28,
423 MostlyIQ2M = 29,
424 MostlyIQ4XS = 30,
425 MostlyIQ1M = 31,
426 MostlyBf16 = 32,
427 MostlyTQ1_0 = 36,
428 MostlyTQ2_0 = 37,
429 MostlyMxfp4Moe = 38,
430 MostlyNvfp4 = 39,
431 MostlyQ1_0 = 40,
432 MostlyQ2_0 = 41,
433}
434
435#[derive(Debug, Clone, Copy, PartialEq, Eq)]
436#[repr(i32)]
437pub enum GgmlType {
438 F32 = 0,
439 F16 = 1,
440 Q4_0 = 2,
441 Q4_1 = 3,
442 Q5_0 = 6,
443 Q5_1 = 7,
444 Q8_0 = 8,
445 Q8_1 = 9,
446 Q2K = 10,
447 Q3K = 11,
448 Q4K = 12,
449 Q5K = 13,
450 Q6K = 14,
451 Q8K = 15,
452 IQ2XXS = 16,
453 IQ2XS = 17,
454 IQ3XXS = 18,
455 IQ1S = 19,
456 IQ4NL = 20,
457 IQ3S = 21,
458 IQ2S = 22,
459 IQ4XS = 23,
460 I8 = 24,
461 I16 = 25,
462 I32 = 26,
463 I64 = 27,
464 F64 = 28,
465 IQ1M = 29,
466 Bf16 = 30,
467 TQ1_0 = 34,
468 TQ2_0 = 35,
469 Mxfp4 = 39,
470 Nvfp4 = 40,
471 Q1_0 = 41,
472 Q2_0 = 42,
473 Count = 43,
474}
475
476#[derive(Debug, Clone, Copy, PartialEq, Eq)]
477#[repr(i32)]
478pub enum LlamaModelKvOverrideType {
479 Int = 0,
480 Float = 1,
481 Bool = 2,
482 Str = 3,
483}
484
485#[repr(C)]
486#[derive(Clone, Copy)]
487pub union LlamaModelKvOverrideValue {
488 pub val_i64: i64,
489 pub val_f64: f64,
490 pub val_bool: bool,
491 pub val_str: [c_char; 128],
492}
493
494#[repr(C)]
495#[derive(Clone, Copy)]
496pub struct LlamaModelKvOverride {
497 pub tag: LlamaModelKvOverrideType,
498 pub key: [c_char; 128],
499 pub value: LlamaModelKvOverrideValue,
500}
501
502#[repr(C)]
503#[derive(Debug, Clone, Copy)]
504pub struct LlamaModelTensorOverride {
505 pub pattern: *const c_char,
506 pub tensor_type: GgmlType,
507}
508
509#[repr(C)]
510#[derive(Debug, Clone, Copy)]
511pub struct LlamaModelImatrixData {
512 pub name: *const c_char,
513 pub data: *const f32,
514 pub size: usize,
515}
516
517#[repr(C)]
518#[derive(Debug, Clone, Copy)]
519pub struct LlamaModelQuantizeParams {
520 pub nthread: i32,
521 pub ftype: LlamaFileType,
522 pub output_tensor_type: GgmlType,
523 pub token_embedding_type: GgmlType,
524 pub allow_requantize: bool,
525 pub quantize_output_tensor: bool,
526 pub only_copy: bool,
527 pub pure: bool,
528 pub keep_split: bool,
529 pub dry_run: bool,
530 pub imatrix: *const LlamaModelImatrixData,
531 pub kv_overrides: *const LlamaModelKvOverride,
532 pub tt_overrides: *const LlamaModelTensorOverride,
533 pub prune_layers: *const i32,
534}
535
536#[repr(C)]
537#[derive(Debug, Clone, Copy)]
538pub struct SamplingConfig {
539 pub version: u32,
540 pub flags: u32,
541 pub seed: u32,
542 pub top_k: i32,
543 pub penalty_last_n: i32,
544 pub temperature: f32,
545 pub top_p: f32,
546 pub presence_penalty: f32,
547 pub frequency_penalty: f32,
548 pub repeat_penalty: f32,
549 pub logit_bias_count: u32,
550 pub min_p: f32,
551 pub logit_bias: [LogitBias; 256],
552}
553
554#[repr(C)]
555#[derive(Debug, Clone, Copy, Default)]
556pub struct KvPageDesc {
557 pub version: u32,
558 pub layer_start: i32,
559 pub layer_end: i32,
560 pub token_start: u64,
561 pub token_count: u64,
562 pub layer_count: u32,
563 pub k_type: u32,
564 pub v_type: u32,
565 pub k_row_bytes: u32,
566 pub v_row_bytes: u32,
567 pub v_element_bytes: u32,
568 pub payload_bytes: u64,
569 pub flags: u64,
570}
571
572pub const NATIVE_MTP_MAX_DRAFT_TOKENS: usize = 8;
573
574#[repr(C)]
575#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
576pub struct NativeMtpDraft {
577 pub version: u32,
578 pub available: bool,
579 pub token_count: i32,
580 pub token_ids: [i32; NATIVE_MTP_MAX_DRAFT_TOKENS],
581 pub proposal_compute_us: i64,
582}
583
584#[repr(C)]
585#[derive(Debug, Clone, Copy, Default, PartialEq)]
586pub struct TokenSignal {
587 pub entropy: f32,
588 pub top_logprob: f32,
589 pub second_logprob: f32,
590 pub margin: f32,
591 pub top_token: i32,
592 pub second_token: i32,
593}
594
595#[repr(C)]
596#[derive(Debug, Clone, Copy, Default, PartialEq)]
597pub struct GenerationSignalWindow {
598 pub token_count: u32,
599 pub mean_entropy: f32,
600 pub max_entropy: f32,
601 pub mean_margin: f32,
602 pub min_margin: f32,
603 pub high_entropy_count: u32,
604 pub repetition_count: u32,
605}
606
607#[cfg(not(feature = "dynamic-runtime"))]
608pub fn native_runtime_loaded() -> bool {
610 true
611}
612
613#[cfg(not(feature = "dynamic-runtime"))]
614pub unsafe fn load_native_runtime_library(
621 _path: impl AsRef<std::path::Path>,
622) -> Result<(), NativeRuntimeLoadError> {
623 Ok(())
624}
625
626#[cfg(not(feature = "dynamic-runtime"))]
627pub unsafe fn load_native_runtime_libraries<I, P>(_paths: I) -> Result<(), NativeRuntimeLoadError>
634where
635 I: IntoIterator<Item = P>,
636 P: AsRef<std::path::Path>,
637{
638 Ok(())
639}
640
641#[derive(Debug)]
642pub enum NativeRuntimeLoadError {
643 Load(String),
644 AlreadyLoaded,
645}
646
647impl std::fmt::Display for NativeRuntimeLoadError {
648 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
649 match self {
650 Self::Load(message) => write!(f, "{message}"),
651 Self::AlreadyLoaded => write!(f, "native runtime library is already loaded"),
652 }
653 }
654}
655
656impl std::error::Error for NativeRuntimeLoadError {}
657
658#[cfg(feature = "dynamic-runtime")]
659mod dynamic {
660 use super::*;
661 use libloading::Library;
662 use std::sync::OnceLock;
663
664 static SYMBOLS: OnceLock<Symbols> = OnceLock::new();
665
666 pub fn native_runtime_loaded() -> bool {
667 SYMBOLS.get().is_some()
668 }
669
670 pub unsafe fn load_native_runtime_library(
679 path: impl AsRef<std::path::Path>,
680 ) -> Result<(), NativeRuntimeLoadError> {
681 let symbols = unsafe { Symbols::load_paths(&[path.as_ref()]) }?;
682 SYMBOLS
683 .set(symbols)
684 .map_err(|_| NativeRuntimeLoadError::AlreadyLoaded)
685 }
686
687 pub unsafe fn load_native_runtime_libraries<I, P>(
700 paths: I,
701 ) -> Result<(), NativeRuntimeLoadError>
702 where
703 I: IntoIterator<Item = P>,
704 P: AsRef<std::path::Path>,
705 {
706 let collected = paths
707 .into_iter()
708 .map(|path| path.as_ref().to_path_buf())
709 .collect::<Vec<_>>();
710 let symbols = unsafe { Symbols::load_paths(&collected) }?;
711 SYMBOLS
712 .set(symbols)
713 .map_err(|_| NativeRuntimeLoadError::AlreadyLoaded)
714 }
715
716 fn symbols() -> &'static Symbols {
717 SYMBOLS
718 .get()
719 .expect("MeshLLM native runtime library has not been loaded")
720 }
721
722 type SkippyAbiVersionFn = unsafe extern "C" fn() -> AbiVersion;
723
724 fn check_runtime_abi(libraries: &[Library]) -> Result<(), NativeRuntimeLoadError> {
725 let mut abi_version = None;
726 for library in libraries.iter().rev() {
727 if let Ok(symbol) =
728 unsafe { library.get::<SkippyAbiVersionFn>(b"skippy_abi_version\0") }
729 {
730 abi_version = Some(unsafe { symbol() });
731 break;
732 }
733 }
734 let Some(version) = abi_version else {
735 return Err(NativeRuntimeLoadError::Load(
736 "native runtime symbol not found: skippy_abi_version".to_string(),
737 ));
738 };
739 if !runtime_abi_supported(version) {
740 return Err(NativeRuntimeLoadError::Load(format!(
741 "native runtime ABI {}.{}.{} is not compatible with required ABI {}.{}.{}",
742 version.major,
743 version.minor,
744 version.patch,
745 ABI_VERSION_MAJOR,
746 ABI_VERSION_MINOR,
747 ABI_VERSION_PATCH,
748 )));
749 }
750 Ok(())
751 }
752
753 macro_rules! dynamic_symbols {
754 ($($name:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret:ty)?;)+) => {
755 #[allow(non_snake_case)]
756 struct Symbols {
757 _libraries: Vec<Library>,
758 $($name: unsafe extern "C" fn($($arg_ty),*) $(-> $ret)?,)+
759 }
760
761 impl Symbols {
762 unsafe fn load_paths<P>(paths: &[P]) -> Result<Self, NativeRuntimeLoadError>
763 where
764 P: AsRef<std::path::Path>,
765 {
766 if paths.is_empty() {
767 return Err(NativeRuntimeLoadError::Load(
768 "native runtime did not provide any libraries".to_string(),
769 ));
770 }
771 let mut libraries = Vec::with_capacity(paths.len());
772 for path in paths {
773 libraries.push(
774 unsafe { crate::dynamic_library::load(path.as_ref()) }
775 .map_err(|err| NativeRuntimeLoadError::Load(err.to_string()))?,
776 );
777 }
778 check_runtime_abi(&libraries)?;
779 $(
780 let mut $name = None;
781 for library in libraries.iter().rev() {
782 if let Ok(symbol) = unsafe {
783 library.get::<unsafe extern "C" fn($($arg_ty),*) $(-> $ret)?>(
784 concat!(stringify!($name), "\0").as_bytes(),
785 )
786 } {
787 $name = Some(*symbol);
788 break;
789 }
790 };
791 let $name = $name.ok_or_else(|| {
792 NativeRuntimeLoadError::Load(format!(
793 "native runtime symbol not found: {}",
794 stringify!($name),
795 ))
796 })?;
797 )+
798 Ok(Self {
799 _libraries: libraries,
800 $($name,)+
801 })
802 }
803 }
804
805 $(
806 #[allow(clippy::missing_safety_doc, clippy::too_many_arguments)]
807 pub unsafe fn $name($($arg: $arg_ty),*) $(-> $ret)? {
808 unsafe { (symbols().$name)($($arg),*) }
809 }
810 )+
811 };
812 }
813
814 dynamic_symbols! {
815 llama_log_set(log_callback: LlamaLogCallback, user_data: *mut c_void);
816 ggml_log_set(log_callback: LlamaLogCallback, user_data: *mut c_void);
817 llama_model_quantize_default_params() -> LlamaModelQuantizeParams;
818 llama_model_quantize(fname_inp: *const c_char, fname_out: *const c_char, params: *const LlamaModelQuantizeParams) -> u32;
819 skippy_error_free(error: *mut Error);
820 skippy_ngram_cache_create(ngram_min: u16, ngram_max: u16, out_cache: *mut *mut NgramCache, out_error: *mut *mut Error) -> Status;
821 skippy_ngram_cache_free(cache: *mut NgramCache);
822 skippy_ngram_cache_reset(cache: *mut NgramCache, token_ids: *const i32, token_count: usize, out_error: *mut *mut Error) -> Status;
823 skippy_ngram_cache_append(cache: *mut NgramCache, token_ids: *const i32, token_count: usize, out_error: *mut *mut Error) -> Status;
824 skippy_ngram_cache_draft(cache: *mut NgramCache, continuation_prefix: *const i32, continuation_prefix_count: usize, max_draft_tokens: u16, output_tokens: *mut i32, output_token_capacity: usize, out_token_count: *mut usize, out_error: *mut *mut Error) -> Status;
825 skippy_backend_device_count(out_count: *mut usize, out_error: *mut *mut Error) -> Status;
826 skippy_backend_device_at(index: usize, out_device: *mut BackendDevice, out_error: *mut *mut Error) -> Status;
827 skippy_model_open(path: *const c_char, config: *const RuntimeConfig, out_model: *mut *mut Model, out_error: *mut *mut Error) -> Status;
828 skippy_model_open_from_parts(paths: *const *const c_char, path_count: usize, config: *const RuntimeConfig, out_model: *mut *mut Model, out_error: *mut *mut Error) -> Status;
829 skippy_model_free(model: *mut Model, out_error: *mut *mut Error) -> Status;
830 skippy_model_llama_model(model: *const Model) -> *const Opaque;
831 skippy_session_create(model: *mut Model, out_session: *mut *mut Session, out_error: *mut *mut Error) -> Status;
832 skippy_session_create_from_resident_prefix(model: *mut Model, cache_seq_id: i32, token_ids: *const i32, token_count: usize, out_session: *mut *mut Session, out_error: *mut *mut Error) -> Status;
833 skippy_session_llama_context(session: *mut Session) -> *mut Opaque;
834 skippy_session_position(session: *const Session) -> i32;
835 skippy_session_batch_size(session: *const Session) -> i32;
836 skippy_session_begin_external_decode(session: *mut Session, out_error: *mut *mut Error) -> Status;
837 skippy_session_end_external_decode(session: *mut Session, out_error: *mut *mut Error) -> Status;
838 skippy_session_set_position(session: *mut Session, n_past: i32, out_error: *mut *mut Error) -> Status;
839 skippy_session_sample_current(session: *mut Session, sampling: *const SamplingConfig, out_predicted_token: *mut i32, out_error: *mut *mut Error) -> Status;
840 skippy_session_configure_chat_sampling(session: *mut Session, sampling: *const SamplingConfig, metadata_json: *const c_char, prompt_token_count: u64, out_error: *mut *mut Error) -> Status;
841 skippy_session_reset(session: *mut Session, out_error: *mut *mut Error) -> Status;
842 skippy_session_free(session: *mut Session, out_error: *mut *mut Error) -> Status;
843 skippy_prefill_chunk(session: *mut Session, token_ids: *const i32, token_count: usize, input_activations: *const c_void, input_activation_bytes: usize, output_activations: *mut c_void, output_activation_capacity: usize, out_output_activation_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
844 skippy_verify_tokens(session: *mut Session, token_ids: *const i32, token_count: usize, output_tokens: *mut i32, output_token_capacity: usize, out_token_count: *mut usize, out_error: *mut *mut Error) -> Status;
845 skippy_decode_step_sampled(session: *mut Session, token_id: i32, sampling: *const SamplingConfig, input_activation: *const c_void, input_activation_bytes: usize, output_activation: *mut c_void, output_activation_capacity: usize, out_output_activation_bytes: *mut usize, out_predicted_token: *mut i32, out_error: *mut *mut Error) -> Status;
846 skippy_decode_batch_sampled(sessions: *const *mut Session, token_ids: *const i32, sampling: *const *const SamplingConfig, request_count: usize, out_predicted_tokens: *mut i32, predicted_token_capacity: usize, out_error: *mut *mut Error) -> Status;
847 skippy_prefill_chunk_frame(session: *mut Session, token_ids: *const i32, token_count: usize, input_desc: *const ActivationDesc, input_payload: *const c_void, output_desc: *mut ActivationDesc, output_payload: *mut c_void, output_payload_capacity: usize, out_output_payload_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
848 skippy_prefill_chunk_frame_sampled(session: *mut Session, token_ids: *const i32, token_count: usize, sampling: *const SamplingConfig, input_desc: *const ActivationDesc, input_payload: *const c_void, output_desc: *mut ActivationDesc, output_payload: *mut c_void, output_payload_capacity: usize, out_output_payload_bytes: *mut usize, out_predicted_token: *mut i32, out_error: *mut *mut Error) -> Status;
849 skippy_prefill_chunk_frame_with_positions(session: *mut Session, token_ids: *const i32, token_count: usize, positions: *const i32, position_count: usize, input_desc: *const ActivationDesc, input_payload: *const c_void, output_desc: *mut ActivationDesc, output_payload: *mut c_void, output_payload_capacity: usize, out_output_payload_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
850 skippy_prefill_chunk_frame_sampled_with_positions(session: *mut Session, token_ids: *const i32, token_count: usize, positions: *const i32, position_count: usize, sampling: *const SamplingConfig, input_desc: *const ActivationDesc, input_payload: *const c_void, output_desc: *mut ActivationDesc, output_payload: *mut c_void, output_payload_capacity: usize, out_output_payload_bytes: *mut usize, out_predicted_token: *mut i32, out_error: *mut *mut Error) -> Status;
851 skippy_decode_step_frame_sampled(session: *mut Session, token_id: i32, sampling: *const SamplingConfig, input_desc: *const ActivationDesc, input_payload: *const c_void, output_desc: *mut ActivationDesc, output_payload: *mut c_void, output_payload_capacity: usize, out_output_payload_bytes: *mut usize, out_predicted_token: *mut i32, out_error: *mut *mut Error) -> Status;
852 skippy_decode_step_frame_sampled_mtp(session: *mut Session, token_id: i32, sampling: *const SamplingConfig, input_desc: *const ActivationDesc, input_payload: *const c_void, output_desc: *mut ActivationDesc, output_payload: *mut c_void, output_payload_capacity: usize, out_output_payload_bytes: *mut usize, out_predicted_token: *mut i32, max_draft_tokens: usize, out_mtp_draft: *mut NativeMtpDraft, out_error: *mut *mut Error) -> Status;
853 skippy_decode_step_frame_batch_sampled(sessions: *const *mut Session, token_ids: *const i32, sampling: *const *const SamplingConfig, input_descs: *const *const ActivationDesc, input_payloads: *const *const c_void, output_descs: *mut ActivationDesc, output_payloads: *const *mut c_void, output_payload_capacities: *const usize, out_output_payload_bytes: *mut usize, out_predicted_tokens: *mut i32, predicted_token_capacity: usize, request_count: usize, out_error: *mut *mut Error) -> Status;
854 skippy_verify_tokens_frame_sampled(session: *mut Session, token_ids: *const i32, token_count: usize, sampling: *const SamplingConfig, input_desc: *const ActivationDesc, input_payload: *const c_void, output_desc: *mut ActivationDesc, output_payload: *mut c_void, output_payload_capacity: usize, out_output_payload_bytes: *mut usize, output_tokens: *mut i32, output_token_capacity: usize, out_token_count: *mut usize, max_draft_tokens: usize, out_mtp_draft: *mut NativeMtpDraft, out_error: *mut *mut Error) -> Status;
855 skippy_session_copy_output_activation_frame(session: *mut Session, token_count: usize, output_desc: *mut ActivationDesc, output_payload: *mut c_void, output_payload_capacity: usize, out_output_payload_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
856 skippy_session_last_token_signal(session: *mut Session, out_signal: *mut TokenSignal, out_error: *mut *mut Error) -> Status;
857 skippy_session_signal_window(session: *mut Session, window_tokens: u32, out_window: *mut GenerationSignalWindow, out_error: *mut *mut Error) -> Status;
858 skippy_trim_session(session: *mut Session, token_count: u64, out_error: *mut *mut Error) -> Status;
859 skippy_retire_verify_checkpoint(session: *mut Session, token_start: u64, token_count: u64, out_error: *mut *mut Error) -> Status;
860 skippy_export_state(session: *mut Session, layer_start: i32, layer_end: i32, output: *mut c_void, output_capacity: usize, out_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
861 skippy_import_state(session: *mut Session, layer_start: i32, layer_end: i32, input: *const c_void, input_bytes: usize, out_error: *mut *mut Error) -> Status;
862 skippy_export_full_state(session: *mut Session, layer_start: i32, layer_end: i32, output: *mut c_void, output_capacity: usize, out_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
863 skippy_import_full_state(session: *mut Session, layer_start: i32, layer_end: i32, input: *const c_void, input_bytes: usize, out_error: *mut *mut Error) -> Status;
864 skippy_export_kv_page(session: *mut Session, layer_start: i32, layer_end: i32, token_start: u64, token_count: u64, out_desc: *mut KvPageDesc, output: *mut c_void, output_capacity: usize, out_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
865 skippy_import_kv_page(session: *mut Session, desc: *const KvPageDesc, input: *const c_void, input_bytes: usize, out_error: *mut *mut Error) -> Status;
866 skippy_export_recurrent_state(session: *mut Session, output: *mut c_void, output_capacity: usize, out_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
867 skippy_import_recurrent_state(session: *mut Session, input: *const c_void, input_bytes: usize, out_error: *mut *mut Error) -> Status;
868 skippy_session_save_prefix(session: *mut Session, cache_seq_id: i32, token_count: u64, out_error: *mut *mut Error) -> Status;
869 skippy_session_restore_prefix(session: *mut Session, cache_seq_id: i32, token_ids: *const i32, token_count: usize, out_error: *mut *mut Error) -> Status;
870 skippy_session_drop_sequence(session: *mut Session, seq_id: i32, out_error: *mut *mut Error) -> Status;
871 skippy_tokenize(model: *mut Model, text: *const c_char, add_special: bool, output_tokens: *mut i32, output_token_capacity: usize, out_token_count: *mut usize, out_error: *mut *mut Error) -> Status;
872 skippy_detokenize(model: *mut Model, tokens: *const i32, token_count: usize, output_text: *mut c_char, output_text_capacity: usize, out_text_bytes: *mut usize, out_error: *mut *mut Error) -> Status;
873 skippy_token_is_eog(model: *mut Model, token_id: i32, out_is_eog: *mut bool, out_error: *mut *mut Error) -> Status;
874 skippy_model_info_open(path: *const c_char, out_info: *mut *mut ModelInfo, out_error: *mut *mut Error) -> Status;
875 skippy_model_info_free(info: *mut ModelInfo, out_error: *mut *mut Error) -> Status;
876 skippy_model_info_tensor_count(info: *mut ModelInfo, out_count: *mut usize, out_error: *mut *mut Error) -> Status;
877 skippy_model_info_tensor_at(info: *mut ModelInfo, index: usize, out_tensor: *mut TensorInfo, out_error: *mut *mut Error) -> Status;
878 skippy_slice_plan_create(info: *mut ModelInfo, out_plan: *mut *mut SlicePlan, out_error: *mut *mut Error) -> Status;
879 skippy_slice_plan_free(plan: *mut SlicePlan, out_error: *mut *mut Error) -> Status;
880 skippy_slice_plan_add_layer_range(plan: *mut SlicePlan, stage_index: i32, layer_start: i32, layer_end: i32, include_embeddings: bool, include_output: bool, out_error: *mut *mut Error) -> Status;
881 skippy_write_slice_gguf(info: *mut ModelInfo, plan: *const SlicePlan, stage_index: i32, output_path: *const c_char, out_error: *mut *mut Error) -> Status;
882 skippy_write_gguf_from_parts(input_paths: *const *const c_char, input_count: usize, output_path: *const c_char, out_error: *mut *mut Error) -> Status;
883 mtmd_default_marker() -> *const c_char;
884 mtmd_helper_log_set(log_callback: LlamaLogCallback, user_data: *mut c_void);
885 mtmd_context_params_default() -> MtmdContextParams;
886 mtmd_init_from_file(mmproj_fname: *const c_char, text_model: *const Opaque, ctx_params: MtmdContextParams) -> *mut MtmdContext;
887 mtmd_free(ctx: *mut MtmdContext);
888 mtmd_helper_bitmap_init_from_buf(ctx: *mut MtmdContext, buf: *const u8, len: usize) -> *mut MtmdBitmap;
889 mtmd_bitmap_free(bitmap: *mut MtmdBitmap);
890 mtmd_input_chunks_init() -> *mut MtmdInputChunks;
891 mtmd_input_chunks_free(chunks: *mut MtmdInputChunks);
892 mtmd_tokenize(ctx: *mut MtmdContext, output: *mut MtmdInputChunks, text: *const MtmdInputText, bitmaps: *const *const MtmdBitmap, n_bitmaps: usize) -> c_int;
893 mtmd_helper_get_n_tokens(chunks: *const MtmdInputChunks) -> usize;
894 mtmd_helper_get_n_pos(chunks: *const MtmdInputChunks) -> i32;
895 mtmd_input_chunks_size(chunks: *const MtmdInputChunks) -> usize;
896 mtmd_input_chunks_get(chunks: *const MtmdInputChunks, index: usize) -> *const Opaque;
897 mtmd_decode_use_mrope(ctx: *const MtmdContext) -> bool;
898 mtmd_input_chunk_get_type(chunk: *const Opaque) -> MtmdInputChunkType;
899 mtmd_input_chunk_get_n_tokens(chunk: *const Opaque) -> usize;
900 mtmd_input_chunk_get_tokens_text(chunk: *const Opaque, out_count: *mut usize) -> *const i32;
901 mtmd_input_chunk_get_tokens_image(chunk: *const Opaque) -> *const Opaque;
902 mtmd_helper_image_get_decoder_pos(image: *const Opaque, pos_0: i32, out_pos: *mut MtmdDecoderPos);
903 mtmd_helper_eval_chunks(ctx: *mut MtmdContext, lctx: *mut Opaque, chunks: *const MtmdInputChunks, n_past: i32, seq_id: i32, n_batch: i32, logits_last: bool, new_n_past: *mut i32) -> c_int;
904 mtmd_helper_eval_chunk_single(ctx: *mut MtmdContext, lctx: *mut Opaque, chunk: *const Opaque, n_past: i32, seq_id: i32, n_batch: i32, logits_last: bool, new_n_past: *mut i32) -> c_int;
905 }
906
907 type SkippyAbiFeaturesFn = unsafe extern "C" fn() -> u64;
913 type SkippyModelOpenWithEventsFn = unsafe extern "C" fn(
914 path: *const c_char,
915 config: *const RuntimeConfig,
916 reporter: *const SkippyRuntimeEventReporterV1,
917 out_model: *mut *mut Model,
918 out_error: *mut *mut Error,
919 ) -> Status;
920 type SkippyModelOpenFromPartsWithEventsFn = unsafe extern "C" fn(
921 paths: *const *const c_char,
922 path_count: usize,
923 config: *const RuntimeConfig,
924 reporter: *const SkippyRuntimeEventReporterV1,
925 out_model: *mut *mut Model,
926 out_error: *mut *mut Error,
927 ) -> Status;
928 type SkippyApplyChatTemplateFn = unsafe extern "C" fn(
929 model: *mut Model,
930 messages: *const ChatMessage,
931 message_count: usize,
932 add_assistant: bool,
933 override_enable_thinking: bool,
934 enable_thinking: bool,
935 output_text: *mut c_char,
936 output_text_capacity: usize,
937 out_text_bytes: *mut usize,
938 out_error: *mut *mut Error,
939 ) -> Status;
940 type SkippyApplyChatTemplateJsonFn = unsafe extern "C" fn(
941 model: *mut Model,
942 messages_json: *const c_char,
943 tools_json: *const c_char,
944 tool_choice_json: *const c_char,
945 add_assistant: bool,
946 override_enable_thinking: bool,
947 enable_thinking: bool,
948 parallel_tool_calls: bool,
949 reasoning_format: *const c_char,
950 output_text: *mut c_char,
951 output_text_capacity: usize,
952 out_text_bytes: *mut usize,
953 output_metadata_json: *mut c_char,
954 output_metadata_json_capacity: usize,
955 out_metadata_json_bytes: *mut usize,
956 out_error: *mut *mut Error,
957 ) -> Status;
958 type SkippyParseChatResponseJsonFn = unsafe extern "C" fn(
959 generated_text: *const c_char,
960 metadata_json: *const c_char,
961 is_partial: bool,
962 output_message_json: *mut c_char,
963 output_message_json_capacity: usize,
964 out_message_json_bytes: *mut usize,
965 out_error: *mut *mut Error,
966 ) -> Status;
967
968 impl Symbols {
969 fn lookup_optional<Sym>(&self, name: &[u8]) -> Option<Sym>
970 where
971 Sym: Copy + 'static,
972 {
973 for library in self._libraries.iter().rev() {
974 if let Ok(sym) = unsafe { library.get::<Sym>(name) } {
975 return Some(*sym);
976 }
977 }
978 None
979 }
980 }
981
982 pub fn skippy_abi_features_optional() -> Option<SkippyAbiFeaturesFn> {
983 static CACHE: OnceLock<Option<SkippyAbiFeaturesFn>> = OnceLock::new();
984 *CACHE.get_or_init(|| {
985 symbols().lookup_optional::<SkippyAbiFeaturesFn>(b"skippy_abi_features\0")
986 })
987 }
988
989 pub fn skippy_model_open_with_events_fn() -> Option<SkippyModelOpenWithEventsFn> {
990 static CACHE: OnceLock<Option<SkippyModelOpenWithEventsFn>> = OnceLock::new();
991 *CACHE.get_or_init(|| {
992 symbols()
993 .lookup_optional::<SkippyModelOpenWithEventsFn>(b"skippy_model_open_with_events\0")
994 })
995 }
996
997 pub fn skippy_model_attach_mtp_draft_model_fn() -> Option<SkippyModelAttachMtpDraftModelFn> {
998 static CACHE: OnceLock<Option<SkippyModelAttachMtpDraftModelFn>> = OnceLock::new();
999 *CACHE.get_or_init(|| {
1000 symbols().lookup_optional::<SkippyModelAttachMtpDraftModelFn>(
1001 b"skippy_model_attach_mtp_draft_model\0",
1002 )
1003 })
1004 }
1005
1006 pub fn skippy_decode_step_sampled_mtp_fn() -> Option<SkippyDecodeStepSampledMtpFn> {
1007 static CACHE: OnceLock<Option<SkippyDecodeStepSampledMtpFn>> = OnceLock::new();
1008 *CACHE.get_or_init(|| {
1009 symbols().lookup_optional::<SkippyDecodeStepSampledMtpFn>(
1010 b"skippy_decode_step_sampled_mtp\0",
1011 )
1012 })
1013 }
1014
1015 pub fn skippy_model_open_from_parts_with_events_fn()
1016 -> Option<SkippyModelOpenFromPartsWithEventsFn> {
1017 static CACHE: OnceLock<Option<SkippyModelOpenFromPartsWithEventsFn>> = OnceLock::new();
1018 *CACHE.get_or_init(|| {
1019 symbols().lookup_optional::<SkippyModelOpenFromPartsWithEventsFn>(
1020 b"skippy_model_open_from_parts_with_events\0",
1021 )
1022 })
1023 }
1024
1025 fn skippy_apply_chat_template_fn() -> Option<SkippyApplyChatTemplateFn> {
1026 static CACHE: OnceLock<Option<SkippyApplyChatTemplateFn>> = OnceLock::new();
1027 *CACHE.get_or_init(|| {
1028 symbols().lookup_optional::<SkippyApplyChatTemplateFn>(b"skippy_apply_chat_template\0")
1029 })
1030 }
1031
1032 fn skippy_apply_chat_template_json_fn() -> Option<SkippyApplyChatTemplateJsonFn> {
1033 static CACHE: OnceLock<Option<SkippyApplyChatTemplateJsonFn>> = OnceLock::new();
1034 *CACHE.get_or_init(|| {
1035 symbols().lookup_optional::<SkippyApplyChatTemplateJsonFn>(
1036 b"skippy_apply_chat_template_json\0",
1037 )
1038 })
1039 }
1040
1041 fn skippy_parse_chat_response_json_fn() -> Option<SkippyParseChatResponseJsonFn> {
1042 static CACHE: OnceLock<Option<SkippyParseChatResponseJsonFn>> = OnceLock::new();
1043 *CACHE.get_or_init(|| {
1044 symbols().lookup_optional::<SkippyParseChatResponseJsonFn>(
1045 b"skippy_parse_chat_response_json\0",
1046 )
1047 })
1048 }
1049
1050 #[allow(clippy::missing_safety_doc, clippy::too_many_arguments)]
1051 pub unsafe fn skippy_apply_chat_template(
1052 model: *mut Model,
1053 messages: *const ChatMessage,
1054 message_count: usize,
1055 add_assistant: bool,
1056 override_enable_thinking: bool,
1057 enable_thinking: bool,
1058 output_text: *mut c_char,
1059 output_text_capacity: usize,
1060 out_text_bytes: *mut usize,
1061 out_error: *mut *mut Error,
1062 ) -> Status {
1063 let Some(function) = skippy_apply_chat_template_fn() else {
1064 return Status::Unsupported;
1065 };
1066 unsafe {
1067 function(
1068 model,
1069 messages,
1070 message_count,
1071 add_assistant,
1072 override_enable_thinking,
1073 enable_thinking,
1074 output_text,
1075 output_text_capacity,
1076 out_text_bytes,
1077 out_error,
1078 )
1079 }
1080 }
1081
1082 #[allow(clippy::missing_safety_doc, clippy::too_many_arguments)]
1083 pub unsafe fn skippy_apply_chat_template_json(
1084 model: *mut Model,
1085 messages_json: *const c_char,
1086 tools_json: *const c_char,
1087 tool_choice_json: *const c_char,
1088 add_assistant: bool,
1089 override_enable_thinking: bool,
1090 enable_thinking: bool,
1091 parallel_tool_calls: bool,
1092 reasoning_format: *const c_char,
1093 output_text: *mut c_char,
1094 output_text_capacity: usize,
1095 out_text_bytes: *mut usize,
1096 output_metadata_json: *mut c_char,
1097 output_metadata_json_capacity: usize,
1098 out_metadata_json_bytes: *mut usize,
1099 out_error: *mut *mut Error,
1100 ) -> Status {
1101 let Some(function) = skippy_apply_chat_template_json_fn() else {
1102 return Status::Unsupported;
1103 };
1104 unsafe {
1105 function(
1106 model,
1107 messages_json,
1108 tools_json,
1109 tool_choice_json,
1110 add_assistant,
1111 override_enable_thinking,
1112 enable_thinking,
1113 parallel_tool_calls,
1114 reasoning_format,
1115 output_text,
1116 output_text_capacity,
1117 out_text_bytes,
1118 output_metadata_json,
1119 output_metadata_json_capacity,
1120 out_metadata_json_bytes,
1121 out_error,
1122 )
1123 }
1124 }
1125
1126 #[allow(clippy::missing_safety_doc, clippy::too_many_arguments)]
1127 pub unsafe fn skippy_parse_chat_response_json(
1128 generated_text: *const c_char,
1129 metadata_json: *const c_char,
1130 is_partial: bool,
1131 output_message_json: *mut c_char,
1132 output_message_json_capacity: usize,
1133 out_message_json_bytes: *mut usize,
1134 out_error: *mut *mut Error,
1135 ) -> Status {
1136 let Some(function) = skippy_parse_chat_response_json_fn() else {
1137 return Status::Unsupported;
1138 };
1139 unsafe {
1140 function(
1141 generated_text,
1142 metadata_json,
1143 is_partial,
1144 output_message_json,
1145 output_message_json_capacity,
1146 out_message_json_bytes,
1147 out_error,
1148 )
1149 }
1150 }
1151
1152 #[allow(clippy::missing_safety_doc, clippy::too_many_arguments)]
1153 pub unsafe fn skippy_decode_step_sampled_mtp(
1154 session: *mut Session,
1155 token_id: i32,
1156 sampling: *const SamplingConfig,
1157 out_predicted_token: *mut i32,
1158 max_draft_tokens: usize,
1159 out_mtp_draft: *mut NativeMtpDraft,
1160 out_error: *mut *mut Error,
1161 ) -> Status {
1162 let Some(function) = skippy_decode_step_sampled_mtp_fn() else {
1163 return Status::Unsupported;
1164 };
1165 unsafe {
1166 function(
1167 session,
1168 token_id,
1169 sampling,
1170 out_predicted_token,
1171 max_draft_tokens,
1172 out_mtp_draft,
1173 out_error,
1174 )
1175 }
1176 }
1177}
1178
1179#[cfg(feature = "dynamic-runtime")]
1180pub use dynamic::*;
1181
1182#[cfg(feature = "dynamic-runtime")]
1183pub fn skippy_abi_features() -> u64 {
1186 try_abi_features().expect("skippy_abi_features not available in loaded runtime")
1187}
1188
1189#[cfg(feature = "dynamic-runtime")]
1192pub fn try_abi_features() -> Option<u64> {
1193 dynamic::skippy_abi_features_optional().map(|features| unsafe { features() })
1194}
1195
1196#[cfg(feature = "dynamic-runtime")]
1198pub fn abi_features() -> u64 {
1199 skippy_abi_features()
1200}
1201
1202#[cfg(not(feature = "dynamic-runtime"))]
1204pub fn try_abi_features() -> Option<u64> {
1205 Some(unsafe { skippy_abi_features() })
1208}
1209
1210#[cfg(not(feature = "dynamic-runtime"))]
1212pub fn abi_features() -> u64 {
1213 unsafe { skippy_abi_features() }
1216}
1217
1218#[cfg(not(feature = "dynamic-runtime"))]
1219unsafe extern "C" {
1220 pub fn llama_log_set(log_callback: LlamaLogCallback, user_data: *mut c_void);
1221
1222 pub fn ggml_log_set(log_callback: LlamaLogCallback, user_data: *mut c_void);
1223
1224 pub fn llama_model_quantize_default_params() -> LlamaModelQuantizeParams;
1225
1226 pub fn llama_model_quantize(
1227 fname_inp: *const c_char,
1228 fname_out: *const c_char,
1229 params: *const LlamaModelQuantizeParams,
1230 ) -> u32;
1231
1232 pub fn skippy_abi_features() -> u64;
1233
1234 pub fn skippy_error_free(error: *mut Error);
1235
1236 pub fn skippy_ngram_cache_create(
1237 ngram_min: u16,
1238 ngram_max: u16,
1239 out_cache: *mut *mut NgramCache,
1240 out_error: *mut *mut Error,
1241 ) -> Status;
1242
1243 pub fn skippy_ngram_cache_free(cache: *mut NgramCache);
1244
1245 pub fn skippy_ngram_cache_reset(
1246 cache: *mut NgramCache,
1247 token_ids: *const i32,
1248 token_count: usize,
1249 out_error: *mut *mut Error,
1250 ) -> Status;
1251
1252 pub fn skippy_ngram_cache_append(
1253 cache: *mut NgramCache,
1254 token_ids: *const i32,
1255 token_count: usize,
1256 out_error: *mut *mut Error,
1257 ) -> Status;
1258
1259 pub fn skippy_ngram_cache_draft(
1260 cache: *mut NgramCache,
1261 continuation_prefix: *const i32,
1262 continuation_prefix_count: usize,
1263 max_draft_tokens: u16,
1264 output_tokens: *mut i32,
1265 output_token_capacity: usize,
1266 out_token_count: *mut usize,
1267 out_error: *mut *mut Error,
1268 ) -> Status;
1269
1270 pub fn skippy_backend_device_count(out_count: *mut usize, out_error: *mut *mut Error)
1271 -> Status;
1272
1273 pub fn skippy_backend_device_at(
1274 index: usize,
1275 out_device: *mut BackendDevice,
1276 out_error: *mut *mut Error,
1277 ) -> Status;
1278
1279 pub fn skippy_model_open(
1280 path: *const c_char,
1281 config: *const RuntimeConfig,
1282 out_model: *mut *mut Model,
1283 out_error: *mut *mut Error,
1284 ) -> Status;
1285
1286 pub fn skippy_model_open_from_parts(
1287 paths: *const *const c_char,
1288 path_count: usize,
1289 config: *const RuntimeConfig,
1290 out_model: *mut *mut Model,
1291 out_error: *mut *mut Error,
1292 ) -> Status;
1293
1294 pub fn skippy_model_attach_mtp_draft_model(
1295 target_model: *mut Model,
1296 path: *const c_char,
1297 config: *const RuntimeConfig,
1298 out_error: *mut *mut Error,
1299 ) -> Status;
1300
1301 pub fn skippy_model_free(model: *mut Model, out_error: *mut *mut Error) -> Status;
1302
1303 pub fn skippy_model_llama_model(model: *const Model) -> *const Opaque;
1304
1305 pub fn skippy_session_create(
1306 model: *mut Model,
1307 out_session: *mut *mut Session,
1308 out_error: *mut *mut Error,
1309 ) -> Status;
1310
1311 pub fn skippy_session_create_from_resident_prefix(
1312 model: *mut Model,
1313 cache_seq_id: i32,
1314 token_ids: *const i32,
1315 token_count: usize,
1316 out_session: *mut *mut Session,
1317 out_error: *mut *mut Error,
1318 ) -> Status;
1319
1320 pub fn skippy_session_llama_context(session: *mut Session) -> *mut Opaque;
1321
1322 pub fn skippy_session_position(session: *const Session) -> i32;
1323
1324 pub fn skippy_session_batch_size(session: *const Session) -> i32;
1325
1326 pub fn skippy_session_begin_external_decode(
1327 session: *mut Session,
1328 out_error: *mut *mut Error,
1329 ) -> Status;
1330
1331 pub fn skippy_session_end_external_decode(
1332 session: *mut Session,
1333 out_error: *mut *mut Error,
1334 ) -> Status;
1335
1336 pub fn skippy_session_set_position(
1337 session: *mut Session,
1338 n_past: i32,
1339 out_error: *mut *mut Error,
1340 ) -> Status;
1341
1342 pub fn skippy_session_sample_current(
1343 session: *mut Session,
1344 sampling: *const SamplingConfig,
1345 out_predicted_token: *mut i32,
1346 out_error: *mut *mut Error,
1347 ) -> Status;
1348
1349 pub fn skippy_session_configure_chat_sampling(
1350 session: *mut Session,
1351 sampling: *const SamplingConfig,
1352 metadata_json: *const c_char,
1353 prompt_token_count: u64,
1354 out_error: *mut *mut Error,
1355 ) -> Status;
1356
1357 pub fn skippy_session_reset(session: *mut Session, out_error: *mut *mut Error) -> Status;
1358
1359 pub fn skippy_session_free(session: *mut Session, out_error: *mut *mut Error) -> Status;
1360
1361 pub fn skippy_prefill_chunk(
1362 session: *mut Session,
1363 token_ids: *const i32,
1364 token_count: usize,
1365 input_activations: *const c_void,
1366 input_activation_bytes: usize,
1367 output_activations: *mut c_void,
1368 output_activation_capacity: usize,
1369 out_output_activation_bytes: *mut usize,
1370 out_error: *mut *mut Error,
1371 ) -> Status;
1372
1373 pub fn skippy_verify_tokens(
1374 session: *mut Session,
1375 token_ids: *const i32,
1376 token_count: usize,
1377 output_tokens: *mut i32,
1378 output_token_capacity: usize,
1379 out_token_count: *mut usize,
1380 out_error: *mut *mut Error,
1381 ) -> Status;
1382
1383 pub fn skippy_decode_step_sampled(
1384 session: *mut Session,
1385 token_id: i32,
1386 sampling: *const SamplingConfig,
1387 input_activation: *const c_void,
1388 input_activation_bytes: usize,
1389 output_activation: *mut c_void,
1390 output_activation_capacity: usize,
1391 out_output_activation_bytes: *mut usize,
1392 out_predicted_token: *mut i32,
1393 out_error: *mut *mut Error,
1394 ) -> Status;
1395
1396 pub fn skippy_decode_step_sampled_mtp(
1397 session: *mut Session,
1398 token_id: i32,
1399 sampling: *const SamplingConfig,
1400 out_predicted_token: *mut i32,
1401 max_draft_tokens: usize,
1402 out_mtp_draft: *mut NativeMtpDraft,
1403 out_error: *mut *mut Error,
1404 ) -> Status;
1405
1406 pub fn skippy_decode_batch_sampled(
1407 sessions: *const *mut Session,
1408 token_ids: *const i32,
1409 sampling: *const *const SamplingConfig,
1410 request_count: usize,
1411 out_predicted_tokens: *mut i32,
1412 predicted_token_capacity: usize,
1413 out_error: *mut *mut Error,
1414 ) -> Status;
1415
1416 pub fn skippy_prefill_chunk_frame(
1417 session: *mut Session,
1418 token_ids: *const i32,
1419 token_count: usize,
1420 input_desc: *const ActivationDesc,
1421 input_payload: *const c_void,
1422 output_desc: *mut ActivationDesc,
1423 output_payload: *mut c_void,
1424 output_payload_capacity: usize,
1425 out_output_payload_bytes: *mut usize,
1426 out_error: *mut *mut Error,
1427 ) -> Status;
1428
1429 pub fn skippy_prefill_chunk_frame_sampled(
1430 session: *mut Session,
1431 token_ids: *const i32,
1432 token_count: usize,
1433 sampling: *const SamplingConfig,
1434 input_desc: *const ActivationDesc,
1435 input_payload: *const c_void,
1436 output_desc: *mut ActivationDesc,
1437 output_payload: *mut c_void,
1438 output_payload_capacity: usize,
1439 out_output_payload_bytes: *mut usize,
1440 out_predicted_token: *mut i32,
1441 out_error: *mut *mut Error,
1442 ) -> Status;
1443
1444 pub fn skippy_prefill_chunk_frame_with_positions(
1445 session: *mut Session,
1446 token_ids: *const i32,
1447 token_count: usize,
1448 positions: *const i32,
1449 position_count: usize,
1450 input_desc: *const ActivationDesc,
1451 input_payload: *const c_void,
1452 output_desc: *mut ActivationDesc,
1453 output_payload: *mut c_void,
1454 output_payload_capacity: usize,
1455 out_output_payload_bytes: *mut usize,
1456 out_error: *mut *mut Error,
1457 ) -> Status;
1458
1459 pub fn skippy_prefill_chunk_frame_sampled_with_positions(
1460 session: *mut Session,
1461 token_ids: *const i32,
1462 token_count: usize,
1463 positions: *const i32,
1464 position_count: usize,
1465 sampling: *const SamplingConfig,
1466 input_desc: *const ActivationDesc,
1467 input_payload: *const c_void,
1468 output_desc: *mut ActivationDesc,
1469 output_payload: *mut c_void,
1470 output_payload_capacity: usize,
1471 out_output_payload_bytes: *mut usize,
1472 out_predicted_token: *mut i32,
1473 out_error: *mut *mut Error,
1474 ) -> Status;
1475
1476 pub fn skippy_verify_tokens_frame_sampled(
1477 session: *mut Session,
1478 token_ids: *const i32,
1479 token_count: usize,
1480 sampling: *const SamplingConfig,
1481 input_desc: *const ActivationDesc,
1482 input_payload: *const c_void,
1483 output_desc: *mut ActivationDesc,
1484 output_payload: *mut c_void,
1485 output_payload_capacity: usize,
1486 out_output_payload_bytes: *mut usize,
1487 output_tokens: *mut i32,
1488 output_token_capacity: usize,
1489 out_token_count: *mut usize,
1490 max_draft_tokens: usize,
1491 out_mtp_draft: *mut NativeMtpDraft,
1492 out_error: *mut *mut Error,
1493 ) -> Status;
1494
1495 pub fn skippy_decode_step_frame_sampled(
1496 session: *mut Session,
1497 token_id: i32,
1498 sampling: *const SamplingConfig,
1499 input_desc: *const ActivationDesc,
1500 input_payload: *const c_void,
1501 output_desc: *mut ActivationDesc,
1502 output_payload: *mut c_void,
1503 output_payload_capacity: usize,
1504 out_output_payload_bytes: *mut usize,
1505 out_predicted_token: *mut i32,
1506 out_error: *mut *mut Error,
1507 ) -> Status;
1508
1509 pub fn skippy_decode_step_frame_sampled_mtp(
1510 session: *mut Session,
1511 token_id: i32,
1512 sampling: *const SamplingConfig,
1513 input_desc: *const ActivationDesc,
1514 input_payload: *const c_void,
1515 output_desc: *mut ActivationDesc,
1516 output_payload: *mut c_void,
1517 output_payload_capacity: usize,
1518 out_output_payload_bytes: *mut usize,
1519 out_predicted_token: *mut i32,
1520 max_draft_tokens: usize,
1521 out_mtp_draft: *mut NativeMtpDraft,
1522 out_error: *mut *mut Error,
1523 ) -> Status;
1524
1525 pub fn skippy_decode_step_frame_batch_sampled(
1526 sessions: *const *mut Session,
1527 token_ids: *const i32,
1528 sampling: *const *const SamplingConfig,
1529 input_descs: *const *const ActivationDesc,
1530 input_payloads: *const *const c_void,
1531 output_descs: *mut ActivationDesc,
1532 output_payloads: *const *mut c_void,
1533 output_payload_capacities: *const usize,
1534 out_output_payload_bytes: *mut usize,
1535 out_predicted_tokens: *mut i32,
1536 predicted_token_capacity: usize,
1537 request_count: usize,
1538 out_error: *mut *mut Error,
1539 ) -> Status;
1540
1541 pub fn skippy_session_copy_output_activation_frame(
1542 session: *mut Session,
1543 token_count: usize,
1544 output_desc: *mut ActivationDesc,
1545 output_payload: *mut c_void,
1546 output_payload_capacity: usize,
1547 out_output_payload_bytes: *mut usize,
1548 out_error: *mut *mut Error,
1549 ) -> Status;
1550
1551 pub fn skippy_session_last_token_signal(
1552 session: *mut Session,
1553 out_signal: *mut TokenSignal,
1554 out_error: *mut *mut Error,
1555 ) -> Status;
1556
1557 pub fn skippy_session_signal_window(
1558 session: *mut Session,
1559 window_tokens: u32,
1560 out_window: *mut GenerationSignalWindow,
1561 out_error: *mut *mut Error,
1562 ) -> Status;
1563
1564 pub fn skippy_trim_session(
1565 session: *mut Session,
1566 token_count: u64,
1567 out_error: *mut *mut Error,
1568 ) -> Status;
1569
1570 pub fn skippy_retire_verify_checkpoint(
1571 session: *mut Session,
1572 token_start: u64,
1573 token_count: u64,
1574 out_error: *mut *mut Error,
1575 ) -> Status;
1576
1577 pub fn skippy_export_state(
1578 session: *mut Session,
1579 layer_start: i32,
1580 layer_end: i32,
1581 output: *mut c_void,
1582 output_capacity: usize,
1583 out_bytes: *mut usize,
1584 out_error: *mut *mut Error,
1585 ) -> Status;
1586
1587 pub fn skippy_import_state(
1588 session: *mut Session,
1589 layer_start: i32,
1590 layer_end: i32,
1591 input: *const c_void,
1592 input_bytes: usize,
1593 out_error: *mut *mut Error,
1594 ) -> Status;
1595
1596 pub fn skippy_export_full_state(
1597 session: *mut Session,
1598 layer_start: i32,
1599 layer_end: i32,
1600 output: *mut c_void,
1601 output_capacity: usize,
1602 out_bytes: *mut usize,
1603 out_error: *mut *mut Error,
1604 ) -> Status;
1605
1606 pub fn skippy_import_full_state(
1607 session: *mut Session,
1608 layer_start: i32,
1609 layer_end: i32,
1610 input: *const c_void,
1611 input_bytes: usize,
1612 out_error: *mut *mut Error,
1613 ) -> Status;
1614
1615 pub fn skippy_export_kv_page(
1616 session: *mut Session,
1617 layer_start: i32,
1618 layer_end: i32,
1619 token_start: u64,
1620 token_count: u64,
1621 out_desc: *mut KvPageDesc,
1622 output: *mut c_void,
1623 output_capacity: usize,
1624 out_bytes: *mut usize,
1625 out_error: *mut *mut Error,
1626 ) -> Status;
1627
1628 pub fn skippy_import_kv_page(
1629 session: *mut Session,
1630 desc: *const KvPageDesc,
1631 input: *const c_void,
1632 input_bytes: usize,
1633 out_error: *mut *mut Error,
1634 ) -> Status;
1635
1636 pub fn skippy_export_recurrent_state(
1637 session: *mut Session,
1638 output: *mut c_void,
1639 output_capacity: usize,
1640 out_bytes: *mut usize,
1641 out_error: *mut *mut Error,
1642 ) -> Status;
1643
1644 pub fn skippy_import_recurrent_state(
1645 session: *mut Session,
1646 input: *const c_void,
1647 input_bytes: usize,
1648 out_error: *mut *mut Error,
1649 ) -> Status;
1650
1651 pub fn skippy_session_save_prefix(
1652 session: *mut Session,
1653 cache_seq_id: i32,
1654 token_count: u64,
1655 out_error: *mut *mut Error,
1656 ) -> Status;
1657
1658 pub fn skippy_session_restore_prefix(
1659 session: *mut Session,
1660 cache_seq_id: i32,
1661 token_ids: *const i32,
1662 token_count: usize,
1663 out_error: *mut *mut Error,
1664 ) -> Status;
1665
1666 pub fn skippy_session_drop_sequence(
1667 session: *mut Session,
1668 seq_id: i32,
1669 out_error: *mut *mut Error,
1670 ) -> Status;
1671
1672 pub fn skippy_tokenize(
1673 model: *mut Model,
1674 text: *const c_char,
1675 add_special: bool,
1676 output_tokens: *mut i32,
1677 output_token_capacity: usize,
1678 out_token_count: *mut usize,
1679 out_error: *mut *mut Error,
1680 ) -> Status;
1681
1682 pub fn skippy_detokenize(
1683 model: *mut Model,
1684 tokens: *const i32,
1685 token_count: usize,
1686 output_text: *mut c_char,
1687 output_text_capacity: usize,
1688 out_text_bytes: *mut usize,
1689 out_error: *mut *mut Error,
1690 ) -> Status;
1691
1692 pub fn skippy_token_is_eog(
1693 model: *mut Model,
1694 token_id: i32,
1695 out_is_eog: *mut bool,
1696 out_error: *mut *mut Error,
1697 ) -> Status;
1698
1699 pub fn skippy_apply_chat_template(
1700 model: *mut Model,
1701 messages: *const ChatMessage,
1702 message_count: usize,
1703 add_assistant: bool,
1704 override_enable_thinking: bool,
1705 enable_thinking: bool,
1706 output_text: *mut c_char,
1707 output_text_capacity: usize,
1708 out_text_bytes: *mut usize,
1709 out_error: *mut *mut Error,
1710 ) -> Status;
1711
1712 pub fn skippy_apply_chat_template_json(
1713 model: *mut Model,
1714 messages_json: *const c_char,
1715 tools_json: *const c_char,
1716 tool_choice_json: *const c_char,
1717 add_assistant: bool,
1718 override_enable_thinking: bool,
1719 enable_thinking: bool,
1720 parallel_tool_calls: bool,
1721 reasoning_format: *const c_char,
1722 output_text: *mut c_char,
1723 output_text_capacity: usize,
1724 out_text_bytes: *mut usize,
1725 output_metadata_json: *mut c_char,
1726 output_metadata_json_capacity: usize,
1727 out_metadata_json_bytes: *mut usize,
1728 out_error: *mut *mut Error,
1729 ) -> Status;
1730
1731 pub fn skippy_parse_chat_response_json(
1732 generated_text: *const c_char,
1733 metadata_json: *const c_char,
1734 is_partial: bool,
1735 output_message_json: *mut c_char,
1736 output_message_json_capacity: usize,
1737 out_message_json_bytes: *mut usize,
1738 out_error: *mut *mut Error,
1739 ) -> Status;
1740
1741 pub fn skippy_model_info_open(
1742 path: *const c_char,
1743 out_info: *mut *mut ModelInfo,
1744 out_error: *mut *mut Error,
1745 ) -> Status;
1746
1747 pub fn skippy_model_info_free(info: *mut ModelInfo, out_error: *mut *mut Error) -> Status;
1748
1749 pub fn skippy_model_info_tensor_count(
1750 info: *mut ModelInfo,
1751 out_count: *mut usize,
1752 out_error: *mut *mut Error,
1753 ) -> Status;
1754
1755 pub fn skippy_model_info_tensor_at(
1756 info: *mut ModelInfo,
1757 index: usize,
1758 out_tensor: *mut TensorInfo,
1759 out_error: *mut *mut Error,
1760 ) -> Status;
1761
1762 pub fn skippy_slice_plan_create(
1763 info: *mut ModelInfo,
1764 out_plan: *mut *mut SlicePlan,
1765 out_error: *mut *mut Error,
1766 ) -> Status;
1767
1768 pub fn skippy_slice_plan_free(plan: *mut SlicePlan, out_error: *mut *mut Error) -> Status;
1769
1770 pub fn skippy_slice_plan_add_layer_range(
1771 plan: *mut SlicePlan,
1772 stage_index: i32,
1773 layer_start: i32,
1774 layer_end: i32,
1775 include_embeddings: bool,
1776 include_output: bool,
1777 out_error: *mut *mut Error,
1778 ) -> Status;
1779
1780 pub fn skippy_write_slice_gguf(
1781 info: *mut ModelInfo,
1782 plan: *const SlicePlan,
1783 stage_index: i32,
1784 output_path: *const c_char,
1785 out_error: *mut *mut Error,
1786 ) -> Status;
1787
1788 pub fn skippy_write_gguf_from_parts(
1789 input_paths: *const *const c_char,
1790 input_count: usize,
1791 output_path: *const c_char,
1792 out_error: *mut *mut Error,
1793 ) -> Status;
1794
1795 pub fn mtmd_default_marker() -> *const c_char;
1796
1797 pub fn mtmd_helper_log_set(log_callback: LlamaLogCallback, user_data: *mut c_void);
1798
1799 pub fn mtmd_context_params_default() -> MtmdContextParams;
1800
1801 pub fn mtmd_init_from_file(
1802 mmproj_fname: *const c_char,
1803 text_model: *const Opaque,
1804 ctx_params: MtmdContextParams,
1805 ) -> *mut MtmdContext;
1806
1807 pub fn mtmd_free(ctx: *mut MtmdContext);
1808
1809 pub fn mtmd_helper_bitmap_init_from_buf(
1810 ctx: *mut MtmdContext,
1811 buf: *const u8,
1812 len: usize,
1813 ) -> *mut MtmdBitmap;
1814
1815 pub fn mtmd_bitmap_free(bitmap: *mut MtmdBitmap);
1816
1817 pub fn mtmd_input_chunks_init() -> *mut MtmdInputChunks;
1818
1819 pub fn mtmd_input_chunks_free(chunks: *mut MtmdInputChunks);
1820
1821 pub fn mtmd_tokenize(
1822 ctx: *mut MtmdContext,
1823 output: *mut MtmdInputChunks,
1824 text: *const MtmdInputText,
1825 bitmaps: *const *const MtmdBitmap,
1826 n_bitmaps: usize,
1827 ) -> c_int;
1828
1829 pub fn mtmd_helper_get_n_tokens(chunks: *const MtmdInputChunks) -> usize;
1830
1831 pub fn mtmd_helper_get_n_pos(chunks: *const MtmdInputChunks) -> i32;
1832
1833 pub fn mtmd_input_chunks_size(chunks: *const MtmdInputChunks) -> usize;
1834
1835 pub fn mtmd_input_chunks_get(chunks: *const MtmdInputChunks, index: usize) -> *const Opaque;
1836
1837 pub fn mtmd_decode_use_mrope(ctx: *const MtmdContext) -> bool;
1838
1839 pub fn mtmd_input_chunk_get_type(chunk: *const Opaque) -> MtmdInputChunkType;
1840
1841 pub fn mtmd_input_chunk_get_n_tokens(chunk: *const Opaque) -> usize;
1842
1843 pub fn mtmd_input_chunk_get_tokens_text(
1844 chunk: *const Opaque,
1845 out_count: *mut usize,
1846 ) -> *const i32;
1847
1848 pub fn mtmd_input_chunk_get_tokens_image(chunk: *const Opaque) -> *const Opaque;
1849
1850 pub fn mtmd_helper_image_get_decoder_pos(
1851 image: *const Opaque,
1852 pos_0: i32,
1853 out_pos: *mut MtmdDecoderPos,
1854 );
1855
1856 pub fn mtmd_helper_eval_chunks(
1857 ctx: *mut MtmdContext,
1858 lctx: *mut Opaque,
1859 chunks: *const MtmdInputChunks,
1860 n_past: i32,
1861 seq_id: i32,
1862 n_batch: i32,
1863 logits_last: bool,
1864 new_n_past: *mut i32,
1865 ) -> c_int;
1866
1867 pub fn mtmd_helper_eval_chunk_single(
1868 ctx: *mut MtmdContext,
1869 lctx: *mut Opaque,
1870 chunk: *const Opaque,
1871 n_past: i32,
1872 seq_id: i32,
1873 n_batch: i32,
1874 logits_last: bool,
1875 new_n_past: *mut i32,
1876 ) -> c_int;
1877}
1878
1879#[cfg(not(feature = "dynamic-runtime"))]
1880pub fn skippy_model_attach_mtp_draft_model_fn() -> Option<SkippyModelAttachMtpDraftModelFn> {
1881 Some(skippy_model_attach_mtp_draft_model)
1882}
1883
1884#[cfg(not(feature = "dynamic-runtime"))]
1885pub fn skippy_decode_step_sampled_mtp_fn() -> Option<SkippyDecodeStepSampledMtpFn> {
1886 Some(skippy_decode_step_sampled_mtp)
1887}
1888
1889pub type Opaque = c_void;
1890
1891#[cfg(test)]
1892mod tests {
1893 use super::*;
1894 use std::mem::{offset_of, size_of};
1895
1896 const fn version(major: u32, minor: u32, patch: u32) -> AbiVersion {
1897 AbiVersion {
1898 major,
1899 minor,
1900 patch,
1901 }
1902 }
1903
1904 #[test]
1905 fn accepts_current_patch_runtime() {
1906 assert!(runtime_abi_supported(version(
1907 ABI_VERSION_MAJOR,
1908 ABI_VERSION_MINOR,
1909 ABI_VERSION_PATCH,
1910 )));
1911 }
1912
1913 #[test]
1914 fn rejects_other_patch_runtimes() {
1915 assert!(!runtime_abi_supported(version(
1916 ABI_VERSION_MAJOR,
1917 ABI_VERSION_MINOR,
1918 ABI_VERSION_PATCH + 1,
1919 )));
1920 assert!(!runtime_abi_supported(version(
1921 ABI_VERSION_MAJOR,
1922 ABI_VERSION_MINOR,
1923 ABI_VERSION_PATCH - 1,
1924 )));
1925 }
1926
1927 #[test]
1928 fn rejects_major_and_minor_mismatches() {
1929 assert!(!runtime_abi_supported(version(
1930 ABI_VERSION_MAJOR + 1,
1931 ABI_VERSION_MINOR,
1932 ABI_VERSION_PATCH,
1933 )));
1934 assert!(!runtime_abi_supported(version(
1935 ABI_VERSION_MAJOR,
1936 ABI_VERSION_MINOR + 1,
1937 ABI_VERSION_PATCH,
1938 )));
1939 }
1940
1941 #[test]
1942 #[cfg(target_pointer_width = "64")]
1943 fn mtmd_context_params_matches_native_layout() {
1944 assert_eq!(size_of::<MtmdContextParams>(), 80);
1945 assert_eq!(offset_of!(MtmdContextParams, batch_max_tokens), 56);
1946 assert_eq!(offset_of!(MtmdContextParams, progress_callback), 64);
1947 assert_eq!(
1948 offset_of!(MtmdContextParams, progress_callback_user_data),
1949 72
1950 );
1951 }
1952
1953 #[test]
1954 #[cfg(not(feature = "dynamic-runtime"))]
1955 fn native_mtmd_defaults_cross_the_ffi_boundary() {
1956 let params = unsafe { mtmd_context_params_default() };
1957
1958 assert_eq!(params.batch_max_tokens, 1024);
1959 assert!(params.progress_callback.is_none());
1960 assert!(params.progress_callback_user_data.is_null());
1961 }
1962}