Skip to content

Commit e37a531

Browse files
committed
Drop ModelRepo wrapper; pass repo + revision separately
Per review: instead of a struct bundling `HFRepository<RepoTypeModel>` and `Option<String>`, plumb both as separate parameters through the download helpers. `HFRepository` is already cheap to clone (`Arc<HFClientInner>` inside), so the explicit `Arc<ModelRepo>` wrapping in the spawn closures is also gone — closures clone the repo handle directly and the revision `Option<String>` per task.
1 parent a13a100 commit e37a531

3 files changed

Lines changed: 93 additions & 88 deletions

File tree

backends/src/lib.rs

Lines changed: 64 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,6 @@ use rand::Rng;
1111
use tokio::sync::{mpsc, oneshot, watch};
1212
use tracing::{instrument, Span};
1313

14-
/// Handle for a model repository on the Hub, bundling the hf-hub repository handle with the
15-
/// optional revision. The revision is now passed per-call in hf-hub 1.0, so we carry it
16-
/// alongside the handle instead of baking it into a `Repo`.
17-
#[derive(Debug, Clone)]
18-
pub struct ModelRepo {
19-
pub repo: HFRepository<RepoTypeModel>,
20-
pub revision: Option<String>,
21-
}
22-
23-
impl ModelRepo {
24-
pub fn new(repo: HFRepository<RepoTypeModel>, revision: Option<String>) -> Self {
25-
Self { repo, revision }
26-
}
27-
}
28-
2914
use text_embeddings_backend_core::{Backend as CoreBackend, Predictions};
3015
pub use text_embeddings_backend_core::{
3116
BackendError, Batch, Embedding, Embeddings, ModelType, Pool,
@@ -106,7 +91,8 @@ impl Backend {
10691
#[allow(clippy::too_many_arguments)]
10792
pub async fn new(
10893
model_path: PathBuf,
109-
api_repo: Option<ModelRepo>,
94+
api_repo: Option<HFRepository<RepoTypeModel>>,
95+
revision: Option<String>,
11096
dtype: DType,
11197
model_type: ModelType,
11298
dense_path: Option<String>,
@@ -119,6 +105,7 @@ impl Backend {
119105
let backend = init_backend(
120106
model_path,
121107
api_repo,
108+
revision,
122109
dtype,
123110
model_type.clone(),
124111
dense_path,
@@ -457,7 +444,8 @@ impl Backend {
457444
#[allow(unused, clippy::too_many_arguments)]
458445
async fn init_backend(
459446
model_path: PathBuf,
460-
api_repo: Option<ModelRepo>,
447+
api_repo: Option<HFRepository<RepoTypeModel>>,
448+
revision: Option<String>,
461449
dtype: DType,
462450
model_type: ModelType,
463451
dense_path: Option<String>,
@@ -466,13 +454,12 @@ async fn init_backend(
466454
otlp_service_name: String,
467455
) -> Result<Box<dyn CoreBackend + Send>, BackendError> {
468456
let mut backend_start_failed = false;
469-
let api_repo = api_repo.map(Arc::new);
470457

471458
#[cfg(feature = "ort")]
472459
{
473460
if let Some(api_repo) = api_repo.as_ref() {
474461
let start = std::time::Instant::now();
475-
let model_files = download_onnx(api_repo.clone())
462+
let model_files = download_onnx(api_repo, revision.as_deref())
476463
.await
477464
.map_err(|err| BackendError::WeightsNotFound(err.to_string()))?;
478465
match model_files.is_empty() {
@@ -490,10 +477,9 @@ async fn init_backend(
490477
if let Some(api_repo) = api_repo.as_ref() {
491478
tracing::info!("Downloading `tokenizer_config.json`");
492479
match api_repo
493-
.repo
494480
.download_file()
495481
.filename("tokenizer_config.json")
496-
.maybe_revision(api_repo.revision.clone())
482+
.maybe_revision(revision.clone())
497483
.send()
498484
.await
499485
{
@@ -517,14 +503,16 @@ async fn init_backend(
517503
if let Some(api_repo) = api_repo.as_ref() {
518504
if cfg!(feature = "python") || cfg!(feature = "candle") {
519505
let start = std::time::Instant::now();
520-
if download_safetensors(api_repo.clone()).await.is_err() {
506+
if download_safetensors(api_repo, revision.as_deref())
507+
.await
508+
.is_err()
509+
{
521510
tracing::warn!("safetensors weights not found. Using `pytorch_model.bin` instead. Model loading will be significantly slower.");
522511
tracing::info!("Downloading `pytorch_model.bin`");
523512
api_repo
524-
.repo
525513
.download_file()
526514
.filename("pytorch_model.bin")
527-
.maybe_revision(api_repo.revision.clone())
515+
.maybe_revision(revision.clone())
528516
.send()
529517
.await
530518
.map_err(|err| BackendError::WeightsNotFound(err.to_string()))?;
@@ -539,7 +527,7 @@ async fn init_backend(
539527
{
540528
let dense_paths = if let Some(api_repo) = api_repo.as_ref() {
541529
let start = std::time::Instant::now();
542-
let dense_paths = download_dense_modules(api_repo, dense_path)
530+
let dense_paths = download_dense_modules(api_repo, revision.as_deref(), dense_path)
543531
.await
544532
.map_err(|err| BackendError::WeightsNotFound(err.to_string()))?;
545533
tracing::info!("Dense modules downloaded in {:?}", start.elapsed());
@@ -693,14 +681,16 @@ enum BackendCommand {
693681
),
694682
}
695683

696-
async fn download_safetensors(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFError> {
684+
async fn download_safetensors(
685+
repo: &HFRepository<RepoTypeModel>,
686+
revision: Option<&str>,
687+
) -> Result<Vec<PathBuf>, HFError> {
697688
// Single file
698689
tracing::info!("Downloading `model.safetensors`");
699-
match api
700-
.repo
690+
match repo
701691
.download_file()
702692
.filename("model.safetensors")
703-
.maybe_revision(api.revision.clone())
693+
.maybe_revision(revision.map(String::from))
704694
.send()
705695
.await
706696
{
@@ -711,11 +701,10 @@ async fn download_safetensors(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFErr
711701
// Sharded weights
712702
// Download and parse index file
713703
tracing::info!("Downloading `model.safetensors.index.json`");
714-
let index_file = api
715-
.repo
704+
let index_file = repo
716705
.download_file()
717706
.filename("model.safetensors.index.json")
718-
.maybe_revision(api.revision.clone())
707+
.maybe_revision(revision.map(String::from))
719708
.send()
720709
.await?;
721710
let index_file_string: String =
@@ -739,13 +728,13 @@ async fn download_safetensors(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFErr
739728
let handles: Vec<_> = safetensors_filenames
740729
.into_iter()
741730
.map(|n| {
742-
let api = Arc::clone(&api);
731+
let repo = repo.clone();
732+
let revision = revision.map(String::from);
743733
tokio::spawn(async move {
744734
tracing::info!("Downloading `{}`", n);
745-
api.repo
746-
.download_file()
735+
repo.download_file()
747736
.filename(n)
748-
.maybe_revision(api.revision.clone())
737+
.maybe_revision(revision)
749738
.send()
750739
.await
751740
})
@@ -755,7 +744,7 @@ async fn download_safetensors(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFErr
755744
let mut safetensors_files = Vec::with_capacity(handles.len());
756745
for handle in handles {
757746
// Await the JoinHandle to get the result of the task,
758-
// then unpack the inner result from api.repo.download_file()
747+
// then unpack the inner result from the download.
759748
let downloaded = handle
760749
.await
761750
.map_err(|err| HFError::Other(format!("safetensors download task failed: {err}")))?;
@@ -766,14 +755,16 @@ async fn download_safetensors(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFErr
766755
}
767756

768757
#[cfg(feature = "ort")]
769-
async fn download_onnx(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFError> {
758+
async fn download_onnx(
759+
repo: &HFRepository<RepoTypeModel>,
760+
revision: Option<&str>,
761+
) -> Result<Vec<PathBuf>, HFError> {
770762
// TODO: Most likely all the download functions could benefit from the information defined in
771763
// `info()` so that we just need to run the HTTP GET request once (and more efficiently
772764
// download the required and existing files only).
773-
let filenames = match api
774-
.repo
765+
let filenames = match repo
775766
.info()
776-
.maybe_revision(api.revision.clone())
767+
.maybe_revision(revision.map(String::from))
777768
.send()
778769
.await
779770
{
@@ -800,15 +791,15 @@ async fn download_onnx(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFError> {
800791
let handles: Vec<_> = files
801792
.into_iter()
802793
.map(|file| {
803-
let api = Arc::clone(&api);
794+
let repo = repo.clone();
795+
let revision = revision.map(String::from);
804796
tokio::spawn(async move {
805797
tracing::info!("Downloading `{}`", file);
806798
let time = std::time::Instant::now();
807-
match api
808-
.repo
799+
match repo
809800
.download_file()
810801
.filename(file.clone())
811-
.maybe_revision(api.revision.clone())
802+
.maybe_revision(revision)
812803
.send()
813804
.await
814805
{
@@ -842,11 +833,10 @@ async fn download_onnx(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFError> {
842833
let mut downloaded_files = Vec::<PathBuf>::new();
843834

844835
tracing::info!("Downloading `onnx/model.onnx`");
845-
match api
846-
.repo
836+
match repo
847837
.download_file()
848838
.filename("onnx/model.onnx")
849-
.maybe_revision(api.revision.clone())
839+
.maybe_revision(revision.map(String::from))
850840
.send()
851841
.await
852842
{
@@ -855,11 +845,10 @@ async fn download_onnx(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFError> {
855845
tracing::warn!("Could not download `onnx/model.onnx`: {err}");
856846
tracing::info!("Downloading `model.onnx`");
857847

858-
match api
859-
.repo
848+
match repo
860849
.download_file()
861850
.filename("model.onnx")
862-
.maybe_revision(api.revision.clone())
851+
.maybe_revision(revision.map(String::from))
863852
.send()
864853
.await
865854
{
@@ -870,11 +859,10 @@ async fn download_onnx(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFError> {
870859
};
871860

872861
tracing::info!("Downloading `onnx/model.onnx_data`");
873-
match api
874-
.repo
862+
match repo
875863
.download_file()
876864
.filename("onnx/model.onnx_data")
877-
.maybe_revision(api.revision.clone())
865+
.maybe_revision(revision.map(String::from))
878866
.send()
879867
.await
880868
{
@@ -883,11 +871,10 @@ async fn download_onnx(api: Arc<ModelRepo>) -> Result<Vec<PathBuf>, HFError> {
883871
tracing::warn!("Could not download `onnx/model.onnx_data`: {err}");
884872
tracing::info!("Downloading `model.onnx_data`");
885873

886-
match api
887-
.repo
874+
match repo
888875
.download_file()
889876
.filename("model.onnx_data")
890-
.maybe_revision(api.revision.clone())
877+
.maybe_revision(revision.map(String::from))
891878
.send()
892879
.await
893880
{
@@ -928,12 +915,15 @@ struct ModuleConfig {
928915
}
929916

930917
#[cfg(feature = "candle")]
931-
async fn download_file(api: &ModelRepo, file_path: &str) -> Result<PathBuf, HFError> {
918+
async fn download_file(
919+
repo: &HFRepository<RepoTypeModel>,
920+
revision: Option<&str>,
921+
file_path: &str,
922+
) -> Result<PathBuf, HFError> {
932923
tracing::info!("Downloading `{}`", file_path);
933-
api.repo
934-
.download_file()
924+
repo.download_file()
935925
.filename(file_path)
936-
.maybe_revision(api.revision.clone())
926+
.maybe_revision(revision.map(String::from))
937927
.send()
938928
.await
939929
}
@@ -956,10 +946,11 @@ async fn parse_dense_paths_from_modules(
956946
#[cfg(feature = "candle")]
957947
#[instrument(skip_all)]
958948
pub async fn download_dense_modules(
959-
api: &ModelRepo,
949+
repo: &HFRepository<RepoTypeModel>,
950+
revision: Option<&str>,
960951
dense_path: Option<String>,
961952
) -> Result<Vec<String>, HFError> {
962-
match download_file(api, "modules.json").await {
953+
match download_file(repo, revision, "modules.json").await {
963954
Ok(modules_path) => {
964955
// If `modules.json` exists, then parse it to capture the Dense modules
965956
match parse_dense_paths_from_modules(&modules_path).await {
@@ -982,7 +973,7 @@ pub async fn download_dense_modules(
982973
module_paths[0].clone()
983974
};
984975

985-
download_dense_module(api, &path_to_use)
976+
download_dense_module(repo, revision, &path_to_use)
986977
.await
987978
.map_err(|err| {
988979
tracing::error!(
@@ -1005,7 +996,7 @@ pub async fn download_dense_modules(
1005996
// NOTE: since the Dense modules here are specified in the
1006997
// `modules.json` file, then fail if any of those cannot be
1007998
// downloaded
1008-
download_dense_module(api, module_path)
999+
download_dense_module(repo, revision, module_path)
10091000
.await
10101001
.map_err(|err| {
10111002
tracing::error!(
@@ -1031,10 +1022,14 @@ pub async fn download_dense_modules(
10311022
}
10321023

10331024
#[cfg(feature = "candle")]
1034-
async fn download_dense_module(api: &ModelRepo, dense_path: &str) -> Result<PathBuf, HFError> {
1025+
async fn download_dense_module(
1026+
repo: &HFRepository<RepoTypeModel>,
1027+
revision: Option<&str>,
1028+
dense_path: &str,
1029+
) -> Result<PathBuf, HFError> {
10351030
// Download `config.json` for the Dense module
10361031
let config_file = format!("{}/config.json", dense_path);
1037-
let config_path = match download_file(api, &config_file).await {
1032+
let config_path = match download_file(repo, revision, &config_file).await {
10381033
Ok(path) => path,
10391034
Err(err) => {
10401035
tracing::warn!("Failed to download `{config_file}` file: {err}");
@@ -1044,11 +1039,11 @@ async fn download_dense_module(api: &ModelRepo, dense_path: &str) -> Result<Path
10441039

10451040
// Try to download the `model.safetensors` first
10461041
let safetensors_file = format!("{}/model.safetensors", dense_path);
1047-
if let Err(err) = download_file(api, &safetensors_file).await {
1042+
if let Err(err) = download_file(repo, revision, &safetensors_file).await {
10481043
tracing::warn!("Failed to download `{safetensors_file}` file: {err}");
10491044
// Fallback to former `pytorch_model.bin`
10501045
let pytorch_file = format!("{}/pytorch_model.bin", dense_path);
1051-
if let Err(err) = download_file(api, &pytorch_file).await {
1046+
if let Err(err) = download_file(repo, revision, &pytorch_file).await {
10521047
tracing::warn!("Failed to download `{pytorch_file}` file: {err}");
10531048
return Err(err);
10541049
}

0 commit comments

Comments
 (0)