diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 44e342554..2966f3e16 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -15,6 +15,7 @@ ** xref:redis-store.adoc[Redis Store] ** xref:chroma-store.adoc[Chroma Store] ** xref:in-process-embedding.adoc[In-Process Embeddings] +** xref:csv.adoc[Loading CSV files] * Advanced topics ** xref:fault-tolerance.adoc[Fault Tolerance] \ No newline at end of file diff --git a/docs/modules/ROOT/pages/csv.adoc b/docs/modules/ROOT/pages/csv.adoc new file mode 100644 index 000000000..b7a5e1244 --- /dev/null +++ b/docs/modules/ROOT/pages/csv.adoc @@ -0,0 +1,129 @@ += Loading CSV files + +include::./includes/attributes.adoc[] + +When working with the Retrieval Augmented Generation (RAG) model, it is often necessary to load tabular data, such as a CSV file. This guide provides recommendations for loading CSV files in a way that is compatible with the RAG model. + +When loading a CSV file, the process involves: + +1. Transforming each row into a *document*. +2. Ingesting the set of documents using an appropriate *document splitter*. +3. Storing the documents in the database. + +You can find a complete example in the [GitHub repository](https://github.com/quarkiverse/quarkus-langchain4j/tree/main/samples/csv-chatbot). + +== From CSV to Documents + +There are multiple ways to load CSV files in Java. In this example, we use the following dependencies: + +[source,xml] +---- + + org.apache.commons + commons-csv + 1.10.0 + +---- + +You can choose a different library; the APIs are similar enough. + +Once you have the dependency, load the CSV and process the rows: + +[source, java] +---- +/** + * The CSV file to load. + */ +@ConfigProperty(name = "csv.file") +File file; + +/** + * The CSV file headers. + * Some libraries provide an API to extract them. + */ +@ConfigProperty(name = "csv.headers") +List headers; + +/** + * Ingest the CSV file. + * This method is executed when the application starts. + */ +public void ingest(@Observes StartupEvent event) throws IOException { + // Configure the CSV format. + CSVFormat csvFormat = CSVFormat.DEFAULT.builder() + .setHeader(headers.toArray(new String[0])) + .setSkipHeaderRecord(true) + .build(); + // This will be the resulting list of documents: + List documents = new ArrayList<>(); + + try (Reader reader = new FileReader(file)) { + // Generate one document per row, using the specified syntax. + Iterable records = csvFormat.parse(reader); + int i = 1; + for (CSVRecord record : records) { + Map metadata = new HashMap<>(); + metadata.put("source", file.getAbsolutePath()); + metadata.put("row", String.valueOf(i++)); + + StringBuilder content = new StringBuilder(); + for (String header : headers) { + // Include all headers in the metadata. + metadata.put(header, record.get(header)); + content.append(header).append(": ").append(record.get(header)).append("\n"); + } + documents.add(new Document(content.toString(), Metadata.from(metadata))); + } + // ... +} +---- + +== Ingesting the Documents + +Once you have the list of documents, they need to be ingested. For this, use a *document splitter*. We recommend the `recurve` splitter, a simple splitter that divides the document into chunks of a given size. While it may not be the most suitable splitter for your use case, it serves as a good starting point. + +[source, java] +---- +var ingestor = EmbeddingStoreIngestor.builder() + .embeddingStore(store) // Injected + .embeddingModel(embeddingModel) // Injected + .documentSplitter(recursive(500, 0)) + .build(); +ingestor.ingest(documents); +---- + +== Implementing the Retriever + +With the documents ingested, you can now implement the retriever: + +[source, java] +---- +package io.quarkiverse.langchain4j.sample.chatbot; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; + +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.retriever.EmbeddingStoreRetriever; +import dev.langchain4j.retriever.Retriever; +import io.quarkiverse.langchain4j.redis.RedisEmbeddingStore; + +@ApplicationScoped +public class RetrieverExample implements Retriever { + + private final EmbeddingStoreRetriever retriever; + + RetrieverExample(RedisEmbeddingStore store, EmbeddingModel model) { + // Limit the number of documents to avoid exceeding the context size. + retriever = EmbeddingStoreRetriever.from(store, model, 10); + } + + @Override + public List findRelevant(String s) { + return retriever.findRelevant(s); + } +} +---- + diff --git a/pom.xml b/pom.xml index aa23dd6c5..846fc5fad 100644 --- a/pom.xml +++ b/pom.xml @@ -105,6 +105,7 @@ samples/review-triage samples/fraud-detection samples/chatbot + samples/csv-chatbot diff --git a/samples/csv-chatbot/pom.xml b/samples/csv-chatbot/pom.xml new file mode 100644 index 000000000..1dd32926d --- /dev/null +++ b/samples/csv-chatbot/pom.xml @@ -0,0 +1,160 @@ + + + 4.0.0 + + + io.quarkiverse.langchain4j + quarkus-langchain4j-parent + 999-SNAPSHOT + ../.. + + + quarkus-langchain4j-sample-csv-chatbot + Quarkus langchain4j - Sample - Chatbot & RAG loading a CSV file + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-websockets + + + io.quarkiverse.langchain4j + quarkus-langchain4j-openai + ${project.version} + + + io.quarkiverse.langchain4j + quarkus-langchain4j-redis + ${project.version} + + + org.apache.commons + commons-csv + 1.10.0 + + + + + + org.mvnpm + importmap + 1.0.8 + + + org.mvnpm.at.mvnpm + vaadin-webcomponents + 24.2.1 + runtime + + + org.mvnpm + es-module-shims + runtime + 1.8.2 + + + org.mvnpm + wc-chatbot + 0.1.2 + runtime + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + + build + + + + + + maven-compiler-plugin + + + maven-surefire-plugin + 3.2.2 + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + 3.2.2 + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + + ${maven.home} + + + + + + + + + native + + + + + mvnpm + + true + + + + central + central + https://repo.maven.apache.org/maven2 + + + + false + + mvnpm.org + mvnpm + https://repo.mvnpm.org/maven2 + + + + + + \ No newline at end of file diff --git a/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ChatBotWebSocket.java b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ChatBotWebSocket.java new file mode 100644 index 000000000..2a98274a3 --- /dev/null +++ b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ChatBotWebSocket.java @@ -0,0 +1,50 @@ +package io.quarkiverse.langchain4j.sample.chatbot; + +import java.io.IOException; + +import jakarta.inject.Inject; +import jakarta.websocket.*; +import jakarta.websocket.server.ServerEndpoint; + +import io.smallrye.mutiny.infrastructure.Infrastructure; + +@ServerEndpoint("/chatbot") +public class ChatBotWebSocket { + + @Inject + MovieMuse bot; + + @Inject + ChatMemoryBean chatMemoryBean; + + @OnOpen + public void onOpen(Session session) { + Infrastructure.getDefaultExecutor().execute(() -> { + String response = bot.chat(session, "hello"); + try { + session.getBasicRemote().sendText(response); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + @OnClose + void onClose(Session session) { + chatMemoryBean.clear(session); + } + + @OnMessage + public void onMessage(String message, Session session) { + Infrastructure.getDefaultExecutor().execute(() -> { + String response = bot.chat(session, message); + try { + session.getBasicRemote().sendText(response); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + } + +} diff --git a/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ChatMemoryBean.java b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ChatMemoryBean.java new file mode 100644 index 000000000..079dc84ce --- /dev/null +++ b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ChatMemoryBean.java @@ -0,0 +1,28 @@ +package io.quarkiverse.langchain4j.sample.chatbot; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.enterprise.context.ApplicationScoped; + +import dev.langchain4j.memory.ChatMemory; +import dev.langchain4j.memory.chat.ChatMemoryProvider; +import dev.langchain4j.memory.chat.MessageWindowChatMemory; + +@ApplicationScoped +public class ChatMemoryBean implements ChatMemoryProvider { + + private final Map memories = new ConcurrentHashMap<>(); + + @Override + public ChatMemory get(Object memoryId) { + return memories.computeIfAbsent(memoryId, id -> MessageWindowChatMemory.builder() + .maxMessages(10) + .id(memoryId) + .build()); + } + + public void clear(Object session) { + memories.remove(session); + } +} diff --git a/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/CsvIngestorExample.java b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/CsvIngestorExample.java new file mode 100644 index 000000000..a67c98001 --- /dev/null +++ b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/CsvIngestorExample.java @@ -0,0 +1,89 @@ +package io.quarkiverse.langchain4j.sample.chatbot; + +import static dev.langchain4j.data.document.splitter.DocumentSplitters.recursive; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVRecord; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.Metadata; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; +import io.quarkiverse.langchain4j.redis.RedisEmbeddingStore; +import io.quarkus.runtime.StartupEvent; + +@ApplicationScoped +public class CsvIngestorExample { + + /** + * The embedding store (the database). + * The bean is provided by the quarkus-langchain4j-redis extension. + */ + @Inject + RedisEmbeddingStore store; + + /** + * The embedding model (how the vector of a document is computed). + * The bean is provided by the LLM (like openai) extension. + */ + @Inject + EmbeddingModel embeddingModel; + + @ConfigProperty(name = "csv.file") + File file; + + @ConfigProperty(name = "csv.headers") + List headers; + + public void ingest(@Observes StartupEvent event) throws IOException { + CSVFormat csvFormat = CSVFormat.DEFAULT.builder() + .setHeader(headers.toArray(new String[0])) + .setSkipHeaderRecord(true) + .build(); + List documents = new ArrayList<>(); + try (Reader reader = new FileReader(file)) { + // Generate on document per row, the document is using the following syntax: + // key1: value1 + // key2: value2 + Iterable records = csvFormat.parse(reader); + int i = 1; + for (CSVRecord record : records) { + + Map metadata = new HashMap<>(); + metadata.put("source", file.getAbsolutePath()); + metadata.put("row", String.valueOf(i++)); + + StringBuilder content = new StringBuilder(); + for (String header : headers) { + metadata.put(header, record.get(header)); // Include all headers in the metadata. + content.append(header).append(": ").append(record.get(header)).append("\n"); + } + documents.add(new Document(content.toString(), Metadata.from(metadata))); + } + + var ingestor = EmbeddingStoreIngestor.builder() + .embeddingStore(store) + .embeddingModel(embeddingModel) + .documentSplitter(recursive(500, 0)) + .build(); + ingestor.ingest(documents); + System.out.printf("Ingested %d documents.%n", documents.size()); + + } + + } +} diff --git a/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ImportmapResource.java b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ImportmapResource.java new file mode 100644 index 000000000..7bdc60403 --- /dev/null +++ b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/ImportmapResource.java @@ -0,0 +1,51 @@ +package io.quarkiverse.langchain4j.sample.chatbot; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; + +import org.mvnpm.importmap.Aggregator; + +/** + * Dynamically create the import map + */ +@ApplicationScoped +@Path("/_importmap") +public class ImportmapResource { + private String importmap; + + // See https://github.com/WICG/import-maps/issues/235 + // This does not seem to be supported by browsers yet... + @GET + @Path("/dynamic.importmap") + @Produces("application/importmap+json") + public String importMap() { + return this.importmap; + } + + @GET + @Path("/dynamic-importmap.js") + @Produces("application/javascript") + public String importMapJson() { + return JAVASCRIPT_CODE.formatted(this.importmap); + } + + @PostConstruct + void init() { + Aggregator aggregator = new Aggregator(); + // Add our own mappings + aggregator.addMapping("icons/", "/icons/"); + aggregator.addMapping("components/", "/components/"); + aggregator.addMapping("fonts/", "/fonts/"); + this.importmap = aggregator.aggregateAsJson(); + } + + private static final String JAVASCRIPT_CODE = """ + const im = document.createElement('script'); + im.type = 'importmap'; + im.textContent = JSON.stringify(%s); + document.currentScript.after(im); + """; +} diff --git a/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/MovieMuse.java b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/MovieMuse.java new file mode 100644 index 000000000..b93aa9fa2 --- /dev/null +++ b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/MovieMuse.java @@ -0,0 +1,18 @@ +package io.quarkiverse.langchain4j.sample.chatbot; + +import dev.langchain4j.service.MemoryId; +import dev.langchain4j.service.SystemMessage; +import dev.langchain4j.service.UserMessage; +import io.quarkiverse.langchain4j.RegisterAiService; + +@RegisterAiService +public interface MovieMuse { + + @SystemMessage(""" + You are MovieMuse, an AI answering questions about the top 100 movies from IMDB. + Your response must be polite, use the same language as the question, and be relevant to the question. + + Introduce yourself with: "Hello, I'm MovieMuse, how can I help you?" + """) + String chat(@MemoryId Object session, @UserMessage String question); +} diff --git a/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/RetrieverExample.java b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/RetrieverExample.java new file mode 100644 index 000000000..65f409e04 --- /dev/null +++ b/samples/csv-chatbot/src/main/java/io/quarkiverse/langchain4j/sample/chatbot/RetrieverExample.java @@ -0,0 +1,26 @@ +package io.quarkiverse.langchain4j.sample.chatbot; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; + +import dev.langchain4j.data.segment.TextSegment; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.retriever.EmbeddingStoreRetriever; +import dev.langchain4j.retriever.Retriever; +import io.quarkiverse.langchain4j.redis.RedisEmbeddingStore; + +@ApplicationScoped +public class RetrieverExample implements Retriever { + + private final EmbeddingStoreRetriever retriever; + + RetrieverExample(RedisEmbeddingStore store, EmbeddingModel model) { + retriever = EmbeddingStoreRetriever.from(store, model, 10); + } + + @Override + public List findRelevant(String s) { + return retriever.findRelevant(s); + } +} diff --git a/samples/csv-chatbot/src/main/resources/META-INF/resources/components/demo-chat.js b/samples/csv-chatbot/src/main/resources/META-INF/resources/components/demo-chat.js new file mode 100644 index 000000000..395e96c78 --- /dev/null +++ b/samples/csv-chatbot/src/main/resources/META-INF/resources/components/demo-chat.js @@ -0,0 +1,45 @@ +import {css, LitElement} from 'lit'; +import '@vaadin/icon'; +import '@vaadin/button'; +import '@vaadin/text-field'; +import '@vaadin/text-area'; +import '@vaadin/form-layout'; +import '@vaadin/progress-bar'; +import '@vaadin/checkbox'; +import '@vaadin/horizontal-layout'; +import '@vaadin/grid'; +import '@vaadin/grid/vaadin-grid-sort-column.js'; + +export class DemoChat extends LitElement { + static styles = css` + .button { + cursor: pointer; + } + `; + + connectedCallback() { + const chatBot = document.getElementsByTagName("chat-bot")[0]; + + const socket = new WebSocket("ws://" + window.location.host + "/chatbot"); + socket.onmessage = function (event) { + chatBot.sendMessage(event.data, { + right: false, + sender: {name: 'Bob', id: '007'} + }); + } + + chatBot.addEventListener("sent", function (e) { + if (e.detail.message.right === true) { + // User message + socket.send(e.detail.message.message); + chatBot.sendMessage("", { + right: false, + sender: {name: 'Bob', id: '007'}, + loading: true + }); + } + }); + } +} + +customElements.define('demo-chat', DemoChat); \ No newline at end of file diff --git a/samples/csv-chatbot/src/main/resources/META-INF/resources/components/demo-title.js b/samples/csv-chatbot/src/main/resources/META-INF/resources/components/demo-title.js new file mode 100644 index 000000000..575da6f24 --- /dev/null +++ b/samples/csv-chatbot/src/main/resources/META-INF/resources/components/demo-title.js @@ -0,0 +1,79 @@ +import {LitElement, html, css} from 'lit'; +import '@vaadin/icon'; +import '@vaadin/button'; +import '@vaadin/text-field'; +import '@vaadin/text-area'; +import '@vaadin/form-layout'; +import '@vaadin/progress-bar'; +import '@vaadin/checkbox'; +import '@vaadin/grid'; +import '@vaadin/grid/vaadin-grid-sort-column.js'; + +export class DemoTitle extends LitElement { + + static styles = css` + h1 { + font-family: "Red Hat Mono", monospace; + font-size: 60px; + font-style: normal; + font-variant: normal; + font-weight: 700; + line-height: 26.4px; + color: var(--main-highlight-text-color); + } + + .title { + text-align: center; + padding: 1em; + background: var(--main-bg-color); + } + + .explanation { + margin-left: auto; + margin-right: auto; + width: 50%; + text-align: justify; + font-size: 20px; + } + + .explanation img { + max-width: 60%; + display: block; + float:left; + margin-right: 2em; + margin-top: 1em; + } + ` + + render() { + return html` +
+

Movie Muse

+
+
+ This demo shows how to build a chatbot powered by GPT 3.5 and retrieval augmented generation. + The application ingested a CSV files listing the top 100 movies from IMDB. + You can now ask questions about the movies and the application will answer. + For example What is the rating of the movie Inception? or What is the genre of the movie Inception? +
+ +
+ +
+ +
+
    +
  1. The user send a question.
  2. +
  3. The application looks for relevant data in the store.
  4. +
  5. The relevant data is retrieved and added to the user's question.
  6. +
  7. The extended question is sent to the LLM model.
  8. +
  9. The response is received and sent back to the user.
  10. +
+
+ ` + } + + +} + +customElements.define('demo-title', DemoTitle); \ No newline at end of file diff --git a/samples/csv-chatbot/src/main/resources/META-INF/resources/fonts/red-hat-font.min.css b/samples/csv-chatbot/src/main/resources/META-INF/resources/fonts/red-hat-font.min.css new file mode 100644 index 000000000..f03010775 --- /dev/null +++ b/samples/csv-chatbot/src/main/resources/META-INF/resources/fonts/red-hat-font.min.css @@ -0,0 +1 @@ +@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg8z6hR4jNCH5Z.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg_T6hR4jNCA.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg8z6hR4jNCH5Z.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg_T6hR4jNCA.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg8z6hR4jNCH5Z.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg_T6hR4jNCA.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:"Red Hat Text";font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/redhattext/v6/RrQXbohi_ic6B3yVSzGBrMxQZqctMc-JPWCN.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Text";font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/redhattext/v6/RrQXbohi_ic6B3yVSzGBrMxQaKctMc-JPQ.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:"Red Hat Text";font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/redhattext/v6/RrQXbohi_ic6B3yVSzGBrMxQZqctMc-JPWCN.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Text";font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/redhattext/v6/RrQXbohi_ic6B3yVSzGBrMxQaKctMc-JPQ.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}/*# sourceMappingURL=red-hat-font.css.map */ \ No newline at end of file diff --git a/samples/csv-chatbot/src/main/resources/META-INF/resources/icons/font-awesome-solid.js b/samples/csv-chatbot/src/main/resources/META-INF/resources/icons/font-awesome-solid.js new file mode 100644 index 000000000..70b8b4031 --- /dev/null +++ b/samples/csv-chatbot/src/main/resources/META-INF/resources/icons/font-awesome-solid.js @@ -0,0 +1,1399 @@ +import '@vaadin/icon'; + +const template = document.createElement('template'); + +template.innerHTML = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +document.head.appendChild(template.content); diff --git a/samples/csv-chatbot/src/main/resources/META-INF/resources/icons/font-awesome.js b/samples/csv-chatbot/src/main/resources/META-INF/resources/icons/font-awesome.js new file mode 100644 index 000000000..225aa21aa --- /dev/null +++ b/samples/csv-chatbot/src/main/resources/META-INF/resources/icons/font-awesome.js @@ -0,0 +1,7 @@ +// import './font-awesome-brands.js'; +// import './font-awesome-regular.js'; +import './font-awesome-solid.js'; + +// export * from './font-awesome-brands.js'; +// export * from './font-awesome-regular.js'; +export * from './font-awesome-solid.js'; diff --git a/samples/csv-chatbot/src/main/resources/META-INF/resources/images/chatbot-architecture.png b/samples/csv-chatbot/src/main/resources/META-INF/resources/images/chatbot-architecture.png new file mode 100644 index 000000000..025b6d7b8 Binary files /dev/null and b/samples/csv-chatbot/src/main/resources/META-INF/resources/images/chatbot-architecture.png differ diff --git a/samples/csv-chatbot/src/main/resources/META-INF/resources/index.html b/samples/csv-chatbot/src/main/resources/META-INF/resources/index.html new file mode 100644 index 000000000..38bf40ab1 --- /dev/null +++ b/samples/csv-chatbot/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + MovieMuse + + + + + + + + +
+ + + +
+ + + + \ No newline at end of file diff --git a/samples/csv-chatbot/src/main/resources/application.properties b/samples/csv-chatbot/src/main/resources/application.properties new file mode 100644 index 000000000..0b574c87e --- /dev/null +++ b/samples/csv-chatbot/src/main/resources/application.properties @@ -0,0 +1,10 @@ +quarkus.langchain4j.redis.dimension=1536 +quarkus.redis.devservices.image-name=redis/redis-stack:latest + +quarkus.langchain4j.openai.timeout=60s + + +quarkus.redis.max-pool-waiting=1000 + +csv.file=src/main/resources/data/movies.csv +csv.headers=index,movie_name,year_of_release,category,run_time,genre,imdb_rating,votes,gross_total \ No newline at end of file diff --git a/samples/csv-chatbot/src/main/resources/data/movies.csv b/samples/csv-chatbot/src/main/resources/data/movies.csv new file mode 100644 index 000000000..35c87ad15 --- /dev/null +++ b/samples/csv-chatbot/src/main/resources/data/movies.csv @@ -0,0 +1,100 @@ +index,movie_name,year_of_release,category,run_time,genre,imdb_rating,votes,gross_total +1.,The Godfather,(1972),R,175 min,"Crime, Drama",9.2,"1,860,471",$134.97M +2.,The Silence of the Lambs,(1991),R,118 min,"Crime, Drama, Thriller",8.6,"1,435,344",$130.74M +3.,Star Wars: Episode V - The Empire Strikes Back,(1980),PG,124 min,"Action, Adventure, Fantasy",8.7,"1,294,805",$290.48M +4.,The Shawshank Redemption,(1994),R,142 min,Drama,9.3,"2,683,302",$28.34M +5.,The Shining,(1980),R,146 min,"Drama, Horror",8.4,"1,025,560",$44.02M +6.,Casablanca,(1942),PG,102 min,"Drama, Romance, War",8.5,"574,092",$1.02M +7.,One Flew Over the Cuckoo's Nest,(1975),R,133 min,Drama,8.7,"1,010,102",$112.00M +8.,Indiana Jones and the Raiders of the Lost Ark,(1981),PG,115 min,"Action, Adventure",8.4,"969,143",$248.16M +9.,The Lord of the Rings: The Return of the King,(2003),PG-13,201 min,"Action, Adventure, Drama",9,"1,849,082",$377.85M +10.,Star Wars: Episode IV - A New Hope,(1977),PG,121 min,"Action, Adventure, Fantasy",8.6,"1,367,430",$322.74M +11.,The Dark Knight,(2008),PG-13,152 min,"Action, Crime, Drama",9,"2,656,768",$534.86M +12.,The Godfather: Part II,(1974),R,202 min,"Crime, Drama",9,"1,273,349",$57.30M +13.,Aliens,(1986),R,137 min,"Action, Adventure, Sci-Fi",8.4,"720,623",$85.16M +14.,Schindler's List,(1993),R,195 min,"Biography, Drama, History",9,"1,357,621",$96.90M +15.,Inception,(2010),PG-13,148 min,"Action, Adventure, Sci-Fi",8.8,"2,356,293",$292.58M +16.,The Lord of the Rings: The Fellowship of the Ring,(2001),PG-13,178 min,"Action, Adventure, Drama",8.8,"1,878,557",$315.54M +17.,Alien,(1979),R,117 min,"Horror, Sci-Fi",8.5,"885,635",$78.90M +18.,Some Like It Hot,(1959),Passed,121 min,"Comedy, Music, Romance",8.2,"269,346",$25.00M +19.,Blade Runner,(1982),R,117 min,"Action, Drama, Sci-Fi",8.1,"773,425",$32.87M +20.,Se7en,(1995),R,127 min,"Crime, Drama, Mystery",8.6,"1,655,745",$100.13M +21.,Apocalypse Now,(1979),R,147 min,"Drama, Mystery, War",8.5,"669,994",$83.47M +22.,12 Angry Men,(1957),Approved,96 min,"Crime, Drama",9,"792,729",$4.36M +23.,The Lord of the Rings: The Two Towers,(2002),PG-13,179 min,"Action, Adventure, Drama",8.8,"1,669,715",$342.55M +24.,Terminator 2: Judgment Day,(1991),R,137 min,"Action, Sci-Fi",8.6,"1,101,850",$204.84M +25.,Star Wars: Episode VI - Return of the Jedi,(1983),PG,131 min,"Action, Adventure, Fantasy",8.3,"1,056,750",$309.13M +26.,Die Hard,(1988),R,132 min,"Action, Thriller",8.2,"887,967",$83.01M +27.,Gone with the Wind,(1939),Passed,238 min,"Drama, Romance, War",8.2,"317,621",$198.68M +28.,Taxi Driver,(1976),R,114 min,"Crime, Drama",8.2,"836,871",$28.26M +29.,Pulp Fiction,(1994),R,154 min,"Crime, Drama",8.9,"2,058,574",$107.93M +30.,The Bridge on the River Kwai,(1957),PG,161 min,"Adventure, Drama, War",8.2,"222,540",$44.91M +31.,The Lion King,(1994),G,88 min,"Animation, Adventure, Drama",8.5,"1,060,900",$422.78M +32.,North by Northwest,(1959),Approved,136 min,"Action, Adventure, Mystery",8.3,"330,232",$13.28M +33.,Rear Window,(1954),PG,112 min,"Mystery, Thriller",8.5,"493,926",$36.76M +34.,Léon: The Professional,(1994),R,110 min,"Action, Crime, Drama",8.5,"1,164,087",$19.50M +35.,Back to the Future,(1985),PG,116 min,"Adventure, Comedy, Sci-Fi",8.5,"1,208,582",$210.61M +36.,Citizen Kane,(1941),PG,119 min,"Drama, Mystery",8.3,"444,359",$1.59M +37.,Goodfellas,(1990),R,145 min,"Biography, Crime, Drama",8.7,"1,164,128",$46.84M +38.,Memento,(2000),R,113 min,"Mystery, Thriller",8.4,"1,241,252",$25.54M +39.,American Beauty,(1999),R,122 min,Drama,8.4,"1,157,536",$130.10M +40.,As Good as It Gets,(1997),PG-13,139 min,"Comedy, Drama, Romance",7.7,"301,756",$148.48M +41.,Forrest Gump,(1994),PG-13,142 min,"Drama, Romance",8.8,"2,082,477",$330.25M +42.,Singin' in the Rain,(1952),G,103 min,"Comedy, Musical, Romance",8.3,"244,548",$8.82M +43.,Braveheart,(1995),R,178 min,"Biography, Drama, History",8.4,"1,040,416",$75.60M +44.,Saving Private Ryan,(1998),R,169 min,"Drama, War",8.6,"1,394,262",$216.54M +45.,Rain Man,(1988),R,133 min,Drama,8,"517,528",$178.80M +46.,The King's Speech,(2010),R,118 min,"Biography, Drama, History",8,"683,379",$138.80M +47.,2001: A Space Odyssey,(1968),G,149 min,"Adventure, Sci-Fi",8.3,"671,980",$56.95M +48.,Kill Bill: Vol. 1,(2003),R,111 min,"Action, Crime, Drama",8.2,"1,119,120",$70.10M +49.,Avanti!,(1972),R,144 min,"Comedy, Romance",7.2,"10,748",$3.30M +50.,"The Good, the Bad and the Ugly",(1966),Approved,178 min,"Adventure, Western",8.8,"763,678",$6.10M +51.,Amélie,(2001),R,122 min,"Comedy, Romance",8.3,"759,411",$33.23M +52.,Modern Times,(1936),G,87 min,"Comedy, Drama, Romance",8.5,"244,162",$0.16M +53.,Lost in Translation,(2003),R,102 min,"Comedy, Drama",7.7,"458,613",$44.59M +54.,Full Metal Jacket,(1987),R,116 min,"Drama, War",8.3,"745,546",$46.36M +55.,Requiem for a Dream,(2000),R,102 min,Drama,8.3,"845,362",$3.64M +56.,Fight Club,(1999),R,139 min,Drama,8.8,"2,128,902",$37.03M +57.,No Country for Old Men,(2007),R,122 min,"Crime, Drama, Thriller",8.2,"977,336",$74.28M +58.,Django Unchained,(2012),R,165 min,"Drama, Western",8.4,"1,557,890",$162.81M +59.,Children of Men,(2006),R,109 min,"Action, Drama, Sci-Fi",7.9,"503,642",$35.55M +60.,Ratatouille,(2007),G,111 min,"Animation, Adventure, Comedy",8.1,"741,322",$206.45M +61.,The Lives of Others,(2006),R,137 min,"Drama, Mystery, Thriller",8.4,"391,480",$11.29M +62.,The Prestige,(2006),PG-13,130 min,"Drama, Mystery, Sci-Fi",8.5,"1,336,235",$53.09M +63.,V for Vendetta,(2005),R,132 min,"Action, Drama, Sci-Fi",8.2,"1,125,038",$70.51M +64.,Chinatown,(1974),R,130 min,"Drama, Mystery, Thriller",8.2,"329,110",$8.49M +65.,City of God,(2002),R,130 min,"Crime, Drama",8.6,"758,914",$7.56M +66.,To Have and Have Not,(1944),Passed,100 min,"Adventure, Comedy, Film-Noir",7.8,"35,528", +67.,Fargo,(1996),R,98 min,"Crime, Thriller",8.1,"681,256",$24.61M +68.,Life of Pi,(2012),PG,127 min,"Adventure, Drama, Fantasy",7.9,"634,357",$124.99M +69.,Slumdog Millionaire,(2008),R,120 min,"Crime, Drama, Romance",8,"848,344",$141.32M +70.,Vertigo,(1958),PG,128 min,"Mystery, Romance, Thriller",8.3,"404,626",$3.20M +71.,Trainspotting,(1996),R,93 min,Drama,8.1,"690,138",$16.50M +72.,Interstellar,(2014),PG-13,169 min,"Adventure, Drama, Sci-Fi",8.6,"1,835,790",$188.02M +73.,The Thing,(1982),R,109 min,"Horror, Mystery, Sci-Fi",8.2,"428,474",$13.78M +74.,The Third Man,(1949),Approved,93 min,"Film-Noir, Mystery, Thriller",8.1,"173,206",$0.45M +75.,12 Monkeys,(1995),R,129 min,"Mystery, Sci-Fi, Thriller",8,"621,205",$57.14M +76.,Life Is Beautiful,(1997),PG-13,116 min,"Comedy, Drama, Romance",8.6,"697,226",$57.60M +77.,The Pianist,(2002),R,150 min,"Biography, Drama, Music",8.5,"834,842",$32.57M +78.,Magnolia,(1999),R,188 min,Drama,8,"315,037",$22.46M +79.,The Dark Knight Rises,(2012),PG-13,164 min,"Action, Drama",8.4,"1,708,002",$448.14M +80.,Star Wars: Episode VII - The Force Awakens,(2015),PG-13,138 min,"Action, Adventure, Sci-Fi",7.8,"933,771",$936.66M +81.,The Hobbit: The Desolation of Smaug,(2013),PG-13,161 min,"Adventure, Fantasy",7.8,"667,864",$258.37M +82.,Mad Max: Fury Road,(2015),R,120 min,"Action, Adventure, Sci-Fi",8.1,"1,006,158",$154.06M +83.,12 Years a Slave,(2013),R,134 min,"Biography, Drama, History",8.1,"703,824",$56.67M +84.,Indiana Jones and the Last Crusade,(1989),PG-13,127 min,"Action, Adventure",8.2,"758,057",$197.17M +85.,"O Brother, Where Art Thou?",(2000),PG-13,107 min,"Adventure, Comedy, Crime",7.7,"315,173",$45.51M +86.,Inglourious Basterds,(2009),R,153 min,"Adventure, Drama, War",8.3,"1,453,288",$120.54M +87.,The Departed,(2006),R,151 min,"Crime, Drama, Thriller",8.5,"1,328,252",$132.38M +88.,A Beautiful Mind,(2001),PG-13,135 min,"Biography, Drama",8.2,"935,549",$170.74M +89.,District 9,(2009),R,112 min,"Action, Sci-Fi, Thriller",7.9,"685,403",$115.65M +90.,The Piano,(1993),R,121 min,"Drama, Music, Romance",7.5,"89,819",$40.16M +91.,Mystic River,(2003),R,138 min,"Crime, Drama, Mystery",7.9,"459,918",$90.14M +92.,The Insider,(1999),R,157 min,"Biography, Drama, Thriller",7.8,"172,759",$28.97M +93.,L.A. Confidential,(1997),R,138 min,"Crime, Drama, Mystery",8.2,"585,555",$64.62M +94.,Heat,(1995),R,170 min,"Action, Crime, Drama",8.3,"658,033",$67.44M +95.,The Usual Suspects,(1995),R,106 min,"Crime, Drama, Mystery",8.5,"1,087,832",$23.34M +96.,Cool Hand Luke,(1967),GP,127 min,"Crime, Drama",8.1,"178,888",$16.22M +97.,Eternal Sunshine of the Spotless Mind,(2004),R,108 min,"Drama, Romance, Sci-Fi",8.3,"1,011,004",$34.40M +98.,City Lights,(1931),G,87 min,"Comedy, Drama, Romance",8.5,"186,059",$0.02M +99.,The Matrix,(1999),R,136 min,"Action, Sci-Fi",8.7,"1,916,083",$171.48M