use std::io::Read;
use std::path::Path;
use indexmap::IndexMap;
use kopitiam_core::{DType, Error, Result, Shape};
use crate::byte_source::ByteSource;
use crate::gguf::GgufLoader;
use crate::metadata::ModelMetadata;
use crate::safetensors::SafeTensorsLoader;
#[derive(Debug, Clone, PartialEq)]
pub struct TensorEntry {
pub name: String,
pub dtype: DType,
pub shape: Shape,
pub(crate) offset: usize,
pub(crate) len: usize,
}
pub struct LoadedModel {
pub(crate) metadata: ModelMetadata,
pub(crate) tensors: IndexMap<String, TensorEntry>,
pub(crate) source: ByteSource,
pub(crate) format: &'static str,
}
impl std::fmt::Debug for LoadedModel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LoadedModel")
.field("format", &self.format)
.field("metadata", &self.metadata)
.field("tensors", &self.tensors.values().collect::<Vec<_>>())
.finish()
}
}
impl LoadedModel {
pub fn metadata(&self) -> &ModelMetadata {
&self.metadata
}
pub fn format(&self) -> &'static str {
self.format
}
pub fn tensor_count(&self) -> usize {
self.tensors.len()
}
pub fn tensor_names(&self) -> impl Iterator<Item = &str> {
self.tensors.keys().map(String::as_str)
}
pub fn tensor(&self, name: &str) -> Option<&TensorEntry> {
self.tensors.get(name)
}
pub fn tensors(&self) -> impl Iterator<Item = &TensorEntry> {
self.tensors.values()
}
pub fn tensor_bytes(&self, name: &str) -> Result<&[u8]> {
let entry = self
.tensors
.get(name)
.ok_or_else(|| Error::MissingTensor { name: name.to_string() })?;
self.source.slice(self.format, entry.offset, entry.len)
}
}
pub trait ModelLoader {
fn format_name(&self) -> &'static str;
fn probe(&self, bytes: &[u8]) -> bool;
fn load(&self, path: &Path) -> Result<LoadedModel>;
}
pub fn load_model(path: impl AsRef<Path>) -> Result<LoadedModel> {
let path = path.as_ref();
let file = std::fs::File::open(path)?;
let mut probe_bytes = Vec::with_capacity(8);
file.take(8).read_to_end(&mut probe_bytes)?;
let gguf = GgufLoader;
if gguf.probe(&probe_bytes) {
return gguf.load(path);
}
SafeTensorsLoader.load(path)
}