1use std::ffi::{c_char, c_void};
16
17pub const NATIVE_SERVING_PLUGIN_ABI_V1: u32 = 1;
18pub const NATIVE_SERVING_PLUGIN_ENTRY_V1: &[u8] = b"mesh_native_serving_plugin_v1\0";
19pub const MAX_DECISION_ID_BYTES: usize = 64;
20pub const TOKENIZER_INVENTORY_SCHEMA: u32 = 1;
21
22#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct TokenizerInventory {
25 pub schema_version: u32,
26 pub model_id: String,
27 pub source_model_sha256: String,
28 pub tokenizer_id: String,
29 pub tokens: Vec<TokenizerInventoryToken>,
30}
31
32#[derive(Clone, Debug, Eq, PartialEq)]
33pub struct TokenizerInventoryToken {
34 pub id: u32,
35 pub piece: TokenizerInventoryPiece,
36}
37
38#[derive(Clone, Debug, Eq, PartialEq)]
39pub enum TokenizerInventoryPiece {
40 Bytes { bytes: Vec<u8> },
41 Control { identity: String },
42}
43
44pub type PluginInstance = *mut c_void;
45pub type ProposalOperation = u64;
46
47#[repr(C)]
48#[derive(Clone, Copy, Debug, Default)]
49pub struct ByteSlice {
50 pub pointer: *const u8,
51 pub length: usize,
52}
53
54impl ByteSlice {
55 #[must_use]
56 pub fn from_bytes(bytes: &[u8]) -> Self {
57 Self {
58 pointer: bytes.as_ptr(),
59 length: bytes.len(),
60 }
61 }
62}
63
64#[repr(transparent)]
65#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub struct TokenizerPieceKind(pub u32);
67
68impl TokenizerPieceKind {
69 pub const BYTES: Self = Self(0);
70 pub const CONTROL: Self = Self(1);
71}
72
73#[repr(C)]
77#[derive(Clone, Copy, Debug)]
78pub struct TokenizerInventoryEntry {
79 pub id: u32,
80 pub piece_kind: TokenizerPieceKind,
81 pub bytes: ByteSlice,
82}
83
84#[repr(C)]
87#[derive(Clone, Copy, Debug)]
88pub struct TokenizerInventoryView {
89 pub struct_size: usize,
90 pub schema_version: u32,
91 pub entries: *const TokenizerInventoryEntry,
92 pub entry_count: usize,
93}
94
95#[repr(C)]
96#[derive(Clone, Copy, Debug, Default)]
97pub struct TokenSlice {
98 pub pointer: *const i32,
99 pub length: usize,
100}
101
102#[repr(C)]
103#[derive(Clone, Copy, Debug, Default)]
104pub struct U64Slice {
105 pub pointer: *const u64,
106 pub length: usize,
107}
108
109impl U64Slice {
110 #[must_use]
111 pub fn from_values(values: &[u64]) -> Self {
112 Self {
113 pointer: values.as_ptr(),
114 length: values.len(),
115 }
116 }
117}
118
119impl TokenSlice {
120 #[must_use]
121 pub fn from_tokens(tokens: &[i32]) -> Self {
122 Self {
123 pointer: tokens.as_ptr(),
124 length: tokens.len(),
125 }
126 }
127}
128
129#[repr(transparent)]
130#[derive(Clone, Copy, Debug, Eq, PartialEq)]
131pub struct PluginStatus(pub u32);
132
133impl PluginStatus {
134 pub const OK: Self = Self(0);
135 pub const INVALID_ARGUMENT: Self = Self(1);
136 pub const INCOMPATIBLE: Self = Self(2);
137 pub const UNAVAILABLE: Self = Self(3);
138 pub const INTERNAL_ERROR: Self = Self(4);
139}
140
141#[repr(transparent)]
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
143pub struct ProposalPollStatus(pub u32);
144
145impl ProposalPollStatus {
146 pub const PENDING: Self = Self(0);
147 pub const READY: Self = Self(1);
148 pub const ABSTAIN: Self = Self(2);
149 pub const FAILED: Self = Self(3);
150}
151
152#[repr(transparent)]
153#[derive(Clone, Copy, Debug, Eq, PartialEq)]
154pub struct GenerationTermination(pub u32);
155
156impl GenerationTermination {
157 pub const CALLBACK_STOP: Self = Self(0);
158 pub const MAX_TOKENS: Self = Self(1);
159 pub const CANCELLED: Self = Self(2);
160}
161
162#[repr(transparent)]
163#[derive(Clone, Copy, Debug, Eq, PartialEq)]
164pub struct ProposalDisposition(pub u32);
165
166impl ProposalDisposition {
167 pub const FULL_ACCEPT: Self = Self(0);
168 pub const FIRST_MISMATCH: Self = Self(1);
169 pub const STOPPED: Self = Self(2);
170}
171
172#[repr(transparent)]
173#[derive(Clone, Copy, Debug, Eq, PartialEq)]
174pub struct ProposalDiscardReason(pub u32);
175
176impl ProposalDiscardReason {
177 pub const DEADLINE_EXCEEDED: Self = Self(0);
178 pub const INVALID_TOKEN_COUNT: Self = Self(1);
179 pub const INVALID_TOKEN_ID: Self = Self(2);
180 pub const POSITION_MISMATCH: Self = Self(3);
181 pub const EXECUTION_FAILED: Self = Self(4);
182}
183
184pub type MonotonicNowNs = unsafe extern "C" fn(context: *mut c_void) -> u64;
185
186#[repr(C)]
187#[derive(Clone, Copy)]
188pub struct ActivationContext {
189 pub struct_size: usize,
190 pub model_id: ByteSlice,
191 pub source_model_sha256: ByteSlice,
192 pub tokenizer_id: ByteSlice,
193 pub tokenizer_inventory: *const TokenizerInventoryView,
194 pub config_path: ByteSlice,
195 pub state_directory: ByteSlice,
196 pub proposal_deadline_ns: u64,
197 pub host_clock_context: *mut c_void,
198 pub monotonic_now_ns: MonotonicNowNs,
199}
200
201#[repr(C)]
202#[derive(Clone, Copy, Debug)]
203pub struct PluginActivation {
204 pub instance: PluginInstance,
205}
206
207#[repr(C)]
208#[derive(Clone, Copy, Debug, Default)]
209pub struct GenerationStart {
210 pub struct_size: usize,
211 pub request_id: u64,
212 pub session_id: u64,
213 pub agent_session_id: ByteSlice,
214 pub prompt_token_ids: TokenSlice,
215}
216
217#[repr(C)]
218#[derive(Clone, Copy, Debug, Default)]
219pub struct GenerationCommit {
220 pub struct_size: usize,
221 pub request_id: u64,
222 pub session_id: u64,
223 pub generated_token_count: u64,
224 pub token_ids: TokenSlice,
225}
226
227#[repr(C)]
228#[derive(Clone, Copy, Debug, Default)]
229pub struct GenerationAbort {
230 pub struct_size: usize,
231 pub request_id: u64,
232 pub session_id: u64,
233}
234
235#[repr(C)]
236#[derive(Clone, Copy, Debug)]
237pub struct GenerationFinish {
238 pub struct_size: usize,
239 pub request_id: u64,
240 pub session_id: u64,
241 pub prompt_token_count: u64,
242 pub prompt_token_digest: [u8; 32],
243 pub prompt_token_ids: TokenSlice,
244 pub generated_token_ids: TokenSlice,
245 pub final_session_position: u64,
246 pub termination: GenerationTermination,
247 pub model_generation_elapsed_us: u64,
248 pub has_request_to_first_token: bool,
249 pub request_to_first_token_us: u64,
250 pub request_to_token_emission_us: U64Slice,
251}
252
253#[repr(C)]
254#[derive(Clone, Copy, Debug, Default)]
255pub struct ProposalQuery {
256 pub struct_size: usize,
257 pub request_id: u64,
258 pub session_id: u64,
259 pub prompt_token_count: u64,
260 pub committed_token_count: u64,
261 pub decode_step: u64,
262 pub max_proposal_tokens: u64,
263 pub absolute_deadline_ns: u64,
264}
265
266#[repr(C)]
267#[derive(Debug)]
268pub struct ProposalOutput {
269 pub struct_size: usize,
270 pub decision_id: *mut u8,
271 pub decision_id_capacity: usize,
272 pub decision_id_length: usize,
273 pub token_ids: *mut i32,
274 pub token_capacity: usize,
275 pub token_length: usize,
276}
277
278#[repr(C)]
279#[derive(Clone, Copy, Debug)]
280pub struct ProposalOutcome {
281 pub struct_size: usize,
282 pub decision_id: ByteSlice,
283 pub disposition: ProposalDisposition,
284 pub proposal_token_count: u64,
285 pub verification_rows: u64,
286 pub accepted_proposal_tokens: u64,
287 pub committed_tokens: TokenSlice,
288 pub verification_row_predictions: TokenSlice,
289 pub canonical_prediction_count: u64,
290 pub has_correction_or_boundary_token: bool,
291 pub correction_or_boundary_token: i32,
292 pub base_position: u64,
293 pub position_after_verification: u64,
294 pub canonical_position: u64,
295 pub trimmed_rows: u64,
296}
297
298#[repr(C)]
299#[derive(Clone, Copy, Debug)]
300pub struct ProposalDiscard {
301 pub struct_size: usize,
302 pub decision_id: ByteSlice,
303 pub reason: ProposalDiscardReason,
304}
305
306pub type ActivatePlugin = unsafe extern "C" fn(
307 context: *const ActivationContext,
308 activation: *mut PluginActivation,
309) -> PluginStatus;
310pub type ShutdownPlugin = unsafe extern "C" fn(instance: PluginInstance) -> PluginStatus;
311pub type BeginGeneration =
312 unsafe extern "C" fn(instance: PluginInstance, event: *const GenerationStart) -> PluginStatus;
313pub type CommitGeneration =
314 unsafe extern "C" fn(instance: PluginInstance, event: *const GenerationCommit) -> PluginStatus;
315pub type AbortGeneration =
316 unsafe extern "C" fn(instance: PluginInstance, event: *const GenerationAbort) -> PluginStatus;
317pub type FinishGeneration =
318 unsafe extern "C" fn(instance: PluginInstance, event: *const GenerationFinish) -> PluginStatus;
319pub type StartProposal = unsafe extern "C" fn(
320 instance: PluginInstance,
321 query: *const ProposalQuery,
322 operation: *mut ProposalOperation,
323) -> PluginStatus;
324pub type PollProposal = unsafe extern "C" fn(
325 instance: PluginInstance,
326 operation: ProposalOperation,
327 output: *mut ProposalOutput,
328) -> ProposalPollStatus;
329pub type CancelProposal =
330 unsafe extern "C" fn(instance: PluginInstance, operation: ProposalOperation);
331pub type ReportProposal =
332 unsafe extern "C" fn(instance: PluginInstance, outcome: *const ProposalOutcome) -> PluginStatus;
333pub type DiscardProposal =
334 unsafe extern "C" fn(instance: PluginInstance, discard: *const ProposalDiscard) -> PluginStatus;
335pub type LastError =
336 unsafe extern "C" fn(instance: PluginInstance, output: *mut c_char, capacity: usize) -> usize;
337
338#[repr(C)]
339pub struct NativeServingPluginV1 {
340 pub abi_version: u32,
341 pub struct_size: usize,
342 pub plugin_name: ByteSlice,
343 pub activate: ActivatePlugin,
344 pub shutdown: ShutdownPlugin,
345 pub begin_generation: BeginGeneration,
346 pub commit_generation: CommitGeneration,
347 pub abort_generation: AbortGeneration,
348 pub finish_generation: FinishGeneration,
349 pub start_proposal: StartProposal,
350 pub poll_proposal: PollProposal,
351 pub cancel_proposal: CancelProposal,
352 pub report_proposal: ReportProposal,
353 pub discard_proposal: DiscardProposal,
354 pub last_error: LastError,
355}
356
357unsafe impl Sync for NativeServingPluginV1 {}
362
363pub type NativeServingPluginEntryV1 = unsafe extern "C" fn() -> *const NativeServingPluginV1;
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn borrowed_slices_preserve_exact_addresses_and_lengths() {
371 let bytes = b"cacheline";
372 let tokens = [-1, 0, 42];
373 let byte_slice = ByteSlice::from_bytes(bytes);
374 let token_slice = TokenSlice::from_tokens(&tokens);
375 assert_eq!(byte_slice.pointer, bytes.as_ptr());
376 assert_eq!(byte_slice.length, bytes.len());
377 assert_eq!(token_slice.pointer, tokens.as_ptr());
378 assert_eq!(token_slice.length, tokens.len());
379 }
380
381 #[test]
382 fn initial_contract_is_v1() {
383 assert_eq!(MAX_DECISION_ID_BYTES, 64);
384 assert_eq!(NATIVE_SERVING_PLUGIN_ABI_V1, 1);
385 }
386}