diff --git a/build.sh b/build.sh new file mode 100644 index 00000000..264cdabf --- /dev/null +++ b/build.sh @@ -0,0 +1,8 @@ +#!/bin/bash +export JDK21_HOME=/usr/local/opt/openjdk/libexec/openjdk.jdk/Contents/Home +#export JDK17_HOME=/usr/local/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home +#export JDK11_HOME=/usr/local/opt/openjdk@11/libexec/openjdk.jdk/Contents/Home +#export JDK8_HOME=/usr/local/opt/openjdk@8/libexec/openjdk.jdk/Contents/Home + +export JAVA_HOME=$JDK21_HOME +mvn install -DskipTests -Denforcer.skip=true diff --git a/elasticsearch-example/pom.xml b/elasticsearch-example/pom.xml index d293ae5f..011de589 100644 --- a/elasticsearch-example/pom.xml +++ b/elasticsearch-example/pom.xml @@ -12,6 +12,7 @@ 8 8 UTF-8 + 2.22.1 @@ -34,6 +35,23 @@ 1.19.6 + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j2-impl + ${log4j.version} + + + + org.testcontainers + elasticsearch + 1.19.4 + \ No newline at end of file diff --git a/elasticsearch-example/src/main/java/ElasticsearchEmbeddingStoreExample.java b/elasticsearch-example/src/main/java/ElasticsearchEmbeddingStoreExample.java index 822e933f..ed695b65 100644 --- a/elasticsearch-example/src/main/java/ElasticsearchEmbeddingStoreExample.java +++ b/elasticsearch-example/src/main/java/ElasticsearchEmbeddingStoreExample.java @@ -5,20 +5,37 @@ import dev.langchain4j.store.embedding.EmbeddingMatch; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.elasticsearch.ElasticsearchEmbeddingStore; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.testcontainers.elasticsearch.ElasticsearchContainer; - +import org.testcontainers.utility.DockerImageName; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; public class ElasticsearchEmbeddingStoreExample { + private static final Logger LOGGER = LogManager.getLogger(ElasticsearchEmbeddingStoreExample.class); + + /** + * To run this example, ensure you have Elasticsearch running locally. If not, then: + * - Execute "docker pull docker.elastic.co/elasticsearch/elasticsearch:8.9.0" + * - Execute "docker run -d -p 9200:9200 -p 9300:9300 -e discovery.type=single-node -e xpack.security.enabled=false docker.elastic.co/elasticsearch/elasticsearch:8.9.0" + * - Wait until Elasticsearch is ready to serve (may take a few minutes) + */ + public static void main(String[] args) throws InterruptedException { + DockerImageName imageName = DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch:8.9.0"); + Map env = new HashMap<>(); + env.put("xpack.security.enabled", "false"); + env.put("discovery.type", "single-node"); - try (ElasticsearchContainer elastic = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.9.0") - .withEnv("xpack.security.enabled", "false")) { + try (ElasticsearchContainer elastic = new ElasticsearchContainer(imageName).withCertPath(null).withEnv(env)) { elastic.start(); EmbeddingStore embeddingStore = ElasticsearchEmbeddingStore.builder() - .serverUrl("http://" + elastic.getHttpHostAddress()) + .serverUrl(elastic.getHttpHostAddress()) .dimension(384) .build(); @@ -32,14 +49,19 @@ public static void main(String[] args) throws InterruptedException { Embedding embedding2 = embeddingModel.embed(segment2).content(); embeddingStore.add(embedding2, segment2); - Thread.sleep(1000); // to be sure that embeddings were persisted + // to be sure that embeddings were persisted + TimeUnit.MILLISECONDS.sleep(1000); Embedding queryEmbedding = embeddingModel.embed("What is your favourite sport?").content(); List> relevant = embeddingStore.findRelevant(queryEmbedding, 1); EmbeddingMatch embeddingMatch = relevant.get(0); - System.out.println(embeddingMatch.score()); // 0.81442887 - System.out.println(embeddingMatch.embedded().text()); // I like football. + // expected 0.8144288659095 + LOGGER.info("Score: {}", embeddingMatch.score()); + // expected "I like football." + LOGGER.info("Embedded: {}", embeddingMatch.embedded().text()); + } catch (Exception e) { + LOGGER.error("Error: {}", e.getMessage()); } } } diff --git a/elasticsearch-example/src/main/resources/log4j2.xml b/elasticsearch-example/src/main/resources/log4j2.xml new file mode 100644 index 00000000..345ffd41 --- /dev/null +++ b/elasticsearch-example/src/main/resources/log4j2.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/neo4j-example/pom.xml b/neo4j-example/pom.xml index f81fd3a7..815d50e9 100644 --- a/neo4j-example/pom.xml +++ b/neo4j-example/pom.xml @@ -12,6 +12,7 @@ 17 17 UTF-8 + 2.22.1 @@ -34,6 +35,23 @@ 1.19.6 + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j2-impl + ${log4j.version} + + + + org.testcontainers + neo4j + 1.19.4 + \ No newline at end of file diff --git a/neo4j-example/src/main/java/Neo4jEmbeddingStoreExample.java b/neo4j-example/src/main/java/Neo4jEmbeddingStoreExample.java index b428ad9a..e7105375 100644 --- a/neo4j-example/src/main/java/Neo4jEmbeddingStoreExample.java +++ b/neo4j-example/src/main/java/Neo4jEmbeddingStoreExample.java @@ -5,17 +5,35 @@ import dev.langchain4j.store.embedding.EmbeddingMatch; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.neo4j.Neo4jEmbeddingStore; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.GraphDatabase; import org.testcontainers.containers.Neo4jContainer; - +import org.testcontainers.utility.DockerImageName; import java.util.List; public class Neo4jEmbeddingStoreExample { + private static final Logger LOGGER = LogManager.getLogger(Neo4jEmbeddingStoreExample.class); + + /** + * To run this example, ensure you have Neo4j running locally, + * and change uri, username and password strings consistently. + * If not, then: + * - Execute "docker pull neo4j:latest" + * - Execute "docker run -d -p 7687:7687 --env NEO4J_AUTH=neo4j/password1234 neo4j:latest" + * - Wait until Neo4j is ready to serve (may take a few minutes) + */ + public static void main(String[] args) { - try (Neo4jContainer neo4j = new Neo4jContainer<>("neo4j:5")) { - neo4j.start(); + String username = "neo4j"; + String password = "password1234"; + + try (Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:latest"))) { + neo4jContainer.withEnv("NEO4J_AUTH", username + "/" + password).start(); EmbeddingStore embeddingStore = Neo4jEmbeddingStore.builder() - .withBasicAuth(neo4j.getBoltUrl(), "neo4j", neo4j.getAdminPassword()) + .driver(GraphDatabase.driver(neo4jContainer.getBoltUrl(), AuthTokens.basic(username, password))) .dimension(384) .build(); @@ -33,8 +51,11 @@ public static void main(String[] args) { List> relevant = embeddingStore.findRelevant(queryEmbedding, 1); EmbeddingMatch embeddingMatch = relevant.get(0); - System.out.println(embeddingMatch.score()); // 0.8144289255142212 - System.out.println(embeddingMatch.embedded().text()); // I like football. + LOGGER.info("Score: {}", embeddingMatch.score()); + // expected "I like football." + LOGGER.info("Embedded: {}", embeddingMatch.embedded().text()); + } catch (Exception e) { + LOGGER.error("Error: {}", e.getMessage()); } } } diff --git a/neo4j-example/src/main/resources/log4j2.xml b/neo4j-example/src/main/resources/log4j2.xml new file mode 100644 index 00000000..345ffd41 --- /dev/null +++ b/neo4j-example/src/main/resources/log4j2.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/ollama-examples/pom.xml b/ollama-examples/pom.xml index a47066a3..eb9c40e1 100644 --- a/ollama-examples/pom.xml +++ b/ollama-examples/pom.xml @@ -40,6 +40,7 @@ 5.10.0 + org.tinylog tinylog-impl diff --git a/ollama-examples/src/main/java/OllamaChatModelTest.java b/ollama-examples/src/main/java/OllamaChatModelTest.java index a179b6ac..20bce561 100644 --- a/ollama-examples/src/main/java/OllamaChatModelTest.java +++ b/ollama-examples/src/main/java/OllamaChatModelTest.java @@ -4,6 +4,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.tinylog.Logger; @Testcontainers class OllamaChatModelTest { @@ -21,40 +22,30 @@ class OllamaChatModelTest { * 2. Run "docker exec -it ollama ollama run mistral" <- specify the desired model here */ - static String MODEL_NAME = "orca-mini"; // try "mistral", "llama2", "codellama", "phi" or "tinyllama" - @Container - static GenericContainer ollama = new GenericContainer<>("langchain4j/ollama-" + MODEL_NAME + ":latest") - .withExposedPorts(11434); + private static GenericContainer ollama = new GenericContainer<>(OllamaContants.OLLAMA_IMAGE_NAME).withExposedPorts(OllamaContants.OLLAMA_PORT); @Test - void simple_example() { - + void example() { ChatLanguageModel model = OllamaChatModel.builder() - .baseUrl(baseUrl()) - .modelName(MODEL_NAME) + .baseUrl(baseUrl(ollama)) + .modelName(OllamaContants.MODEL_NAME) .build(); + simpleExample(model); + jsonOutputExample(model); + } + void simpleExample(ChatLanguageModel model) { String answer = model.generate("Provide 3 short bullet points explaining why Java is awesome"); - - System.out.println(answer); + Logger.info("Answer: {}", answer); } - @Test - void json_output_example() { - - ChatLanguageModel model = OllamaChatModel.builder() - .baseUrl(baseUrl()) - .modelName(MODEL_NAME) - .format("json") - .build(); - + void jsonOutputExample(ChatLanguageModel model) { String json = model.generate("Give me a JSON with 2 fields: name and age of a John Doe, 42"); - - System.out.println(json); + Logger.info("JSON: {}", json); } - static String baseUrl() { + static String baseUrl(GenericContainer ollama) { return String.format("http://%s:%d", ollama.getHost(), ollama.getFirstMappedPort()); } } diff --git a/ollama-examples/src/main/java/OllamaContants.java b/ollama-examples/src/main/java/OllamaContants.java new file mode 100644 index 00000000..7fd08ff8 --- /dev/null +++ b/ollama-examples/src/main/java/OllamaContants.java @@ -0,0 +1,14 @@ +public class OllamaContants { + + static final String MODEL_ORCA_MINI = "orca-mini"; + static final String MODEL_MISTRAL = "mistral"; + static final String MODEL_LLAMA2 = "llama2"; + static final String MODEL_CODE_LLAMA = "codellama"; + static final String MODEL_PHI = "phi"; + static final String MODEL_TINY_LLAMA = "tinyllama"; + + // try "mistral", "llama2", "codellama", "phi" or "tinyllama" + static final String MODEL_NAME = MODEL_ORCA_MINI; + public static final String OLLAMA_IMAGE_NAME = "langchain4j/ollama-" + MODEL_NAME + ":latest"; + public static final Integer OLLAMA_PORT = 11434; +} diff --git a/ollama-examples/src/main/java/OllamaStreamingChatModelTest.java b/ollama-examples/src/main/java/OllamaStreamingChatModelTest.java index 56a7f672..f3a2b19a 100644 --- a/ollama-examples/src/main/java/OllamaStreamingChatModelTest.java +++ b/ollama-examples/src/main/java/OllamaStreamingChatModelTest.java @@ -7,6 +7,7 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.tinylog.Logger; import java.util.concurrent.CompletableFuture; @@ -26,20 +27,15 @@ class OllamaStreamingChatModelTest { * 2. Run "docker exec -it ollama ollama run llama2" <- specify the desired model here */ - static String MODEL_NAME = "orca-mini"; // try "mistral", "llama2", "codellama" or "phi" - static String DOCKER_IMAGE_NAME = "langchain4j/ollama-" + MODEL_NAME + ":latest"; - static Integer PORT = 11434; - @Container - static GenericContainer ollama = new GenericContainer<>(DOCKER_IMAGE_NAME) - .withExposedPorts(PORT); + private static GenericContainer ollama = new GenericContainer<>(OllamaContants.OLLAMA_IMAGE_NAME).withExposedPorts(OllamaContants.OLLAMA_PORT); @Test - void streaming_example() { + void streamingExample() { StreamingChatLanguageModel model = OllamaStreamingChatModel.builder() - .baseUrl(String.format("http://%s:%d", ollama.getHost(), ollama.getMappedPort(PORT))) - .modelName(MODEL_NAME) + .baseUrl(String.format("http://%s:%d", ollama.getHost(), ollama.getMappedPort(OllamaContants.OLLAMA_PORT))) + .modelName(OllamaContants.MODEL_NAME) .temperature(0.0) .build(); @@ -50,7 +46,7 @@ void streaming_example() { @Override public void onNext(String token) { - System.out.print(token); + Logger.info("Token: {}", token); } @Override diff --git a/ollama-examples/src/main/resources/tinylog.properties b/ollama-examples/src/main/resources/tinylog.properties new file mode 100644 index 00000000..cc12392d --- /dev/null +++ b/ollama-examples/src/main/resources/tinylog.properties @@ -0,0 +1,4 @@ +#https://tinylog.org/v2/configuration/ +writer = console +writer.level = info +writer.format = {date: HH:mm:ss.SSS} {{level}:|min-size=8} {message} \ No newline at end of file diff --git a/open-ai-examples/src/main/java/OpenAiImageModelExamples.java b/open-ai-examples/src/main/java/OpenAiImageModelExamples.java index 6c1036cd..a5492594 100644 --- a/open-ai-examples/src/main/java/OpenAiImageModelExamples.java +++ b/open-ai-examples/src/main/java/OpenAiImageModelExamples.java @@ -11,7 +11,7 @@ import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.model.openai.OpenAiImageModel; import dev.langchain4j.model.output.Response; -import dev.langchain4j.retriever.EmbeddingStoreRetriever; +import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; @@ -76,7 +76,7 @@ public static void main(String[] args) throws URISyntaxException { ConversationalRetrievalChain chain = ConversationalRetrievalChain .builder() .chatLanguageModel(OpenAiChatModel.builder().apiKey(System.getenv("OPENAI_API_KEY")).build()) - .retriever(EmbeddingStoreRetriever.from(embeddingStore, embeddingModel)) + .contentRetriever(new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel)) .build(); PromptTemplate drawPromptTemplate = PromptTemplate.from( diff --git a/pom.xml b/pom.xml index 869f13a4..a60c8cbc 100644 --- a/pom.xml +++ b/pom.xml @@ -36,5 +36,4 @@ javafx-example quarkus-example - \ No newline at end of file diff --git a/redis-example/pom.xml b/redis-example/pom.xml index e16dd4d3..40531d52 100644 --- a/redis-example/pom.xml +++ b/redis-example/pom.xml @@ -12,10 +12,10 @@ 8 8 UTF-8 + 2.22.1 - dev.langchain4j langchain4j-redis @@ -28,16 +28,22 @@ 0.30.0 + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + - org.slf4j - slf4j-simple - 2.0.7 + org.apache.logging.log4j + log4j-slf4j2-impl + ${log4j.version} - com.redis.testcontainers + com.redis testcontainers-redis - 1.6.4 + 2.0.1 @@ -47,5 +53,4 @@ - \ No newline at end of file diff --git a/redis-example/src/main/java/RedisEmbeddingStoreExample.java b/redis-example/src/main/java/RedisEmbeddingStoreExample.java index c3c8b2a8..d183081e 100644 --- a/redis-example/src/main/java/RedisEmbeddingStoreExample.java +++ b/redis-example/src/main/java/RedisEmbeddingStoreExample.java @@ -1,4 +1,5 @@ -import com.redis.testcontainers.RedisStackContainer; + +import com.redis.testcontainers.RedisContainer; import dev.langchain4j.data.embedding.Embedding; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel; @@ -6,42 +7,45 @@ import dev.langchain4j.store.embedding.EmbeddingMatch; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.redis.RedisEmbeddingStore; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.testcontainers.utility.DockerImageName; import java.util.List; -import static com.redis.testcontainers.RedisStackContainer.DEFAULT_IMAGE_NAME; -import static com.redis.testcontainers.RedisStackContainer.DEFAULT_TAG; public class RedisEmbeddingStoreExample { + private static final Logger LOGGER = LogManager.getLogger(RedisEmbeddingStoreExample.class); public static void main(String[] args) { - - RedisStackContainer redis = new RedisStackContainer(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG)); - redis.start(); - - EmbeddingStore embeddingStore = RedisEmbeddingStore.builder() - .host(redis.getHost()) - .port(redis.getFirstMappedPort()) - .dimension(384) - .build(); - - EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel(); - - TextSegment segment1 = TextSegment.from("I like football."); - Embedding embedding1 = embeddingModel.embed(segment1).content(); - embeddingStore.add(embedding1, segment1); - - TextSegment segment2 = TextSegment.from("The weather is good today."); - Embedding embedding2 = embeddingModel.embed(segment2).content(); - embeddingStore.add(embedding2, segment2); - - Embedding queryEmbedding = embeddingModel.embed("What is your favourite sport?").content(); - List> relevant = embeddingStore.findRelevant(queryEmbedding, 1); - EmbeddingMatch embeddingMatch = relevant.get(0); - - System.out.println(embeddingMatch.score()); // 0.8144288659095 - System.out.println(embeddingMatch.embedded().text()); // I like football. - - redis.stop(); + try (RedisContainer redis = new RedisContainer(DockerImageName.parse("redis/redis-stack-server:latest"))) { + redis.start(); + + EmbeddingStore embeddingStore = RedisEmbeddingStore.builder() + .host(redis.getHost()) + .port(redis.getFirstMappedPort()) + .dimension(384) + .build(); + + EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel(); + + TextSegment segment1 = TextSegment.from("I like football."); + Embedding embedding1 = embeddingModel.embed(segment1).content(); + embeddingStore.add(embedding1, segment1); + + TextSegment segment2 = TextSegment.from("The weather is good today."); + Embedding embedding2 = embeddingModel.embed(segment2).content(); + embeddingStore.add(embedding2, segment2); + + Embedding queryEmbedding = embeddingModel.embed("What is your favourite sport?").content(); + List> relevant = embeddingStore.findRelevant(queryEmbedding, 1); + EmbeddingMatch embeddingMatch = relevant.get(0); + // expected 0.8144288659095 + LOGGER.info("Score: {}", embeddingMatch.score()); + // expected "I like football." + LOGGER.info("Embedded: {}", embeddingMatch.embedded().text()); + } catch (Exception e) { + LOGGER.error("Error: {}", e.getMessage()); + } } } diff --git a/redis-example/src/main/resources/log4j2.xml b/redis-example/src/main/resources/log4j2.xml new file mode 100644 index 00000000..345ffd41 --- /dev/null +++ b/redis-example/src/main/resources/log4j2.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file