Ollama
Ollama runs decision models, such as Nimble, Tev1 and Clef, locally on your machine, with no
API key and no cost per request. OllamaDecisionModel implements the DecisionModel
API on top of Ollama's System One API (POST /v1/systemone), available in Ollama v0.35.0 or later.
This integration is experimental and may change in future releases.
Maven Dependency
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>1.23.0</version>
</dependency>
Usage
Download a decision model first:
ollama pull nimble
Then:
DecisionModel decisionModel = OllamaDecisionModel.builder()
.baseUrl("http://localhost:11434")
.modelName("nimble")
.build();
DecisionRequest request = DecisionRequest.builder()
.input("Help! My payouts have been failing for 3 days and nobody answers my emails.")
.question("team", ChoiceQuestion.builder()
.text("Which team should handle this ticket?")
.option("billing", "Payments, payouts, invoices, refunds")
.option("support", "Problems using the product")
.option("sales", "Pricing, upgrades, new accounts")
.build())
.question("urgent", YesNoQuestion.of("Does this need attention today?"))
.build();
DecisionResponse response = decisionModel.decide(request);
response.choice("team").value(); // "billing"
response.yesNo("urgent").probability(); // 0.97
The model name must be set, either on the builder or on each request. A model that has not been downloaded is
rejected by Ollama, and the request fails with a ModelNotFoundException.
Without a GPU, or while Ollama loads the model, a request can take several seconds, so consider a longer
timeout(...). Ollama unloads a model after 5 minutes without requests (unless configured otherwise on the server):
to keep it loaded longer, set keepAlive(...) in seconds, or a negative value to keep it loaded. The other settings
are httpClientBuilder, maxRetries, logRequests, logResponses, logger, customHeaders and listeners.
Both decide() and decideAsync() are supported.
Input
The input can be:
- text;
- a
Mapof named values, which is sent as a JSON object; - a list of
TextContents andImageContents, whose texts are joined with blank lines.
The whole input must fit in the context window the model is loaded with (it depends on the model and on the Ollama server's configuration): Ollama never truncates it. The request body must also be at most 64 KiB, or 32 MiB with images.
Images
Models with vision, such as Clef and Clef Flash (Ollama v0.35.1 or later), can judge PNG, JPEG and WebP images:
DecisionModel decisionModel = OllamaDecisionModel.builder()
.baseUrl("http://localhost:11434")
.modelName("clef-flash")
.build();
DecisionRequest request = DecisionRequest.builder()
.input(List.of(
TextContent.from("The customer says the package arrived like this."),
ImageContent.from(base64Image, "image/jpeg")))
.question("damaged", YesNoQuestion.of("Is the item visibly damaged?"))
.build();
Ollama receives the images separately from the rest of the input, so each image is replaced in the input by its
position among the images: the input above is sent as
The customer says the package arrived like this. followed by [image 1].
In a Map, an image is replaced the same way, so that its name says what it is:
Map.of("comment", comment, "photo", ImageContent.from(...)) is sent as
{"comment": "...", "photo": "[image 1]"}. Images are numbered from 1 in the order they appear in the input, across
all values of the Map, so questions can refer to them, for example "Does [image 2] show the same item as
[image 1]?". This [image N] convention comes from LangChain4j, not from Ollama.
Images can be given as base64 data, or by an http, https or file URL, in which case they are downloaded or read
by your application, on the calling thread (also for decideAsync()), before the request is sent. data: URLs are
not supported. Other contents, such as audio, are rejected with an UnsupportedFeatureException before calling
Ollama.
Do not pass image URLs that come from untrusted sources, such as users, tools or retrieved content: your application
would download any http URL and read any local file URL, and send the content to Ollama. Download and check such
images yourself, and pass them as base64 data.
Questions and answers
DecisionModel | Ollama | Notes |
|---|---|---|
YesNoQuestion | noul | yesWhen and noWhen are sent as the criteria when both are set; a single one is appended to the question instead (for example, Answer yes when: ...) |
ChoiceQuestion | choice | options are sent with their descriptions |
ScaleQuestion | score | levels are sent as text, as label: description when they have a description |
Yes/no answers include the probability of "yes". Choice and scale answers include the probability of each option or level, and a confidence.
A request can have at most 64 questions. A scale question can have 2 to 10 levels, and a choice question needs at least 2 options; the maximum number of options depends on the model (for example, 26 for Tev1).
Because of these limits, set maxToolsPerDecisionRequest(64) on the
tool selection components when there are more than 64 tools, and for
re-ranking, lower maxSegmentsPerRequest(...) so that the
segments of one request fit in the context window.
response.tokenUsage() reports the input and output tokens counted by Ollama.
Whatever the model, OllamaDecisionModel reports ModelProvider.OLLAMA as its provider, for example in listeners
and metrics.
Switching from TypeSafeDecisionModel
Ollama's decision models can also be called with TypeSafeDecisionModel
and a custom base URL, but Ollama rejects some of its requests, for example scale levels with descriptions, and it
does not support images. To switch to OllamaDecisionModel, replace the class name: the base URL and the model name
stay the same. Listeners and metrics then report OLLAMA instead of TYPESAFE as the provider.