Class MockDecisionModel

java.lang.Object
dev.langchain4j.test.mock.MockDecisionModel
All Implemented Interfaces:
DecisionModel

@Experimental public class MockDecisionModel extends Object implements DecisionModel
A DecisionModel for tests: it needs no network, answers the way the test tells it to, and records the requests it receives.

Script the answers step by step, one step per request. For example, a model that answers every yes/no question with yes:

MockDecisionModel model = MockDecisionModel.thatAnswersYesNoQuestions(question -> 0.9).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 questions of the request, so a test notices when the code asks the model more often than the script expects, unless the code under test handles the error itself. decide(...) throws it, and decideAsync(...) returns a future that fails with it. To answer every further request with the last step, end the script with thenRepeats().

A script can give different answers to the same question, for example to a loop that asks whether its task is done and stops at the third answer:

MockDecisionModel model = MockDecisionModel.thatAnswersYesNoQuestions(question -> 0.1)
        .thenAnswersYesNoQuestions(question -> 0.1)
        .thenAnswersYesNoQuestions(question -> 0.9);
It can also let the first request fail and the next one succeed, for example to test your own retry logic:
MockDecisionModel model = MockDecisionModel.thatFails(new RateLimitException("Too many requests"))
        .thenAnswersYesNoQuestions(question -> 0.9);
A step can compute its answers from the request with thatAnswers(Function), thatAnswersQuestions(Function) or thatAnswersYesNoQuestions(ToDoubleFunction), for example to answer concurrent requests by their questions: thatAnswersQuestions(question -> ...).thenRepeats(). If the function throws, the request fails with that exception, unchanged.

Like on every mock in this package, thatResponds(...) and thenResponds(...) give a step as the whole response, here a DecisionResponse: the request is answered with the answers and the metadata of that response, for example the token usage that a cost check reads, checked like the answers of a real model, so a choice answer then also carries the options of its question.

The model checks its answers like a real one: a request with a question that got no answer fails with an InvalidDecisionResponseException. Unless a step gives the whole response, a response reports the model name of the request. decideAsync(...) completes on another thread, where a step that computes its answers 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.