openvino_genai_sys/lib.rs
1//! This crate provides low-level, unsafe, Rust bindings to OpenVINO™ GenAI using its [C API]. If
2//! you are looking to use OpenVINO™ GenAI from Rust, you likely should look at the ergonomic, safe
3//! bindings in [openvino-genai], which depends on this crate. See the repository [README] for more
4//! information, including build instructions.
5//!
6//! [C API]: https://github.com/openvinotoolkit/openvino.genai
7//! [openvino-genai-sys]: https://crates.io/crates/openvino-genai-sys
8//! [openvino-genai]: https://crates.io/crates/openvino-genai
9//! [README]: https://github.com/intel/openvino-rs/tree/main/crates/openvino-genai-sys
10//!
11//! An example interaction with raw [openvino-genai-sys]:
12//! ```no_run
13//! openvino_genai_sys::library::load().expect("to have an OpenVINO GenAI library available");
14//! ```
15
16#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
17#![allow(unused, dead_code)]
18#![deny(clippy::all)]
19#![warn(clippy::pedantic)]
20#![warn(clippy::cargo)]
21#![allow(
22 clippy::must_use_candidate,
23 clippy::suspicious_doc_comments,
24 clippy::wildcard_imports,
25 clippy::doc_markdown
26)]
27
28mod linking;
29
30mod generated;
31pub use generated::*;
32
33// Re-export shared types from openvino-sys so that users of both crates share a single definition.
34pub use openvino_sys::ov_status_e;
35pub use openvino_sys::ov_tensor_t;
36
37/// Contains extra utilities for finding and loading the OpenVINO GenAI shared libraries.
38pub mod library {
39 use std::path::PathBuf;
40
41 /// When compiled with the `runtime-linking` feature, load the function definitions from a
42 /// shared library; with the `dynamic-linking` feature, this function does nothing since the
43 /// library has already been linked.
44 ///
45 /// # Errors
46 ///
47 /// When compiled with the `runtime-linking` feature, this may fail if the `openvino-finder`
48 /// cannot discover the library on the current system.
49 pub fn load() -> Result<(), String> {
50 super::generated::load()?;
51 init_variadic(find().as_deref())
52 }
53
54 /// Load the OpenVINO GenAI shared library from an explicit path.
55 ///
56 /// This is useful when the library is located in a non-standard directory that cannot be
57 /// discovered by the `openvino-finder` search paths or environment variables — for example,
58 /// when the path is read from a configuration file.
59 ///
60 /// The `path` should point to the `openvino_genai_c` shared library file
61 /// (e.g., `libopenvino_genai_c.so`).
62 ///
63 /// # Errors
64 ///
65 /// May fail if the shared library cannot be opened or is invalid.
66 pub fn load_from(path: impl Into<std::path::PathBuf>) -> Result<(), String> {
67 let path = path.into();
68 super::generated::load_from(path.clone())?;
69 init_variadic(Some(&path))
70 }
71
72 /// Initialize the variadic pipeline creation functions from the loaded library.
73 #[allow(unused_variables)]
74 fn init_variadic(path: Option<&std::path::Path>) -> Result<(), String> {
75 #[cfg(feature = "runtime-linking")]
76 if let Some(path) = path {
77 super::runtime_variadic::init_variadic_fns(path)?;
78 }
79 Ok(())
80 }
81
82 /// Return the location of the shared library `openvino-genai-sys` will link to. If compiled
83 /// with runtime linking, this will attempt to discover the location of an `openvino_genai_c`
84 /// shared library on the system. Otherwise (with dynamic linking or compilation from source),
85 /// this relies on a static path discovered at build time.
86 ///
87 /// Knowing the location of the OpenVINO GenAI libraries can be useful for ensuring the correct
88 /// runtime environment is configured.
89 pub fn find() -> Option<PathBuf> {
90 if cfg!(feature = "runtime-linking") {
91 openvino_finder::find("openvino_genai_c", openvino_finder::Linking::Dynamic)
92 } else {
93 Some(PathBuf::from(env!("OPENVINO_GENAI_LIB_PATH")))
94 }
95 }
96}
97
98// The C API exposes variadic pipeline constructors for device property key-value pairs. We provide
99// fixed-signature helpers in both linking modes so higher-level crates can pass a slice of property
100// pointers instead of manually expanding a C varargs list.
101
102const MAX_PROPERTIES: usize = 8;
103
104fn pad_props(
105 props: &[*const ::std::os::raw::c_char],
106) -> [*const ::std::os::raw::c_char; MAX_PROPERTIES * 2] {
107 let mut out = [std::ptr::null(); MAX_PROPERTIES * 2];
108 let n = props.len().min(MAX_PROPERTIES * 2);
109 out[..n].copy_from_slice(&props[..n]);
110 out
111}
112
113#[cfg(feature = "dynamic-linking")]
114mod dynamic_variadic {
115 use super::*;
116
117 unsafe extern "C" {
118 #[link_name = "ov_genai_llm_pipeline_create"]
119 fn ov_genai_llm_pipeline_create_raw(
120 models_path: *const ::std::os::raw::c_char,
121 device: *const ::std::os::raw::c_char,
122 property_args_size: usize,
123 pipe: *mut *mut ov_genai_llm_pipeline,
124 ...
125 ) -> ov_status_e;
126
127 #[link_name = "ov_genai_vlm_pipeline_create"]
128 fn ov_genai_vlm_pipeline_create_raw(
129 models_path: *const ::std::os::raw::c_char,
130 device: *const ::std::os::raw::c_char,
131 property_args_size: usize,
132 pipe: *mut *mut ov_genai_vlm_pipeline,
133 ...
134 ) -> ov_status_e;
135
136 #[link_name = "ov_genai_whisper_pipeline_create"]
137 fn ov_genai_whisper_pipeline_create_raw(
138 models_path: *const ::std::os::raw::c_char,
139 device: *const ::std::os::raw::c_char,
140 property_args_size: usize,
141 pipeline: *mut *mut ov_genai_whisper_pipeline,
142 ...
143 ) -> ov_status_e;
144 }
145
146 /// Create an LLM pipeline (dynamic-linking variant).
147 ///
148 /// `props` contains flattened key-value pairs as C string pointers: `[k1, v1, k2, v2, ...]`.
149 /// Pass an empty slice for no properties. Up to `MAX_PROPERTIES` pairs (16 pointers).
150 ///
151 /// # Safety
152 ///
153 /// The caller must ensure that `models_path`, `device`, and all pointers in `props` are
154 /// valid C strings, and `pipe` is a valid pointer to receive the created pipeline.
155 ///
156 /// # Panics
157 ///
158 /// Panics if `props` contains more than `MAX_PROPERTIES * 2` entries.
159 pub unsafe fn ov_genai_llm_pipeline_create(
160 models_path: *const ::std::os::raw::c_char,
161 device: *const ::std::os::raw::c_char,
162 property_args_size: usize,
163 pipe: *mut *mut ov_genai_llm_pipeline,
164 props: &[*const ::std::os::raw::c_char],
165 ) -> ov_status_e {
166 assert!(
167 props.len() <= MAX_PROPERTIES * 2,
168 "too many properties (max {})",
169 MAX_PROPERTIES
170 );
171 let p = pad_props(props);
172 ov_genai_llm_pipeline_create_raw(
173 models_path,
174 device,
175 property_args_size,
176 pipe,
177 p[0],
178 p[1],
179 p[2],
180 p[3],
181 p[4],
182 p[5],
183 p[6],
184 p[7],
185 p[8],
186 p[9],
187 p[10],
188 p[11],
189 p[12],
190 p[13],
191 p[14],
192 p[15],
193 )
194 }
195
196 /// Create a VLM pipeline (dynamic-linking variant).
197 ///
198 /// See [`ov_genai_llm_pipeline_create`] for details on the `props` parameter.
199 ///
200 /// # Safety
201 ///
202 /// Same safety requirements as [`ov_genai_llm_pipeline_create`].
203 ///
204 /// # Panics
205 ///
206 /// Panics if `props` contains more than `MAX_PROPERTIES * 2` entries.
207 pub unsafe fn ov_genai_vlm_pipeline_create(
208 models_path: *const ::std::os::raw::c_char,
209 device: *const ::std::os::raw::c_char,
210 property_args_size: usize,
211 pipe: *mut *mut ov_genai_vlm_pipeline,
212 props: &[*const ::std::os::raw::c_char],
213 ) -> ov_status_e {
214 assert!(
215 props.len() <= MAX_PROPERTIES * 2,
216 "too many properties (max {})",
217 MAX_PROPERTIES
218 );
219 let p = pad_props(props);
220 ov_genai_vlm_pipeline_create_raw(
221 models_path,
222 device,
223 property_args_size,
224 pipe,
225 p[0],
226 p[1],
227 p[2],
228 p[3],
229 p[4],
230 p[5],
231 p[6],
232 p[7],
233 p[8],
234 p[9],
235 p[10],
236 p[11],
237 p[12],
238 p[13],
239 p[14],
240 p[15],
241 )
242 }
243
244 /// Create a Whisper pipeline (dynamic-linking variant).
245 ///
246 /// See [`ov_genai_llm_pipeline_create`] for details on the `props` parameter.
247 ///
248 /// # Safety
249 ///
250 /// Same safety requirements as [`ov_genai_llm_pipeline_create`].
251 ///
252 /// # Panics
253 ///
254 /// Panics if `props` contains more than `MAX_PROPERTIES * 2` entries.
255 pub unsafe fn ov_genai_whisper_pipeline_create(
256 models_path: *const ::std::os::raw::c_char,
257 device: *const ::std::os::raw::c_char,
258 property_args_size: usize,
259 pipeline: *mut *mut ov_genai_whisper_pipeline,
260 props: &[*const ::std::os::raw::c_char],
261 ) -> ov_status_e {
262 assert!(
263 props.len() <= MAX_PROPERTIES * 2,
264 "too many properties (max {})",
265 MAX_PROPERTIES
266 );
267 let p = pad_props(props);
268 ov_genai_whisper_pipeline_create_raw(
269 models_path,
270 device,
271 property_args_size,
272 pipeline,
273 p[0],
274 p[1],
275 p[2],
276 p[3],
277 p[4],
278 p[5],
279 p[6],
280 p[7],
281 p[8],
282 p[9],
283 p[10],
284 p[11],
285 p[12],
286 p[13],
287 p[14],
288 p[15],
289 )
290 }
291}
292
293#[cfg(feature = "dynamic-linking")]
294pub use dynamic_variadic::{
295 ov_genai_llm_pipeline_create, ov_genai_vlm_pipeline_create, ov_genai_whisper_pipeline_create,
296};
297
298// For runtime linking, we load these functions manually and expose the same fixed-signature
299// helpers as the dynamic-linking path. These use OnceLock so they can be initialized either lazily
300// (from `find()`) or explicitly (from `load_from`). The `library::load()` and
301// `library::load_from()` functions call `init_variadic_fns` to populate them.
302#[cfg(feature = "runtime-linking")]
303mod runtime_variadic {
304 use super::*;
305 use std::path::Path;
306 use std::sync::OnceLock;
307
308 // The C API uses variadic args for property key-value pairs. We define the
309 // function pointer with 16 extra `*const c_char` slots (enough for 8 properties).
310 // The C function only reads `property_args_size * 2` args from the va_list,
311 // so trailing NULLs are harmless.
312 type CreateFn = unsafe extern "C" fn(
313 *const ::std::os::raw::c_char, // models_path
314 *const ::std::os::raw::c_char, // device
315 usize, // property_args_size (number of key-value pairs)
316 *mut *mut ::std::os::raw::c_void, // pipe
317 // Up to 8 property key-value pairs (16 args):
318 *const ::std::os::raw::c_char,
319 *const ::std::os::raw::c_char,
320 *const ::std::os::raw::c_char,
321 *const ::std::os::raw::c_char,
322 *const ::std::os::raw::c_char,
323 *const ::std::os::raw::c_char,
324 *const ::std::os::raw::c_char,
325 *const ::std::os::raw::c_char,
326 *const ::std::os::raw::c_char,
327 *const ::std::os::raw::c_char,
328 *const ::std::os::raw::c_char,
329 *const ::std::os::raw::c_char,
330 *const ::std::os::raw::c_char,
331 *const ::std::os::raw::c_char,
332 *const ::std::os::raw::c_char,
333 *const ::std::os::raw::c_char,
334 ) -> ov_status_e;
335
336 static LLM_CREATE: OnceLock<CreateFn> = OnceLock::new();
337 static VLM_CREATE: OnceLock<CreateFn> = OnceLock::new();
338 static WHISPER_CREATE: OnceLock<CreateFn> = OnceLock::new();
339
340 /// Initialize the variadic pipeline creation functions from the library at `path`.
341 ///
342 /// Called internally by `library::load()` and `library::load_from()`.
343 pub(crate) fn init_variadic_fns(path: &Path) -> Result<(), String> {
344 unsafe {
345 let lib = libloading::Library::new(path).map_err(|e| {
346 format!(
347 "failed to open shared library for variadic fns at {}: {}",
348 path.display(),
349 e,
350 )
351 })?;
352
353 if let Ok(sym) = lib.get::<CreateFn>(b"ov_genai_llm_pipeline_create") {
354 let _ = LLM_CREATE.set(*sym);
355 }
356 if let Ok(sym) = lib.get::<CreateFn>(b"ov_genai_vlm_pipeline_create") {
357 let _ = VLM_CREATE.set(*sym);
358 }
359 if let Ok(sym) = lib.get::<CreateFn>(b"ov_genai_whisper_pipeline_create") {
360 let _ = WHISPER_CREATE.set(*sym);
361 }
362
363 // Leak the library to keep function pointers valid.
364 std::mem::forget(lib);
365 }
366 Ok(())
367 }
368
369 /// Create an LLM pipeline (runtime-linking variant).
370 ///
371 /// `props` contains flattened key-value pairs as C string pointers: `[k1, v1, k2, v2, ...]`.
372 /// Pass an empty slice for no properties. Up to `MAX_PROPERTIES` pairs (16 pointers).
373 ///
374 /// # Safety
375 ///
376 /// The caller must ensure that `models_path`, `device`, and all pointers in `props` are
377 /// valid C strings, and `pipe` is a valid pointer to receive the created pipeline.
378 ///
379 /// # Panics
380 ///
381 /// Panics if `library::load()` or `library::load_from()` has not been called first,
382 /// or if `props` contains more than `MAX_PROPERTIES * 2` entries.
383 pub unsafe fn ov_genai_llm_pipeline_create(
384 models_path: *const ::std::os::raw::c_char,
385 device: *const ::std::os::raw::c_char,
386 property_args_size: usize,
387 pipe: *mut *mut ov_genai_llm_pipeline,
388 props: &[*const ::std::os::raw::c_char],
389 ) -> ov_status_e {
390 assert!(
391 props.len() <= MAX_PROPERTIES * 2,
392 "too many properties (max {MAX_PROPERTIES})"
393 );
394 let p = pad_props(props);
395 let f = LLM_CREATE
396 .get()
397 .expect("`openvino_genai_c` function not loaded: `ov_genai_llm_pipeline_create`; call library::load() or library::load_from() first");
398 f(
399 models_path,
400 device,
401 property_args_size,
402 pipe.cast(),
403 p[0],
404 p[1],
405 p[2],
406 p[3],
407 p[4],
408 p[5],
409 p[6],
410 p[7],
411 p[8],
412 p[9],
413 p[10],
414 p[11],
415 p[12],
416 p[13],
417 p[14],
418 p[15],
419 )
420 }
421
422 /// Create a VLM pipeline (runtime-linking variant).
423 ///
424 /// See [`ov_genai_llm_pipeline_create`] for details on the `props` parameter.
425 ///
426 /// # Safety
427 ///
428 /// Same safety requirements as [`ov_genai_llm_pipeline_create`].
429 ///
430 /// # Panics
431 ///
432 /// Panics if `library::load()` or `library::load_from()` has not been called first,
433 /// or if `props` contains more than `MAX_PROPERTIES * 2` entries.
434 pub unsafe fn ov_genai_vlm_pipeline_create(
435 models_path: *const ::std::os::raw::c_char,
436 device: *const ::std::os::raw::c_char,
437 property_args_size: usize,
438 pipe: *mut *mut ov_genai_vlm_pipeline,
439 props: &[*const ::std::os::raw::c_char],
440 ) -> ov_status_e {
441 assert!(
442 props.len() <= MAX_PROPERTIES * 2,
443 "too many properties (max {MAX_PROPERTIES})"
444 );
445 let p = pad_props(props);
446 let f = VLM_CREATE
447 .get()
448 .expect("`openvino_genai_c` function not loaded: `ov_genai_vlm_pipeline_create`; call library::load() or library::load_from() first");
449 f(
450 models_path,
451 device,
452 property_args_size,
453 pipe.cast(),
454 p[0],
455 p[1],
456 p[2],
457 p[3],
458 p[4],
459 p[5],
460 p[6],
461 p[7],
462 p[8],
463 p[9],
464 p[10],
465 p[11],
466 p[12],
467 p[13],
468 p[14],
469 p[15],
470 )
471 }
472
473 /// Create a Whisper pipeline (runtime-linking variant).
474 ///
475 /// See [`ov_genai_llm_pipeline_create`] for details on the `props` parameter.
476 ///
477 /// # Safety
478 ///
479 /// Same safety requirements as [`ov_genai_llm_pipeline_create`].
480 ///
481 /// # Panics
482 ///
483 /// Panics if `library::load()` or `library::load_from()` has not been called first,
484 /// or if `props` contains more than `MAX_PROPERTIES * 2` entries.
485 pub unsafe fn ov_genai_whisper_pipeline_create(
486 models_path: *const ::std::os::raw::c_char,
487 device: *const ::std::os::raw::c_char,
488 property_args_size: usize,
489 pipeline: *mut *mut ov_genai_whisper_pipeline,
490 props: &[*const ::std::os::raw::c_char],
491 ) -> ov_status_e {
492 assert!(
493 props.len() <= MAX_PROPERTIES * 2,
494 "too many properties (max {MAX_PROPERTIES})"
495 );
496 let p = pad_props(props);
497 let f = WHISPER_CREATE
498 .get()
499 .expect("`openvino_genai_c` function not loaded: `ov_genai_whisper_pipeline_create`; call library::load() or library::load_from() first");
500 f(
501 models_path,
502 device,
503 property_args_size,
504 pipeline.cast(),
505 p[0],
506 p[1],
507 p[2],
508 p[3],
509 p[4],
510 p[5],
511 p[6],
512 p[7],
513 p[8],
514 p[9],
515 p[10],
516 p[11],
517 p[12],
518 p[13],
519 p[14],
520 p[15],
521 )
522 }
523}
524
525#[cfg(feature = "runtime-linking")]
526pub use runtime_variadic::{
527 ov_genai_llm_pipeline_create, ov_genai_vlm_pipeline_create, ov_genai_whisper_pipeline_create,
528};