Asymmetric Sentence Transformers models keep query text and document text on separate processing paths so retrieval code can tune each side for its role. Older projects may still wrap those paths with Asym, but current Sentence Transformers uses Router to select the query or document route during encoding.
The migration keeps the existing query and document module stacks and changes the dispatch layer around them. Router.for_query_document() names the two routes, while encode_query() and encode_document() pass the matching task value to the router at inference time.
Run the change in a branch or scratch copy when the old model was trained with different query and document towers. A smoke test should prove that both routes exist and that query and document embeddings keep the dimension expected by the retrieval index.
from sentence_transformers import SentenceTransformer from sentence_transformers.models import Asym model = SentenceTransformer( modules=[ Asym( { "query": query_modules, "document": document_modules, } ) ] )
Keep the actual query_modules and document_modules lists from the existing model; those modules are the state being migrated.
from sentence_transformers import SentenceTransformer from sentence_transformers.sentence_transformer.modules import Normalize, Router
sentence_transformers.models remains a compatibility import in recent releases, but it raises deprecation warnings. Use the current module import path in migrated code.
router = Router.for_query_document( query_modules=query_modules, document_modules=document_modules, ) model = SentenceTransformer(modules=[router, Normalize()])
Only keep Normalize() after the router when the old pipeline applied normalization after both routes. Route-specific modules should stay inside their matching list.
query_embedding = model.encode_query(["How do I migrate Asym routes?"]) document_embeddings = model.encode_document( [ "Use Router.for_query_document and encode_query for retrieval queries.", "Set the cache directory before loading a local model.", ] )
encode_query() selects the query route, and encode_document() selects the document route. Avoid passing {“query”: …} or {“document”: …} dictionaries to encode() after the migration.
from sentence_transformers import SentenceTransformer from sentence_transformers.sentence_transformer.modules import Normalize, Router query_embedder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") document_embedder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") router = Router.for_query_document( query_modules=list(query_embedder.children()), document_modules=list(document_embedder.children()), ) model = SentenceTransformer(modules=[router, Normalize()]) query_embedding = model.encode_query(["How do I migrate Asym routes?"]) document_embeddings = model.encode_document( [ "Use Router.for_query_document and encode_query for retrieval queries.", "Set the cache directory before loading a local model.", ] ) print("Router routes:", ", ".join(sorted(model[0].sub_modules))) print("Query embedding shape:", query_embedding.shape) print("Document embedding shape:", document_embeddings.shape)
The smoke test uses two copies of one public model so the route API can be checked quickly. Replace those module lists with the real query and document towers when testing a production migration.
$ python router_migration_check.py Router routes: document, query Query embedding shape: (1, 384) Document embedding shape: (2, 384)
$ rm router_migration_check.py
Keep a project test that exercises encode_query() and encode_document() if the model is part of a retrieval service or scheduled index build.