Embedding applications depend on model-loading rules and output formats as well as model weights. Sentence Transformers v6 changes several of those contracts, so migrating a 5.x project requires checking that its embeddings and document rankings still behave as the application expects.
A separate Python virtual environment keeps the working v5 installation available while dependencies and callers change. The shell commands use Linux or macOS paths and assume an existing project with a working pytest suite, run from the project root with its v5 environment active. Original model files also need protection because saving a model with a newer library does not guarantee that the older library can reload it.
The main compatibility checks concern custom module classes, reranker score types, and quantization results. Embeddings and rankings saved while v5 is still installed, from stable input texts and unchanged model weights, separate regressions from intentional API changes. Passing application tests matters more than simply importing the upgraded package.
Steps to migrate Sentence Transformers code to v6:
- Record the dependencies of the active v5 environment in requirements-v5.txt.
$ python -m pip freeze > requirements-v5.txt
This file records installed versions for recovery; it is not the dependency file to install unchanged into v6.
Related: How to create a requirements freeze file in Python - Back up the project source, requirements-v5.txt, and saved model directories outside the working tree.
The original source, dependencies, and model files form the rollback set. The v5 environment must remain intact; any v6 model exports belong in a new directory. Rollback means returning to that source and those original models with the v5 interpreter, rather than trying to downgrade newly saved artifacts.
- Review any custom module code referenced by the model's modules.json.
Allowing custom code can execute Python with the permissions of the application. In v6, classes outside Sentence Transformers require explicit trust even for local models and locally installed packages. A local path is not a security boundary. An unreviewed model should remain untrusted.
- Open the project code that imports a moved module or loads a custom-code model in your text editor.
- Replace any deprecated imports with their current paths.
sentence_transformers.quantization -> sentence_transformers.util.quantization sentence_transformers.models -> sentence_transformers.sentence_transformer.modulesOther moved paths, such as losses and evaluators, are listed in the upstream migration guide. These paths exist from 5.4 onward, so leave them unchanged on an earlier 5.x release and change them once v6 is installed.
Related: How to migrate Sentence Transformers code to v5 - Enable custom-module loading only for a model whose code you have reviewed, using that model's own path.
from sentence_transformers import SentenceTransformer model = SentenceTransformer( "models/custom", trust_remote_code=True, )
The same argument works on v5. Ordinary models composed entirely of built-in Sentence Transformers modules do not need this flag. For Hub-hosted custom code, a reviewed commit pinned with revision prevents an upstream code change from silently changing what is trusted. The upstream migration guide describes the local-model trust change.
- Save the changed project code.
- Run the existing application tests with warnings treated as errors.
$ python -W error -m pytest -q
A failure here on v5 names code to change before comparing releases. A deprecated import fails with the path to use, and a local custom-code model fails with a FutureWarning until it has trust_remote_code=True.
- Create a file named capture_references.py in the project root using your text editor, with your application's own models and inputs in place of these samples.
- capture_references.py
import json from pathlib import Path import numpy as np from sentence_transformers import ( CrossEncoder, SentenceTransformer, ) REFERENCE_DIR = Path("tests/reference") MODEL = "sentence-transformers/all-MiniLM-L6-v2" RERANKER = "cross-encoder/ms-marco-MiniLM-L6-v2" TEXTS = [ "Invoices are emailed on the first day of each month.", "Reset a forgotten password from the sign-in page.", "The mobile app stores offline maps on the device.", ] QUERY = "How do I change my password?"
Both model values can be Hub names or local directories. TEXTS are the embedding inputs and also the documents ranked for QUERY, so use real documents whose order matters to the application.
- Add the code that saves the v5 embeddings and ranking at the end of capture_references.py.
- capture_references.py
model = SentenceTransformer(MODEL, device="cpu") reranker = CrossEncoder(RERANKER, device="cpu") embeddings = model.encode(TEXTS) ranked = reranker.rank(QUERY, TEXTS) ranked_ids = [row["corpus_id"] for row in ranked] REFERENCE_DIR.mkdir(parents=True, exist_ok=True) np.save(REFERENCE_DIR / "embeddings.npy", embeddings) (REFERENCE_DIR / "reference.json").write_text( json.dumps( { "model": MODEL, "reranker": RERANKER, "texts": TEXTS, "query": QUERY, "ranked_ids": ranked_ids, }, indent=2, ) ) print("Saved", embeddings.shape, "ranked IDs", ranked_ids)
The device="cpu" argument keeps the v5 and v6 runs on the same device. An application that passes encode() options such as normalize_embeddings=True, or loads a custom-code model with trust_remote_code=True, needs the same arguments here and in the test.
- Save capture_references.py.
- Run the capture script with the v5 interpreter.
$ python capture_references.py ##### snipped ##### Saved (3, 384) ranked IDs [1, 0, 2]
- Open the project's requirements.txt in your text editor.
- Replace the old Sentence Transformers constraints with these v6-compatible entries.
- requirements.txt
sentence-transformers==6.2.0 transformers>=5.0.0,<6.0.0 huggingface-hub>=1.3.0,<3.0.0 torch>=2.2 numpy>=1.24.0 scikit-learn>=1.1.0 pytest
Unrelated application dependencies stay in the file. Sentence Transformers 6.2.0 requires Python 3.10 or newer. Its package metadata specifies these dependency floors. Existing constraints or lock files that still require Transformers 4.x or Hugging Face Hub below 1.3 must be updated too. Projects using the training extra also need datasets 2.16+ and accelerate 1.3+.
- Save requirements.txt.
- Create a separate virtual environment for v6.
$ python -m venv .venv-v6
- Install the CPU PyTorch wheel into the v6 environment on Linux when the project does not need CUDA.
$ .venv-v6/bin/python -m pip install torch \ --index-url https://download.pytorch.org/whl/cpuWithout this wheel, torch>=2.2 in requirements.txt installs the PyPI build, which can pull in the large CUDA runtime packages on Linux. On Linux arm64 that build also makes the later pip check report nvidia-cusparselt-cu13 as not supported and exit with an error, because pip does not recognize that wheel's platform tag.
Related: How to install Sentence Transformers with pip - Install the updated requirements into the v6 environment.
$ .venv-v6/bin/python -m pip install -r requirements.txt
The explicit interpreter path keeps the original environment available. A dependency-resolution failure is a reason to reconcile the conflicting application requirement before proceeding.
- Check the v6 environment for dependency conflicts.
$ .venv-v6/bin/python -m pip check No broken requirements found.
- Open the project code that ranks documents or quantizes embeddings in your text editor.
- Replace deprecated reranker output arguments with the v6 score contract in the ranking caller.
ranked = reranker.rank(query, documents) scores = [row["score"] for row in ranked]
The v6 call omits convert_to_numpy and convert_to_tensor. They are deprecated no-ops that 6.2.0 only logs, so warnings treated as errors do not catch one left behind. Each score is now a Python float, so a test expecting a NumPy scalar or zero-dimensional tensor needs the new type. Callers that need a score array or tensor use predict() on query-document pairs instead; those scores follow input order rather than the sorted order returned by rank().
- Adapt quantization callers to consume one matrix per input when passing a list of two-dimensional arrays.
from sentence_transformers.util.quantization import ( quantize_embeddings, ) quantized = quantize_embeddings( matrices, precision="int8", ranges=ranges ) first_matrix = quantized[0] first_token = quantized[0][0]
Here matrices is the existing list of floating-point token-embedding matrices, and ranges is the application's fixed calibration array with shape (2, embedding_dim). V6 returns a list for this input rather than the v5 stacked array, and each input may have a different token count, so a test that indexes a stacked array needs the list form. A consumer that requires one stacked array can use np.stack(quantized) only when all matrix shapes match. Releases before 5.7 truncate integer values where later ones floor and clip them, so values saved by those releases can differ even with fixed ranges.
- Save the application code.
- Create a file named test_v6_migration.py in the project's tests directory using your text editor.
- tests/test_v6_migration.py
import json from pathlib import Path import numpy as np import pytest from sentence_transformers import ( CrossEncoder, SentenceTransformer, ) REFERENCE_DIR = Path("tests/reference") reference = json.loads( (REFERENCE_DIR / "reference.json").read_text() )
- Add the embedding and ranking comparisons at the end of tests/test_v6_migration.py.
- tests/test_v6_migration.py
def test_embeddings_match_v5(): model = SentenceTransformer( reference["model"], device="cpu" ) baseline = np.load(REFERENCE_DIR / "embeddings.npy") actual = model.encode(reference["texts"]) np.testing.assert_allclose( actual, baseline, rtol=1e-5, atol=1e-6 ) def test_ranking_matches_v5(): reranker = CrossEncoder(reference["reranker"], device="cpu") ranked = reranker.rank( reference["query"], reference["texts"] ) ranked_ids = [row["corpus_id"] for row in ranked] assert ranked_ids == reference["ranked_ids"] assert all(type(row["score"]) is float for row in ranked)
The tolerance suits the same float32 model on the same CPU, and the application's accuracy requirements can tighten it. Half-precision reranker and similarity scores now use float32 scoring, so ties and rankings can change intentionally. Multiprocess int8 or uint8 encoding also uses shared calibration after merging results; existing indexes built through that v5 path need regeneration. Those applications need retrieval-quality checks before replacing their indexes. The v6 release notes describe these changes and other silent ones, including chat-message input and functions assigned to model.similarity.
- Add a trust test at the end of tests/test_v6_migration.py when the project loads a custom-code model, using that model's own path.
- tests/test_v6_migration.py
def test_custom_model_requires_trust(): with pytest.raises(ValueError): SentenceTransformer("models/custom", device="cpu") model = SentenceTransformer( "models/custom", trust_remote_code=True, device="cpu" ) assert model.encode(["Trust check"]).shape[0] == 1
- Save tests/test_v6_migration.py.
- Confirm that the migrated application passes its regression suite with warnings treated as errors. Example output:
$ .venv-v6/bin/python -W error -m pytest -q ........ [100%] 8 passed in 9.85s
Counts and timing vary by application. A warning, changed embedding outside tolerance, unexpected document order, or failed model load blocks migration even if installation succeeded. Passing results leave the v6 application usable while the original environment and model files remain available for rollback.