Class MockEmbeddingModel
- All Implemented Interfaces:
EmbeddingModel
EmbeddingModel for tests: it needs no network, returns the vectors the test tells it to, and records the
requests it receives.
Script the answers step by step, one step per request. One call of embed(...), embedAll(...) or
embedAsync(...) is one request, also when it embeds several texts. A call of embedAll(...) without
texts returns at once, without asking the model: it takes no step and records no request. For example, a model that
returns the same vector for every text of every request:
MockEmbeddingModel model = MockEmbeddingModel.thatEmbeds(0.1f, 0.2f).thenRepeats();
Every that... method creates a mock with its first step, and every then... method adds a step. Each
step answers exactly one request. A request after the last step fails with an IllegalStateException that
shows the texts of the request, so a test notices when the code calls the model more often than the script expects,
unless the code under test handles the error itself. embed(...) throws it, and embedAsync(...)
returns a future that fails with it. So a mock that is called more than once, for example to embed the documents of
a search and later its query, ends its script with thenRepeats(), which answers every further request with
the last step.
A script can let a request fail and the next one succeed, for example to test your own retry logic after a rate limit:
MockEmbeddingModel model = MockEmbeddingModel.thatFails(new RateLimitException("Too many requests"))
.thenEmbeds(0.1f, 0.2f)
.thenRepeats();
or, when your code ingests documents in batches, with one ingest(...) or embedAll(...) call per
batch, let its second batch fail and the next batches succeed:
MockEmbeddingModel model = MockEmbeddingModel.thatEmbeds(0.1f, 0.2f)
.thenFails(new InternalServerException("Service unavailable"))
.thenEmbeds(0.1f, 0.2f)
.thenRepeats();
A real model that splits one call into several requests to its provider fails the whole call when one of them fails,
so one call is always one step.
Use thatEmbeds(Function) when the test depends on which texts are close to each other, for example on the
ranking of retrieved segments or on a minimum score: the function in the test then says which texts are close. Use
thatEmbeds(float...) when it does not matter. When a test depends on the meaning of texts, use a real
embedding model that runs in-process, for example AllMiniLmL6V2QuantizedEmbeddingModel from the module
langchain4j-embeddings-all-minilm-l6-v2-q.
Like on every mock in this package, thatResponds(...) and thenResponds(...) give a step as the whole
response, here an EmbeddingResponse: the request is answered with exactly that response, including its
metadata, for example the token usage of an embedding call in a cost test. The response is used as given, also when
its number of embeddings differs from the number of texts of the request, for example to test how your code handles
a provider that returns too few embeddings.
The mock accepts the per-call parameters modelName and inputType, and rejects dimensions with
an UnsupportedFeatureException, like a model without that parameter; a rejected request takes no step. Unless
a step gives the whole response, a response reports the model name of the request. embedAsync(...) completes
on another thread, where a step that computes its vectors also computes them.
A mock remembers which steps were taken and which requests it received, so create a new one for each test. A mock
shared between tests, for example as a bean, fails with a clear message as soon as its script runs out, unless the
script ends with thenRepeats(). The steps are taken in the order the requests arrive. A mock keeps every
request it receives, so it is not meant for load tests or other long runs.
-
Method Summary
Modifier and TypeMethodDescriptionintEmbeds the text"test"with the first step of the script that returns vectors, and returns the length of its vector; a step that gives the whole response gives the length of its first embedding.doEmbed(EmbeddingRequest request) Performs the embedding forEmbeddingModel.embed(EmbeddingRequest).doEmbedAsync(EmbeddingRequest request) Answers likedoEmbed(EmbeddingRequest), with the next step of the script, and completes the future on another thread, where a step that computes its vectors also computes them.The last request received so far.TheEmbeddingModelListeners that are notified aroundEmbeddingModel.embed(EmbeddingRequest).request()The only request received so far.requests()A copy of the requests received so far, in the order they arrived.The per-callparametersthis model honors.static MockEmbeddingModelthatEmbeds(float... vector) A model that answers the first request with the given vector for every text.static MockEmbeddingModelthatEmbeds(Function<String, float[]> embeddings) A model that answers the first request with the vector computed from each text, so the test says which texts are close to each other.static MockEmbeddingModelthatFails(RuntimeException exception) A model whose first request fails with exactly the given exception.static MockEmbeddingModelthatResponds(EmbeddingResponse response) A model that answers the first request with exactly the given response, including its metadata, for example its token usage or model name, to test code that reads them, such as a cost check:thenEmbeds(float... vector) Adds a step: every text of the next request gets the given vector.thenEmbeds(Function<String, float[]> embeddings) Adds a step: every text of the next request gets the vector computed from it, seethatEmbeds(Function).thenFails(RuntimeException exception) Adds a step: the next request fails with exactly the given exception.Answers every further request with the last step of the script.thenResponds(EmbeddingResponse response) Adds a step: the next request is answered with exactly the given response, including its metadata, seethatResponds(EmbeddingResponse).withListeners(EmbeddingModelListener... listeners) Notifies the given listeners of each call, like a real model does.withListeners(List<EmbeddingModelListener> listeners) Notifies the given listeners of each call, like a real model does.Methods inherited from class Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface EmbeddingModel
addListener, addListeners, defaultRequestParameters, embed, embed, embed, embedAll, embedAsync, modelName, provider, supportedContentTypes
-
Method Details
-
thenEmbeds
Adds a step: every text of the next request gets the given vector.- Throws:
IllegalArgumentException- if the vector has no dimensionIllegalStateException- if the script already ends withthenRepeats()
-
thenEmbeds
Adds a step: every text of the next request gets the vector computed from it, seethatEmbeds(Function).- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
thenResponds
Adds a step: the next request is answered with exactly the given response, including its metadata, seethatResponds(EmbeddingResponse).- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
thenFails
Adds a step: the next request fails with exactly the given exception.- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
thenRepeats
Answers every further request with the last step of the script. Only the last step repeats; the script does not start over. A last step that computes its vectors computes them again for each request. Without it, a request after the last step fails. It must be the last call of the script.- Throws:
IllegalStateException- if the script already ends withthenRepeats()
-
withListeners
Notifies the given listeners of each call, like a real model does. -
withListeners
Notifies the given listeners of each call, like a real model does. -
supportedParameters
Description copied from interface:EmbeddingModelThe per-callparametersthis model honors. A request that populates a parameter outside this set is rejected byEmbeddingModel.embed(EmbeddingRequest)with anUnsupportedFeatureException, so a parameter a model cannot apply is never silently ignored. Defaults to an empty set (the model accepts no per-call parameters).- Specified by:
supportedParametersin interfaceEmbeddingModel
-
doEmbedAsync
Answers likedoEmbed(EmbeddingRequest), with the next step of the script, and completes the future on another thread, where a step that computes its vectors also computes them. It never throws: anullrequest gives a failed future.- Specified by:
doEmbedAsyncin interfaceEmbeddingModel- Parameters:
request- the request, with parameters already merged and validated.- Returns:
- a
CompletableFutureof the response.
-
doEmbed
Description copied from interface:EmbeddingModelPerforms the embedding forEmbeddingModel.embed(EmbeddingRequest). This is the low-level method a provider overrides to build the actual API call;EmbeddingModel.embed(EmbeddingRequest)handles per-call parameter validation andlistenerdispatch around it.An implementation must override either this method (preferred) or
EmbeddingModel.embedAll(List)— the two have mutually-recursive defaults, so overriding neither leads to infinite recursion. The default implementation embeds the text of each input viaEmbeddingModel.embedAll(List), which keeps implementations that only provideEmbeddingModel.embedAll(List)working.When this method is called, the request's parameters have already been merged with
EmbeddingModel.defaultRequestParameters()and validated againstEmbeddingModel.supportedParameters()andEmbeddingModel.supportedContentTypes().- Specified by:
doEmbedin interfaceEmbeddingModel- Parameters:
request- the request, with parameters already merged and validated.- Returns:
- the response.
-
dimension
public int dimension()Embeds the text"test"with the first step of the script that returns vectors, and returns the length of its vector; a step that gives the whole response gives the length of its first embedding. It takes no step and records no request, so the script andrequests()stay as they are. A script without such a step throws the exception of its first step, if that step fails, or anIllegalStateException. When an embedding store asks the model for its dimension, for example while a framework starts, set the dimension of the store explicitly instead.- Specified by:
dimensionin interfaceEmbeddingModel- Returns:
- dimension of the embedding
-
listeners
Description copied from interface:EmbeddingModelTheEmbeddingModelListeners that are notified aroundEmbeddingModel.embed(EmbeddingRequest). Configure them on the model's builder (vialisteners(...)). Defaults to an empty list.- Specified by:
listenersin interfaceEmbeddingModel
-
requests
A copy of the requests received so far, in the order they arrived. -
request
The only request received so far.- Throws:
IllegalStateException- if the mock received no request or several requests
-
lastRequest
The last request received so far.- Throws:
IllegalStateException- if the mock received no request
-
thatEmbeds
A model that answers the first request with the given vector for every text. All texts are then equally close to each other, so a test with this model cannot depend on their ranking. Add the next steps withthen...methods, or return this vector for every request:thatEmbeds(0.1f, 0.2f).thenRepeats().- Throws:
IllegalArgumentException- if the vector has no dimension
-
thatEmbeds
A model that answers the first request with the vector computed from each text, so the test says which texts are close to each other. For example, with
two texts that both contain "refund" get a relevance score of 1, and a text with "refund" and a text without it get a score of 0.5. Add the next steps withMockEmbeddingModel model = MockEmbeddingModel.thatEmbeds( text -> text.contains("refund") ? new float[] {1, 0} : new float[] {0, 1}) .thenRepeats();then...methods, or compute the vectors of every request withthenRepeats(), as above. If the function throws, the request fails with that exception, unchanged. If it returns no vector, the request fails with anIllegalStateExceptionthat shows the text. -
thatResponds
A model that answers the first request with exactly the given response, including its metadata, for example its token usage or model name, to test code that reads them, such as a cost check:
The response is used as given, also when its number of embeddings differs from the number of texts of the request. Add the next steps withMockEmbeddingModel model = MockEmbeddingModel.thatResponds(EmbeddingResponse.builder() .embeddings(List.of(Embedding.from(new float[] {0.1f, 0.2f}))) .tokenUsage(new TokenUsage(12)) .build());then...methods, or answer every request with this response:thatResponds(response).thenRepeats(). -
thatFails
A model whose first request fails with exactly the given exception. To let every request fail, addthenRepeats(). To let later requests succeed, for example to test your own retry logic, add steps withthen...methods.
-