From ec5c3ed461933209a322ed8f966d5fe5fb603664 Mon Sep 17 00:00:00 2001 From: pablo_silberkasten Date: Mon, 2 Sep 2024 15:47:33 -0700 Subject: [PATCH 01/28] Simple Oracle Embedding Store Example --- oracle-example/pom.xml | 46 ++++++++++++ .../java/OracleEmbeddingStoreExample.java | 73 +++++++++++++++++++ pom.xml | 3 +- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 oracle-example/pom.xml create mode 100644 oracle-example/src/main/java/OracleEmbeddingStoreExample.java diff --git a/oracle-example/pom.xml b/oracle-example/pom.xml new file mode 100644 index 00000000..b6e6495b --- /dev/null +++ b/oracle-example/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + dev.langchain4j + oracle-example + 0.34.0-SNAPSHOT + + + 8 + 8 + UTF-8 + 23.5.0.24.07 + + + + + + com.oracle.database.jdbc + ucp + ${jdbc.version} + + + + dev.langchain4j + langchain4j-oracle + 0.34.0-SNAPSHOT + + + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2-q + 0.33.0 + + + + org.testcontainers + oracle-free + 1.20.0 + + + + + \ No newline at end of file diff --git a/oracle-example/src/main/java/OracleEmbeddingStoreExample.java b/oracle-example/src/main/java/OracleEmbeddingStoreExample.java new file mode 100644 index 00000000..0c2e4087 --- /dev/null +++ b/oracle-example/src/main/java/OracleEmbeddingStoreExample.java @@ -0,0 +1,73 @@ +import dev.langchain4j.data.embedding.Embedding; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.embedding.onnx.allminilml6v2q.AllMiniLmL6V2QuantizedEmbeddingModel; +import dev.langchain4j.rag.content.Content; +import dev.langchain4j.rag.content.retriever.ContentRetriever; +import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; +import dev.langchain4j.rag.query.Query; +import dev.langchain4j.store.embedding.EmbeddingStore; +import dev.langchain4j.store.embedding.oracle.CreateOption; +import dev.langchain4j.store.embedding.oracle.OracleEmbeddingStore; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; +import org.testcontainers.oracle.OracleContainer; + +import java.sql.SQLException; + +public class OracleEmbeddingStoreExample { + + public static void main(String[] args) throws SQLException { + + PoolDataSource dataSource = PoolDataSourceFactory.getPoolDataSource(); + dataSource.setConnectionFactoryClassName( + "oracle.jdbc.datasource.impl.OracleDataSource"); + String urlFromEnv = System.getenv("ORACLE_JDBC_URL"); + + if (urlFromEnv == null) { + OracleContainer oracleContainer = new OracleContainer( + "gvenzl/oracle-free:23.4-slim-faststart") + .withDatabaseName("pdb1") + .withUsername("testuser") + .withPassword("testpwd"); + oracleContainer.start(); + + dataSource.setURL(oracleContainer.getJdbcUrl()); + dataSource.setUser(oracleContainer.getUsername()); + dataSource.setPassword(oracleContainer.getPassword()); + } else { + dataSource.setURL(urlFromEnv); + dataSource.setUser(System.getenv("ORACLE_JDBC_USER")); + dataSource.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + } + + EmbeddingStore embeddingStore = OracleEmbeddingStore.builder() + .dataSource(dataSource) + .embeddingTable("test_content_retriever", + CreateOption.CREATE_OR_REPLACE) + .build(); + + EmbeddingModel embeddingModel = new AllMiniLmL6V2QuantizedEmbeddingModel(); + + ContentRetriever retriever = EmbeddingStoreContentRetriever.builder() + .embeddingStore(embeddingStore) + .embeddingModel(embeddingModel) + .maxResults(2) + .minScore(0.5) + .build(); + + TextSegment segment1 = TextSegment.from("I like soccer."); + Embedding embedding1 = embeddingModel.embed(segment1).content(); + embeddingStore.add(embedding1, segment1); + + TextSegment segment2 = TextSegment.from("I love Stephen King."); + Embedding embedding2 = embeddingModel.embed(segment2).content(); + embeddingStore.add(embedding2, segment2); + + Content match = retriever + .retrieve(Query.from("What is your favourite sport?")) + .get(0); + + System.out.println(match.textSegment()); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index b389155e..c0edbf25 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ weaviate-example javafx-example quarkus-example + oracle-example - \ No newline at end of file + From e47970a59de0e568b36ccbdc745b9d384397c3f4 Mon Sep 17 00:00:00 2001 From: pablo_silberkasten Date: Mon, 2 Sep 2024 15:59:13 -0700 Subject: [PATCH 02/28] Simple Example #2 --- oracle-example/src/main/java/OracleEmbeddingStoreExample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oracle-example/src/main/java/OracleEmbeddingStoreExample.java b/oracle-example/src/main/java/OracleEmbeddingStoreExample.java index 0c2e4087..fe49a294 100644 --- a/oracle-example/src/main/java/OracleEmbeddingStoreExample.java +++ b/oracle-example/src/main/java/OracleEmbeddingStoreExample.java @@ -31,10 +31,10 @@ public static void main(String[] args) throws SQLException { .withUsername("testuser") .withPassword("testpwd"); oracleContainer.start(); - dataSource.setURL(oracleContainer.getJdbcUrl()); dataSource.setUser(oracleContainer.getUsername()); dataSource.setPassword(oracleContainer.getPassword()); + } else { dataSource.setURL(urlFromEnv); dataSource.setUser(System.getenv("ORACLE_JDBC_USER")); From 549f53406abf909b728bbd9853a06e33f2bfa38f Mon Sep 17 00:00:00 2001 From: LangChain4j Date: Thu, 5 Sep 2024 17:46:06 +0200 Subject: [PATCH 03/28] Update oracle-example/pom.xml --- oracle-example/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oracle-example/pom.xml b/oracle-example/pom.xml index b6e6495b..af9b3306 100644 --- a/oracle-example/pom.xml +++ b/oracle-example/pom.xml @@ -6,7 +6,7 @@ dev.langchain4j oracle-example - 0.34.0-SNAPSHOT + 0.34.0 8 From ef1a9716bb883fc27a3f4587756df4a86c9731a1 Mon Sep 17 00:00:00 2001 From: LangChain4j Date: Thu, 5 Sep 2024 17:46:13 +0200 Subject: [PATCH 04/28] Update oracle-example/pom.xml --- oracle-example/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oracle-example/pom.xml b/oracle-example/pom.xml index af9b3306..e6340e73 100644 --- a/oracle-example/pom.xml +++ b/oracle-example/pom.xml @@ -26,7 +26,7 @@ dev.langchain4j langchain4j-oracle - 0.34.0-SNAPSHOT + 0.34.0 From 1f3dbf88555709b3c60157d876c61a190d9af6da Mon Sep 17 00:00:00 2001 From: LangChain4j Date: Thu, 5 Sep 2024 17:46:18 +0200 Subject: [PATCH 05/28] Update oracle-example/pom.xml --- oracle-example/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oracle-example/pom.xml b/oracle-example/pom.xml index e6340e73..26ad5feb 100644 --- a/oracle-example/pom.xml +++ b/oracle-example/pom.xml @@ -32,7 +32,7 @@ dev.langchain4j langchain4j-embeddings-all-minilm-l6-v2-q - 0.33.0 + 0.34.0 From 3350d3fe04a776d056b471fc2c82938dfdff7be4 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Thu, 10 Apr 2025 13:58:02 -0400 Subject: [PATCH 06/28] Add examples --- .../java/OracleDocumentLoaderExample.java | 39 +++++++++++++++++++ .../java/OracleDocumentSplitterExample.java | 35 +++++++++++++++++ .../java/OracleEmbeddingModelExample.java | 28 +++++++++++++ .../OracleSummaryLanguageModelExample.java | 34 ++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 oracle-example/src/main/java/OracleDocumentLoaderExample.java create mode 100644 oracle-example/src/main/java/OracleDocumentSplitterExample.java create mode 100644 oracle-example/src/main/java/OracleEmbeddingModelExample.java create mode 100644 oracle-example/src/main/java/OracleSummaryLanguageModelExample.java diff --git a/oracle-example/src/main/java/OracleDocumentLoaderExample.java b/oracle-example/src/main/java/OracleDocumentLoaderExample.java new file mode 100644 index 00000000..bc774b76 --- /dev/null +++ b/oracle-example/src/main/java/OracleDocumentLoaderExample.java @@ -0,0 +1,39 @@ +import com.fasterxml.jackson.databind.ObjectMapper; +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.loader.oracle.FilePreference; +import dev.langchain4j.data.document.loader.oracle.OracleDocumentLoader; +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + +public class OracleDocumentLoaderExample { + + public static void main(String[] args) throws SQLException, IOException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + // Can build pref as a string + // String pref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; + + // Alternatively, can use FilePreference, DirectoryPreference, or TablePreference + ObjectMapper mapper = new ObjectMapper(); + FilePreference loaderPref = new FilePreference(); + loaderPref.setFilename(System.getenv("DEMO_FILE")); + String pref = mapper.writeValueAsString(loaderPref); + + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + + List docs = loader.loadDocuments(pref); + for (Document doc : docs) { + System.out.println("metadata=" + doc.metadata()); + System.out.println("text=" + doc.text()); + } + } +} diff --git a/oracle-example/src/main/java/OracleDocumentSplitterExample.java b/oracle-example/src/main/java/OracleDocumentSplitterExample.java new file mode 100644 index 00000000..34ffe3fa --- /dev/null +++ b/oracle-example/src/main/java/OracleDocumentSplitterExample.java @@ -0,0 +1,35 @@ +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.loader.oracle.OracleDocumentLoader; +import dev.langchain4j.data.document.splitter.oracle.OracleDocumentSplitter; +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + +public class OracleDocumentSplitterExample { + + public static void main(String[] args) throws SQLException, IOException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + String loadPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; + String splitPref = "{\"by\": \"chars\", \"max\": 50}"; + + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitPref); + + List docs = loader.loadDocuments(loadPref); + for (Document doc : docs) { + String[] chunks = splitter.split(doc.text()); + for (String chunk : chunks) { + System.out.println("chunk=" + chunk); + } + } + } +} diff --git a/oracle-example/src/main/java/OracleEmbeddingModelExample.java b/oracle-example/src/main/java/OracleEmbeddingModelExample.java new file mode 100644 index 00000000..93ca0ce3 --- /dev/null +++ b/oracle-example/src/main/java/OracleEmbeddingModelExample.java @@ -0,0 +1,28 @@ +import dev.langchain4j.data.embedding.Embedding; +import dev.langchain4j.model.oracle.OracleEmbeddingModel; +import dev.langchain4j.model.output.Response; +import java.sql.Connection; +import java.sql.SQLException; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + +public class OracleEmbeddingModelExample { + + public static void main(String[] args) throws SQLException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + String pref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; + + OracleEmbeddingModel model = new OracleEmbeddingModel(conn, pref); + + Response response = model.embed("I love Java"); + Embedding embedding = response.content(); + + System.out.println(embedding); + } +} diff --git a/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java b/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java new file mode 100644 index 00000000..fc7f2ac8 --- /dev/null +++ b/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java @@ -0,0 +1,34 @@ +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.loader.oracle.OracleDocumentLoader; +import dev.langchain4j.model.oracle.OracleSummaryLanguageModel; +import dev.langchain4j.model.output.Response; +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + +public class OracleSummaryLanguageModelExample { + + public static void main(String[] args) throws SQLException, IOException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + String loadPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; + String summaryPref = "{\"provider\": \"database\", \"gLevel\": \"S\"}"; + + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + OracleSummaryLanguageModel model = new OracleSummaryLanguageModel(conn, summaryPref); + + List docs = loader.loadDocuments(loadPref); + for (Document doc : docs) { + Response resp = model.generate(doc.text()); + System.out.println("summary=" + resp.content()); + } + } +} From 989295841618d6a0e11fe2d15864089e7fae9403 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Thu, 10 Apr 2025 13:58:18 -0400 Subject: [PATCH 07/28] Update version --- oracle-example/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oracle-example/pom.xml b/oracle-example/pom.xml index 26ad5feb..ab3b2b29 100644 --- a/oracle-example/pom.xml +++ b/oracle-example/pom.xml @@ -26,7 +26,7 @@ dev.langchain4j langchain4j-oracle - 0.34.0 + 1.0.0-alpha2-SNAPSHOT From a1abcd4b01de056bbbea25d10fb7277092a7ea30 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Wed, 16 Apr 2025 15:41:29 -0400 Subject: [PATCH 08/28] Add loadOnnxModel example --- .../src/main/java/OracleEmbeddingModelExample.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/oracle-example/src/main/java/OracleEmbeddingModelExample.java b/oracle-example/src/main/java/OracleEmbeddingModelExample.java index 93ca0ce3..8a0f32ae 100644 --- a/oracle-example/src/main/java/OracleEmbeddingModelExample.java +++ b/oracle-example/src/main/java/OracleEmbeddingModelExample.java @@ -16,6 +16,15 @@ public static void main(String[] args) throws SQLException { pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); Connection conn = pds.getConnection(); + // load an ONNX model into the database + // remember to create a directory alias with + // create or replace directory MODEL_DIR as '/path/to/model'; + OracleEmbeddingModel.loadOnnxModel( + conn, + System.getenv("DEMO_ONNX_DIR"), + System.getenv("DEMO_ONNX_FILE"), + System.getenv("DEMO_ONNX_MODEL")); + String pref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; OracleEmbeddingModel model = new OracleEmbeddingModel(conn, pref); From 2b66a3a81c41db8a0615eb5e759bb3a28dba7edd Mon Sep 17 00:00:00 2001 From: David Jiang Date: Fri, 25 Apr 2025 13:39:40 -0400 Subject: [PATCH 09/28] Add comments --- .../main/java/OracleDocumentLoaderExample.java | 7 +++++++ .../main/java/OracleDocumentSplitterExample.java | 7 ++++++- .../main/java/OracleEmbeddingModelExample.java | 16 +++++++++++++++- .../java/OracleSummaryLanguageModelExample.java | 5 +++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/oracle-example/src/main/java/OracleDocumentLoaderExample.java b/oracle-example/src/main/java/OracleDocumentLoaderExample.java index bc774b76..8f9d3616 100644 --- a/oracle-example/src/main/java/OracleDocumentLoaderExample.java +++ b/oracle-example/src/main/java/OracleDocumentLoaderExample.java @@ -9,6 +9,13 @@ import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; +/** + * Demonstrate loading a document from the file system. + * The documents can be in any format supported by the Oracle Text filter + * including Word, PDF, HTML, and text files. If it is a rich text document + * like Word or PDF, it will be converted into plain text and + * contain any metadata associated with it. + */ public class OracleDocumentLoaderExample { public static void main(String[] args) throws SQLException, IOException { diff --git a/oracle-example/src/main/java/OracleDocumentSplitterExample.java b/oracle-example/src/main/java/OracleDocumentSplitterExample.java index 34ffe3fa..a9646e6f 100644 --- a/oracle-example/src/main/java/OracleDocumentSplitterExample.java +++ b/oracle-example/src/main/java/OracleDocumentSplitterExample.java @@ -8,6 +8,11 @@ import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; +/** + * Demonstrate chunking or splitting text in a document. You can customize + * how to split the content such as by words, characters, or vocabulary + * (for tokens) to match a tokenizer in the preference. + */ public class OracleDocumentSplitterExample { public static void main(String[] args) throws SQLException, IOException { @@ -19,7 +24,7 @@ public static void main(String[] args) throws SQLException, IOException { Connection conn = pds.getConnection(); String loadPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; - String splitPref = "{\"by\": \"chars\", \"max\": 50}"; + String splitPref = "{\"by\": \"words\", \"max\": 200}"; OracleDocumentLoader loader = new OracleDocumentLoader(conn); OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitPref); diff --git a/oracle-example/src/main/java/OracleEmbeddingModelExample.java b/oracle-example/src/main/java/OracleEmbeddingModelExample.java index 8a0f32ae..2ec39db8 100644 --- a/oracle-example/src/main/java/OracleEmbeddingModelExample.java +++ b/oracle-example/src/main/java/OracleEmbeddingModelExample.java @@ -1,11 +1,17 @@ import dev.langchain4j.data.embedding.Embedding; +import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.oracle.OracleEmbeddingModel; import dev.langchain4j.model.output.Response; import java.sql.Connection; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; +/** + * Demonstrate getting the vector embeddings with an ONNX model. + */ public class OracleEmbeddingModelExample { public static void main(String[] args) throws SQLException { @@ -29,9 +35,17 @@ public static void main(String[] args) throws SQLException { OracleEmbeddingModel model = new OracleEmbeddingModel(conn, pref); + // embed a single string Response response = model.embed("I love Java"); Embedding embedding = response.content(); - System.out.println(embedding); + + // embed a list of text + List textSegments = new ArrayList<>(); + textSegments.add(TextSegment.from("hello world")); + textSegments.add(TextSegment.from("goodbye world")); + textSegments.add(TextSegment.from("1,2,3")); + Response> resp = model.embedAll(textSegments); + System.out.println(resp.content()); } } diff --git a/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java b/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java index fc7f2ac8..f61094f8 100644 --- a/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java +++ b/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java @@ -9,6 +9,11 @@ import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; +/** + * Demonstrate summarizing a document. You can customize which provider to use + * such as database for extracting a summary with Oracle Text or a third-party + * provider via a REST call. + */ public class OracleSummaryLanguageModelExample { public static void main(String[] args) throws SQLException, IOException { From e20467c9b3a4b3f5d55863f7f528900b0067620c Mon Sep 17 00:00:00 2001 From: David Jiang Date: Fri, 25 Apr 2025 13:40:11 -0400 Subject: [PATCH 10/28] Add ingest example --- .../src/main/java/OracleIngestExample.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 oracle-example/src/main/java/OracleIngestExample.java diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java new file mode 100644 index 00000000..ad005c30 --- /dev/null +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -0,0 +1,103 @@ +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.loader.oracle.OracleDocumentLoader; +import dev.langchain4j.data.document.splitter.oracle.OracleDocumentSplitter; +import dev.langchain4j.data.embedding.Embedding; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.oracle.OracleEmbeddingModel; +import dev.langchain4j.store.embedding.EmbeddingMatch; +import dev.langchain4j.store.embedding.EmbeddingSearchRequest; +import dev.langchain4j.store.embedding.EmbeddingSearchResult; +import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; +import static dev.langchain4j.store.embedding.oracle.CreateOption.CREATE_OR_REPLACE; +import dev.langchain4j.store.embedding.oracle.EmbeddingTable; +import dev.langchain4j.store.embedding.oracle.OracleEmbeddingStore; +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + +/** + * Demonstrate using the OracleEmbeddingStore with the following + * components to ingest the documents and perform a vector search. + * OracleDocumentLoader to load the documents + * OracleDocumentSplitter to split the text + * OracleEmbeddingModel to get the vector embeddings + * OracleEmbeddingStore to store the vector embeddings + */ +public class OracleIngestExample { + + public static void main(String[] args) throws SQLException, IOException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; + String splitterPref = "{\"by\": \"chars\", \"max\": 50}"; + + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + OracleEmbeddingModel embedder = new OracleEmbeddingModel(conn, embedderPref); + OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); + + // column names for the output table + String tableName = "TEST"; + String idColumn = "ID"; + String embeddingColumn = "EMBEDDING"; + String textColumn = "TEXT"; + String metadataColumn = "METADATA"; + + // The call to build() should create a table with the configured names + OracleEmbeddingStore embeddingStore = OracleEmbeddingStore.builder() + .dataSource(pds) + .embeddingTable(EmbeddingTable.builder() + .createOption(CREATE_OR_REPLACE) + .name(tableName) + .idColumn(idColumn) + .embeddingColumn(embeddingColumn) + .textColumn(textColumn) + .metadataColumn(metadataColumn) + .build()) + .build(); + + OracleEmbeddingModel.loadOnnxModel( + conn, + System.getenv("DEMO_ONNX_DIR"), + System.getenv("DEMO_ONNX_FILE"), + System.getenv("DEMO_ONNX_MODEL")); + + String loaderPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; + List docs = loader.loadDocuments(loaderPref); + + // ingest the documents with the following components + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(splitter) + .embeddingModel(embedder) + .embeddingStore(embeddingStore) + .build(); + ingestor.ingest(docs); + + // get the question from the user + String question = "What is a database?"; + + // get the vector representation + Embedding questionAsVector = embedder.embed(question).content(); + + // perform the vector search + EmbeddingSearchResult result = embeddingStore.search( + EmbeddingSearchRequest.builder() + .queryEmbedding(questionAsVector) + .build() + ); + + // display the results + List> results = result.matches(); + for (EmbeddingMatch match : results) { + System.out.println("Score: " + match.score()); + System.out.println("Text Segment: " + match.embedded().text()); + } + } +} From 5171b6071ac7a97f241f97c483db8e2d577d27df Mon Sep 17 00:00:00 2001 From: David Jiang Date: Wed, 30 Apr 2025 11:36:48 -0400 Subject: [PATCH 11/28] Formatting and comments --- .../src/main/java/OracleDocumentLoaderExample.java | 10 ++++------ .../src/main/java/OracleDocumentSplitterExample.java | 6 +++--- .../src/main/java/OracleEmbeddingModelExample.java | 5 +++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/oracle-example/src/main/java/OracleDocumentLoaderExample.java b/oracle-example/src/main/java/OracleDocumentLoaderExample.java index 8f9d3616..8d86324b 100644 --- a/oracle-example/src/main/java/OracleDocumentLoaderExample.java +++ b/oracle-example/src/main/java/OracleDocumentLoaderExample.java @@ -10,11 +10,10 @@ import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate loading a document from the file system. - * The documents can be in any format supported by the Oracle Text filter - * including Word, PDF, HTML, and text files. If it is a rich text document - * like Word or PDF, it will be converted into plain text and - * contain any metadata associated with it. + * Demonstrate loading a document from the file system. The documents can be in + * any format supported by the Oracle Text filter including Word, PDF, HTML, and + * text files. If it is a rich text document like Word or PDF, it will be + * converted into plain text and contain any metadata associated with it. */ public class OracleDocumentLoaderExample { @@ -28,7 +27,6 @@ public static void main(String[] args) throws SQLException, IOException { // Can build pref as a string // String pref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; - // Alternatively, can use FilePreference, DirectoryPreference, or TablePreference ObjectMapper mapper = new ObjectMapper(); FilePreference loaderPref = new FilePreference(); diff --git a/oracle-example/src/main/java/OracleDocumentSplitterExample.java b/oracle-example/src/main/java/OracleDocumentSplitterExample.java index a9646e6f..8a5c1202 100644 --- a/oracle-example/src/main/java/OracleDocumentSplitterExample.java +++ b/oracle-example/src/main/java/OracleDocumentSplitterExample.java @@ -9,9 +9,9 @@ import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate chunking or splitting text in a document. You can customize - * how to split the content such as by words, characters, or vocabulary - * (for tokens) to match a tokenizer in the preference. + * Demonstrate chunking or splitting text in a document. You can customize how + * to split the content such as by words, characters, or vocabulary (for tokens) + * to match a tokenizer in the preference. */ public class OracleDocumentSplitterExample { diff --git a/oracle-example/src/main/java/OracleEmbeddingModelExample.java b/oracle-example/src/main/java/OracleEmbeddingModelExample.java index 2ec39db8..4d4726b9 100644 --- a/oracle-example/src/main/java/OracleEmbeddingModelExample.java +++ b/oracle-example/src/main/java/OracleEmbeddingModelExample.java @@ -10,7 +10,8 @@ import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate getting the vector embeddings with an ONNX model. + * Demonstrate getting the vector embeddings with an ONNX model located on the + * server. */ public class OracleEmbeddingModelExample { @@ -39,7 +40,7 @@ public static void main(String[] args) throws SQLException { Response response = model.embed("I love Java"); Embedding embedding = response.content(); System.out.println(embedding); - + // embed a list of text List textSegments = new ArrayList<>(); textSegments.add(TextSegment.from("hello world")); From 0b54bdbc686c3013e5018e7b7d773cb81215cb67 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Wed, 30 Apr 2025 11:38:25 -0400 Subject: [PATCH 12/28] Group preferences together --- .../src/main/java/OracleIngestExample.java | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index ad005c30..f38b77ba 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -19,11 +19,13 @@ import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate using the OracleEmbeddingStore with the following - * components to ingest the documents and perform a vector search. + * Demonstrate using the OracleEmbeddingStore to ingest documents into a vector + * store and then perform a vector search. + * + * The following components are used: * OracleDocumentLoader to load the documents * OracleDocumentSplitter to split the text - * OracleEmbeddingModel to get the vector embeddings + * OracleEmbeddingModel to get the vector embeddings * OracleEmbeddingStore to store the vector embeddings */ public class OracleIngestExample { @@ -36,21 +38,34 @@ public static void main(String[] args) throws SQLException, IOException { pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); Connection conn = pds.getConnection(); - String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; + // set the loader, splitter, and embedding preferences + String loaderPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; String splitterPref = "{\"by\": \"chars\", \"max\": 50}"; + String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; OracleDocumentLoader loader = new OracleDocumentLoader(conn); - OracleEmbeddingModel embedder = new OracleEmbeddingModel(conn, embedderPref); OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); + OracleEmbeddingModel embedder = new OracleEmbeddingModel(conn, embedderPref); + + // load the ONNX model for embedding + OracleEmbeddingModel.loadOnnxModel( + conn, + System.getenv("DEMO_ONNX_DIR"), + System.getenv("DEMO_ONNX_FILE"), + System.getenv("DEMO_ONNX_MODEL")); - // column names for the output table + // load the document + List docs = loader.loadDocuments(loaderPref); + + // set column names for the output table String tableName = "TEST"; String idColumn = "ID"; String embeddingColumn = "EMBEDDING"; String textColumn = "TEXT"; String metadataColumn = "METADATA"; - // The call to build() should create a table with the configured names + // setup the embedding store + // build() should create a table with the configured names OracleEmbeddingStore embeddingStore = OracleEmbeddingStore.builder() .dataSource(pds) .embeddingTable(EmbeddingTable.builder() @@ -63,15 +78,6 @@ public static void main(String[] args) throws SQLException, IOException { .build()) .build(); - OracleEmbeddingModel.loadOnnxModel( - conn, - System.getenv("DEMO_ONNX_DIR"), - System.getenv("DEMO_ONNX_FILE"), - System.getenv("DEMO_ONNX_MODEL")); - - String loaderPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; - List docs = loader.loadDocuments(loaderPref); - // ingest the documents with the following components EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(splitter) @@ -79,8 +85,8 @@ public static void main(String[] args) throws SQLException, IOException { .embeddingStore(embeddingStore) .build(); ingestor.ingest(docs); - - // get the question from the user + + // get the question String question = "What is a database?"; // get the vector representation From 26230c40bca3bdf0f74e0d6d044a1d0a649bd369 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Mon, 5 May 2025 18:22:33 -0400 Subject: [PATCH 13/28] Display chunks, prompt user --- .../src/main/java/OracleIngestExample.java | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index f38b77ba..5f57a903 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -13,8 +13,11 @@ import dev.langchain4j.store.embedding.oracle.OracleEmbeddingStore; import java.io.IOException; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; +import java.util.Scanner; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; @@ -25,7 +28,7 @@ * The following components are used: * OracleDocumentLoader to load the documents * OracleDocumentSplitter to split the text - * OracleEmbeddingModel to get the vector embeddings + * OracleEmbeddingModel to get the vector embeddings * OracleEmbeddingStore to store the vector embeddings */ public class OracleIngestExample { @@ -40,7 +43,7 @@ public static void main(String[] args) throws SQLException, IOException { // set the loader, splitter, and embedding preferences String loaderPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; - String splitterPref = "{\"by\": \"chars\", \"max\": 50}"; + String splitterPref = "{\"by\": \"chars\", \"max\": 200}"; String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; OracleDocumentLoader loader = new OracleDocumentLoader(conn); @@ -86,24 +89,50 @@ public static void main(String[] args) throws SQLException, IOException { .build(); ingestor.ingest(docs); - // get the question - String question = "What is a database?"; + // check the chunks + System.out.println("chunks inserted:"); + String queryEmbeddingStore = "select * from %s".formatted(tableName); + try (PreparedStatement stmt = conn.prepareStatement(queryEmbeddingStore)) { + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + String id = rs.getString(idColumn); + String text = rs.getString(textColumn); - // get the vector representation - Embedding questionAsVector = embedder.embed(question).content(); + String ending = text.length() > 50 ? "..." : ""; + System.out.println(id + "\t" + text.substring(0, 50) + ending); + } + } + } + + Scanner scanner = new Scanner(System.in); + + while (true) { + System.out.println("\nEnter a query or type 'exit' to quit:"); - // perform the vector search - EmbeddingSearchResult result = embeddingStore.search( - EmbeddingSearchRequest.builder() - .queryEmbedding(questionAsVector) - .build() - ); + // get the query + String query = scanner.nextLine(); + if (query.equalsIgnoreCase("exit")) { + break; + } - // display the results - List> results = result.matches(); - for (EmbeddingMatch match : results) { - System.out.println("Score: " + match.score()); - System.out.println("Text Segment: " + match.embedded().text()); + // get the vector representation + Embedding queryAsVector = embedder.embed(query).content(); + + // perform the vector search + EmbeddingSearchResult result = embeddingStore.search( + EmbeddingSearchRequest.builder() + .queryEmbedding(queryAsVector) + .build() + ); + + // display the results + List> results = result.matches(); + for (EmbeddingMatch match : results) { + System.out.println("Score: " + match.score()); + System.out.println("Text Segment: " + match.embedded().text()); + } } + + scanner.close(); } } From b58dbd96a7811bb3ce67c207fc769874b37b84dc Mon Sep 17 00:00:00 2001 From: David Jiang Date: Thu, 8 May 2025 12:05:43 -0400 Subject: [PATCH 14/28] Add manual example --- .../src/main/java/OracleIngestExample.java | 166 ++++++++++++++---- 1 file changed, 133 insertions(+), 33 deletions(-) diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index 5f57a903..2ea5ceb8 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -17,23 +17,133 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -import java.util.Scanner; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate using the OracleEmbeddingStore to ingest documents into a vector - * store and then perform a vector search. + * Demonstrate how to use low-level LangChain4j APIs to load the documents, + * split the text, and get the vector embeddings or the OracleEmbeddingStore + * to hide the manual steps for ingesting documents into an embedding store + * for search/retrieval. * * The following components are used: * OracleDocumentLoader to load the documents * OracleDocumentSplitter to split the text * OracleEmbeddingModel to get the vector embeddings * OracleEmbeddingStore to store the vector embeddings + * + * Define the following environment variables before running + * ORACLE_JDBC_URL + * ORACLE_JDBC_USER + * ORACLE_JDBC_PASSWORD + * DEMO_ONNX_DIR + * DEMO_ONNX_FILE + * DEMO_ONNX_MODEL + * DEMO_FILE */ public class OracleIngestExample { public static void main(String[] args) throws SQLException, IOException { + lowLevelExample(); + ingestExample(); + } + + /** + * This example demonstrates how to use low-level LangChain4j APIs to load + * the documents, split the text, and get the vector embeddings for + * search/retrieval. + */ + public static void lowLevelExample() throws SQLException, IOException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + // load the ONNX model for embedding + OracleEmbeddingModel.loadOnnxModel( + conn, + System.getenv("DEMO_ONNX_DIR"), + System.getenv("DEMO_ONNX_FILE"), + System.getenv("DEMO_ONNX_MODEL")); + + // Load the document that includes the information you'd like to "chat" about with the model. + String loaderPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + List docs = loader.loadDocuments(loaderPref); + + // Split document into segments 100 tokens each + String splitterPref = "{\"by\": \"words\", \"max\": 100}"; + OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); + List segments = null; + for (Document doc : docs) { + segments = splitter.split(doc); + for (TextSegment segment : segments) { + System.out.println("segment=" + segment.text()); + } + } + + // Embed segments (convert them into vectors that represent the meaning) using embedding model + String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; + OracleEmbeddingModel embeddingModel = new OracleEmbeddingModel(conn, embedderPref); + List embeddings = embeddingModel.embedAll(segments).content(); + for (Embedding embedding : embeddings) { + System.out.println("embedding=" + embedding); + } + + // set column names for the output table + String tableName = "TEST"; + String idColumn = "ID"; + String embeddingColumn = "EMBEDDING"; + String textColumn = "TEXT"; + String metadataColumn = "METADATA"; + + // setup the embedding store + // build() should create a table with the configured names + OracleEmbeddingStore embeddingStore = OracleEmbeddingStore.builder() + .dataSource(pds) + .embeddingTable(EmbeddingTable.builder() + .createOption(CREATE_OR_REPLACE) + .name(tableName) + .idColumn(idColumn) + .embeddingColumn(embeddingColumn) + .textColumn(textColumn) + .metadataColumn(metadataColumn) + .build()) + .build(); + + // Store embeddings into embedding store for further search / retrieval + embeddingStore.addAll(embeddings, segments); + + // Specify the question you want to ask the model + String question = "Who is Charlie?"; + + // Embed the question + Embedding questionEmbedding = embeddingModel.embed(question).content(); + + // Find relevant embeddings in embedding store by semantic similarity + // You can play with parameters below to find a sweet spot for your specific use case + EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder() + .queryEmbedding(questionEmbedding) + .maxResults(3) + .minScore(0.6) + .build(); + List> relevantEmbeddings = embeddingStore.search(embeddingSearchRequest).matches(); + + // display the results + System.out.println(question); + for (EmbeddingMatch match : relevantEmbeddings) { + System.out.println("Score: " + match.score()); + System.out.println("Text Segment: " + match.embedded().text()); + } + } + + /** + * This example demonstrates how to use the EmbeddingStoreIngestor to hide + * manual steps of ingesting documents for search/retrieval. + */ + public static void ingestExample() throws SQLException, IOException { PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); pds.setURL(System.getenv("ORACLE_JDBC_URL")); @@ -43,7 +153,7 @@ public static void main(String[] args) throws SQLException, IOException { // set the loader, splitter, and embedding preferences String loaderPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; - String splitterPref = "{\"by\": \"chars\", \"max\": 200}"; + String splitterPref = "{\"by\": \"words\", \"max\": 100}"; String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; OracleDocumentLoader loader = new OracleDocumentLoader(conn); @@ -104,35 +214,25 @@ public static void main(String[] args) throws SQLException, IOException { } } - Scanner scanner = new Scanner(System.in); - - while (true) { - System.out.println("\nEnter a query or type 'exit' to quit:"); - - // get the query - String query = scanner.nextLine(); - if (query.equalsIgnoreCase("exit")) { - break; - } - - // get the vector representation - Embedding queryAsVector = embedder.embed(query).content(); - - // perform the vector search - EmbeddingSearchResult result = embeddingStore.search( - EmbeddingSearchRequest.builder() - .queryEmbedding(queryAsVector) - .build() - ); - - // display the results - List> results = result.matches(); - for (EmbeddingMatch match : results) { - System.out.println("Score: " + match.score()); - System.out.println("Text Segment: " + match.embedded().text()); - } + // get the question + String question = "Who is Charlie?"; + + // get the vector representation + Embedding questionAsVector = embedder.embed(question).content(); + + // perform the vector search + EmbeddingSearchResult result = embeddingStore.search( + EmbeddingSearchRequest.builder() + .queryEmbedding(questionAsVector) + .build() + ); + + // display the results + System.out.println(question); + List> results = result.matches(); + for (EmbeddingMatch match : results) { + System.out.println("Score: " + match.score()); + System.out.println("Text Segment: " + match.embedded().text()); } - - scanner.close(); } } From 645281a8e9a74b0b7495853ec522079e535c1cef Mon Sep 17 00:00:00 2001 From: David Jiang Date: Thu, 8 May 2025 12:06:18 -0400 Subject: [PATCH 15/28] Add directory and table examples --- .../java/OracleDocumentLoaderExample.java | 84 +++++++++++++++++-- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/oracle-example/src/main/java/OracleDocumentLoaderExample.java b/oracle-example/src/main/java/OracleDocumentLoaderExample.java index 8d86324b..c1ee58a5 100644 --- a/oracle-example/src/main/java/OracleDocumentLoaderExample.java +++ b/oracle-example/src/main/java/OracleDocumentLoaderExample.java @@ -1,7 +1,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.loader.oracle.DirectoryPreference; import dev.langchain4j.data.document.loader.oracle.FilePreference; import dev.langchain4j.data.document.loader.oracle.OracleDocumentLoader; +import dev.langchain4j.data.document.loader.oracle.TablePreference; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; @@ -10,14 +12,31 @@ import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate loading a document from the file system. The documents can be in - * any format supported by the Oracle Text filter including Word, PDF, HTML, and - * text files. If it is a rich text document like Word or PDF, it will be - * converted into plain text and contain any metadata associated with it. + * Demonstrate loading documents from the file system or a table. + * The documents can be in any format supported by the Oracle Text filter + * including Word, PDF, HTML, and text files. If it is a rich text document + * like Word or PDF, it will be converted into plain text and contain any + * metadata associated with it. + * + * Define the following environment variables before running + * ORACLE_JDBC_URL + * ORACLE_JDBC_USER + * ORACLE_JDBC_PASSWORD + * DEMO_FILE + * DEMO_DIRECTORY + * DEMO_OWNER + * DEMO_TABLE + * DEMO_COLUMN */ public class OracleDocumentLoaderExample { public static void main(String[] args) throws SQLException, IOException { + loadFromFile(); + loadFromDirectory(); + loadFromTable(); + } + + private static void loadFromFile() throws IOException, SQLException { PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); pds.setURL(System.getenv("ORACLE_JDBC_URL")); @@ -26,8 +45,8 @@ public static void main(String[] args) throws SQLException, IOException { Connection conn = pds.getConnection(); // Can build pref as a string - // String pref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; - // Alternatively, can use FilePreference, DirectoryPreference, or TablePreference + // String pref = "{\"file\": \"...\"}"; + // Alternatively, can use FilePreference ObjectMapper mapper = new ObjectMapper(); FilePreference loaderPref = new FilePreference(); loaderPref.setFilename(System.getenv("DEMO_FILE")); @@ -41,4 +60,57 @@ public static void main(String[] args) throws SQLException, IOException { System.out.println("text=" + doc.text()); } } + + private static void loadFromDirectory() throws IOException, SQLException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + // Can build pref as a string + // String pref = "{\"dir\": \"...\"}"; + // Alternatively, can use DirectoryPreference + ObjectMapper mapper = new ObjectMapper(); + DirectoryPreference loaderPref = new DirectoryPreference(); + loaderPref.setDirectory(System.getenv("DEMO_DIRECTORY")); + String pref = mapper.writeValueAsString(loaderPref); + + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + + List docs = loader.loadDocuments(pref); + for (Document doc : docs) { + System.out.println("metadata=" + doc.metadata()); + System.out.println("text=" + doc.text()); + } + } + + private static void loadFromTable() throws IOException, SQLException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + // Can build pref as a string + // String pref = "{\"owner\": \"...\", \"tablename\": \"...\", \"colname\": \"...\"}"; + // Alternatively, can use TablePreference + ObjectMapper mapper = new ObjectMapper(); + TablePreference loaderPref = new TablePreference(); + loaderPref.setOwner(System.getenv("DEMO_OWNER")); + loaderPref.setTableName(System.getenv("DEMO_TABLE")); + loaderPref.setColumnName(System.getenv("DEMO_COLUMN")); + String pref = mapper.writeValueAsString(loaderPref); + + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + + List docs = loader.loadDocuments(pref); + for (Document doc : docs) { + System.out.println("metadata=" + doc.metadata()); + System.out.println("text=" + doc.text()); + } + } + } From 4102e1534a3d0877e725f5fa1a0f172e40f3016c Mon Sep 17 00:00:00 2001 From: David Jiang Date: Thu, 8 May 2025 12:13:08 -0400 Subject: [PATCH 16/28] Add comment about environment variables --- .../main/java/OracleDocumentSplitterExample.java | 8 +++++++- .../src/main/java/OracleEmbeddingModelExample.java | 14 +++++++++++--- .../java/OracleSummaryLanguageModelExample.java | 6 ++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/oracle-example/src/main/java/OracleDocumentSplitterExample.java b/oracle-example/src/main/java/OracleDocumentSplitterExample.java index 8a5c1202..11f281e5 100644 --- a/oracle-example/src/main/java/OracleDocumentSplitterExample.java +++ b/oracle-example/src/main/java/OracleDocumentSplitterExample.java @@ -12,6 +12,12 @@ * Demonstrate chunking or splitting text in a document. You can customize how * to split the content such as by words, characters, or vocabulary (for tokens) * to match a tokenizer in the preference. + * + * Define the following environment variables before running + * ORACLE_JDBC_URL + * ORACLE_JDBC_USER + * ORACLE_JDBC_PASSWORD + * DEMO_FILE */ public class OracleDocumentSplitterExample { @@ -24,7 +30,7 @@ public static void main(String[] args) throws SQLException, IOException { Connection conn = pds.getConnection(); String loadPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; - String splitPref = "{\"by\": \"words\", \"max\": 200}"; + String splitPref = "{\"by\": \"words\", \"max\": 100}"; OracleDocumentLoader loader = new OracleDocumentLoader(conn); OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitPref); diff --git a/oracle-example/src/main/java/OracleEmbeddingModelExample.java b/oracle-example/src/main/java/OracleEmbeddingModelExample.java index 4d4726b9..9b9bbc92 100644 --- a/oracle-example/src/main/java/OracleEmbeddingModelExample.java +++ b/oracle-example/src/main/java/OracleEmbeddingModelExample.java @@ -12,6 +12,14 @@ /** * Demonstrate getting the vector embeddings with an ONNX model located on the * server. + * + * Define the following environment variables before running + * ORACLE_JDBC_URL + * ORACLE_JDBC_USER + * ORACLE_JDBC_PASSWORD + * DEMO_ONNX_DIR + * DEMO_ONNX_FILE + * DEMO_ONNX_MODEL */ public class OracleEmbeddingModelExample { @@ -43,9 +51,9 @@ public static void main(String[] args) throws SQLException { // embed a list of text List textSegments = new ArrayList<>(); - textSegments.add(TextSegment.from("hello world")); - textSegments.add(TextSegment.from("goodbye world")); - textSegments.add(TextSegment.from("1,2,3")); + textSegments.add(TextSegment.from("I like soccer.")); + textSegments.add(TextSegment.from("I love Stephen King.")); + textSegments.add(TextSegment.from("The weather is good today.")); Response> resp = model.embedAll(textSegments); System.out.println(resp.content()); } diff --git a/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java b/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java index f61094f8..5efd4ae0 100644 --- a/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java +++ b/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java @@ -13,6 +13,12 @@ * Demonstrate summarizing a document. You can customize which provider to use * such as database for extracting a summary with Oracle Text or a third-party * provider via a REST call. + * + * Define the following environment variables before running + * ORACLE_JDBC_URL + * ORACLE_JDBC_USER + * ORACLE_JDBC_PASSWORD + * DEMO_FILE */ public class OracleSummaryLanguageModelExample { From 2184fa6827e2d3876048bca0053bb04cebd5cd2f Mon Sep 17 00:00:00 2001 From: David Jiang Date: Thu, 8 May 2025 12:19:21 -0400 Subject: [PATCH 17/28] Add example files --- .../story-about-happy-carrot.docx | Bin 0 -> 11575 bytes .../story-about-happy-carrot.pdf | Bin 0 -> 35359 bytes .../story-about-happy-carrot.txt | 28 ++++++++++++++++++ .../sub-directory/story-about-happy-carrot.md | 28 ++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 oracle-example/example-files/story-about-happy-carrot.docx create mode 100644 oracle-example/example-files/story-about-happy-carrot.pdf create mode 100644 oracle-example/example-files/story-about-happy-carrot.txt create mode 100644 oracle-example/example-files/sub-directory/story-about-happy-carrot.md diff --git a/oracle-example/example-files/story-about-happy-carrot.docx b/oracle-example/example-files/story-about-happy-carrot.docx new file mode 100644 index 0000000000000000000000000000000000000000..c6f1e9519dc8b90713a9c1905e65985c8b6fdf75 GIT binary patch literal 11575 zcma)iWmud^w=M4Ou8kAi-Q9z`y9IX-5}e=`BuJ3p?gaM)cP9{}@kRn1$jp2*llz_X z-1_l$Kh=BHmU?UVuBxRX4+(_;_R=DhwnSb&|7!5hU(B7%R9u{#U71y$#ju_`;C_jv z`LnlLf`fs1Lx6#y{Zq`u*_p}9!9GV-ROt%~T07-g1TCmfX{jFC!5~FM8IGQF%FKdy zd4Qtk)w22}hzF}-bHu?)ia&ETxL<2tO1fUjmNULCc>tY0-|iF2#Z9FyRUMB@Zh2;t z?xuiHel+gUHO!nQVZC8NbjVwmi{`Vn70fM(P-0J;1PztzC65(YYg7p!NNuAK=L0dl zvWpxIRF^;*!C{|1(steU!`G+z`niJ1?`!4n;e+wj7cx?P&6)kPxhflgLHgR-7m$3Z zki4kXvyC413RWT$l*!0V`ZuR^snd&4LqS1SpL+SAT}VQ5c40>qFT_#M)R!(AlZb1L z-#}NRxA(J|Tl%i?LAg-*fKWk}^bZS0r{zynuxE|mxCy^V22OerD7C^6%T#j|r-a;3 z@h^Yo{(7j)-Nqx!$1KV3Hx)!c0pMyC~BwPt? zWHs-m{@aJUKqM2dlOoe=6 zsEMGc3lTUS*#zA_gpYDXwzD~QsK)5R=7eI8ow7k38ai>)6i}FX|5;EU>e?`SLY_ zlW3*DX^UG_+8{a{Qa6#HCtmm46k%hPf=BG_Zi`PKnJo22&%#vTZiJ+MBr+)$@dYCH zEYt|f{3w{q*L_o7`LXlWF$&=biW@9X>^5EM23<++77AWwaO7v137C((OJ%Um;rS5o zBmCJC@Q-Ic)!$Yf_EQpHBdn);6x*$^X4OUWG*2oa4YYnP z*bkP%YxUq7rjtkFH3c!W+mdyjE6B_xuU`Bn^X(aLE(({!!>sWpTko)$%Jx=+lxt!!JhDXgY?3PdVO6NyFyJw=lGAv^Yuy zZJ*hO&zg_Q*J;!PApafwF!S(2s^{RNJO>~1U%_{Gb#rof34c+dsQecetU+MNI{8j6 zwT)(5T%(x-7aA(U#HxIz<6CMvSw7+|Pj2xNuhRnH^-aY3eMg$#Z2^pP^4ygaZh{mP z#HY75z8%aP(;lIFXgurtjrcLC&;qisX}eb%kH+J)mB1oM@O%Ss)U|lTm>eFN*=E{4 zK0r=Q*;#&ruYK!h+$2I)x=(^}@GU*2E^D+L0h=4JPH!#)B^#x(k5?Rh2|^t+0(+J7 zUUxbn-i*A@ya)r(8d`A{glW>6>AV}tPH292sMx66CCZXZI*7I}^>!9W0)%YL39Bj& zu=e~0bNQ7y{A|%%PpucPfa4t{hH7Rt$Kbk295c#Oy$NpCvJc42PlE=2mm&3A=A@Vg zyIifk*K1*CuO)w(>}8&5q2t`?oYCS*0B^>d+6%@|dpklj1~R4V zc_&$YF;f*i=&R&~ZV+}+8S1Bmoyo3_zC_TN2DLdGC>}N5FPfZQtM}o zh_@At%D7Yg2pb&pIyJf|QsR-1mzOW|4m&3M{;ixPcAqFC1QmPB$kp>hH!4Ug@CU<; zQ2TC58XK)??oRld0xu}8A;5AZNBLJ%MuL^CVi@nn)jn6E+3yY@@?|;fVmTOr(LBYi zQVtfK{y4lMluKl4Ys#W#NDbW`G_qA5l#{DxiTHXNFkzc^$P!^m2Al+VT~(>U)RQ>;gaE`r=Cu=S+SvX{5n zpAaQ1X+z~ZO(m19?h1WWW(R+~zjb}}Uf`mUJr!DP>_Hk`oMru@5>yg{&*RCz?59|J zLYZxGJFgHli-^vQ;OdcZ9HP+amy7p!=QJ8uUsEP<82S}08?7vwZLqOU1V>f~q+Fbk z=N#&HHP|p*-eMGZmTWIE^t5!kL$^)7xuq=!xjjX)gXEZn|xNcVCtI;=k^u&OfNui0HL z71>I6xP99{Qb^a3Kvz5)DT0R>rxa)+{gc>~FVtl)#POt8>{PXgXZ zZsQ`XoSzxF6Y!5ePDP!ZxWkNHw(x_)4EubRmdn-mW~gDpLfUKd*$Bn+Iv=CNL-t`r z3JyvVJtHcE-d-1b&+)FRIm8E!^h03^Zx~~uOmiC6SrWzE>&9Zp@3Piz(Nnyf(6Jm` z83XV93I~@$D3(oOuxrl>+0(}Aq87;BHe=)W6v`C(tiPk(Qr`ecc$+iQ?6(oF^RVwF##uCS{~t#yxb z6`ww!!1nt~oN}e_n|NGgxnj3lt3F2gKJVKH)c4h~ zwaxC|=6R{7PO$;J5D2aDkRqYpy-CV*w1*4$UL`+l{CQ?S#U1hq3|f`_g66wA79vgw zmbj$Z6J_KRPFX${w2k4*n_-;R$jWb zmaZ=}`=2_VA3Mo6xagrRDS-DFYnTb%rJay2xacuH47kHGWg6&TB@ZOWMdlGWAr07v zN+aLL%*ciZeUlCkT9Hgnv^@#R6M9|T+m#G=pu3Rwp0i4n#!fF(X|5V3r%?lbPo=>$ z+?ZYiF~?X+Bg*kd*(zo|tOmc{zWJG>Jx#P{s1gnkCWmninNO#k(5=>s`}v0epM$em zguDB&$uk^Souek6AvTdNWcJx#zB;!ycP6do4=1Oc-MA5?bZ_Kf_-N0v2yU(rvql; z5*Acr^E77!yC~nRyz``-oR8;>*a&bug^dgwe*o@at_ea{??Ao6ZfvBDQpiL$ui=y) zHVttni>_1^qCZsWYLPio`P|XqLHl*BfIr#V>55pcz6lAHYDq617UP&^OkCE!Y-&ia zXiB`rerd`%mQ3tBm$!A~z!bUF*?|E<{F-bn7$QJwZ0 ziHWUPDi<6l#>6@=v#9KTLfIYaHEoP6dJaC2XnT!5mxFcl(z8hIu(nR@vI($H<~+XNU^;JfOj_F zvRI%0b*AxIll}P=TEmMrrnMnZZQNF@MGfcFXD>4y`^ssWHEpICFSC|sYyLG%>X1%* zqJ-nKH9arZ7bC|OH$ID)zmEtneYGY-;dv&d+UZ{Ctys0|)~nO|!8lXZcq zlP#MDRvFRX1(^%<)WqA|Ja6=B^Igm2OwU$%XLZA9*ID0ZUS%%+CNO7(<@EDHuJduTtH8yy zr{C&k29p6-9H{5I?YZcC*|hws@LVn2+-w}Jeid*=tmnn| zMabSVsla@>)=5=1ebwp(;HCa~Czw>h!>_Vr^MUchsOO0^-{U3YjWzNT3zd~@Be$#+ z*{8B}{qdy1i2fThHn!BEcg|EMsrKD~4@!?ex0^?iwWCcf!_dC%giItYEm14K7Pmv| zJNg6zU`vxjG(FR*QWL#=y&Uri1G%QcJbO{GRXBr)N-!pqsxs9Zl@%w()?(%xUyA;# zv>36cD1tkyBbaelNj>myKGC0G;)G~zZ0FJ=sROW)#VaU>?mEckLZn-mvf61DBbMn5 z95@381WTr@C42A~6E?{8MblS$&8M|QmeqkKWz)v|?kc6Zia%{mS5CF=oYeW%&oWao z_f;(IgNT_4*qvfEwz9Qr`wv?ktL(-|QaUjCHAYd?%#r1WWu|`GQl(H+4PGDB+PH*t z+!iF`xqCxqnzq!`(7Xq@BcV~L_YY{JAyQSI^u3&LfHL}9Ac+=eV6Nhil5S%cn8?$pSw_fbiu=E0{jJ*52oUNWPOJHbf z8xxd>E~X;wUIEBC&d|EQaB;-KGtRZD=9MpU7qq?(5)3qA5owjbX!{Xo0%tb*ojDKd z)u(0Q+{JTP#q>#WGOvT^?=Vg3s&z#ju+a>Cqe(xE?5pjlH!&8<8nrOk$315{ZN3R~ z*F6l_A5#(?2@^Dv4dfC??PS25yREyxA#cB;c#_l99((P&Y^rCYAZsLzis7M;-UzT1 z#Rw)QD1F##Q^f`h*>Wy72I}Xo6HAwDWMTL|5Q`(&#W_F}f59baa2J=rq>E zJ5e*5SU=qmgcWI35A5gPFq!U#fYXF_iZ?<937U0MAWmcwhF_G6n*_{jFYV^*f0jIZ zSD_%VvOP7(=C@!(L=y0m0r7;lrimwVmNIh10Ne9!hbLQKM~WiZO-bStKK^R+C{#4V zxteeeEB-Cj212lb($~~PUJ}@F^so!)2~+bM3ksJe@(GgzlUwAk<={w*()$)M6CIaoBXu#D!-#R1R;uid!KTRt4IIyiHNlG1zxy4;tvSw16zeg+YK<1mp;DYs8`=6=k0U3a6k$=>&rT>g;T z1oXc7?)4aAb!w<&YJ9fK(_q}p6 z=GND*Y42QTMcwhE>UOZ{06ofIQa-j=hDcLAs8-=#! zJTN5W-VxEgY8Z`d(@#hyUVXQtohS0OekdROouLoy5{yg`j`f{gcCpNBvcP=t6I)3+ z^N_=vk!)(rU$7c6O$4Uu5)6>6A7WBhT<;Z$`c}1Qk-A z5>7Wm8h z%4$=jRyos%_(UMG%l7#-aE7m;_XAAcCYomE8&-c!PXhj9+c-{=?$Rj)P=i;g{t9G|5M0Qk)L z^0P})c8yCAY^HAW@J{Lst9L#pesLkTv}N*&tfo==8g{I$gI>5`2r`L)D4G&Fl6>;y zH;Afi8-2?(CV*KEYfQ>*Z!Qijd2w4QWh7o%i|HqTWHsxa;dA9uE=7Cv@&ugNIXGXv#ha`RIuA0Ea$p$`21Mmyp@N*+of- z=U^Sy92LQ45A%obfCyDvY;0J>OnWF5sCB{y4!f?Y{~_;>ENFbhPy? zi}v9s3E^wca&RwEvF=#>pn$t=G<9_zTsE0$_`xMqpIFU+=AHmFvweF;kST-Jrhl!f zFdhV?#BY(gF7~tD;u8}q7FWoahp$Fs$|5A1vjg~}LiOcbL-NHs0Mg8l&RgQ~*b^PN z93^R$3>HXaDKPJA@Wi7ifYk4qZoun2BkxeWMTSS**2x#-K*P4qg!R)OIhfI|%zKGj z+QTz6x;p5H$t#LrKCY@Ar2}Y2Jh{Hy!gI^)mOFjm@iI~$Ufb(yvGTwT^TBph&DD#6 z-PtO4B~(nx!sF_5iS}Wh0QN6Ftw&#!J@`E)322$1S-azn#Vy~R5?2ZM#={i}@)mg+ z?Qb^)tzo*Es4N~t(=eTw+CY)cDuu%{>JKG0p7Ff@fPsHSk=*;WRaD&u9^8&d~l0eUS{GsYSq#KHO><}zX%eDOnE?$D)W%Sr)Dmy< z&XbFLQVnv(F2*j)jCHY9LU0Map~u))RrIx(lq;DS8eW?(QJ5rWDT+x;fmqPFO}p7!!;Hku0syQv5;*^Z~zoAZs@wPaYjbC zU62z}qNPxh9BJ_5rMzgz>j^38zb$I1+$b<)D>vLO_KYMclC5^o@|?rC^*WK$*4TDb zK;WT)13gD{I3OXa0EpyS^Xp<}BNp?1G^KX5gwh3=tdkqMBZz!=dgzebdYzVV zrRYiR=qO^d#^ZUqtM&T?_z@`vAahD=UU81^g41f)vz)8+mg6jTkcc$`dhfZl$HkD2 zza5=sxG~E)niabveh({<7wL|^;cY#kBM1?75Xz8LWHn4RDO$WOUFEk#6bN3j$qdWC zYsgrBKB^Gx|pcq*?l5d7Z< zx?JjxtYvUuVCC%pC`_MkL);xK9Nm8XP3Rf8E_31dzZ9l;otKi0M*zh-+Z3g3`)>kn znfUz2X0QNJ40JeSa1}<-Jg^0fcKA@CSaC2{w-8sJLKJPMgXjASY9QzV*kP)-+0Nl( zpr31mrLaFTqtG z+z^9TX`MeLCLg?+8LZlEdc7^~ifjmUqIRwIJN#VUc%ygeSkZ3^o_O|OrQTC0&#&i< zYv;eI8feai4Rq9RJ8D@*x<;zgBQd8-LBibY^b%4wo9o}Wq-Wl@Zz5|oPr*qR-eXwF zDY@zMpH8W|AdK|X=gY5#DfFi~eq`-*;}S6CX12L+iCDO{CE;Egwemb^^4$?O4V{mR zXXy{<<)8N7vY0t2Ff0OzT(9a~Jz4#v#9YfY$!+?s2E@6xK=nB-V`3HpsnuCciCs7S zyqj1OzIXf}@g&q-)Sr6CK)6%M9=iH!aHNZCnZILkT$2A@x#DQ+Hb?p*&^CnM_o;PT z^C;EqVRj?*!04je^CE8BOGr1+@Bj$_+Xg&8z$m%K#jdE*1nN2vF=OXCwEGPVj8@@0 zBy3StmvdZ6eBgTY2JU~^jpvqvogC1hao5Ieu)fI?3x-_L!eD-Q$DqR}E8R+3-{t?Y zH#!}b(MI#hC}!EEfho70_B@dx^Kp)~-<_Wn^X}*iC$hmF=jQZY!$ajf@8{mp5AIY8 z#<7;!(2I!FoX>|LJu#UX;SUr`n(vH0s+o$?WE;ylO2%$(AZ=!f*1Y4lf|za}JS&gm zr|ReJh)r0N{h7t+$@U>T$JXNmvj@9R*R4ZcqOW$}nXU_(AKOsK8_x6LI<-?0K#vqY z-}P4or1K!1ZHolGQ{b?=FI z*Eg>1vC&3E9xzA`saZr#l;B~s*i{p*PlT$XJxIB9LdF%&ZyFQZa&@dF3c>wNdOLT6 zd3#ba({Y2{YpqsXa2>O9jpyuT3JhSg``Qw&T7!CtwCbDY`<G%gpr*Lc2IIEFO-uDz$NE*KjUK3H4$la>Igc3VV!r1^h zNHl_aH4c0bp;o=b1nk7J&5n@t4+|IJR7|LaZtz8E^Xk*miB;$~f-xz!s|4(LAQs;v z3c}rX;H-^+A@&g)j246Ui~5)zZlh_#6Y@vNNoq32!mJkc;8UV~WcSKrrxHWHOP#PK zff>a1>zg6)j-sso4nKH;Iw(Se)k&6188xe-4S6Nj+KlsV2dAICeGMWOMtW_L2l7?X zjfD36ux_?!RgO0~7g9lMI72UL21a|vDR&t7CYJF)ya-<_AmVic9*riy8Ed#cuFn{O zIJfkUB)fV$BQJp5viCf$I~pDF={R&fD^d!1oj`g6id~C!glbM(B15tGEHwO`Wy%`@ zH%Os^P~V~u2}{XBO4%)}W>kP?UDExyeSq4|X^r12YJEVTco|L;wQwPAUg!blVNaUO z6lo}F_S8}C)REOI?Qt3m<=s5;51R&>H{(!@!A!N?Tqm?LE9V3}t_efD`JPkF$7>a>fl8>Fv23n z5cQn2`gh4Z>_Mk^Gqy0Q-O74DL>p(~$!sEN!{8RkB^n}cY1{44k>~0QPZ4(qDoU?u z(<`Io7Y0Q&jaL#u~dXd0yd0@R$Z8Ft1XxfA#4K9ksRU8>DVEjmDwcd`PIMMZF zKx25Ap};1x+P)PRx+qK}SQFt}?&gL1mdQG%u;vVP%@1Gdi=ooxk?c4)(N&bDGFot_ zIU(Q2f-Xu3l39Lz?dOa$EUii!Y$gaJ)k&U!OCVVuc7wkSe_UwOZc;n=8Po@tSTmg0 zwlNkci4;6GElLapNo|Urhx9pkh+;zo!8Nb`B3RC{T@v!k@C2$D7?9(~Ync`i4as|F z`fy3?6?lt8230wV*|bChd_2-@k)IoYGE-G)Lb%{HmEg=Wn;i<*mgDX+Y2!&u@)b<- zBMms&J6fQi8k?$Zu}T&~b$6QMzI*~W>hS$JoUr-~KJF%fIu+u_$-azOaYCOBy*qiV zKNQoAb!j1nT;2{zQdGjj>Iec|lYu`yG>STcG{r6pu>kH2E{nhqEZM;8$Tga|0-OM( zjFv$Y3G)$7sy*CN@cE%Gj4s$q0Y?$IR}HNg=n@nAiQgfhwXq^?Q1=Lo?ZYjo)S*(+ z6V=e;A|Z2>C@nU91CSn=Y2=z|6@cmBEyiHDaeZOXe#(Prr|G7cgMD}8+(sy_vJ~#mXPEk7LKv2 z$r|*&MbTMa<99(+YF3to0UD&)5H(JMo1Owwi0t3VNYY%McH3d222r(Hd|DiR0jhR~3 zAPPJ+COZs8LP>_j`~g3_1KRD`?l#Ej!z;L?w5lKsg(fmb{I1F{Js@=<7@mnSW_^{R z)R`ZJ>q;rguf*Y~wlvFsHBn#aFsw!k}-aP}6B?qO3A)lB*kv0<9 z5Oz3A9F0GoLZXw#h?8+M6!>>YY*%R8Nc-LNAMl_uJOlPh-yN379)X+F0eE6LxUc}^ zL{&e(hR-m5t;nG!L8RDYP$-xj<@ogJAB=<_n4#HahE^bnG{479kL{ z^!n7n8{E99!)N&>ig9eA2~&%sleqAsa22M(x4ea#H;aC5l)Mjf*}l>{h%a|ZEbn@r z5o*a9FZQ|~ZMuzAZwO-_2XPa_g9uvolVr`B$AP{Yu)(z80PIk(q(kg`n-N>b{rfB%XQKnt z+4~np|NEg6%=0=jaJJ&F7s<+`RXIpjw6{R$b@DNP z?qzz4Z}v_S`#CcHLF5^$$g4jS6dL?}Lj#+uKX;tEuvty`xeocbx`le>Cp(-Xq-r3C zw4)5%DfL{On7l!xVD||XKmE8NfzLI*+kY1Yv%{9`OISzCB*d_vv7M8w1KMHHB2Qw@ z;Q~bHkIW25`ot3tYZ$B#T~0%0CYCG^K-<`T7Clm`v433VudkeSBfacN{2qnvQb9YB z&Swf;MUHPH1X~`0UachdF?3(^2Ev?KJZ|C?{ItWr6K19|g_fSKBMPR;jD=mX(BK%dzTfQE#Nfrk_vbqr~3tTa58*zD(>qAe<+Qy+aKnY6`%;0x|t2e zsK)H%9&1uZ4M|>Ck^Z>nT&gc#swug#ILbglA>M(1W(O}17-mSR%?;hcHWa?METCqokZotFVYyRwDDi7M*aFx>$50nXoKEgS)bFzH=Tl{Jr z(7-I^UFp}$-EJ9Y7IbldaW z{2!%%5gC7n|4zR98_x4w===@;4;AnKa{77c{MlZdj(fhF{F8O|;_!c;l$WISM|**| zQU5D5{ch~6Nbt}18^5gn=NP}ke=h|8hHsJo3IA7F z_&fghtJ}Zv&M1H4|9O%7JN$Rz-rw+=zheG3jqeZe|4;NAo@?4)uK#ua^Y8cc_Zy(U ewXXde`1gH~iahkMRfqP>be?*CpH%5yK>q^~zEp1j literal 0 HcmV?d00001 diff --git a/oracle-example/example-files/story-about-happy-carrot.pdf b/oracle-example/example-files/story-about-happy-carrot.pdf new file mode 100644 index 0000000000000000000000000000000000000000..76969ed6ef2783680612b58fe458a2efa5cbfb5a GIT binary patch literal 35359 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�d8D?%?7vS482UL5V+{z!seI z1;*eXBoY~3Ne02@jQq!g:Gq|&E~tkH}}VoJP`qz+m!xgJoVf98KihAWLmUI269 zMyfjYGNVG65=9O6GlnM!Avy`V0y{~ky5TvZ3TaUbs*O}FGS2S)^vO?gB{-XcK5K}o zE>J_+b}BwGFop7Cct?Q5b4MbYl#@m)ic-NMD*#6oq=t6y^OJ}5V4&I23)jM#3~aDv zb!cVx*hyS3krS+uTXtpr7*&Ho?9;kXgmKsSO;E(^-D{2H1RmYj`azRjT%C{Er{L}_ zmb9bKJvo>#cAQ>@^%j2K$aCf}Zj5$W11G}1A9sP&8OI`>4zhbn%s~l;Nl1AnC=`&? z^FcFoqd!aizkItZ2qeI>5iwedFcVd?2`Zp7c*O2+ft@bX3oEb*5`<5wB*Szf?$N_C zAJiW37FAEOt&*xOPbj?;mg3bRXdt8!PL~YJDQ8+i=eln!4T#Fq^q^<5=CNr{+xG%|)N_WWvKIbo<<$MTLcsn1zs;#wZO8@+Ut ziHo0gSUWw=8Pik682b>-4)>GAk^-l5dpRA zfvrQwwMRI?^FAaB-F6%&|6Un;7PU|9^C0I|lX4K^=J5*JL(%l1_dg1ih7O6Y)i$|6 z3|#WHX4Rdw)*k^IJIO>-Y3F+Fdsry1Tm@ABvh>!6!bJvsCSIATaNv4I{Hg`$PVS9x z4v!P>{Uwl@^n2M%0#8&P%(y0|>Q&KFBw_#C{5coq>^|oudhX%%19$$ts^8NP|4bxi z%GxQ*A2h$#o|1Fvl>QHa_rC3iGIQentz^8aEBD^it&L0HR+;;xwSUH)!u9O?4TSl& z|Lk?EkG7cAoc!((kK`eHX_mg$90|`}#e_Ti%0BjI=pQ@lk$Qq7;qjHv=MS)H&O2Bv zu)p$~JflloTEe;&5@si-S92e3uKaP=IPgUO!}u4aJZCRgR&{I&N}N<3FsE?h(u8Qq zh?IGzde;)RyCrY+f64pJ`(ownko$Ru?O#~48Gmc(4>}lNBzeW#?#}z>GrTL7?4DZ` zRF{-G*P`*h;5~o!$H8C!N=-0|lI&SD=W6oiO5Hip1#Wldbxd|Y^SAfX!}t!J6Nmjg zU;SsC{(E*MmOHMHuGMkQF9qI51iWF$J2Ryic)t_y!kr)m@ExZ?z{`X{H%KYy2PCGa z79(!uLAn4Hbb%017t&=%po>r;H})VjR1~GA@p1ugC^83W?*1wV2maPywVAHn-!`$pcwELDO5270~1r=&2S(9x5L1|48uGF zGjlTxy=ImMK%b-PH3wc-gdt{Wf#GHgBTL}*aj1F?jLm_U+o6h?SeT*v&A`+Ec&8Ma zI&#AfwxS7BU2xIvl!4=&=p~xE(*~$mWJl$ zrk18gDXD46CWeWomZ_Eo#%30&X{L#07Ab})b~Yv!mL_SIiHU~BW+^5XsRl`A<`#)5 kiKZq%kEEJdnA(A2ytpK>s019hz_2zm<5g94^>^b10I$JvkpKVy literal 0 HcmV?d00001 diff --git a/oracle-example/example-files/story-about-happy-carrot.txt b/oracle-example/example-files/story-about-happy-carrot.txt new file mode 100644 index 00000000..66ae976d --- /dev/null +++ b/oracle-example/example-files/story-about-happy-carrot.txt @@ -0,0 +1,28 @@ +Once upon a time in the town of VeggieVille, there lived a cheerful carrot named Charlie. +Charlie was a radiant carrot, always beaming with joy and positivity. +His vibrant orange skin and lush green top were a sight to behold, but it was his infectious laughter and warm personality that really set him apart. + +Charlie had a diverse group of friends, each a vegetable with their own unique characteristics. +There was Bella the blushing beetroot, always ready with a riddle or two; Timmy the timid tomato, a gentle soul with a heart of gold; and Percy the prankster potato, whose jokes always brought a smile to everyone's faces. +Despite their differences, they shared a close bond, their friendship as robust as their natural goodness. + +Their lives were filled with delightful adventures, from playing hide-and-seek amidst the leafy lettuce to swimming in the dewy droplets that pooled on the cabbage leaves. +Their favorite place, though, was the sunlit corner of the vegetable patch, where they would bask in the warmth of the sun, share stories, and have hearty laughs. + +One day, a bunch of pesky caterpillars invaded VeggieVille. +The vegetables were terrified, fearing they would be nibbled to nothingness. +But Charlie, with his usual sunny disposition, had an idea. +He proposed they host a grand feast for the caterpillars, with the juiciest leaves from the outskirts of the town. +Charlie's optimism was contagious, and his friends eagerly joined in to prepare the feast. + +When the caterpillars arrived, they were pleasantly surprised. +They enjoyed the feast and were so impressed with the vegetables' hospitality that they promised not to trouble VeggieVille again. +In return, they agreed to help pollinate the flowers, contributing to a more lush and vibrant VeggieVille. + +Charlie's idea had saved the day, but he humbly attributed the success to their teamwork and friendship. +They celebrated their victory with a grand party, filled with laughter, dance, and merry games. +That night, under the twinkling stars, they made a pact to always stand by each other, come what may. + +From then on, the story of the happy carrot and his friends spread far and wide, a tale of friendship, unity, and positivity. +Charlie, Bella, Timmy, and Percy continued to live their joyful lives, their laughter echoing through VeggieVille. +And so, the tale of the happy carrot and his friends serves as a reminder that no matter the challenge, with optimism, teamwork, and a bit of creativity, anything is possible. \ No newline at end of file diff --git a/oracle-example/example-files/sub-directory/story-about-happy-carrot.md b/oracle-example/example-files/sub-directory/story-about-happy-carrot.md new file mode 100644 index 00000000..66ae976d --- /dev/null +++ b/oracle-example/example-files/sub-directory/story-about-happy-carrot.md @@ -0,0 +1,28 @@ +Once upon a time in the town of VeggieVille, there lived a cheerful carrot named Charlie. +Charlie was a radiant carrot, always beaming with joy and positivity. +His vibrant orange skin and lush green top were a sight to behold, but it was his infectious laughter and warm personality that really set him apart. + +Charlie had a diverse group of friends, each a vegetable with their own unique characteristics. +There was Bella the blushing beetroot, always ready with a riddle or two; Timmy the timid tomato, a gentle soul with a heart of gold; and Percy the prankster potato, whose jokes always brought a smile to everyone's faces. +Despite their differences, they shared a close bond, their friendship as robust as their natural goodness. + +Their lives were filled with delightful adventures, from playing hide-and-seek amidst the leafy lettuce to swimming in the dewy droplets that pooled on the cabbage leaves. +Their favorite place, though, was the sunlit corner of the vegetable patch, where they would bask in the warmth of the sun, share stories, and have hearty laughs. + +One day, a bunch of pesky caterpillars invaded VeggieVille. +The vegetables were terrified, fearing they would be nibbled to nothingness. +But Charlie, with his usual sunny disposition, had an idea. +He proposed they host a grand feast for the caterpillars, with the juiciest leaves from the outskirts of the town. +Charlie's optimism was contagious, and his friends eagerly joined in to prepare the feast. + +When the caterpillars arrived, they were pleasantly surprised. +They enjoyed the feast and were so impressed with the vegetables' hospitality that they promised not to trouble VeggieVille again. +In return, they agreed to help pollinate the flowers, contributing to a more lush and vibrant VeggieVille. + +Charlie's idea had saved the day, but he humbly attributed the success to their teamwork and friendship. +They celebrated their victory with a grand party, filled with laughter, dance, and merry games. +That night, under the twinkling stars, they made a pact to always stand by each other, come what may. + +From then on, the story of the happy carrot and his friends spread far and wide, a tale of friendship, unity, and positivity. +Charlie, Bella, Timmy, and Percy continued to live their joyful lives, their laughter echoing through VeggieVille. +And so, the tale of the happy carrot and his friends serves as a reminder that no matter the challenge, with optimism, teamwork, and a bit of creativity, anything is possible. \ No newline at end of file From eaf976470c2546f8e2c37c4f40737a88b988790f Mon Sep 17 00:00:00 2001 From: David Jiang Date: Fri, 9 May 2025 16:15:32 -0400 Subject: [PATCH 18/28] Add third-party examples --- .../java/OracleEmbeddingModelExample.java | 45 +++++++++++++++++-- .../OracleSummaryLanguageModelExample.java | 39 +++++++++++++++- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/oracle-example/src/main/java/OracleEmbeddingModelExample.java b/oracle-example/src/main/java/OracleEmbeddingModelExample.java index 9b9bbc92..bf6d803a 100644 --- a/oracle-example/src/main/java/OracleEmbeddingModelExample.java +++ b/oracle-example/src/main/java/OracleEmbeddingModelExample.java @@ -10,20 +10,26 @@ import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate getting the vector embeddings with an ONNX model located on the - * server. + * Demonstrate getting the vector embeddings. You can customize which provider + * to use such as database for an ONNX model or with a third-party provider. * - * Define the following environment variables before running + * This example requires the following environment variables: * ORACLE_JDBC_URL * ORACLE_JDBC_USER * ORACLE_JDBC_PASSWORD * DEMO_ONNX_DIR * DEMO_ONNX_FILE * DEMO_ONNX_MODEL + * DEMO_CREDENTIAL */ public class OracleEmbeddingModelExample { public static void main(String[] args) throws SQLException { + databaseEmbeddings(); + thirdPartyEmbeddings(); + } + + public static void databaseEmbeddings() throws SQLException { PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); pds.setURL(System.getenv("ORACLE_JDBC_URL")); @@ -57,4 +63,37 @@ public static void main(String[] args) throws SQLException { Response> resp = model.embedAll(textSegments); System.out.println(resp.content()); } + + public static void thirdPartyEmbeddings() throws SQLException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + // For a third-party provider, remember to create a credential + // with dbms_vector.create_credential() and then refer to it here + String pref = "{\n" + + " \"provider\": \"ocigenai\",\n" + + " \"credential_name\": \"" + System.getenv("DEMO_CREDENTIAL") + "\",\n" + + " \"url\": \"https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/actions/embedText\",\n" + + " \"model\": \"cohere.embed-english-light-v3.0\"\n" + + "}"; + + OracleEmbeddingModel model = new OracleEmbeddingModel(conn, pref); + + // embed a single string + Response response = model.embed("I love Java"); + Embedding embedding = response.content(); + System.out.println(embedding); + + // embed a list of text + List textSegments = new ArrayList<>(); + textSegments.add(TextSegment.from("I like soccer.")); + textSegments.add(TextSegment.from("I love Stephen King.")); + textSegments.add(TextSegment.from("The weather is good today.")); + Response> resp = model.embedAll(textSegments); + System.out.println(resp.content()); + } } diff --git a/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java b/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java index 5efd4ae0..cc987184 100644 --- a/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java +++ b/oracle-example/src/main/java/OracleSummaryLanguageModelExample.java @@ -11,18 +11,24 @@ /** * Demonstrate summarizing a document. You can customize which provider to use - * such as database for extracting a summary with Oracle Text or a third-party - * provider via a REST call. + * such as database for extracting a summary with Oracle Text or with a + * third-party provider. * * Define the following environment variables before running * ORACLE_JDBC_URL * ORACLE_JDBC_USER * ORACLE_JDBC_PASSWORD * DEMO_FILE + * DEMO_CREDENTIAL */ public class OracleSummaryLanguageModelExample { public static void main(String[] args) throws SQLException, IOException { + databaseSummary(); + thirdPartySummary(); + } + + public static void databaseSummary() throws SQLException, IOException { PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); pds.setURL(System.getenv("ORACLE_JDBC_URL")); @@ -42,4 +48,33 @@ public static void main(String[] args) throws SQLException, IOException { System.out.println("summary=" + resp.content()); } } + + public static void thirdPartySummary() throws SQLException, IOException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + // For a third-party provider, remember to create a credential + // with dbms_vector.create_credential() and then refer to it here + String loadPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; + String summaryPref = "{\n" + + " \"provider\": \"ocigenai\",\n" + + " \"credential_name\": \"" + System.getenv("DEMO_CREDENTIAL") + "\",\n" + + " \"url\": \"https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/actions/chat\",\n" + + " \"model\": \"cohere.command-r-08-2024\",\n" + + "}"; + String proxy = System.getenv("DEMO_PROXY"); + + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + OracleSummaryLanguageModel model = new OracleSummaryLanguageModel(conn, summaryPref, proxy); + + List docs = loader.loadDocuments(loadPref); + for (Document doc : docs) { + Response resp = model.generate(doc.text()); + System.out.println("summary=" + resp.content()); + } + } } From 47e9d0f889e782664ecec2ac99f7664fb502b7a9 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Fri, 9 May 2025 16:15:52 -0400 Subject: [PATCH 19/28] Update comments --- .../main/java/OracleDocumentLoaderExample.java | 2 +- .../main/java/OracleDocumentSplitterExample.java | 2 +- .../src/main/java/OracleIngestExample.java | 16 +++++----------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/oracle-example/src/main/java/OracleDocumentLoaderExample.java b/oracle-example/src/main/java/OracleDocumentLoaderExample.java index c1ee58a5..75d73a26 100644 --- a/oracle-example/src/main/java/OracleDocumentLoaderExample.java +++ b/oracle-example/src/main/java/OracleDocumentLoaderExample.java @@ -18,7 +18,7 @@ * like Word or PDF, it will be converted into plain text and contain any * metadata associated with it. * - * Define the following environment variables before running + * This example requires the following environment variables: * ORACLE_JDBC_URL * ORACLE_JDBC_USER * ORACLE_JDBC_PASSWORD diff --git a/oracle-example/src/main/java/OracleDocumentSplitterExample.java b/oracle-example/src/main/java/OracleDocumentSplitterExample.java index 11f281e5..1d980056 100644 --- a/oracle-example/src/main/java/OracleDocumentSplitterExample.java +++ b/oracle-example/src/main/java/OracleDocumentSplitterExample.java @@ -13,7 +13,7 @@ * to split the content such as by words, characters, or vocabulary (for tokens) * to match a tokenizer in the preference. * - * Define the following environment variables before running + * This example requires the following environment variables: * ORACLE_JDBC_URL * ORACLE_JDBC_USER * ORACLE_JDBC_PASSWORD diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index 2ea5ceb8..46961721 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -21,18 +21,12 @@ import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate how to use low-level LangChain4j APIs to load the documents, - * split the text, and get the vector embeddings or the OracleEmbeddingStore - * to hide the manual steps for ingesting documents into an embedding store - * for search/retrieval. + * Demonstrate how to ingest documents either by using the low-level + * LangChain4j APIs to load the documents, split the text, and get the vector + * embeddings or the OracleEmbeddingStore to hide the manual steps of ingesting + * into an embedding store for search/retrieval. * - * The following components are used: - * OracleDocumentLoader to load the documents - * OracleDocumentSplitter to split the text - * OracleEmbeddingModel to get the vector embeddings - * OracleEmbeddingStore to store the vector embeddings - * - * Define the following environment variables before running + * This example requires the following environment variables: * ORACLE_JDBC_URL * ORACLE_JDBC_USER * ORACLE_JDBC_PASSWORD From 6c0b359cff7b9a24c76b3113c77e2d603ab78759 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Tue, 20 May 2025 14:00:33 -0400 Subject: [PATCH 20/28] Rename to segment --- .../src/main/java/OracleDocumentSplitterExample.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oracle-example/src/main/java/OracleDocumentSplitterExample.java b/oracle-example/src/main/java/OracleDocumentSplitterExample.java index 1d980056..d379432f 100644 --- a/oracle-example/src/main/java/OracleDocumentSplitterExample.java +++ b/oracle-example/src/main/java/OracleDocumentSplitterExample.java @@ -37,9 +37,9 @@ public static void main(String[] args) throws SQLException, IOException { List docs = loader.loadDocuments(loadPref); for (Document doc : docs) { - String[] chunks = splitter.split(doc.text()); - for (String chunk : chunks) { - System.out.println("chunk=" + chunk); + String[] segments = splitter.split(doc.text()); + for (String segment : segments) { + System.out.println("segment=" + segment); } } } From 4aff691c226cae5f26935ad6f58c4ac5f61ba058 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Tue, 20 May 2025 14:01:06 -0400 Subject: [PATCH 21/28] Change to directory, add summary --- .../src/main/java/OracleIngestExample.java | 123 +++++++++--------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index 46961721..d9aec6b5 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -4,6 +4,8 @@ import dev.langchain4j.data.embedding.Embedding; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.oracle.OracleEmbeddingModel; +import dev.langchain4j.model.oracle.OracleSummaryLanguageModel; +import dev.langchain4j.model.output.Response; import dev.langchain4j.store.embedding.EmbeddingMatch; import dev.langchain4j.store.embedding.EmbeddingSearchRequest; import dev.langchain4j.store.embedding.EmbeddingSearchResult; @@ -13,18 +15,17 @@ import dev.langchain4j.store.embedding.oracle.OracleEmbeddingStore; import java.io.IOException; import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate how to ingest documents either by using the low-level - * LangChain4j APIs to load the documents, split the text, and get the vector - * embeddings or the OracleEmbeddingStore to hide the manual steps of ingesting - * into an embedding store for search/retrieval. + * Demonstrate how to ingest documents either by using the low-level LangChain4j + * APIs to load the documents, split the text, and get the vector embeddings or + * the OracleEmbeddingStore to hide the manual steps of ingesting into an + * embedding store for search/retrieval. * * This example requires the following environment variables: * ORACLE_JDBC_URL @@ -33,7 +34,7 @@ * DEMO_ONNX_DIR * DEMO_ONNX_FILE * DEMO_ONNX_MODEL - * DEMO_FILE + * DEMO_DIRECTORY */ public class OracleIngestExample { @@ -61,31 +62,38 @@ public static void lowLevelExample() throws SQLException, IOException { System.getenv("DEMO_ONNX_DIR"), System.getenv("DEMO_ONNX_FILE"), System.getenv("DEMO_ONNX_MODEL")); + + // set the loader, splitter, embedding, and summary model preferences + String loaderPref = "{\"dir\": \"" + System.getenv("DEMO_DIRECTORY") + "\"}"; + String splitterPref = "{\"by\": \"words\", \"max\": 100}"; + String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; + String summaryPref = "{\"provider\": \"database\", \"glevel\": \"S\"}"; - // Load the document that includes the information you'd like to "chat" about with the model. - String loaderPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; OracleDocumentLoader loader = new OracleDocumentLoader(conn); + OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); + OracleEmbeddingModel embeddingModel = new OracleEmbeddingModel(conn, embedderPref); + OracleSummaryLanguageModel summaryModel = new OracleSummaryLanguageModel(conn, summaryPref); + + // Load the documents with the information that you would like to search on List docs = loader.loadDocuments(loaderPref); - // Split document into segments 100 tokens each - String splitterPref = "{\"by\": \"words\", \"max\": 100}"; - OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); - List segments = null; - for (Document doc : docs) { - segments = splitter.split(doc); - for (TextSegment segment : segments) { - System.out.println("segment=" + segment.text()); - } + // Split document into segments + List allSegments = new ArrayList<>(); + for (Document doc : docs) { + // For each doc, add a summary to the metadata + // This will get copied into each segment + Response resp = summaryModel.generate(doc.text()); + doc.metadata().put("summary", resp.content()); + + List segments = splitter.split(doc); + allSegments.addAll(segments); } // Embed segments (convert them into vectors that represent the meaning) using embedding model - String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; - OracleEmbeddingModel embeddingModel = new OracleEmbeddingModel(conn, embedderPref); - List embeddings = embeddingModel.embedAll(segments).content(); - for (Embedding embedding : embeddings) { - System.out.println("embedding=" + embedding); - } + List embeddings = embeddingModel.embedAll(allSegments).content(); + // setup the embedding store + // set column names for the output table String tableName = "TEST"; String idColumn = "ID"; @@ -93,7 +101,6 @@ public static void lowLevelExample() throws SQLException, IOException { String textColumn = "TEXT"; String metadataColumn = "METADATA"; - // setup the embedding store // build() should create a table with the configured names OracleEmbeddingStore embeddingStore = OracleEmbeddingStore.builder() .dataSource(pds) @@ -108,16 +115,15 @@ public static void lowLevelExample() throws SQLException, IOException { .build(); // Store embeddings into embedding store for further search / retrieval - embeddingStore.addAll(embeddings, segments); + embeddingStore.addAll(embeddings, allSegments); - // Specify the question you want to ask the model - String question = "Who is Charlie?"; + // Get the question + String question = "Who is John Doe?"; // Embed the question Embedding questionEmbedding = embeddingModel.embed(question).content(); // Find relevant embeddings in embedding store by semantic similarity - // You can play with parameters below to find a sweet spot for your specific use case EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder() .queryEmbedding(questionEmbedding) .maxResults(3) @@ -129,7 +135,8 @@ public static void lowLevelExample() throws SQLException, IOException { System.out.println(question); for (EmbeddingMatch match : relevantEmbeddings) { System.out.println("Score: " + match.score()); - System.out.println("Text Segment: " + match.embedded().text()); + System.out.println("Segment: " + match.embedded().text()); + System.out.println("Metadata: " + match.embedded().metadata()); } } @@ -145,15 +152,6 @@ public static void ingestExample() throws SQLException, IOException { pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); Connection conn = pds.getConnection(); - // set the loader, splitter, and embedding preferences - String loaderPref = "{\"file\": \"" + System.getenv("DEMO_FILE") + "\"}"; - String splitterPref = "{\"by\": \"words\", \"max\": 100}"; - String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; - - OracleDocumentLoader loader = new OracleDocumentLoader(conn); - OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); - OracleEmbeddingModel embedder = new OracleEmbeddingModel(conn, embedderPref); - // load the ONNX model for embedding OracleEmbeddingModel.loadOnnxModel( conn, @@ -161,9 +159,17 @@ public static void ingestExample() throws SQLException, IOException { System.getenv("DEMO_ONNX_FILE"), System.getenv("DEMO_ONNX_MODEL")); - // load the document - List docs = loader.loadDocuments(loaderPref); + // set the loader, splitter, and embedding preferences + String loaderPref = "{\"dir\": \"" + System.getenv("DEMO_DIRECTORY") + "\"}"; + String splitterPref = "{\"by\": \"words\", \"max\": 100}"; + String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); + OracleEmbeddingModel embeddingModel = new OracleEmbeddingModel(conn, embedderPref); + + // setup the embedding store + // set column names for the output table String tableName = "TEST"; String idColumn = "ID"; @@ -171,7 +177,6 @@ public static void ingestExample() throws SQLException, IOException { String textColumn = "TEXT"; String metadataColumn = "METADATA"; - // setup the embedding store // build() should create a table with the configured names OracleEmbeddingStore embeddingStore = OracleEmbeddingStore.builder() .dataSource(pds) @@ -185,39 +190,32 @@ public static void ingestExample() throws SQLException, IOException { .build()) .build(); - // ingest the documents with the following components + // build an ingestor with the following components EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(splitter) - .embeddingModel(embedder) + .embeddingModel(embeddingModel) .embeddingStore(embeddingStore) .build(); - ingestor.ingest(docs); - // check the chunks - System.out.println("chunks inserted:"); - String queryEmbeddingStore = "select * from %s".formatted(tableName); - try (PreparedStatement stmt = conn.prepareStatement(queryEmbeddingStore)) { - try (ResultSet rs = stmt.executeQuery()) { - while (rs.next()) { - String id = rs.getString(idColumn); - String text = rs.getString(textColumn); - - String ending = text.length() > 50 ? "..." : ""; - System.out.println(id + "\t" + text.substring(0, 50) + ending); - } - } - } + // load and ingest the documents + // this will call the splitter to split into segments, + // embedding model to get the embeddings, and then store the + // embeddings into the embedding store for further search / retrieval + List docs = loader.loadDocuments(loaderPref); + ingestor.ingest(docs); // get the question - String question = "Who is Charlie?"; + String question = "Who is John Doe?"; // get the vector representation - Embedding questionAsVector = embedder.embed(question).content(); + Embedding questionAsVector = embeddingModel.embed(question).content(); // perform the vector search EmbeddingSearchResult result = embeddingStore.search( EmbeddingSearchRequest.builder() .queryEmbedding(questionAsVector) + .maxResults(3) + .minScore(0.6) .build() ); @@ -226,7 +224,8 @@ public static void ingestExample() throws SQLException, IOException { List> results = result.matches(); for (EmbeddingMatch match : results) { System.out.println("Score: " + match.score()); - System.out.println("Text Segment: " + match.embedded().text()); + System.out.println("Segment: " + match.embedded().text()); + System.out.println("Metadata: " + match.embedded().metadata()); } } } From 6c55c7c29878b9f7cc255a95266fe60393c86a89 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Tue, 20 May 2025 14:01:36 -0400 Subject: [PATCH 22/28] Create biography-of-john-doe.txt --- .../example-files/biography-of-john-doe.txt | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 oracle-example/example-files/biography-of-john-doe.txt diff --git a/oracle-example/example-files/biography-of-john-doe.txt b/oracle-example/example-files/biography-of-john-doe.txt new file mode 100644 index 00000000..272c7db8 --- /dev/null +++ b/oracle-example/example-files/biography-of-john-doe.txt @@ -0,0 +1,49 @@ +John Doe: An Imaginary Luminary + +Early Life and Education +John Doe was born on April 1, 1980, in the quaint town of Fictionville, USA. +The only child of Jane and Joe Doe, he exhibited an early inclination towards creativity and innovation. +Growing up, John was fascinated by the stories his mother, a local librarian, would read to him, +which sparked his love for literature and storytelling. + +Doe attended Fictionville High School, where he excelled academically and was a star athlete. +His teachers often noted his exceptional ability to blend creativity with logic. +John's high school science fair project, a prototype of an environmentally-friendly engine, won him the +National Young Innovators Award. +This achievement earned him a scholarship to Prestige University, where he majored in Mechanical Engineering +and minored in Creative Writing. + +Career Beginnings +After graduating with honors, Doe joined a renowned tech firm, InnoTech, as a junior engineer. +His innovative approach and dedication quickly propelled him up the ranks. +By the age of 30, John had become the lead engineer of InnoTech's research and development department, +where he spearheaded groundbreaking projects in renewable energy. + +However, his love for writing never waned. +In his spare time, John wrote short stories, many of which were published in esteemed literary magazines. +His unique blend of scientific knowledge and creative storytelling earned him a devoted following. + +Entrepreneurial Ventures +At 35, Doe ventured into entrepreneurship, founding EcoSolutions, a company focused on sustainable technologies. +Under his leadership, EcoSolutions developed the "GreenDrive," an eco-friendly engine that revolutionized +the automotive industry. +His success in business was paralleled by his literary accomplishments; he published his debut novel, +"The Inventor's Dilemma," which became a bestseller. + +Philanthropy and Personal Life +John Doe's success in business and literature was matched by his commitment to philanthropy. +He established the "Doe Foundation" to support education and environmental initiatives. +His efforts earned him various humanitarian awards. + +In his personal life, John is known to be a private individual. +He married his college sweetheart, Emily, and they have two children. +The family resides in a sustainable home in Fictionville, where Doe enjoys gardening and mentoring young entrepreneurs. + +Legacy +John Doe's legacy is multifaceted: a pioneering engineer, a celebrated author, and a dedicated philanthropist. +His contributions to technology and literature have left an indelible mark on the world, +inspiring countless individuals to pursue their dreams with passion and determination. + +As he often says, "Innovation is the intersection of imagination and reality." +John Doe's life is a testament to this belief, a blend of the fantastical and the tangible, +making him not just a figure of success, but a symbol of the limitless potential of the human spirit. \ No newline at end of file From ac7aad8d426eaafce3dacd869007978ae5e23f45 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Tue, 20 May 2025 14:46:58 -0400 Subject: [PATCH 23/28] Comments --- oracle-example/src/main/java/OracleIngestExample.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index d9aec6b5..01dc0924 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -79,9 +79,9 @@ public static void lowLevelExample() throws SQLException, IOException { // Split document into segments List allSegments = new ArrayList<>(); - for (Document doc : docs) { - // For each doc, add a summary to the metadata - // This will get copied into each segment + for (Document doc : docs) { + // Example of modifying the metadata + // For each doc, add a summary that will get copied into each segment Response resp = summaryModel.generate(doc.text()); doc.metadata().put("summary", resp.content()); From 95224bd9c5a10c938bda7ad98acd7993b2978797 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Wed, 21 May 2025 12:35:50 -0400 Subject: [PATCH 24/28] Move example files to resources --- .../example-files/biography-of-john-doe.txt | 0 .../example-files/story-about-happy-carrot.docx | Bin .../example-files/story-about-happy-carrot.pdf | Bin .../example-files/story-about-happy-carrot.txt | 0 .../sub-directory/story-about-happy-carrot.md | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename oracle-example/{ => src/main/resources}/example-files/biography-of-john-doe.txt (100%) rename oracle-example/{ => src/main/resources}/example-files/story-about-happy-carrot.docx (100%) rename oracle-example/{ => src/main/resources}/example-files/story-about-happy-carrot.pdf (100%) rename oracle-example/{ => src/main/resources}/example-files/story-about-happy-carrot.txt (100%) rename oracle-example/{ => src/main/resources}/example-files/sub-directory/story-about-happy-carrot.md (100%) diff --git a/oracle-example/example-files/biography-of-john-doe.txt b/oracle-example/src/main/resources/example-files/biography-of-john-doe.txt similarity index 100% rename from oracle-example/example-files/biography-of-john-doe.txt rename to oracle-example/src/main/resources/example-files/biography-of-john-doe.txt diff --git a/oracle-example/example-files/story-about-happy-carrot.docx b/oracle-example/src/main/resources/example-files/story-about-happy-carrot.docx similarity index 100% rename from oracle-example/example-files/story-about-happy-carrot.docx rename to oracle-example/src/main/resources/example-files/story-about-happy-carrot.docx diff --git a/oracle-example/example-files/story-about-happy-carrot.pdf b/oracle-example/src/main/resources/example-files/story-about-happy-carrot.pdf similarity index 100% rename from oracle-example/example-files/story-about-happy-carrot.pdf rename to oracle-example/src/main/resources/example-files/story-about-happy-carrot.pdf diff --git a/oracle-example/example-files/story-about-happy-carrot.txt b/oracle-example/src/main/resources/example-files/story-about-happy-carrot.txt similarity index 100% rename from oracle-example/example-files/story-about-happy-carrot.txt rename to oracle-example/src/main/resources/example-files/story-about-happy-carrot.txt diff --git a/oracle-example/example-files/sub-directory/story-about-happy-carrot.md b/oracle-example/src/main/resources/example-files/sub-directory/story-about-happy-carrot.md similarity index 100% rename from oracle-example/example-files/sub-directory/story-about-happy-carrot.md rename to oracle-example/src/main/resources/example-files/sub-directory/story-about-happy-carrot.md From 9674e79ced55588b482e67bb1386767570f0429f Mon Sep 17 00:00:00 2001 From: David Jiang Date: Wed, 21 May 2025 14:10:01 -0400 Subject: [PATCH 25/28] Split ingest example --- .../src/main/java/OracleIngestExample.java | 115 +-------------- .../main/java/OracleIngestManualExample.java | 131 ++++++++++++++++++ 2 files changed, 133 insertions(+), 113 deletions(-) create mode 100644 oracle-example/src/main/java/OracleIngestManualExample.java diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index 01dc0924..b35dae9c 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -4,8 +4,6 @@ import dev.langchain4j.data.embedding.Embedding; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.oracle.OracleEmbeddingModel; -import dev.langchain4j.model.oracle.OracleSummaryLanguageModel; -import dev.langchain4j.model.output.Response; import dev.langchain4j.store.embedding.EmbeddingMatch; import dev.langchain4j.store.embedding.EmbeddingSearchRequest; import dev.langchain4j.store.embedding.EmbeddingSearchResult; @@ -16,16 +14,13 @@ import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; /** - * Demonstrate how to ingest documents either by using the low-level LangChain4j - * APIs to load the documents, split the text, and get the vector embeddings or - * the OracleEmbeddingStore to hide the manual steps of ingesting into an - * embedding store for search/retrieval. + * Demonstrate how to ingest documents using an OracleEmbeddingStore to hide + * the manual steps of ingesting into an embedding store for search/retrieval. * * This example requires the following environment variables: * ORACLE_JDBC_URL @@ -39,112 +34,6 @@ public class OracleIngestExample { public static void main(String[] args) throws SQLException, IOException { - lowLevelExample(); - ingestExample(); - } - - /** - * This example demonstrates how to use low-level LangChain4j APIs to load - * the documents, split the text, and get the vector embeddings for - * search/retrieval. - */ - public static void lowLevelExample() throws SQLException, IOException { - PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); - pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); - pds.setURL(System.getenv("ORACLE_JDBC_URL")); - pds.setUser(System.getenv("ORACLE_JDBC_USER")); - pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); - Connection conn = pds.getConnection(); - - // load the ONNX model for embedding - OracleEmbeddingModel.loadOnnxModel( - conn, - System.getenv("DEMO_ONNX_DIR"), - System.getenv("DEMO_ONNX_FILE"), - System.getenv("DEMO_ONNX_MODEL")); - - // set the loader, splitter, embedding, and summary model preferences - String loaderPref = "{\"dir\": \"" + System.getenv("DEMO_DIRECTORY") + "\"}"; - String splitterPref = "{\"by\": \"words\", \"max\": 100}"; - String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; - String summaryPref = "{\"provider\": \"database\", \"glevel\": \"S\"}"; - - OracleDocumentLoader loader = new OracleDocumentLoader(conn); - OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); - OracleEmbeddingModel embeddingModel = new OracleEmbeddingModel(conn, embedderPref); - OracleSummaryLanguageModel summaryModel = new OracleSummaryLanguageModel(conn, summaryPref); - - // Load the documents with the information that you would like to search on - List docs = loader.loadDocuments(loaderPref); - - // Split document into segments - List allSegments = new ArrayList<>(); - for (Document doc : docs) { - // Example of modifying the metadata - // For each doc, add a summary that will get copied into each segment - Response resp = summaryModel.generate(doc.text()); - doc.metadata().put("summary", resp.content()); - - List segments = splitter.split(doc); - allSegments.addAll(segments); - } - - // Embed segments (convert them into vectors that represent the meaning) using embedding model - List embeddings = embeddingModel.embedAll(allSegments).content(); - - // setup the embedding store - - // set column names for the output table - String tableName = "TEST"; - String idColumn = "ID"; - String embeddingColumn = "EMBEDDING"; - String textColumn = "TEXT"; - String metadataColumn = "METADATA"; - - // build() should create a table with the configured names - OracleEmbeddingStore embeddingStore = OracleEmbeddingStore.builder() - .dataSource(pds) - .embeddingTable(EmbeddingTable.builder() - .createOption(CREATE_OR_REPLACE) - .name(tableName) - .idColumn(idColumn) - .embeddingColumn(embeddingColumn) - .textColumn(textColumn) - .metadataColumn(metadataColumn) - .build()) - .build(); - - // Store embeddings into embedding store for further search / retrieval - embeddingStore.addAll(embeddings, allSegments); - - // Get the question - String question = "Who is John Doe?"; - - // Embed the question - Embedding questionEmbedding = embeddingModel.embed(question).content(); - - // Find relevant embeddings in embedding store by semantic similarity - EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder() - .queryEmbedding(questionEmbedding) - .maxResults(3) - .minScore(0.6) - .build(); - List> relevantEmbeddings = embeddingStore.search(embeddingSearchRequest).matches(); - - // display the results - System.out.println(question); - for (EmbeddingMatch match : relevantEmbeddings) { - System.out.println("Score: " + match.score()); - System.out.println("Segment: " + match.embedded().text()); - System.out.println("Metadata: " + match.embedded().metadata()); - } - } - - /** - * This example demonstrates how to use the EmbeddingStoreIngestor to hide - * manual steps of ingesting documents for search/retrieval. - */ - public static void ingestExample() throws SQLException, IOException { PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); pds.setURL(System.getenv("ORACLE_JDBC_URL")); diff --git a/oracle-example/src/main/java/OracleIngestManualExample.java b/oracle-example/src/main/java/OracleIngestManualExample.java new file mode 100644 index 00000000..070509d7 --- /dev/null +++ b/oracle-example/src/main/java/OracleIngestManualExample.java @@ -0,0 +1,131 @@ +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.loader.oracle.OracleDocumentLoader; +import dev.langchain4j.data.document.splitter.oracle.OracleDocumentSplitter; +import dev.langchain4j.data.embedding.Embedding; +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.oracle.OracleEmbeddingModel; +import dev.langchain4j.model.oracle.OracleSummaryLanguageModel; +import dev.langchain4j.model.output.Response; +import dev.langchain4j.store.embedding.EmbeddingMatch; +import dev.langchain4j.store.embedding.EmbeddingSearchRequest; +import static dev.langchain4j.store.embedding.oracle.CreateOption.CREATE_OR_REPLACE; +import dev.langchain4j.store.embedding.oracle.EmbeddingTable; +import dev.langchain4j.store.embedding.oracle.OracleEmbeddingStore; +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; + +/** + * Demonstrate how to ingest documents by using the low-level LangChain4j + * APIs with OracleDocumentLoader, OracleSummaryLanguageModel, + * OracleDocumentSplitter, and OracleEmbeddingModel to load documents, + * generate a summary, split the text, and get the vector embeddings. + * + * This example requires the following environment variables: + * ORACLE_JDBC_URL + * ORACLE_JDBC_USER + * ORACLE_JDBC_PASSWORD + * DEMO_ONNX_DIR + * DEMO_ONNX_FILE + * DEMO_ONNX_MODEL + * DEMO_DIRECTORY + */ +public class OracleIngestManualExample { + + public static void main(String[] args) throws SQLException, IOException { + PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource(); + pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource"); + pds.setURL(System.getenv("ORACLE_JDBC_URL")); + pds.setUser(System.getenv("ORACLE_JDBC_USER")); + pds.setPassword(System.getenv("ORACLE_JDBC_PASSWORD")); + Connection conn = pds.getConnection(); + + // load the ONNX model for embedding + OracleEmbeddingModel.loadOnnxModel( + conn, + System.getenv("DEMO_ONNX_DIR"), + System.getenv("DEMO_ONNX_FILE"), + System.getenv("DEMO_ONNX_MODEL")); + + // set the loader, splitter, embedding, and summary model preferences + String loaderPref = "{\"dir\": \"" + System.getenv("DEMO_DIRECTORY") + "\"}"; + String splitterPref = "{\"by\": \"words\", \"max\": 100}"; + String embedderPref = "{\"provider\": \"database\", \"model\": \"" + System.getenv("DEMO_ONNX_MODEL") + "\"}"; + String summaryPref = "{\"provider\": \"database\", \"glevel\": \"S\"}"; + + OracleDocumentLoader loader = new OracleDocumentLoader(conn); + OracleDocumentSplitter splitter = new OracleDocumentSplitter(conn, splitterPref); + OracleEmbeddingModel embeddingModel = new OracleEmbeddingModel(conn, embedderPref); + OracleSummaryLanguageModel summaryModel = new OracleSummaryLanguageModel(conn, summaryPref); + + // Load the documents with the information that you would like to search on + List docs = loader.loadDocuments(loaderPref); + + // Split document into segments + List allSegments = new ArrayList<>(); + for (Document doc : docs) { + // Example of modifying the metadata + // For each doc, add a summary that will get copied into each segment + Response resp = summaryModel.generate(doc.text()); + doc.metadata().put("summary", resp.content()); + + List segments = splitter.split(doc); + allSegments.addAll(segments); + } + + // Embed segments (convert them into vectors that represent the meaning) using embedding model + List embeddings = embeddingModel.embedAll(allSegments).content(); + + // setup the embedding store + + // set column names for the output table + String tableName = "TEST"; + String idColumn = "ID"; + String embeddingColumn = "EMBEDDING"; + String textColumn = "TEXT"; + String metadataColumn = "METADATA"; + + // build() should create a table with the configured names + OracleEmbeddingStore embeddingStore = OracleEmbeddingStore.builder() + .dataSource(pds) + .embeddingTable(EmbeddingTable.builder() + .createOption(CREATE_OR_REPLACE) + .name(tableName) + .idColumn(idColumn) + .embeddingColumn(embeddingColumn) + .textColumn(textColumn) + .metadataColumn(metadataColumn) + .build()) + .build(); + + // Store embeddings into embedding store for further search / retrieval + embeddingStore.addAll(embeddings, allSegments); + + // Get the question + String question = "Who is John Doe?"; + + // Embed the question + Embedding questionEmbedding = embeddingModel.embed(question).content(); + + // Find relevant embeddings in embedding store by semantic similarity + EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder() + .queryEmbedding(questionEmbedding) + .maxResults(3) + .minScore(0.6) + .build(); + List> relevantEmbeddings = embeddingStore.search(embeddingSearchRequest).matches(); + + // display the results + System.out.println(question); + for (EmbeddingMatch match : relevantEmbeddings) { + System.out.println("Score: " + match.score()); + System.out.println("Segment: " + match.embedded().text()); + System.out.println("Metadata: " + match.embedded().metadata()); + } + } + +} From 79795e3a9829c19098e5e40c783a962b0c8332d3 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Tue, 27 May 2025 17:27:12 -0400 Subject: [PATCH 26/28] Change back to Charlie example --- .../src/main/java/OracleIngestExample.java | 4 +- .../main/java/OracleIngestManualExample.java | 4 +- .../example-files/biography-of-john-doe.txt | 49 ------------------- 3 files changed, 4 insertions(+), 53 deletions(-) delete mode 100644 oracle-example/src/main/resources/example-files/biography-of-john-doe.txt diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index b35dae9c..62df6e46 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -94,7 +94,7 @@ public static void main(String[] args) throws SQLException, IOException { ingestor.ingest(docs); // get the question - String question = "Who is John Doe?"; + String question = "What is the carrot called?"; // get the vector representation Embedding questionAsVector = embeddingModel.embed(question).content(); @@ -113,8 +113,8 @@ public static void main(String[] args) throws SQLException, IOException { List> results = result.matches(); for (EmbeddingMatch match : results) { System.out.println("Score: " + match.score()); - System.out.println("Segment: " + match.embedded().text()); System.out.println("Metadata: " + match.embedded().metadata()); + System.out.println("Text: " + match.embedded().text()); } } } diff --git a/oracle-example/src/main/java/OracleIngestManualExample.java b/oracle-example/src/main/java/OracleIngestManualExample.java index 070509d7..ab936162 100644 --- a/oracle-example/src/main/java/OracleIngestManualExample.java +++ b/oracle-example/src/main/java/OracleIngestManualExample.java @@ -106,7 +106,7 @@ public static void main(String[] args) throws SQLException, IOException { embeddingStore.addAll(embeddings, allSegments); // Get the question - String question = "Who is John Doe?"; + String question = "What is the carrot called?"; // Embed the question Embedding questionEmbedding = embeddingModel.embed(question).content(); @@ -123,8 +123,8 @@ public static void main(String[] args) throws SQLException, IOException { System.out.println(question); for (EmbeddingMatch match : relevantEmbeddings) { System.out.println("Score: " + match.score()); - System.out.println("Segment: " + match.embedded().text()); System.out.println("Metadata: " + match.embedded().metadata()); + System.out.println("Text: " + match.embedded().text()); } } diff --git a/oracle-example/src/main/resources/example-files/biography-of-john-doe.txt b/oracle-example/src/main/resources/example-files/biography-of-john-doe.txt deleted file mode 100644 index 272c7db8..00000000 --- a/oracle-example/src/main/resources/example-files/biography-of-john-doe.txt +++ /dev/null @@ -1,49 +0,0 @@ -John Doe: An Imaginary Luminary - -Early Life and Education -John Doe was born on April 1, 1980, in the quaint town of Fictionville, USA. -The only child of Jane and Joe Doe, he exhibited an early inclination towards creativity and innovation. -Growing up, John was fascinated by the stories his mother, a local librarian, would read to him, -which sparked his love for literature and storytelling. - -Doe attended Fictionville High School, where he excelled academically and was a star athlete. -His teachers often noted his exceptional ability to blend creativity with logic. -John's high school science fair project, a prototype of an environmentally-friendly engine, won him the -National Young Innovators Award. -This achievement earned him a scholarship to Prestige University, where he majored in Mechanical Engineering -and minored in Creative Writing. - -Career Beginnings -After graduating with honors, Doe joined a renowned tech firm, InnoTech, as a junior engineer. -His innovative approach and dedication quickly propelled him up the ranks. -By the age of 30, John had become the lead engineer of InnoTech's research and development department, -where he spearheaded groundbreaking projects in renewable energy. - -However, his love for writing never waned. -In his spare time, John wrote short stories, many of which were published in esteemed literary magazines. -His unique blend of scientific knowledge and creative storytelling earned him a devoted following. - -Entrepreneurial Ventures -At 35, Doe ventured into entrepreneurship, founding EcoSolutions, a company focused on sustainable technologies. -Under his leadership, EcoSolutions developed the "GreenDrive," an eco-friendly engine that revolutionized -the automotive industry. -His success in business was paralleled by his literary accomplishments; he published his debut novel, -"The Inventor's Dilemma," which became a bestseller. - -Philanthropy and Personal Life -John Doe's success in business and literature was matched by his commitment to philanthropy. -He established the "Doe Foundation" to support education and environmental initiatives. -His efforts earned him various humanitarian awards. - -In his personal life, John is known to be a private individual. -He married his college sweetheart, Emily, and they have two children. -The family resides in a sustainable home in Fictionville, where Doe enjoys gardening and mentoring young entrepreneurs. - -Legacy -John Doe's legacy is multifaceted: a pioneering engineer, a celebrated author, and a dedicated philanthropist. -His contributions to technology and literature have left an indelible mark on the world, -inspiring countless individuals to pursue their dreams with passion and determination. - -As he often says, "Innovation is the intersection of imagination and reality." -John Doe's life is a testament to this belief, a blend of the fantastical and the tangible, -making him not just a figure of success, but a symbol of the limitless potential of the human spirit. \ No newline at end of file From a63661ae2b45ccf01921f7100685022db742f4a3 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Tue, 27 May 2025 17:36:59 -0400 Subject: [PATCH 27/28] Revert "Change back to Charlie example" This reverts commit 79795e3a9829c19098e5e40c783a962b0c8332d3. --- .../src/main/java/OracleIngestExample.java | 4 +- .../main/java/OracleIngestManualExample.java | 4 +- .../example-files/biography-of-john-doe.txt | 49 +++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 oracle-example/src/main/resources/example-files/biography-of-john-doe.txt diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index 62df6e46..b35dae9c 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -94,7 +94,7 @@ public static void main(String[] args) throws SQLException, IOException { ingestor.ingest(docs); // get the question - String question = "What is the carrot called?"; + String question = "Who is John Doe?"; // get the vector representation Embedding questionAsVector = embeddingModel.embed(question).content(); @@ -113,8 +113,8 @@ public static void main(String[] args) throws SQLException, IOException { List> results = result.matches(); for (EmbeddingMatch match : results) { System.out.println("Score: " + match.score()); + System.out.println("Segment: " + match.embedded().text()); System.out.println("Metadata: " + match.embedded().metadata()); - System.out.println("Text: " + match.embedded().text()); } } } diff --git a/oracle-example/src/main/java/OracleIngestManualExample.java b/oracle-example/src/main/java/OracleIngestManualExample.java index ab936162..070509d7 100644 --- a/oracle-example/src/main/java/OracleIngestManualExample.java +++ b/oracle-example/src/main/java/OracleIngestManualExample.java @@ -106,7 +106,7 @@ public static void main(String[] args) throws SQLException, IOException { embeddingStore.addAll(embeddings, allSegments); // Get the question - String question = "What is the carrot called?"; + String question = "Who is John Doe?"; // Embed the question Embedding questionEmbedding = embeddingModel.embed(question).content(); @@ -123,8 +123,8 @@ public static void main(String[] args) throws SQLException, IOException { System.out.println(question); for (EmbeddingMatch match : relevantEmbeddings) { System.out.println("Score: " + match.score()); + System.out.println("Segment: " + match.embedded().text()); System.out.println("Metadata: " + match.embedded().metadata()); - System.out.println("Text: " + match.embedded().text()); } } diff --git a/oracle-example/src/main/resources/example-files/biography-of-john-doe.txt b/oracle-example/src/main/resources/example-files/biography-of-john-doe.txt new file mode 100644 index 00000000..272c7db8 --- /dev/null +++ b/oracle-example/src/main/resources/example-files/biography-of-john-doe.txt @@ -0,0 +1,49 @@ +John Doe: An Imaginary Luminary + +Early Life and Education +John Doe was born on April 1, 1980, in the quaint town of Fictionville, USA. +The only child of Jane and Joe Doe, he exhibited an early inclination towards creativity and innovation. +Growing up, John was fascinated by the stories his mother, a local librarian, would read to him, +which sparked his love for literature and storytelling. + +Doe attended Fictionville High School, where he excelled academically and was a star athlete. +His teachers often noted his exceptional ability to blend creativity with logic. +John's high school science fair project, a prototype of an environmentally-friendly engine, won him the +National Young Innovators Award. +This achievement earned him a scholarship to Prestige University, where he majored in Mechanical Engineering +and minored in Creative Writing. + +Career Beginnings +After graduating with honors, Doe joined a renowned tech firm, InnoTech, as a junior engineer. +His innovative approach and dedication quickly propelled him up the ranks. +By the age of 30, John had become the lead engineer of InnoTech's research and development department, +where he spearheaded groundbreaking projects in renewable energy. + +However, his love for writing never waned. +In his spare time, John wrote short stories, many of which were published in esteemed literary magazines. +His unique blend of scientific knowledge and creative storytelling earned him a devoted following. + +Entrepreneurial Ventures +At 35, Doe ventured into entrepreneurship, founding EcoSolutions, a company focused on sustainable technologies. +Under his leadership, EcoSolutions developed the "GreenDrive," an eco-friendly engine that revolutionized +the automotive industry. +His success in business was paralleled by his literary accomplishments; he published his debut novel, +"The Inventor's Dilemma," which became a bestseller. + +Philanthropy and Personal Life +John Doe's success in business and literature was matched by his commitment to philanthropy. +He established the "Doe Foundation" to support education and environmental initiatives. +His efforts earned him various humanitarian awards. + +In his personal life, John is known to be a private individual. +He married his college sweetheart, Emily, and they have two children. +The family resides in a sustainable home in Fictionville, where Doe enjoys gardening and mentoring young entrepreneurs. + +Legacy +John Doe's legacy is multifaceted: a pioneering engineer, a celebrated author, and a dedicated philanthropist. +His contributions to technology and literature have left an indelible mark on the world, +inspiring countless individuals to pursue their dreams with passion and determination. + +As he often says, "Innovation is the intersection of imagination and reality." +John Doe's life is a testament to this belief, a blend of the fantastical and the tangible, +making him not just a figure of success, but a symbol of the limitless potential of the human spirit. \ No newline at end of file From 41078eca398359c563010e89b7ec6569bdaafed8 Mon Sep 17 00:00:00 2001 From: David Jiang Date: Tue, 27 May 2025 17:44:23 -0400 Subject: [PATCH 28/28] Display metadata before text --- oracle-example/src/main/java/OracleIngestExample.java | 4 ++-- oracle-example/src/main/java/OracleIngestManualExample.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/oracle-example/src/main/java/OracleIngestExample.java b/oracle-example/src/main/java/OracleIngestExample.java index b35dae9c..b30132f5 100644 --- a/oracle-example/src/main/java/OracleIngestExample.java +++ b/oracle-example/src/main/java/OracleIngestExample.java @@ -112,9 +112,9 @@ public static void main(String[] args) throws SQLException, IOException { System.out.println(question); List> results = result.matches(); for (EmbeddingMatch match : results) { - System.out.println("Score: " + match.score()); - System.out.println("Segment: " + match.embedded().text()); + System.out.println("\nScore: " + match.score()); System.out.println("Metadata: " + match.embedded().metadata()); + System.out.println("Text: " + match.embedded().text()); } } } diff --git a/oracle-example/src/main/java/OracleIngestManualExample.java b/oracle-example/src/main/java/OracleIngestManualExample.java index 070509d7..376f5de0 100644 --- a/oracle-example/src/main/java/OracleIngestManualExample.java +++ b/oracle-example/src/main/java/OracleIngestManualExample.java @@ -122,9 +122,9 @@ public static void main(String[] args) throws SQLException, IOException { // display the results System.out.println(question); for (EmbeddingMatch match : relevantEmbeddings) { - System.out.println("Score: " + match.score()); - System.out.println("Segment: " + match.embedded().text()); + System.out.println("\nScore: " + match.score()); System.out.println("Metadata: " + match.embedded().metadata()); + System.out.println("Text: " + match.embedded().text()); } }