Auto-configuration and starter for Ollama
- Adds auto-configuration and a starter for Ollama. - Add testconteiner orca-mini integration test. - Improve code style and documentation. Resolves #68, Resolves #69
This commit is contained in:
committed by
Christian Tzolov
parent
820eb5f0ab
commit
6bdea65cd5
1
pom.xml
1
pom.xml
@@ -22,6 +22,7 @@
|
||||
<module>spring-ai-spring-boot-autoconfigure</module>
|
||||
<module>spring-ai-spring-boot-starters/spring-ai-starter-openai</module>
|
||||
<module>spring-ai-spring-boot-starters/spring-ai-starter-azure-openai</module>
|
||||
<module>spring-ai-spring-boot-starters/spring-ai-starter-ollama</module>
|
||||
<module>spring-ai-spring-boot-starters/spring-ai-starter-transformers-embedding</module>
|
||||
<module>spring-ai-docs</module>
|
||||
<module>vector-stores/spring-ai-pgvector-store</module>
|
||||
|
||||
@@ -1,9 +1,56 @@
|
||||
## Ollama
|
||||
# Ollama
|
||||
|
||||
Ollama lets you ge tup an running with large language models locally
|
||||
Ollama lets you get up and running with large language models locally.
|
||||
|
||||
Refer to the official [README](https://github.com/jmorganca/ollama) to get started.
|
||||
|
||||
Note, installing `ollama run llama2` will download a 4GB docker image.
|
||||
|
||||
You can run the disabled test in `OllamaClientTests.java` to kick the tires.
|
||||
You can run the disabled test in `OllamaClientTests.java` to kick the tires.
|
||||
|
||||
## How to use
|
||||
|
||||
Add the `spring-ai-ollama` dependency to your project's pom:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-ollama</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
then create an client and use generate response:
|
||||
|
||||
```java
|
||||
var ollamaClient = new OllamaClient("http://127.0.0.1:11434", "llama2",
|
||||
ollamaResult -> {
|
||||
if (ollamaResult.getDone()) {
|
||||
....
|
||||
}
|
||||
});
|
||||
|
||||
AiResponse aiResponse = ollamaClient.generate(new Prompt("Hello"));
|
||||
```
|
||||
|
||||
### Spring Boot Starter
|
||||
|
||||
For convenience you can opt for the Ollama Boot starter.
|
||||
For this add the following dependency:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
and use the `spring.ai.ollama.*` properties to configure it if you want to use something other than the default values.
|
||||
|
||||
The complete list of supported properties are:
|
||||
|
||||
| Property | Description | Default |
|
||||
| -------- | ----- | ----- |
|
||||
| spring.ai.ollama.base-url | Base URL where Ollama API server is running. | `http://localhost:11434` |
|
||||
| spring.ai.ollama.model | Language model to use. | `llama2` |
|
||||
|
||||
@@ -49,6 +49,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-ollama</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Transformers Embedding Client -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.autoconfigure.ollama;
|
||||
|
||||
import org.springframework.ai.ollama.client.OllamaClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass(OllamaClient.class)
|
||||
@EnableConfigurationProperties(OllamaProperties.class)
|
||||
public class OllamaAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public OllamaClient ollamaClient(OllamaProperties properties) {
|
||||
return new OllamaClient(properties.getBaseUrl(), properties.getModel());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.autoconfigure.ollama;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(OllamaProperties.CONFIG_PREFIX)
|
||||
public class OllamaProperties {
|
||||
|
||||
public static final String CONFIG_PREFIX = "spring.ai.ollama";
|
||||
|
||||
/**
|
||||
* Base URL where Ollama API server is running.
|
||||
*/
|
||||
private String baseUrl = "http://localhost:11434";
|
||||
|
||||
/**
|
||||
* Language model to use.
|
||||
*/
|
||||
private String model = "llama2";
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration
|
||||
org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration
|
||||
org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration
|
||||
org.springframework.ai.autoconfigure.vectorstore.pgvector.PgVectorStoreAutoConfiguration
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.autoconfigure.ollama;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.ai.ollama.client.OllamaClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@Disabled("As it downloads the 3GB 'orca-mini' it can take couple of minutes to initialize.")
|
||||
@Testcontainers
|
||||
public class OllamaAutoConfigurationIT {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(OllamaAutoConfigurationIT.class);
|
||||
|
||||
@Container
|
||||
static GenericContainer<?> ollamaContainer = new GenericContainer<>("ollama/ollama").withExposedPorts(11434);
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withPropertyValues("spring.ai.ollama.baseUrl=http://localhost:" + ollamaContainer.getMappedPort(11434),
|
||||
"spring.ai.ollama.model=orca-mini")
|
||||
.withConfiguration(AutoConfigurations.of(OllamaAutoConfiguration.class));
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeAll() {
|
||||
logger.info("Start pulling the 'orca-mini' model (3GB) ... would take several minutes ...");
|
||||
new RestTemplate().postForLocation("http://localhost:{port}/api/pull",
|
||||
new HttpEntity<>(Map.of("name", "orca-mini")), ollamaContainer.getMappedPort(11434));
|
||||
logger.info("orca-mini pulling competed!");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generate() {
|
||||
contextRunner.run(context -> {
|
||||
OllamaClient client = context.getBean(OllamaClient.class);
|
||||
assertThat(client.getBaseUrl()).isEqualTo("http://localhost:" + ollamaContainer.getMappedPort(11434));
|
||||
assertThat(client.getModel()).isEqualTo("orca-mini");
|
||||
|
||||
String response = client.generate("Hello");
|
||||
|
||||
assertThat(response).isNotEmpty();
|
||||
logger.info("Response: " + response);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.autoconfigure.ollama;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.ollama.client.OllamaClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class OllamaAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(OllamaAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void defaults() {
|
||||
contextRunner.run(context -> {
|
||||
OllamaProperties properties = context.getBean(OllamaProperties.class);
|
||||
assertThat(properties.getBaseUrl()).isEqualTo("http://localhost:11434");
|
||||
assertThat(properties.getModel()).isEqualTo("llama2");
|
||||
|
||||
OllamaClient client = context.getBean(OllamaClient.class);
|
||||
assertThat(client.getBaseUrl()).isEqualTo("http://localhost:11434");
|
||||
assertThat(client.getModel()).isEqualTo("llama2");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void overrideProperties() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.ai.ollama.base-url=http://localhost:8080", "spring.ai.ollama.model=myModel")
|
||||
.run(context -> {
|
||||
OllamaProperties properties = context.getBean(OllamaProperties.class);
|
||||
assertThat(properties.getBaseUrl()).isEqualTo("http://localhost:8080");
|
||||
assertThat(properties.getModel()).isEqualTo("myModel");
|
||||
|
||||
OllamaClient client = context.getBean(OllamaClient.class);
|
||||
assertThat(client.getBaseUrl()).isEqualTo("http://localhost:8080");
|
||||
assertThat(client.getModel()).isEqualTo("myModel");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void customConfig() {
|
||||
contextRunner.withUserConfiguration(CustomConfig.class).run(context -> {
|
||||
OllamaClient ollamaClient = context.getBean(OllamaClient.class);
|
||||
assertThat(ollamaClient.getBaseUrl()).isEqualTo("http://localhost:8080");
|
||||
assertThat(ollamaClient.getModel()).isEqualTo("myModel");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomConfig {
|
||||
|
||||
@Bean
|
||||
OllamaClient myClient() {
|
||||
return new OllamaClient("http://localhost:8080", "myModel");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring AI Starter - Ollama</name>
|
||||
<description>Spring AI Ollama Auto Configuration</description>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
<connection>git://github.com/spring-projects-experimental/spring-ai.git</connection>
|
||||
<developerConnection>git@github.com:spring-projects-experimental/spring-ai.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- production dependencies -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-spring-boot-autoconfigure</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-ollama</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
Reference in New Issue
Block a user