From 159ac03904062953f96cdb9825799a3286f906e4 Mon Sep 17 00:00:00 2001 From: arc53-machine <232052973+arc53-machine@users.noreply.github.com> Date: Tue, 29 Sep 2026 12:36:57 +0100 Subject: [PATCH] Let FastEmbed download a model when completing its cache fails Completing a partial snapshot is best effort; a network error or rate limit there is logged and FastEmbed still tries its own sources. --- docsgpt/vectorstore/embeddings_local.py | 8 ++++++-- tests/vectorstore/test_embeddings_local.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docsgpt/vectorstore/embeddings_local.py b/docsgpt/vectorstore/embeddings_local.py index 88573cd5..97cfe939 100644 --- a/docsgpt/vectorstore/embeddings_local.py +++ b/docsgpt/vectorstore/embeddings_local.py @@ -263,7 +263,8 @@ def _complete_model_cache(repo: str, cache_dir: Optional[str]) -> None: holds. The chunker caches only ``tokenizer.json`` there (see ``docsgpt/parser/tokenization.py``), so FastEmbed then finds no graph and fails to load. Fetching the files it needs first makes either order work. - Offline, nothing is fetched and FastEmbed reports what is missing. + Offline, nothing is fetched and FastEmbed reports what is missing; a + failed download is logged and left to FastEmbed's own sources too. Args: repo: The model's FastEmbed name. @@ -293,7 +294,10 @@ def _complete_model_cache(repo: str, cache_dir: Optional[str]) -> None: if os.environ.get("HF_HUB_OFFLINE", "").strip().upper() in {"1", "TRUE", "YES", "ON"}: return logger.warning("The cached %s has no %s; downloading the model files.", source, ", ".join(needed)) - snapshot_download(repo_id=source, allow_patterns=[*_FASTEMBED_SUPPORT_FILES, *needed], cache_dir=cache) + try: + snapshot_download(repo_id=source, allow_patterns=[*_FASTEMBED_SUPPORT_FILES, *needed], cache_dir=cache) + except Exception as exc: # noqa: BLE001 -- best effort: FastEmbed still tries its own sources + logger.warning("Could not complete the cached %s (%s); leaving the download to FastEmbed.", source, exc) def _pad_to_longest_in_batch(model: Any) -> None: diff --git a/tests/vectorstore/test_embeddings_local.py b/tests/vectorstore/test_embeddings_local.py index b3090a1c..f226881b 100644 --- a/tests/vectorstore/test_embeddings_local.py +++ b/tests/vectorstore/test_embeddings_local.py @@ -460,6 +460,17 @@ class TestIncompleteModelCache: assert "onnx/model.onnx" in downloads[0]["allow_patterns"] assert "tokenizer_config.json" in downloads[0]["allow_patterns"] + def test_a_failed_repair_leaves_loading_to_fastembed(self, monkeypatch, tmp_path): + """The repair is best effort: a network error or rate limit here must + not stop FastEmbed from trying its own download.""" + from docsgpt.vectorstore import embeddings_local + + monkeypatch.delenv("HF_HUB_OFFLINE", raising=False) + with patch("fastembed.TextEmbedding._list_supported_models", return_value=[self._description()]), \ + patch("huggingface_hub.hf_hub_download", side_effect=FileNotFoundError("missing")), \ + patch("huggingface_hub.snapshot_download", side_effect=OSError("rate limited")): + embeddings_local._complete_model_cache("sentence-transformers/all-mpnet-base-v2", str(tmp_path)) + def test_a_complete_snapshot_downloads_nothing(self, monkeypatch, tmp_path): assert self._complete(monkeypatch, tmp_path, cached={"tokenizer.json", "onnx/model.onnx"}) == []