use crate::error::Result;
use anyhow::anyhow;
use std::path::PathBuf;
pub fn parse_owner_name(model_id: &str) -> Result<(String, String)> {
model_id
.split_once('/')
.map(|(owner, name)| (owner.to_string(), name.to_string()))
.ok_or_else(|| anyhow!("model_id must be 'owner/name', got '{model_id}'"))
}
pub(crate) fn hf_client(cache_dir: Option<PathBuf>) -> Result<hf_hub::HFClientSync> {
if let Some(dir) = cache_dir {
Ok(hf_hub::HFClientBuilder::new()
.cache_dir(dir)
.build()
.map(hf_hub::HFClientSync::from_inner)
.map_err(|e| anyhow!("{e}"))?
.map_err(|e| anyhow!("{e}"))?)
} else {
Ok(hf_hub::HFClientSync::new().map_err(|e| anyhow!("{e}"))?)
}
}
pub fn fetch_tokenizer_file(
model_id: &str,
revision: Option<&str>,
cache_dir: Option<PathBuf>,
) -> Result<PathBuf> {
let (owner, name) = parse_owner_name(model_id)?;
let client = hf_client(cache_dir)?;
let repo = client.model(owner, name);
repo.download_file()
.filename("tokenizer.json".to_string())
.maybe_revision(revision.map(str::to_string))
.send()
.map_err(|e| anyhow!("tokenizer.json missing for '{model_id}': {e}"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_owner_name_splits_once() {
let (owner, name) = parse_owner_name("jinaai/some-model").unwrap();
assert_eq!(owner, "jinaai");
assert_eq!(name, "some-model");
}
#[test]
fn parse_owner_name_rejects_bare_id() {
let err = parse_owner_name("no-slash").unwrap_err().to_string();
assert!(err.contains("model_id must be 'owner/name'"), "{err}");
}
}