[Bug]: URL-backed custom models with the same basename reuse the wrong cached model
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 25/100
Línea de trabajo
Read fastembed/common/model_management.py around the URL loader at lines 535–550 and trace how the model name determines the cache directory and cache-hit behavior. Use the issue’s reproduction to compare both registrations with a shared cache against the fresh-cache control; done means each registered name loads its own archive, with existing basename-only cache entries handled consistently for offline users.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What happened?
Two URL-backed custom models can be registered under different names, such as owner-a/shared-model and owner-b/shared-model. When they use the same cache directory, loading the second model silently reuses the first model's files. It returns the first model's embeddings without requesting the second archive.
The URL loader derives the on-disk directory from the last segment of model_name and treats an existing nonempty directory as a cache hit. Both registrations therefore look up cache_dir/shared-model. Source at the tested revision
Expected behavior
Each registration should load its own model files. A cache hit for one registered name should leave other names free to download their own archives. A separate cache_dir for each model is a workaround.
Before implementing a change, I'd appreciate guidance on how the loader should handle existing basename-only cache entries, especially for offline users.
Reproduction
Save this as repro.py and run it with FastEmbed and onnx installed. It builds two small ONNX archives and serves them on loopback. No pretrained model or external service is needed.
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
import json
from pathlib import Path
import tarfile
from tempfile import TemporaryDirectory
from threading import Thread
import onnx
from onnx import TensorProto as T, helper as h
from tokenizers import Tokenizer
from tokenizers.models import WordLevel
from tokenizers.pre_tokenizers import Whitespace
from fastembed import TextEmbedding
from fastembed.common.model_description import ModelSource, PoolingType
def archive(root, name, factor):
folder = root / name / "shared-model"
folder.mkdir(parents=True)
tokenizer = Tokenizer(WordLevel({"[UNK]": 0, "[PAD]": 1, "hello": 2, "world": 3}, unk_token="[UNK]"))
tokenizer.pre_tokenizer = Whitespace()
tokenizer.enable_padding(pad_id=1, pad_token="[PAD]")
tokenizer.enable_truncation(max_length=8)
tokenizer.save(str(folder / "tokenizer.json"))
(folder / "tokenizer_config.json").write_text(json.dumps({"model_max_length": 8, "pad_token": "[PAD]"}))
(folder / "special_tokens_map.json").write_text(json.dumps({"unk_token": "[UNK]", "pad_token": "[PAD]"}))
graph = h.make_graph(
[
h.make_node("Cast", ["input_ids"], ["tokens"], to=T.FLOAT),
h.make_node("ReduceSum", ["tokens"], ["sum"], axes=[1], keepdims=1),
h.make_node("Mul", ["sum", "factor"], ["scaled"]),
h.make_node("Concat", ["sum", "scaled"], ["embedding"], axis=1),
],
name,
[h.make_tensor_value_info("input_ids", T.INT64, ["batch", "sequence"])],
[h.make_tensor_value_info("embedding", T.FLOAT, ["batch", 2])],
[h.make_tensor("factor", T.FLOAT, [1], [factor])],
)
model = h.make_model(graph, opset_imports=[h.make_opsetid("", 11)], ir_version=9)
onnx.checker.check_model(model)
onnx.save(model, str(folder / "model.onnx"))
with tarfile.open(root / f"{name}.tar.gz", "w:gz") as out:
out.add(folder, arcname="shared-model")
with TemporaryDirectory() as tmp:
root = Path(tmp)
archive(root, "a", 1.0)
archive(root, "b", -1.0)
with ThreadingHTTPServer(("127.0.0.1", 0), partial(SimpleHTTPRequestHandler, directory=tmp)) as server:
thread = Thread(target=server.serve_forever)
thread.start()
try:
base = f"http://127.0.0.1:{server.server_port}"
for owner in ("a", "b"):
TextEmbedding.add_custom_model(
model=f"owner-{owner}/shared-model",
pooling=PoolingType.DISABLED,
normalization=False,
sources=ModelSource(url=f"{base}/{owner}.tar.gz"),
dim=2,
model_file="model.onnx",
)
for owner, cache in (("a", "shared"), ("b", "shared"), ("b", "fresh")):
model = TextEmbedding(f"owner-{owner}/shared-model", cache_dir=str(root / cache), threads=1)
print(owner, cache, next(model.embed(["hello world"])).tolist(), flush=True)
finally:
server.shutdown()
thread.join()
Observed:
GET /a.tar.gz
a shared [5.0, 5.0]
b shared [5.0, 5.0]
GET /b.tar.gz
b fresh [5.0, -5.0]
The expected output for b shared is [5.0, -5.0], matching the fresh-cache control.
Environment
Python 3.11.15 on macOS arm64. FastEmbed source from main at f91d9168c0291daeafb0a54cebc7c5b4b83f312f (project version 0.9.1), loaded through PYTHONPATH with the installed runtime dependencies. ONNX 1.23.2, ONNX Runtime 1.30.0 and NumPy 2.4.6.
- Lenguaje dominante
- Python
- Estrellas
- 3.3k
- Forks
- 296
- Merge medio
- 4 d 11 h
- PR fusionados (30 d)
- 38
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de qdrant/fastembed
-
[Bug]: Revision-pinned HF downloads can falsely report intact files as corruptedPosiblemente ocupada @N7766 la tomó hace 1 día. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
[Bug]: Model download sleeps after its final failed attemptPosiblemente ocupada @elysiopires la tomó hace 10 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 35/100
Los mantenedores suelen responder en 1 día
-
Weekly tests failedPosiblemente ocupada @joein la tomó hace 13 días. Abierto
qdrant/fastembed#843 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
[Bug]: rerank_pairs raises TypeError when pairs are passed as a tuple of pairsPosiblemente ocupada @michaelkillgta la tomó hace 30 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 22/100
Los mantenedores suelen responder en 1 día
-
[Feature]: Support huggingface-hub 2.0Quizá libre de nuevo @joein la tomó hace 40 días y no hay ningún pull request abierto. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 65/100
qdrant/fastembed#742 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de qdrant/fastembed
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
adafruit/Fruit-Jam-OS#98 ·
-
python triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
microsoft/semantic-kernel#14753 ·
Los mantenedores suelen responder en 5 días
-
area:cli area:configuration documentation priority:low size:small
Dificultad 1/5 1-3 horas Aptitud para principiantes 80/100
RonaldHensbergen/composable-data-stack#1222 ·
Los mantenedores suelen responder en 1 día
-
bug python
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
pvlib/pvlib-python#2939 ·
Los mantenedores suelen responder en 1 día