FAQ & Known Issues
On this page
Which embedder should I use?
This can't be answered generically without knowing your constraints, but purely from what the source supports:
- No API key, no network dependency, Arabic-focused →
ArabicEmbedder. - No API key, no network dependency, general multilingual →
HuggingFaceEmbedder. - Hosted, highest quality, willing to pay →
OpenAIEmbedder(text-embedding-3-large) orGeminiEmbedder(gemini-embedding-001, 3072-dim by default). - Hosted, cost-sensitive →
MistralEmbedder(mistral-embed, $0.10/1M tokens perMODEL_SPECS) orOpenAIEmbedderwithtext-embedding-3-small($0.02/1M tokens). - Local model server, want to swap models without redeploying code →
OllamaEmbedder.
Do all embedders return the same output shape?
Mostly. encode() on a single string returns a 1D array on every embedder except OpenAIEmbedder, GeminiEmbedder, and MistralEmbedder, which always return a 2D array of shape (1, dim) even for single-string input (their encode() wraps the string in a list internally and never unwraps the result). ArabicEmbedder, HuggingFaceEmbedder, and OllamaEmbedder explicitly return a 1D array for single-string input. Check the "Returns" section on each class's API reference page before assuming shape.
Known issues found in source
These are documented here rather than silently worked around, since this documentation is meant to reflect the implementation as written.
-
__all__references an undefined name.embeddings/__init__.pyincludes"EmbedderMode"in__all__, but no such class/function/constant exists anywhere in the package.from embeddings import *will raiseImportError: cannot import name 'EmbedderMode'. Import the specific classes you need instead (e.g.from embeddings import OpenAIEmbedder). -
A broken exception-chaining line in
ArabicEmbedder._load_model(). The fallback branch when a model fails to load andauto_download=Falsereads:Python raise Exception.add_note("the model is not available locally and auto_download is disabled ") from eException.add_noteis an unbound instance method being called on the class itself with a string argument, which does not construct or raise a new exception the way the surrounding code appears to intend. This is very likely to raise aTypeErrorat that call site instead of a clear message about auto-download being disabled, when that specific failure path is hit. -
BaseEmbedder.encode_with_cache()is effectively dead code for every current subclass. It implements a generic MD5-keyed caching layer, but none of the six concrete embedders call it from within their ownencode(); each implements its own separate caching logic instead (see Architecture → Execution flow). Callingembedder.encode_with_cache(...)directly still works as documented — it just isn't used internally. -
Filename typo. The
HuggingFaceEmbedderclass lives in a file namedhugginface_embedder.py(missing anh), nothuggingface_embedder.py. This doesn't affectfrom embeddings import HuggingFaceEmbeddersince__init__.pyre-exports it, but it will matter if you ever import the submodule path directly. -
Return-value inconsistency for single-item stats.
BaseEmbedder.get_stats()branches on whetherself._statsis adictor a dataclass (to supportHuggingFaceEmbedder, which replacesself._statswith anEmbeddingStatsdataclass instance rather than the dictBaseEmbedder.__init__creates). This works correctly today but meansself._stats's type is not uniform across the class hierarchy — anything overriding_statson a subclass needs to either use a dict with the same five keys or expose a.to_dict()method, orget_stats()'svars(self._stats).copy()fallback will be used instead.
Is this thread-safe? Async-safe?
From reading the source:
- Local model embedders (
ArabicEmbedder,HuggingFaceEmbedder) delegate tosentence_transformers.SentenceTransformer.encode(), which is not documented as thread-safe in this codebase — no locks are taken aroundself.model.encode(...)calls here. MistralEmbedderandOpenAIEmbedder's rate limiters (MistralRateLimiter,RateLimiter) explicitly usethreading.RLock, making the rate-limiting bookkeeping itself thread-safe. The rest of each class (cache dict/OrderedDict mutation) is not additionally locked.- No embedder class uses
asynciointernally in its ownencode(). Async support is provided uniformly byBaseEmbedder.aencode()/aencode_with_cache()/abatch_similarity(), which just run the synchronous method viaasyncio.to_thread. This makes async calls safe to await but does not make concurrent synchronous calls into the same embedder instance safe with respect to shared mutable state likeself._cacheorself._stats.
None of the classes document or test concurrent access explicitly, so treat any of the above as "as implemented," not as a guarantee.