llama_cpp_2/
speculative.rs1use crate::context::LlamaContext;
4use crate::llama_batch::LlamaBatch;
5use crate::ptr::Ptr;
6use crate::status_is_ok;
7use crate::token::LlamaToken;
8
9#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct MtpSpeculativeParams {
12 pub n_max: i32,
14 pub n_min: i32,
16 pub p_min: f32,
18}
19
20impl Default for MtpSpeculativeParams {
21 fn default() -> Self {
22 Self {
23 n_max: 3,
24 n_min: 0,
25 p_min: 0.0,
26 }
27 }
28}
29
30#[derive(Debug, Eq, PartialEq, thiserror::Error)]
32pub enum MtpSpeculativeError {
33 #[error("invalid MTP speculative parameters")]
35 InvalidParams,
36 #[error("llama.cpp failed to initialize MTP speculative decoding")]
38 InitFailed,
39 #[error("llama.cpp MTP speculative call failed with status {0}")]
41 Status(i32),
42 #[error("llama.cpp MTP draft exceeded configured maximum")]
44 DraftOverflow,
45}
46
47#[derive(Debug)]
52pub struct MtpSpeculative<'model> {
53 raw: Ptr<llama_cpp_sys_2::llama_rs_mtp_speculative>,
54 target_context: LlamaContext<'model>,
55 draft_context: LlamaContext<'model>,
56 n_max: usize,
57}
58
59unsafe impl Send for MtpSpeculative<'_> {}
62
63impl<'model> MtpSpeculative<'model> {
64 pub fn new(
72 mut target_context: LlamaContext<'model>,
73 mut draft_context: LlamaContext<'model>,
74 params: MtpSpeculativeParams,
75 ) -> Result<Self, MtpSpeculativeError> {
76 if params.n_max <= 0 || params.n_min < 0 || params.n_min > params.n_max {
77 return Err(MtpSpeculativeError::InvalidParams);
78 }
79 let n_max =
80 usize::try_from(params.n_max).map_err(|_| MtpSpeculativeError::InvalidParams)?;
81
82 let raw = unsafe {
83 llama_cpp_sys_2::llama_rs_mtp_speculative_init(
84 target_context.context.as_mut_ptr(),
85 draft_context.context.as_mut_ptr(),
86 params.n_max,
87 params.n_min,
88 params.p_min,
89 )
90 };
91 let raw = Ptr::new(raw).ok_or(MtpSpeculativeError::InitFailed)?;
92
93 Ok(Self {
94 raw,
95 target_context,
96 draft_context,
97 n_max,
98 })
99 }
100
101 #[must_use]
103 pub fn target_context(&self) -> &LlamaContext<'model> {
104 &self.target_context
105 }
106
107 pub fn target_context_mut(&mut self) -> &mut LlamaContext<'model> {
109 &mut self.target_context
110 }
111
112 pub fn draft_context_mut(&mut self) -> &mut LlamaContext<'model> {
114 &mut self.draft_context
115 }
116
117 pub fn begin(&mut self, prompt_tokens: &[LlamaToken]) -> Result<(), MtpSpeculativeError> {
123 let prompt = tokens_to_raw(prompt_tokens);
124 let status = unsafe {
125 llama_cpp_sys_2::llama_rs_mtp_speculative_begin(
126 self.raw.as_mut_ptr(),
127 prompt.as_ptr(),
128 prompt.len(),
129 )
130 };
131 status_to_result(status)
132 }
133
134 pub fn process(&mut self, batch: &LlamaBatch<'_>) -> Result<(), MtpSpeculativeError> {
142 let status = unsafe {
143 llama_cpp_sys_2::llama_rs_mtp_speculative_process(
144 self.raw.as_mut_ptr(),
145 std::ptr::from_ref(&batch.llama_batch),
146 )
147 };
148 status_to_result(status)
149 }
150
151 pub fn draft(
158 &mut self,
159 n_past: i32,
160 id_last: LlamaToken,
161 prompt_tokens: &[LlamaToken],
162 ) -> Result<Vec<LlamaToken>, MtpSpeculativeError> {
163 if n_past < 0 {
164 return Err(MtpSpeculativeError::InvalidParams);
165 }
166
167 let prompt = tokens_to_raw(prompt_tokens);
168 let mut raw_out = vec![0; self.n_max];
169 let mut out_len = 0_usize;
170 let status = unsafe {
171 llama_cpp_sys_2::llama_rs_mtp_speculative_draft(
172 self.raw.as_mut_ptr(),
173 n_past,
174 id_last.0,
175 prompt.as_ptr(),
176 prompt.len(),
177 raw_out.as_mut_ptr(),
178 raw_out.len(),
179 &raw mut out_len,
180 )
181 };
182 if status == llama_cpp_sys_2::LLAMA_RS_STATUS_ALLOCATION_FAILED {
183 return Err(MtpSpeculativeError::DraftOverflow);
184 }
185 status_to_result(status)?;
186 raw_out.truncate(out_len);
187 Ok(raw_out.into_iter().map(LlamaToken).collect())
188 }
189
190 pub fn accept(&mut self, n_accepted: u16) -> Result<(), MtpSpeculativeError> {
196 let status = unsafe {
197 llama_cpp_sys_2::llama_rs_mtp_speculative_accept(self.raw.as_mut_ptr(), n_accepted)
198 };
199 status_to_result(status)
200 }
201}
202
203impl Drop for MtpSpeculative<'_> {
204 fn drop(&mut self) {
205 unsafe { llama_cpp_sys_2::llama_rs_mtp_speculative_free(self.raw.as_mut_ptr()) }
206 }
207}
208
209fn tokens_to_raw(tokens: &[LlamaToken]) -> Vec<llama_cpp_sys_2::llama_token> {
210 tokens.iter().map(|token| token.0).collect()
211}
212
213fn status_to_result(status: llama_cpp_sys_2::llama_rs_status) -> Result<(), MtpSpeculativeError> {
214 if status_is_ok(status) {
215 Ok(())
216 } else {
217 Err(MtpSpeculativeError::Status(status as i32))
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 static_assertions::assert_impl_all!(MtpSpeculative<'static>: Send);
226 static_assertions::assert_not_impl_any!(MtpSpeculative<'static>: Sync);
228}