soev_retrieval.engine
Search one or more vector stores, combine the results, and put your own rules around a store.
The searches are written once over VectorStore, so they work with every store.
Search
A search embeds the query with the embedder of each index, searches each scope, and combines the lists into one ranking.
search_scopes
functionsearch_scopesasync (groups, query, *, limit, combine, alpha=None) -> Sequence[Hit]Search every group's scopes for query and return one ranking from combine, best first.
Every search names one target of one store. Scopes in one target are one search, which matches any of their
filters, so the store scores their hits against each other. An unfiltered scope makes its whole target
unfiltered. Lists from different targets or stores meet only in combine, which receives one list per search, in
group and scope order. Each search returns at most limit hits. The query is embedded once per group, with the
group's own embedder. A group with no scopes embeds and searches nothing. Errors are the store's, as
hybrid_search raises them.
Example
from soev_retrieval.domain import DeclaredFilter, SearchScope
from soev_retrieval.engine import Rrf, SearchGroup, search_scopes
hits = await search_scopes(
[
SearchGroup(
store=policy_store,
scopes=[SearchScope("policies", where=DeclaredFilter("year", "gte", 2020))],
embed=embed_policy_query,
),
SearchGroup(store=case_store, scopes=[SearchScope("cases")], embed=embed_case_query),
],
"pension transfer rules",
limit=10,
combine=Rrf(),
)paramgroupsSequence[SearchGroup]The stores to search, each with its scopes and its embedder.
paramquerystrThe query text.
paramlimitintThe maximum number of hits of each search.
paramcombineCombineThe strategy that turns the lists of all searches into one ranking.
paramalphafloat | None= NoneThe weight of the vector side for every search, or None for each store's own default.
Returns
Sequence[Hit]The ranking that combine returns, or no hits when no group has a scope.
Raises:
InvalidQueryError: A store rejected a search.StoreUnavailable: A store gave no answer.Unconfigured: A group has no embedder, and its store cannot embed the query.
SearchGroup
dataclassSearchGroup(store, scopes, embed=None)The scopes one store serves, with the embedder its index was built with (None leaves it to the store).
paramstoreVectorStoreThe store that serves the scopes.
paramscopesSequence[SearchScope]The scopes to search in the store.
paramembedEmbed | None= NoneThe embedder of the store's index, or None to leave embedding to the store.
hybrid_search
functionhybrid_searchasync (store, target, query, *, limit, alpha=None, vector=None, where=None, embed=None) -> Sequence[Hit]Return at most limit chunks of target matching query, best first.
The query vector is vector when given, else embed(query) when an embedder is given, else left to the
store, which raises Unconfigured if it cannot embed. alpha and where go to the store unchanged.
See VectorStore.hybrid for their meaning and the errors raised.
paramstoreVectorStoreThe store to search.
paramtargetTargetThe target to search.
paramquerystrThe query text.
paramlimitintThe maximum number of hits.
paramalphafloat | None= NoneThe weight of the vector side, or None for the store's own default.
paramvectorSequence[float] | None= NoneThe query embedding. When it is given, embed is not called.
paramwhereFilter | None= NoneThe filter that restricts the candidates, or None to restrict nothing.
paramembedEmbed | None= NoneThe embedder for the query when vector is None.
Returns
Sequence[Hit]At most limit hits, best first.
Raises:
InvalidQueryError: The store rejected the call.StoreUnavailable: The store gave no answer.Unconfigured: No vector and no embedder are given, and the store cannot embed the query.
Embed
type aliasEmbed = Callable[[str], Awaitable[Sequence[float]]]An async function that embeds one query text into one vector.
Give a search the embedder that the index of its store was built with, so that the query vector and the stored vectors compare.
Combine
protocolCombineA strategy that turns the query and one ranked list of hits per search into one ranking, best first.
Scores from different searches do not compare (see Hit), so a strategy works from ranks, or rescores
every hit on one scale. Hits with the same id are the same chunk and appear once.
search_scopes calls it. Rrf is the stock strategy.
method__call__async (query, ranked, /) -> Sequence[Hit]Return one ranking of the hits in ranked, best first.
paramquerystrThe query text that the searches answered.
paramrankedSequence[Sequence[Hit]]One list of hits for each search, each best first.
Returns
Sequence[Hit]The combined ranking, with each chunk once.
Rrf
dataclassRrf(k=60)Reciprocal rank fusion: a hit scores the sum of 1 / (k + rank) over the lists it appears in, rank from 1.
Uses ranks only, so lists whose scores do not compare can be fused. Each fused hit carries its fused score and the properties of its first appearance.
paramkint= 60The rank constant. It damps the weight of the top ranks: a larger k makes the ranks count more equally.
method__call__async (query, ranked, /) -> Sequence[Hit]Fuse the lists of ranked into one ranking, best first.
paramquerystrNot read: the fusion uses ranks only.
paramrankedSequence[Sequence[Hit]]One list of hits for each search, each best first.
Returns
Sequence[Hit]Each hit once, with its fused score and the properties of its first appearance, highest score first.
Rules around a store
Each of these classes is a VectorStore over another store. It retries a call, rewrites a filter before the call,
or drops results after the call.
Retrying
dataclassRetrying(inner, attempts, backoff=1.0, sleep=asyncio.sleep)VectorStore over another, calling it up to attempts times while it raises StoreUnavailable.
Waits backoff seconds before the second attempt, doubling before each further one, and raises the last
StoreUnavailable when every attempt failed. A rejected call (InvalidQueryError) or an Unconfigured
store is raised at once: calling again would get the same answer.
paraminnerVectorStoreThe store that answers each call.
paramattemptsintThe maximum number of calls to inner for one call. It must be at least 1.
parambackofffloat= 1.0The seconds to wait before the second attempt.
paramsleepCallable[[float], Awaitable[None]]= asyncio.sleepWhat waits. Tests pass a recorder.
Raises:
ValueError: When the instance is made:attemptsis less than 1.
methodhybridasync (target, *, query, vector=None, limit, where=None, alpha=None) -> Sequence[Hit]Call inner.hybrid with the same arguments, again while it raises StoreUnavailable.
paramtargetTargetThe target to search.
paramquerystrThe query text.
paramvectorSequence[float] | None= NoneThe query embedding, or None to leave embedding to the inner store.
paramlimitintThe maximum number of hits.
paramwhereFilter | None= NoneThe filter for the inner store, or None to restrict nothing.
paramalphafloat | None= NoneThe weight of the vector side, or None for the default of the inner store.
Returns
Sequence[Hit]The hits of the first attempt that the inner store answered.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer in any attempt.Unconfigured: The inner store cannot serve the call.
methodnear_vectorasync (target, *, vector, limit, where=None) -> Sequence[Hit]Call inner.near_vector with the same arguments, again while it raises StoreUnavailable.
paramtargetTargetThe target to search.
paramvectorSequence[float]The query embedding.
paramlimitintThe maximum number of hits.
paramwhereFilter | None= NoneThe filter for the inner store, or None to restrict nothing.
Returns
Sequence[Hit]The hits of the first attempt that the inner store answered.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer in any attempt.Unconfigured: The inner store cannot serve the call.
methodbm25async (target, *, query, limit, where=None) -> Sequence[Hit]Call inner.bm25 with the same arguments, again while it raises StoreUnavailable.
paramtargetTargetThe target to search.
paramquerystrThe query text.
paramlimitintThe maximum number of hits.
paramwhereFilter | None= NoneThe filter for the inner store, or None to restrict nothing.
Returns
Sequence[Hit]The hits of the first attempt that the inner store answered.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer in any attempt.Unconfigured: The inner store cannot serve the call.
methodfetchasync (target, *, where=None, limit, order_by=None, descending=False) -> Sequence[Chunk]Call inner.fetch with the same arguments, again while it raises StoreUnavailable.
paramtargetTargetThe target to read.
paramwhereFilter | None= NoneThe filter for the inner store, or None for every chunk.
paramlimitintThe maximum number of chunks.
paramorder_bystr | None= NoneA stored field to sort on, or None for the order of the inner store.
paramdescendingbool= FalseWhether to sort on order_by in descending order.
Returns
Sequence[Chunk]The chunks of the first attempt that the inner store answered.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer in any attempt.Unconfigured: The inner store cannot serve the call.
methodfieldsasync (target) -> FieldSpecReturn the field spec of target from the inner store, again while it raises StoreUnavailable.
paramtargetTargetThe target to describe.
Returns
FieldSpecThe field spec of the first attempt that the inner store answered.
Raises:
InvalidQueryError: The inner store does not know the target.StoreUnavailable: The inner store gave no answer in any attempt.
PreFiltered
dataclassPreFiltered(inner, rewrite)VectorStore over another, calling it with every predicate of the filter replaced by what rewrite returns.
rewrite sees the target and each predicate of the filter of each method (the three searches and fetch),
wherever it sits in the expression, and the expression around it is kept. It is for an operator the store cannot
serve itself: a folder scope (under) resolved through a catalog into the document ids it covers, a name looked
up into a key.
A predicate rewritten to NOTHING matches nothing, so its branch of an AnyOf drops out, its AllOf matches
nothing, and its Not matches everything. When the whole filter comes to match nothing, the call answers an
empty sequence without the inner store being called. Every other argument reaches the inner store unchanged,
results come back untouched, and fields passes through. PostFiltered acts on the results instead. The two
compose in either order.
paraminnerVectorStoreThe store that answers each call.
paramrewriteRewriteThe rule that replaces each predicate.
methodhybridasync (target, *, query, vector=None, limit, where=None, alpha=None) -> Sequence[Hit]Rewrite and fold where, and call inner.hybrid with it and the other arguments unchanged.
paramtargetTargetThe target to search.
paramquerystrThe query text.
paramvectorSequence[float] | None= NoneThe query embedding, or None to leave embedding to the inner store.
paramlimitintThe maximum number of hits.
paramwhereFilter | None= NoneThe filter whose predicates rewrite replaces, or None to restrict nothing.
paramalphafloat | None= NoneThe weight of the vector side, or None for the default of the inner store.
Returns
Sequence[Hit]The hits of the inner store, or no hits when the rewritten filter matches nothing.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer.Unconfigured: The inner store cannot serve the call.
methodnear_vectorasync (target, *, vector, limit, where=None) -> Sequence[Hit]Rewrite and fold where, and call inner.near_vector with it and the other arguments unchanged.
paramtargetTargetThe target to search.
paramvectorSequence[float]The query embedding.
paramlimitintThe maximum number of hits.
paramwhereFilter | None= NoneThe filter whose predicates rewrite replaces, or None to restrict nothing.
Returns
Sequence[Hit]The hits of the inner store, or no hits when the rewritten filter matches nothing.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer.Unconfigured: The inner store cannot serve the call.
methodbm25async (target, *, query, limit, where=None) -> Sequence[Hit]Rewrite and fold where, and call inner.bm25 with it and the other arguments unchanged.
paramtargetTargetThe target to search.
paramquerystrThe query text.
paramlimitintThe maximum number of hits.
paramwhereFilter | None= NoneThe filter whose predicates rewrite replaces, or None to restrict nothing.
Returns
Sequence[Hit]The hits of the inner store, or no hits when the rewritten filter matches nothing.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer.Unconfigured: The inner store cannot serve the call.
methodfetchasync (target, *, where=None, limit, order_by=None, descending=False) -> Sequence[Chunk]Rewrite and fold where, and call inner.fetch with it and the other arguments unchanged.
paramtargetTargetThe target to read.
paramwhereFilter | None= NoneThe filter whose predicates rewrite replaces, or None for every chunk.
paramlimitintThe maximum number of chunks.
paramorder_bystr | None= NoneA stored field to sort on, or None for the order of the inner store.
paramdescendingbool= FalseWhether to sort on order_by in descending order.
Returns
Sequence[Chunk]The chunks of the inner store, or no chunks when the rewritten filter matches nothing.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer.Unconfigured: The inner store cannot serve the call.
methodfieldsasync (target) -> FieldSpecReturn the field spec of target from the inner store, unchanged.
paramtargetTargetThe target to describe.
Returns
FieldSpecThe field spec that the inner store returns.
Raises:
InvalidQueryError: The inner store does not know the target.StoreUnavailable: The inner store gave no answer.
Rewrite
protocolRewriteThe rule that gives the filter to put in the place of one predicate of the caller's filter.
It gets the target and one predicate, and returns the predicate itself, a replacement the inner store can serve,
or NOTHING when no chunk could match it.
PreFiltered calls it once for each predicate of each filter.
method__call__async (target, term, /) -> FilterReturn the filter to put in the place of term.
paramtargetTargetThe target that the call named.
paramtermDeclaredFilterOne predicate of the caller's filter.
Returns
Filterterm itself, a filter that the inner store can serve, or NOTHING when no chunk can match term.
PostFiltered
dataclassPostFiltered(inner, keep)VectorStore over another, returning only what keep returns.
keep sees every result the inner store answered with (Hits from a search, Chunks from a fetch) and returns
those to pass on. It runs after the inner store applied limit, so a call can return fewer results than asked
for. The store does not fetch more results to replace them. It is for a rule the index cannot apply itself: a
revocation the index does not know about yet, a blocklist, a score floor, deduplication. Every argument reaches
the inner store unchanged, and fields passes through untouched.
paraminnerVectorStoreThe store that answers each call.
paramkeepKeepThe rule that picks the results to return.
methodhybridasync (target, *, query, vector=None, limit, where=None, alpha=None) -> Sequence[Hit]Call inner.hybrid with the same arguments, and return the hits that keep returns.
paramtargetTargetThe target to search.
paramquerystrThe query text.
paramvectorSequence[float] | None= NoneThe query embedding, or None to leave embedding to the inner store.
paramlimitintThe maximum number of hits that the inner store returns, before keep drops any.
paramwhereFilter | None= NoneThe filter for the inner store, or None to restrict nothing.
paramalphafloat | None= NoneThe weight of the vector side, or None for the default of the inner store.
Returns
Sequence[Hit]The hits that keep returns.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer.Unconfigured: The inner store cannot serve the call.
methodnear_vectorasync (target, *, vector, limit, where=None) -> Sequence[Hit]Call inner.near_vector with the same arguments, and return the hits that keep returns.
paramtargetTargetThe target to search.
paramvectorSequence[float]The query embedding.
paramlimitintThe maximum number of hits that the inner store returns, before keep drops any.
paramwhereFilter | None= NoneThe filter for the inner store, or None to restrict nothing.
Returns
Sequence[Hit]The hits that keep returns.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer.Unconfigured: The inner store cannot serve the call.
methodbm25async (target, *, query, limit, where=None) -> Sequence[Hit]Call inner.bm25 with the same arguments, and return the hits that keep returns.
paramtargetTargetThe target to search.
paramquerystrThe query text.
paramlimitintThe maximum number of hits that the inner store returns, before keep drops any.
paramwhereFilter | None= NoneThe filter for the inner store, or None to restrict nothing.
Returns
Sequence[Hit]The hits that keep returns.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer.Unconfigured: The inner store cannot serve the call.
methodfetchasync (target, *, where=None, limit, order_by=None, descending=False) -> Sequence[Chunk]Call inner.fetch with the same arguments, and return the chunks that keep returns.
paramtargetTargetThe target to read.
paramwhereFilter | None= NoneThe filter for the inner store, or None for every chunk.
paramlimitintThe maximum number of chunks that the inner store returns, before keep drops any.
paramorder_bystr | None= NoneA stored field to sort on, or None for the order of the inner store.
paramdescendingbool= FalseWhether to sort on order_by in descending order.
Returns
Sequence[Chunk]The chunks that keep returns.
Raises:
InvalidQueryError: The inner store rejected the call.StoreUnavailable: The inner store gave no answer.Unconfigured: The inner store cannot serve the call.
methodfieldsasync (target) -> FieldSpecReturn the field spec of target from the inner store, unchanged.
paramtargetTargetThe target to describe.
Returns
FieldSpecThe field spec that the inner store returns.
Raises:
InvalidQueryError: The inner store does not know the target.StoreUnavailable: The inner store gave no answer.
Keep
protocolKeepThe rule that picks, from the results a store answered with, the ones to return.
It gets the target and the results, in order, and returns the ones to pass on.
PostFiltered calls it with the hits of each search and with the chunks of each fetch.
method__call__async [R: Hit | Chunk](target, results, /) -> Sequence[R]Return the results to pass on, in the order to return them.
paramtargetTargetThe target that the call named.
paramresultsSequence[R]What the inner store answered with, in its order: hits from a search, chunks from a fetch.
Returns
Sequence[R]The results to return to the caller. They are of the same type as results.