ontocast.tool.sentence_transformer¶
One process-wide cache of guarded local sentence-transformer encoders.
Three independent subsystems load local sentence-transformers: semantic chunking
(:mod:ontocast.tool.chunk.chunker), retrieval embeddings
(:mod:ontocast.tool.vector_store.embedding) and entity clustering
(:mod:ontocast.tool.agg.clustering). Point two of them at the same checkpoint
and a per-subsystem cache means the same weights resident twice; point all three
at it and it is three times.
Caching alone is not enough, though, and getting it half-right is worse than not
sharing at all: once two subsystems hold the same model object, a lock that
lives in one of them protects nothing. So the cache hands out a
:class:SharedEncoder that owns the model and the lock that serialises it.
Every consumer encodes through that one guarded path, and — unlike a single
process-wide lock — two different checkpoints never serialise against each other.
Sharing weights is not the same as sharing semantics: retrieval applies
EmbeddingConfig document/query prefixes and clustering and chunking do not.
The three consumers are interchangeable in what they load, not in what they
mean.
SharedEncoder
¶
A process-shared SentenceTransformer plus the lock that serialises it.
Concurrent encode() on one model instance is correct with default
arguments — torch.inference_mode() is thread-local, eval() and
to(self.device) are idempotent, and all sorting state is call-local. The
lock is therefore not buying correctness; it buys a bound on peak memory
(every concurrent encode allocates its own activation batch, and the unit
fan-out is PARALLEL_WORKERS wide) and it forecloses the cases that
would corrupt: a caller passing an explicit device=, or using
truncate_embeddings(). On CPU the cost is close to zero, since parallel
encodes contend for one intra-op thread pool anyway.
Source code in ontocast/tool/sentence_transformer.py
model
property
¶
The underlying model, for non-inference attribute reads only.
Reading get_sentence_embedding_dimension() or max_seq_length here
is fine. Calling encode() on it bypasses the lock this class exists
to hold — use :meth:encode.
__init__(model_name, model, *, device=None, serialize=True)
¶
Wrap a loaded model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_name
|
str
|
Checkpoint id this was loaded from. |
required |
model
|
Any
|
The loaded |
required |
device
|
str | None
|
Device it was requested on; |
None
|
serialize
|
bool
|
Whether to hold a lock across inference. The GPU opt-out, where concurrent encodes are genuine parallelism rather than contention. |
True
|
Source code in ontocast/tool/sentence_transformer.py
encode(texts, **kwargs)
¶
Encode texts, serialised against other users of this model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
texts
|
list[str]
|
Strings to encode. |
required |
**kwargs
|
Any
|
Passed through to |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
Whatever |
Source code in ontocast/tool/sentence_transformer.py
SharedSentenceTransformerEmbeddings
¶
Bases: Embeddings
LangChain Embeddings view over a :class:SharedEncoder.
langchain_huggingface.HuggingFaceEmbeddings always constructs its own
SentenceTransformer (its model config forbids extra fields, so a
prebuilt model cannot be injected), which is why semantic chunking used to
hold a second copy of a checkpoint the process already had resident. This
adapter is the whole of the interface that class provided to us.
Source code in ontocast/tool/sentence_transformer.py
__init__(encoder, *, normalize=False)
¶
Wrap a shared encoder.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
encoder
|
SharedEncoder
|
The guarded encoder to delegate to. |
required |
normalize
|
bool
|
Whether to L2-normalise the returned vectors. |
False
|
Source code in ontocast/tool/sentence_transformer.py
embed_documents(texts)
¶
Embed texts as documents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
texts
|
list[str]
|
Strings to embed. |
required |
Returns:
| Type | Description |
|---|---|
list[list[float]]
|
list[list[float]]: One vector per input. |
Source code in ontocast/tool/sentence_transformer.py
embed_query(text)
¶
Embed a single query string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
String to embed. |
required |
Returns:
| Type | Description |
|---|---|
list[float]
|
list[float]: The embedding vector. |
get_shared_encoder(model_name, *, device=None, feature='Local sentence-transformer models', serialize=True)
¶
Return the process-wide guarded encoder for (model_name, device).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_name
|
str
|
HuggingFace model id or local path. |
required |
device
|
str | None
|
Device to load on; |
None
|
feature
|
str
|
What needs the model, used in the missing-dependency message. |
'Local sentence-transformer models'
|
serialize
|
bool
|
Whether the handle serialises inference. Only consulted when the handle is first created. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
SharedEncoder |
SharedEncoder
|
The shared handle. |