Class MockModerationModel

java.lang.Object
dev.langchain4j.test.mock.MockModerationModel
All Implemented Interfaces:
ModerationModel

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

Script the answers step by step, one step per request. One call of moderate(...) is one request, also when it moderates several messages. For example, a model that flags nothing, however often it is called:

MockModerationModel model = MockModerationModel.thatDoesNotFlag().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. So a mock that is called more than once, for example to moderate every message of a conversation, 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 what your application does when the moderation service times out for one message, with LangChain4j's TimeoutException, and then works again:

MockModerationModel model = MockModerationModel.thatFails(new TimeoutException("Read timed out"))
        .thenDoesNotFlag()
        .thenRepeats();
Or it can flag the first message and accept the next ones: MockModerationModel.thatFlags("How do I hack ...").thenDoesNotFlag().thenRepeats(). To flag a message by its content, compute the moderation from the request with thatModerates(Function).

Like on every mock in this package, thatResponds(...) and thenResponds(...) give a step as the whole response, here a ModerationResponse: the request is answered with exactly that response, including its metadata.

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 Details

    • thenFlags

      public MockModerationModel thenFlags(String flaggedText)
      Adds a step: the next request is flagged, with the given text reported as the flagged one. The text is not compared with the request, see thatFlags(String).
      Throws:
      IllegalStateException - if the script already ends with thenRepeats()
    • thenDoesNotFlag

      public MockModerationModel thenDoesNotFlag()
      Adds a step: the next request is not flagged.
      Throws:
      IllegalStateException - if the script already ends with thenRepeats()
    • thenModerates

      public MockModerationModel thenModerates(Function<ModerationRequest, Moderation> moderation)
      Adds a step: the moderation of the next request is computed from the request.
      Throws:
      IllegalStateException - if the script already ends with thenRepeats()
    • thenResponds

      public MockModerationModel thenResponds(ModerationResponse response)
      Adds a step: the next request is answered with exactly the given response, including its metadata, see thatResponds(ModerationResponse).
      Throws:
      IllegalStateException - if the script already ends with thenRepeats()
    • thenFails

      public MockModerationModel thenFails(RuntimeException exception)
      Adds a step: the next request fails with exactly the given exception.
      Throws:
      IllegalStateException - if the script already ends with thenRepeats()
    • thenRepeats

      public MockModerationModel 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 moderation computes it 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 with thenRepeats()
    • withListeners

      public MockModerationModel withListeners(ModerationModelListener... listeners)
      Notifies the given listeners of each call, like a real model does.
    • withListeners

      public MockModerationModel withListeners(List<ModerationModelListener> listeners)
      Notifies the given listeners of each call, like a real model does.
    • doModerate

      public ModerationResponse doModerate(ModerationRequest moderationRequest)
      Description copied from interface: ModerationModel
      Performs the actual moderation. This method should be overridden by implementations.
      Specified by:
      doModerate in interface ModerationModel
      Parameters:
      moderationRequest - the moderation request.
      Returns:
      the moderation response.
    • listeners

      public List<ModerationModelListener> listeners()
      Description copied from interface: ModerationModel
      Returns the list of listeners for this moderation model.
      Specified by:
      listeners in interface ModerationModel
      Returns:
      the list of listeners, or an empty list if none are registered.
    • requests

      public List<ModerationRequest> requests()
      A copy of the requests received so far, in the order they arrived.
    • request

      public ModerationRequest request()
      The only request received so far.
      Throws:
      IllegalStateException - if the mock received no request or several requests
    • lastRequest

      public ModerationRequest lastRequest()
      The last request received so far.
      Throws:
      IllegalStateException - if the mock received no request
    • thatFlags

      public static MockModerationModel thatFlags(String flaggedText)
      A model that flags the first request, reporting the given text as the flagged one. The text is not compared with the request: this step flags the next request, whatever it contains. A real moderation model reports the first flagged text of the request, so pass one of the texts that the code under test sends. To flag only some texts, compute the moderation from the request:
      MockModerationModel model = MockModerationModel.thatModerates(request -> request.texts().stream()
                      .filter(text -> text.contains("hack"))
                      .findFirst()
                      .map(Moderation::flagged)
                      .orElseGet(Moderation::notFlagged))
              .thenRepeats();
      
      Add the next steps with then... methods, or flag every request: thatFlags("...").thenRepeats().
    • thatDoesNotFlag

      public static MockModerationModel thatDoesNotFlag()
      A model that does not flag the first request. Add the next steps with then... methods, or flag nothing: thatDoesNotFlag().thenRepeats().
    • thatModerates

      public static MockModerationModel thatModerates(Function<ModerationRequest, Moderation> moderation)
      A model that computes the moderation of the first request from the request. Add the next steps with then... methods, or compute the moderation of every request with thenRepeats(). If the function throws, the request fails with that exception, unchanged.
    • thatResponds

      public static MockModerationModel thatResponds(ModerationResponse response)
      A model that answers the first request with exactly the given response, including its metadata, to test code that reads it:
      MockModerationModel model = MockModerationModel.thatResponds(ModerationResponse.builder()
              .moderation(Moderation.flagged("My phone number is 555-0100"))
              .metadata(Map.of("detection_type", "pii", "score", 0.97))
              .build());
      
      The metadata depends on the provider, and many moderation models report none. Add the next steps with then... methods, or answer every request with this response: thatResponds(response).thenRepeats().
    • thatFails

      public static MockModerationModel thatFails(RuntimeException exception)
      A model whose first request fails with exactly the given exception. To let every request fail, add thenRepeats(). To let later requests succeed, for example to test how your application handles a moderation service that is down for a moment, add steps with then... methods.