Skip to main content

combs_formats/
lib.rs

1//! # combs-formats
2//!
3//! File-format adapter layer. The runtime and model crates never touch file
4//! formats directly; they go through the [`ModelSource`] trait (the
5//! LiteRT-LM `ModelResources` equivalent). Phase 1 ships the
6//! [`safetensors`] adapter (HuggingFace `config.json` + `model.safetensors`,
7//! mmap-backed, zero-copy views). GGUF / ONNX / litertlm adapters plug in
8//! here later by implementing the same trait.
9
10mod flatbuf;
11mod gguf;
12mod litertlm;
13mod metadata;
14mod protomin;
15mod safetensors;
16mod source;
17mod spm;
18mod tflite;
19mod tokenizer;
20
21pub use gguf::GgufSource;
22
23/// Reference CPU dequantizers for GGUF quant formats. These scalar
24/// implementations are the harmony reference that the fused GPU kernels in
25/// `combs-models` validate against: every kernel is tested against a
26/// portable reference.
27pub mod quants {
28    pub use crate::gguf::{
29        dequantize_q4_0, dequantize_q4_k, dequantize_q5_0, dequantize_q5_k, dequantize_q6_k,
30        dequantize_q8_0,
31    };
32}
33pub use metadata::{Activation, AttentionPattern, ModelMetadata, RopeScaling, VisionConfig};
34pub use safetensors::SafetensorsSource;
35pub use litertlm::{SectionInfo, read_sections as litertlm_read_sections};
36pub use source::{ModelSource, QuantFormat, QuantTensor, SamplerConfig, TensorDtype, TensorReader};
37pub use spm::{ensure_tokenizer_json_from_spm, spm_added_tokens};
38pub use tflite::TfliteSource;
39pub use tokenizer::TokenizerSpec;
40
41use std::path::Path;
42
43/// Opens any supported model path: a `.gguf` file, or a directory in the
44/// HuggingFace safetensors layout. This is the single entry point the CLI,
45/// FFI and server use — format detection lives here.
46pub fn open_model_source(path: impl AsRef<Path>) -> Result<Box<dyn ModelSource>> {
47    let path = path.as_ref();
48    if path.is_file() && path.extension().is_some_and(|e| e == "gguf") {
49        return Ok(Box::new(GgufSource::load(path)?));
50    }
51    if path.is_file() && path.extension().is_some_and(|e| e == "task" || e == "tflite") {
52        return Ok(Box::new(TfliteSource::load(path)?));
53    }
54    if path.is_file() && path.extension().is_some_and(|e| e == "litertlm") {
55        return litertlm::open_litertlm(path);
56    }
57    if path.is_dir() {
58        return Ok(Box::new(SafetensorsSource::load(path)?));
59    }
60    Err(FormatError::MissingFile(path.display().to_string()))
61}
62
63/// Errors produced by format adapters.
64#[derive(Debug, thiserror::Error)]
65pub enum FormatError {
66    /// An I/O error while reading model files.
67    #[error("io error: {0}")]
68    Io(#[from] std::io::Error),
69
70    /// A JSON parse error (config.json, generation_config.json, …).
71    #[error("json error in {context}: {source}")]
72    Json {
73        /// Which file/section failed to parse.
74        context: String,
75        /// The underlying serde error.
76        source: serde_json::Error,
77    },
78
79    /// The safetensors container is malformed.
80    #[error("safetensors error: {0}")]
81    Safetensors(String),
82
83    /// A requested tensor does not exist in the source.
84    #[error("tensor not found: {0}")]
85    TensorNotFound(String),
86
87    /// A tensor has an unsupported dtype for this build.
88    #[error("unsupported dtype for tensor {tensor}: {dtype}")]
89    UnsupportedDtype {
90        /// Tensor name.
91        tensor: String,
92        /// Dtype string from the container.
93        dtype: String,
94    },
95
96    /// The model directory is missing a required file.
97    #[error("missing file: {0}")]
98    MissingFile(String),
99
100    /// The config is missing a required field.
101    #[error("missing config field: {0}")]
102    MissingField(String),
103}
104
105/// Convenient result alias for this crate.
106pub type Result<T> = std::result::Result<T, FormatError>;