Skip to content

Commit dc1d4ae

Browse files
authored
Merge pull request #1148 from edeandrea/update-ollama-dev-service
Re-write the Ollama dev service
2 parents cefbc8c + d94639b commit dc1d4ae

File tree

18 files changed

+469
-458
lines changed

18 files changed

+469
-458
lines changed

core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/devservice/DevServicesOllamaProcessor.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.math.RoundingMode;
55
import java.util.ArrayList;
66
import java.util.HashMap;
7+
import java.util.LinkedHashSet;
78
import java.util.List;
89
import java.util.Map;
910
import java.util.Objects;
@@ -23,6 +24,7 @@
2324
import io.quarkiverse.langchain4j.deployment.items.DevServicesChatModelRequiredBuildItem;
2425
import io.quarkiverse.langchain4j.deployment.items.DevServicesEmbeddingModelRequiredBuildItem;
2526
import io.quarkiverse.langchain4j.deployment.items.DevServicesModelRequired;
27+
import io.quarkiverse.langchain4j.deployment.items.DevServicesOllamaConfigBuildItem;
2628
import io.quarkus.deployment.IsNormal;
2729
import io.quarkus.deployment.annotations.BuildProducer;
2830
import io.quarkus.deployment.annotations.BuildStep;
@@ -46,9 +48,11 @@ private void handleModels(List<DevServicesChatModelRequiredBuildItem> devService
4648
List<DevServicesEmbeddingModelRequiredBuildItem> devServicesEmbeddingModels,
4749
LoggingSetupBuildItem loggingSetupBuildItem,
4850
Optional<ConsoleInstalledBuildItem> consoleInstalledBuildItem,
51+
Optional<DevServicesOllamaConfigBuildItem> ollamaDevServicesConfig,
4952
LaunchModeBuildItem launchMode,
5053
LangChain4jBuildConfig config,
5154
BuildProducer<DevServicesResultBuildItem> producer) {
55+
5256
if (devServicesChatModels.isEmpty() && devServicesEmbeddingModels.isEmpty()) {
5357
return;
5458
}
@@ -57,18 +61,27 @@ private void handleModels(List<DevServicesChatModelRequiredBuildItem> devService
5761
var ollamaEmbeddingModels = devServicesEmbeddingModels.stream().filter(bi -> OLLAMA_PROVIDER.equals(bi.getProvider()))
5862
.toList();
5963

60-
List<DevServicesModelRequired> allOllamaModels = new ArrayList<>();
64+
Set<DevServicesModelRequired> allOllamaModels = new LinkedHashSet<>();
6165
allOllamaModels.addAll(ollamaChatModels);
6266
allOllamaModels.addAll(ollamaEmbeddingModels);
6367
if (allOllamaModels.isEmpty()) {
6468
return;
6569
}
6670

67-
OllamaClient client = OllamaClient.create(new OllamaClient.Options("localhost", config.devservices().port()));
71+
var devServiceHost = ollamaDevServicesConfig
72+
.map(c -> c.getConfig().get("langchain4j-ollama-dev-service.ollama.host"))
73+
.orElse("localhost");
74+
75+
var devServicePort = ollamaDevServicesConfig
76+
.map(c -> c.getConfig().get("langchain4j-ollama-dev-service.ollama.port"))
77+
.map(Integer::parseInt)
78+
.orElseGet(() -> config.devservices().port());
79+
80+
OllamaClient client = OllamaClient.create(new OllamaClient.Options(devServiceHost, devServicePort));
6881
try {
6982
Set<ModelName> localModels = client.localModels().stream().map(mi -> ModelName.of(mi.name()))
7083
.collect(Collectors.toSet());
71-
List<String> modelsToPull = new ArrayList<>(ollamaChatModels.size() + ollamaEmbeddingModels.size());
84+
List<String> modelsToPull = new ArrayList<>(allOllamaModels.size());
7285
for (var requiredModel : allOllamaModels) {
7386
if (localModels.contains(ModelName.of(requiredModel.getModelName()))) {
7487
LOGGER.debug("Ollama already has model " + requiredModel.getModelName() + " pulled locally");
@@ -92,7 +105,7 @@ private void handleModels(List<DevServicesChatModelRequiredBuildItem> devService
92105
});
93106
for (String model : modelsToPull) {
94107
// we pull one model at a time and provide progress updates to the user via logging
95-
LOGGER.info("Pulling model " + model);
108+
LOGGER.infof("Pulling model %s", model);
96109
AtomicReference<Long> LAST_UPDATE_REF = new AtomicReference<>();
97110

98111
CompletableFuture<Void> cf = new CompletableFuture<>();
@@ -120,9 +133,9 @@ public void onNext(OllamaClient.PullAsyncLine line) {
120133
BigDecimal progress = percentage.setScale(2, RoundingMode.HALF_DOWN);
121134
if (progress.compareTo(ONE_HUNDRED) >= 0) {
122135
// avoid showing 100% for too long
123-
LOGGER.infof("Verifying and cleaning up\n", progress);
136+
LOGGER.info("Verifying and cleaning up\n");
124137
} else {
125-
LOGGER.infof("%s - Progress: %s%%\n", model, progress);
138+
LOGGER.infof("Downloading %s - Progress: %s%%\n", model, progress);
126139
}
127140
}
128141
}
@@ -171,7 +184,7 @@ public void onComplete() {
171184

172185
compressor.close();
173186

174-
String ollamaBaseUrl = String.format("http://localhost:%d", config.devservices().port());
187+
String ollamaBaseUrl = String.format("http://%s:%d", devServiceHost, devServicePort);
175188

176189
Map<String, String> modelBaseUrls = new HashMap<>();
177190
for (var bi : allOllamaModels) {

core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/devservice/JdkOllamaClient.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.IOException;
44
import java.io.UncheckedIOException;
55
import java.net.ConnectException;
6+
import java.net.Socket;
67
import java.net.URI;
78
import java.net.URISyntaxException;
89
import java.net.http.HttpClient;
@@ -37,6 +38,15 @@ public JdkOllamaClient(Options options) {
3738
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
3839
}
3940

41+
@Override
42+
public boolean isRunning() {
43+
try (var s = new Socket(this.options.host(), this.options.port())) {
44+
return true;
45+
} catch (Exception e) {
46+
return false;
47+
}
48+
}
49+
4050
@Override
4151
public List<ModelInfo> localModels() {
4252
String serverUrl = String.format("http://%s:%d/api/tags", options.host(), options.port());

core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/devservice/OllamaClient.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ static OllamaClient create(Options options) {
1111
return new JdkOllamaClient(options);
1212
}
1313

14+
/**
15+
* Whether or not the server is running
16+
*/
17+
boolean isRunning();
18+
1419
/**
1520
* Returns a list of all models the server has
1621
*/
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.quarkiverse.langchain4j.ollama.devservices;
1+
package io.quarkiverse.langchain4j.deployment.items;
22

33
import java.util.Map;
44

@@ -7,11 +7,11 @@
77
/**
88
* Build item used to carry running values to Dev UI.
99
*/
10-
public final class OllamaDevServicesConfigBuildItem extends SimpleBuildItem {
10+
public final class DevServicesOllamaConfigBuildItem extends SimpleBuildItem {
1111

1212
private final Map<String, String> config;
1313

14-
public OllamaDevServicesConfigBuildItem(Map<String, String> config) {
14+
public DevServicesOllamaConfigBuildItem(Map<String, String> config) {
1515
this.config = config;
1616
}
1717

docs/modules/ROOT/pages/includes/quarkus-langchain4j-ollama.adoc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,40 @@ endif::add-copy-button-to-env-var[]
4242
|boolean
4343
|`true`
4444

45+
a|icon:lock[title=Fixed at build time] [[quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-devservices-enabled]] [.property-path]##link:#quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-devservices-enabled[`quarkus.langchain4j.ollama.devservices.enabled`]##
46+
47+
[.description]
48+
--
49+
If Dev Services for Ollama has been explicitly enabled or disabled. Dev Services are generally enabled by default, unless there is an existing configuration present.
50+
51+
52+
ifdef::add-copy-button-to-env-var[]
53+
Environment variable: env_var_with_copy_button:+++QUARKUS_LANGCHAIN4J_OLLAMA_DEVSERVICES_ENABLED+++[]
54+
endif::add-copy-button-to-env-var[]
55+
ifndef::add-copy-button-to-env-var[]
56+
Environment variable: `+++QUARKUS_LANGCHAIN4J_OLLAMA_DEVSERVICES_ENABLED+++`
57+
endif::add-copy-button-to-env-var[]
58+
--
59+
|boolean
60+
|`true`
61+
62+
a|icon:lock[title=Fixed at build time] [[quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-devservices-image-name]] [.property-path]##link:#quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-devservices-image-name[`quarkus.langchain4j.ollama.devservices.image-name`]##
63+
64+
[.description]
65+
--
66+
The Ollama container image to use.
67+
68+
69+
ifdef::add-copy-button-to-env-var[]
70+
Environment variable: env_var_with_copy_button:+++QUARKUS_LANGCHAIN4J_OLLAMA_DEVSERVICES_IMAGE_NAME+++[]
71+
endif::add-copy-button-to-env-var[]
72+
ifndef::add-copy-button-to-env-var[]
73+
Environment variable: `+++QUARKUS_LANGCHAIN4J_OLLAMA_DEVSERVICES_IMAGE_NAME+++`
74+
endif::add-copy-button-to-env-var[]
75+
--
76+
|string
77+
|`ollama/ollama:latest`
78+
4579
a|icon:lock[title=Fixed at build time] [[quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-chat-model-model-id]] [.property-path]##link:#quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-chat-model-model-id[`quarkus.langchain4j.ollama.chat-model.model-id`]##
4680

4781
[.description]

docs/modules/ROOT/pages/includes/quarkus-langchain4j-ollama_quarkus.langchain4j.adoc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,40 @@ endif::add-copy-button-to-env-var[]
4242
|boolean
4343
|`true`
4444

45+
a|icon:lock[title=Fixed at build time] [[quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-devservices-enabled]] [.property-path]##link:#quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-devservices-enabled[`quarkus.langchain4j.ollama.devservices.enabled`]##
46+
47+
[.description]
48+
--
49+
If Dev Services for Ollama has been explicitly enabled or disabled. Dev Services are generally enabled by default, unless there is an existing configuration present.
50+
51+
52+
ifdef::add-copy-button-to-env-var[]
53+
Environment variable: env_var_with_copy_button:+++QUARKUS_LANGCHAIN4J_OLLAMA_DEVSERVICES_ENABLED+++[]
54+
endif::add-copy-button-to-env-var[]
55+
ifndef::add-copy-button-to-env-var[]
56+
Environment variable: `+++QUARKUS_LANGCHAIN4J_OLLAMA_DEVSERVICES_ENABLED+++`
57+
endif::add-copy-button-to-env-var[]
58+
--
59+
|boolean
60+
|`true`
61+
62+
a|icon:lock[title=Fixed at build time] [[quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-devservices-image-name]] [.property-path]##link:#quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-devservices-image-name[`quarkus.langchain4j.ollama.devservices.image-name`]##
63+
64+
[.description]
65+
--
66+
The Ollama container image to use.
67+
68+
69+
ifdef::add-copy-button-to-env-var[]
70+
Environment variable: env_var_with_copy_button:+++QUARKUS_LANGCHAIN4J_OLLAMA_DEVSERVICES_IMAGE_NAME+++[]
71+
endif::add-copy-button-to-env-var[]
72+
ifndef::add-copy-button-to-env-var[]
73+
Environment variable: `+++QUARKUS_LANGCHAIN4J_OLLAMA_DEVSERVICES_IMAGE_NAME+++`
74+
endif::add-copy-button-to-env-var[]
75+
--
76+
|string
77+
|`ollama/ollama:latest`
78+
4579
a|icon:lock[title=Fixed at build time] [[quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-chat-model-model-id]] [.property-path]##link:#quarkus-langchain4j-ollama_quarkus-langchain4j-ollama-chat-model-model-id[`quarkus.langchain4j.ollama.chat-model.model-id`]##
4680

4781
[.description]

docs/modules/ROOT/pages/ollama.adoc

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,26 @@ Once installed, check that Ollama is running using:
2020
2121
=== Dev Service
2222
23-
Quarkus LangChain4j automatically handles the pulling of the models configured by the application, so there is no need for users to do so manually.
23+
The Dev Service included with the Ollama extension can do lots of things.
24+
25+
The Dev Service automatically handles the pulling of the models configured by the application, so there is no need for users to do so manually.
26+
27+
Additionally, if you aren't already running a local Ollama instance (either via the https://ollama.com/download[desktop client] or a local container image) then it will first start the https://java.testcontainers.org/modules/ollama[Ollama container] on a random port and bind it to your application by setting `quarkus.langchain4j.ollama.*.base-url` to the URL where Ollama is running.
28+
29+
The container will also share downloaded models with any local client, so a model only needs to be downloaded the first time, regardless of whether you use the local Ollama client or the container provided by the Dev Service.
30+
31+
If the Dev Service starts an Ollama container, it will expose the following configuration properties that you can use within your own configuration should you need to:
32+
33+
[source,properties,subs=attributes+]
34+
----
35+
langchain4j-ollama-dev-service.ollama.host=host <1>
36+
langchain4j-ollama-dev-service.ollama.port=port <2>
37+
langchain4j-ollama-dev-service.ollama.endpoint=http://${langchain4j-ollama-dev-service.ollama.host}:${langchain4j-ollama-dev-service.ollama.port} <3>
38+
----
39+
40+
. The host that the container is running on. Typically `localhost`, but it could be the name of the container network.
41+
. The port that the Ollama container is running on.
42+
. The fully-qualified url (host + port) to the running Ollama container.
2443
2544
WARNING: Models are huge. For example Llama3 is 4.7Gb, so make sure you have enough disk space.
2645
@@ -41,7 +60,7 @@ To integrate with models running on Ollama, add the following dependency into yo
4160
4261
If no other LLM extension is installed, link:../ai-services.adoc[AI Services] will automatically utilize the configured Ollama model.
4362
44-
By default, the extension uses `llama3.1`, the model we pulled in the previous section.
63+
By default, the extension uses `llama3.2`, the model we pulled in the previous section.
4564
You can change it by setting the `quarkus.langchain4j.ollama.chat-model.model-id` property in the `application.properties` file:
4665
4766
[source,properties,subs=attributes+]

integration-tests/simple-ollama/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,6 @@
2222
<artifactId>quarkus-langchain4j-ollama</artifactId>
2323
<version>${project.version}</version>
2424
</dependency>
25-
<dependency>
26-
<groupId>io.quarkiverse.langchain4j</groupId>
27-
<artifactId>quarkus-langchain4j-ollama-devservices</artifactId>
28-
<version>${project.version}</version>
29-
</dependency>
3025
<dependency>
3126
<groupId>io.quarkus</groupId>
3227
<artifactId>quarkus-junit5</artifactId>

model-providers/ollama/deployment/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@
3131
<artifactId>quarkus-langchain4j-ollama</artifactId>
3232
<version>${project.version}</version>
3333
</dependency>
34+
<dependency>
35+
<groupId>org.testcontainers</groupId>
36+
<artifactId>ollama</artifactId>
37+
<exclusions>
38+
<exclusion>
39+
<groupId>junit</groupId>
40+
<artifactId>junit</artifactId>
41+
</exclusion>
42+
</exclusions>
43+
</dependency>
3444
<dependency>
3545
<groupId>io.quarkus</groupId>
3646
<artifactId>quarkus-junit5-internal</artifactId>

model-providers/ollama/deployment/src/main/java/io/quarkiverse/langchain4j/ollama/deployment/LangChain4jOllamaOpenAiBuildConfig.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static io.quarkus.runtime.annotations.ConfigPhase.BUILD_TIME;
44

5+
import io.quarkiverse.langchain4j.ollama.deployment.devservices.OllamaDevServicesBuildConfig;
56
import io.quarkus.runtime.annotations.ConfigRoot;
67
import io.smallrye.config.ConfigMapping;
78

@@ -18,4 +19,9 @@ public interface LangChain4jOllamaOpenAiBuildConfig {
1819
* Embedding model related settings
1920
*/
2021
EmbeddingModelBuildConfig embeddingModel();
22+
23+
/**
24+
* Dev services related settings
25+
*/
26+
OllamaDevServicesBuildConfig devservices();
2127
}

0 commit comments

Comments
 (0)