This is an automated email from the ASF dual-hosted git repository.

jamesnetherton pushed a commit to branch camel-quarkus-main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus-examples.git


The following commit(s) were added to refs/heads/camel-quarkus-main by this 
push:
     new d919f70f Add the langchain4j-ingest-rag example: RAG document Q&A over 
an ingested folder
d919f70f is described below

commit d919f70f866bff0fba9d4dd2271a2a01a60bd31a
Author: Jiří Ondrušek <[email protected]>
AuthorDate: Fri Sep 25 15:31:58 2026 +0200

    Add the langchain4j-ingest-rag example: RAG document Q&A over an ingested 
folder
    
    A folder of documents becomes a knowledge base through the
    camel-quarkus-langchain4j-ingest extension, declared in 
application.properties
    only: the files are split, embedded in-process and stored in pgvector, 
started
    by Dev Services. A Quarkus LangChain4j AI service answers questions 
grounded in
    the retrieved segments through the RetrievalAugmentor the
    camel-quarkus-langchain4j-embeddingstore extension produces, so the example
    carries no RAG wiring; the chat model is served by Ollama. The /search,
    /chat/preview and /chat endpoints show each step. Tests run in JVM and 
native
    mode with a WireMock stand-in for Ollama.
    
    Requires the RAG augmentor fix of apache/camel-quarkus#9229.
    
    Co-authored-by: Claude Fable 5.1 <[email protected]>
---
 docs/modules/ROOT/attachments/examples.json        |   5 +
 langchain4j-ingest-rag/README.adoc                 | 139 +++++++++
 langchain4j-ingest-rag/documents/company-policy.md |  44 +++
 langchain4j-ingest-rag/documents/product-guide.md  |  67 ++++
 langchain4j-ingest-rag/documents/support-faq.md    |  61 ++++
 .../eclipse-formatter-config.xml                   | 276 +++++++++++++++++
 langchain4j-ingest-rag/pom.xml                     | 341 +++++++++++++++++++++
 .../acme/langchain4j/ingest/rag/ChatResource.java  |  59 ++++
 .../langchain4j/ingest/rag/DocumentAssistant.java  |  38 +++
 .../langchain4j/ingest/rag/SearchResource.java     |  58 ++++
 .../src/main/resources/application.properties      |  52 ++++
 .../ingest/rag/Langchain4jIngestRagIT.java         |  24 ++
 .../ingest/rag/Langchain4jIngestRagTest.java       |  80 +++++
 .../langchain4j/ingest/rag/OllamaTestResource.java |  90 ++++++
 pom.xml                                            |   1 +
 15 files changed, 1335 insertions(+)

diff --git a/docs/modules/ROOT/attachments/examples.json 
b/docs/modules/ROOT/attachments/examples.json
index de2599c5..e4b8ee09 100644
--- a/docs/modules/ROOT/attachments/examples.json
+++ b/docs/modules/ROOT/attachments/examples.json
@@ -94,6 +94,11 @@
     "description": "Shows how you can build a simple Kamelet and use with your 
Camel applications.",
     "link": 
"https://github.com/apache/camel-quarkus-examples/tree/main/kamelet-chucknorris";
   },
+  {
+    "title": "LangChain4j Ingest RAG",
+    "description": "Shows how to build a RAG document Q\u0026A where a folder 
of documents is ingested into a vector store by the langchain4j-ingest 
extension and an AI service answers questions grounded in them",
+    "link": 
"https://github.com/apache/camel-quarkus-examples/tree/main/langchain4j-ingest-rag";
+  },
   {
     "title": "Leader election in Kubernetes: A Camel Quarkus Master example",
     "description": "Shows how to use Camel master component.",
diff --git a/langchain4j-ingest-rag/README.adoc 
b/langchain4j-ingest-rag/README.adoc
new file mode 100644
index 00000000..3991be2d
--- /dev/null
+++ b/langchain4j-ingest-rag/README.adoc
@@ -0,0 +1,139 @@
+= LangChain4j Ingest RAG: A Camel Quarkus example
+:cq-example-description: An example that shows how to build a RAG document Q&A 
where a folder of documents is ingested into a vector store by the 
langchain4j-ingest extension and an AI service answers questions grounded in 
them
+
+{cq-description}
+
+TIP: Check the 
https://camel.apache.org/camel-quarkus/latest/first-steps.html[Camel Quarkus 
User guide] for prerequisites
+and other general information.
+
+== What the example does
+
+The goal is to answer questions using only the content of your own documents, 
the
+https://en.wikipedia.org/wiki/Retrieval-augmented_generation[RAG] pattern.
+
+* The `documents/` folder is the knowledge base. The 
`camel-quarkus-langchain4j-ingest` extension reads every file in it, splits the 
text into overlapping segments, embeds them and writes them to a 
https://github.com/pgvector/pgvector[pgvector] store. The whole pipeline is 
declared in `application.properties`; there is no route and no ingestion code.
+* `DocumentAssistant` is a plain Quarkus LangChain4j `@RegisterAiService` 
interface. The `camel-quarkus-langchain4j-embeddingstore` extension, which the 
ingest extension depends on, produces a `RetrievalAugmentor` from the 
application's embedding store and model, and Quarkus LangChain4j picks it up, 
so every question is augmented with the retrieved segments. Again, no RAG code.
+* The embedding model runs in-process (`all-MiniLM-L6-v2`), registered by 
Quarkus LangChain4j from the `langchain4j-embeddings-all-minilm-l6-v2` 
dependency, so ingestion and retrieval need no API key. Only the chat model is 
served by https://ollama.com/[Ollama].
+
+Three endpoints show the steps:
+
+* `GET /search?q=...` returns the segments closest to the question, with the 
document they came from.
+* `GET /chat/preview?q=...` returns the prompt the assistant would send, the 
question plus the retrieved context, without calling a model.
+* `GET /chat?q=...` asks the assistant.
+
+== Prerequisites
+
+* Docker, for the pgvector database that Quarkus Dev Services start in dev and 
test mode.
+* Ollama serving the chat model. Install it from 
https://ollama.com/[ollama.com] and pull the model once:
++
+[source,shell]
+----
+ollama pull llama3.2
+----
++
+Or run it in a container:
++
+[source,shell]
+----
+docker run --rm -d -v cqex-ingest-ollama:/root/.ollama -p 11434:11434 --name 
cqex-ingest-ollama docker.io/ollama/ollama:0.24.0
+docker exec -it cqex-ingest-ollama ollama pull llama3.2
+----
++
+An Ollama running elsewhere, or another model, is selected with environment 
variables:
++
+[source,shell]
+----
+export OLLAMA_BASE_URL=http://ollama.example.com:11434
+export OLLAMA_MODEL=llama3.2
+----
+
+== Start in the Development mode
+
+[source,shell]
+----
+mvn clean compile quarkus:dev
+----
+
+The above command compiles the project, starts the application and lets the 
Quarkus tooling watch for changes in your
+workspace. Any modifications in your project will automatically take effect in 
the running application.
+
+TIP: Please refer to the Development mode section of
+https://camel.apache.org/camel-quarkus/latest/first-steps.html#_development_mode[Camel
 Quarkus User guide] for more details.
+
+A pgvector database is started for you, and the log shows the folder being 
ingested:
+
+[source,shell]
+----
+INFO  [org.apa.cam.qua.com.lan.ing.IngestRoutes] (main) Ingestion pipeline 
'docs': source=file:documents
+----
+
+Then ask questions:
+
+[source,shell]
+----
+curl "http://localhost:8080/chat?q=What%20is%20the%20return%20policy%3F";
+curl "http://localhost:8080/chat?q=How%20do%20I%20reset%20my%20password%3F";
+curl 
"http://localhost:8080/chat?q=Our%20users%20can%20log%20in%20but%20synchronization%20is%20very%20slow%20with%20high%20CPU%20usage%20and%20no%20error%20codes.%20What%20network%20configuration%20might%20cause%20this%3F";
+----
+
+To see what retrieval found, and what the model was given, without calling the 
model:
+
+[source,shell]
+----
+curl "http://localhost:8080/search?q=What%20is%20the%20return%20policy%3F";
+curl 
"http://localhost:8080/chat/preview?q=What%20is%20the%20return%20policy%3F";
+----
+
+Drop your own `.md` or `.txt` files into `documents/`: a new or edited file is 
picked up on the next poll of the running application, and the whole folder is 
indexed again on every start.
+
+=== Package and run the application
+
+Once you are done with developing you may want to package and run the 
application.
+
+TIP: Find more details about the JVM mode and Native mode in the Package and 
run section of
+https://camel.apache.org/camel-quarkus/latest/first-steps.html#_package_and_run_the_application[Camel
 Quarkus User guide]
+
+Outside of dev mode there are no Dev Services, so start a pgvector database 
first. The connection details below are the ones configured for the `prod` 
profile in `application.properties`:
+
+[source,shell]
+----
+docker run --rm -d --name pgvector -p 5432:5432 -e POSTGRES_USER=postgres -e 
POSTGRES_PASSWORD=postgres -e POSTGRES_DB=documents 
docker.io/pgvector/pgvector:pg17
+----
+
+==== JVM mode
+
+[source,shell]
+----
+mvn clean package -DskipTests
+java -jar target/quarkus-app/quarkus-run.jar
+----
+
+==== Native mode
+
+IMPORTANT: Native mode requires having GraalVM and other tools installed. 
Please check the Prerequisites section
+of 
https://camel.apache.org/camel-quarkus/latest/first-steps.html#_prerequisites[Camel
 Quarkus User guide].
+
+To prepare a native executable using GraalVM, run the following command:
+
+[source,shell]
+----
+mvn clean package -DskipTests -Dnative
+./target/*-runner
+----
+
+== Testing
+
+The tests start pgvector through Dev Services and ingest the `documents/` 
folder. The chat model is played by a WireMock server answering the Ollama API 
with canned replies, so no model is needed. The fake model knows the answer 
only when the retrieved document text is part of the request, so a correct 
answer proves that retrieval grounded the prompt. To run them against a real 
Ollama instead, export `OLLAMA_BASE_URL` (and `OLLAMA_MODEL`) as shown above.
+
+[source,shell]
+----
+# JVM mode
+mvn clean test
+
+# native mode
+mvn clean verify -Dnative
+----
+
+== Feedback
+
+Please report bugs and propose improvements via 
https://github.com/apache/camel-quarkus/issues[GitHub issues of Camel Quarkus] 
project.
diff --git a/langchain4j-ingest-rag/documents/company-policy.md 
b/langchain4j-ingest-rag/documents/company-policy.md
new file mode 100644
index 00000000..4e60b6d8
--- /dev/null
+++ b/langchain4j-ingest-rag/documents/company-policy.md
@@ -0,0 +1,44 @@
+# Company Policy: Returns, Refunds and Exchanges
+
+This policy applies to every order placed in the Acme web store. It explains 
how to return a
+product, when a refund is issued and how exchanges work.
+
+## Standard returns
+
+Products can be returned within 30 days of delivery. To be accepted, a 
returned item must be
+unused, in its original packaging and accompanied by the order number. Returns 
requested after
+the 30-day window are declined, except for defective items covered by the 
warranty section below.
+
+To start a return, sign in to your account, open the order and choose "Return 
items". A prepaid
+shipping label is generated for orders within the European Union and the 
United Kingdom. For
+other destinations the return shipping cost is deducted from the refund.
+
+## Refunds
+
+Refunds are issued to the original payment method within 5 to 7 business days 
after the returned
+item reaches our warehouse. Shipping fees paid on the original order are not 
refunded unless the
+return is caused by our mistake. If a refund has not arrived after 10 business 
days, contact
+support with the order number and the return tracking number.
+
+## Exchanges
+
+An item can be exchanged for a different size or colour of the same product 
within the same
+30-day window. Exchanges ship free of charge once the original item has been 
received. To
+exchange a product for a different model, return it for a refund and place a 
new order.
+
+## Defective, damaged or incorrect items
+
+Report a defective, damaged or incorrect item within 14 days of delivery 
through the order page.
+We cover the return shipping and send a replacement, or issue a full refund 
including the original
+shipping fees. Hardware products carry a 24-month warranty against 
manufacturing defects; a
+warranty claim is handled by support and does not require the original 
packaging.
+
+## Items that cannot be returned
+
+Gift cards, downloadable software licences, opened consumables and 
personalised products cannot be
+returned. Items marked "final sale" on the product page are sold without a 
right of return.
+
+## Gift returns
+
+A product received as a gift can be returned within the standard 30 days. The 
refund is issued as
+store credit to the person returning the item, so the buyer is not notified.
diff --git a/langchain4j-ingest-rag/documents/product-guide.md 
b/langchain4j-ingest-rag/documents/product-guide.md
new file mode 100644
index 00000000..e4930275
--- /dev/null
+++ b/langchain4j-ingest-rag/documents/product-guide.md
@@ -0,0 +1,67 @@
+# Acme Sync Product Guide
+
+Acme Sync is the desktop client that keeps a local folder synchronised with 
your Acme cloud
+workspace. This guide covers installation, activation, the first 
synchronisation and the most
+common problems.
+
+## System requirements
+
+Acme Sync runs on Windows 10 and later, macOS 13 and later, and Ubuntu 22.04 
and later. The client
+needs 4 GB of memory, 500 MB of disk space for the application and outbound 
HTTPS access to
+sync.acme.example on port 443. No inbound connection is required.
+
+## Installation
+
+On Windows, run the downloaded installer and follow the wizard; administrator 
rights are required
+once, for the installation itself. On macOS, open the disk image and drag Acme 
Sync to the
+Applications folder. On Ubuntu, install the .deb package with `sudo apt 
install ./acme-sync.deb`.
+The client starts automatically at login after the installation.
+
+## Licensing and activation
+
+Acme Sync is included in every Team and Enterprise subscription. On first 
start, sign in with your
+Acme account: the licence is checked online and cached for 30 days, so the 
client keeps working
+while you are offline. A licence can be active on three devices at the same 
time; a fourth device
+signs out the oldest one.
+
+## The initial synchronisation
+
+The first synchronisation downloads the whole workspace, which can take a 
while for large
+workspaces. Progress is shown in the tray icon menu. Choose "Selective sync" 
in the preferences to
+download only some folders; folders left out remain visible online. Files are 
checked every
+30 seconds, and a change made on another device usually appears within a 
minute.
+
+## Troubleshooting
+
+### Slow synchronisation and high CPU usage without error codes
+
+When users can sign in but synchronisation is extremely slow and the client 
consumes a lot of CPU,
+with no error codes in the log, the cause is nearly always the network path 
rather than the client.
+The two configurations to check are:
+
+- A proxy or security appliance that inspects TLS traffic on port 443. Acme 
Sync detects the
+  replaced certificate and falls back to a per-file verification mode, which 
is slow and CPU
+  intensive. Add sync.acme.example to the proxy bypass list so the traffic is 
not inspected.
+- A VPN link with a reduced MTU. Fragmented packets make the client retransmit 
large chunks
+  repeatedly. Set the MTU on the VPN adapter to 1400 or lower, or enable 
"Small chunks" in the
+  advanced preferences of the client.
+
+After changing either setting, restart the client. Synchronisation returns to 
normal speed and the
+CPU usage drops within a few minutes.
+
+### The client cannot reach the server
+
+If the tray icon shows "Offline" although the network works, verify that 
outbound HTTPS to
+sync.acme.example on port 443 is allowed by the firewall. The client does not 
support SOCKS proxies;
+use an HTTP proxy configured in the preferences.
+
+### Log files
+
+Logs are written to the "logs" folder inside the application data directory. 
Choose "Collect
+diagnostics" in the tray icon menu to create an archive with the logs and the 
current configuration,
+and attach it when contacting support.
+
+## Updates
+
+Updates are downloaded automatically and installed at the next start of the 
client. Enterprise
+administrators can pin a version and roll out updates through the admin 
console.
diff --git a/langchain4j-ingest-rag/documents/support-faq.md 
b/langchain4j-ingest-rag/documents/support-faq.md
new file mode 100644
index 00000000..e2d43ce2
--- /dev/null
+++ b/langchain4j-ingest-rag/documents/support-faq.md
@@ -0,0 +1,61 @@
+# Support FAQ
+
+## Account management
+
+### How do I reset my password?
+
+Open the sign-in page and choose "Forgot password". Enter the email address 
registered on the
+account; a reset link is sent to it within a few minutes. The link is valid 
for 15 minutes and can
+be used once. If no email arrives, check the spam folder and make sure the 
address is the one
+registered on the account. Accounts protected by two-factor authentication ask 
for the second
+factor after the new password has been set.
+
+### How do I change my registered email address?
+
+Sign in, open "Account settings" and choose "Change email". A confirmation 
link is sent to the new
+address; the change takes effect once the link is opened. Invoices and 
notifications are sent to
+the new address from then on.
+
+### How do I enable two-factor authentication?
+
+In "Account settings", open "Security" and choose "Enable two-factor 
authentication". Scan the QR
+code with an authenticator application and enter the generated code to 
confirm. Store the recovery
+codes shown at the end in a safe place: they are the only way back into the 
account if the
+authenticator device is lost.
+
+### How do I delete my account?
+
+Account deletion is requested in "Account settings" under "Delete account". 
The account is
+deactivated immediately and permanently deleted after 30 days; signing in 
during that period
+cancels the deletion. Data in shared workspaces owned by other members is not 
affected.
+
+## Billing and subscriptions
+
+### Which payment methods are accepted?
+
+We accept Visa, Mastercard and American Express cards, as well as SEPA direct 
debit for customers
+in the European Union. Enterprise subscriptions can be paid by invoice with 
30-day payment terms.
+
+### Where can I find my invoices?
+
+Invoices are available in "Billing" under "Invoices" and are also emailed to 
the billing contact
+on the day they are issued. Each invoice can be downloaded as a PDF.
+
+### What happens if my payment fails?
+
+A failed payment is retried three times over 10 days. During that period the 
subscription keeps
+working and the billing contact is notified after each attempt. If every 
attempt fails, the
+subscription is downgraded to the free plan; data is kept for 90 days.
+
+## Technical support
+
+### How do I contact support?
+
+Support is reached through the "Help" menu in the application or by email at
[email protected]. Include the account email address and, for technical 
problems, the
+diagnostics archive created by the client.
+
+### What is the response time?
+
+Free plan requests are answered within 2 business days. Team subscriptions get 
a first response
+within 8 business hours, Enterprise subscriptions within 1 hour around the 
clock.
diff --git a/langchain4j-ingest-rag/eclipse-formatter-config.xml 
b/langchain4j-ingest-rag/eclipse-formatter-config.xml
new file mode 100644
index 00000000..2248b2b8
--- /dev/null
+++ b/langchain4j-ingest-rag/eclipse-formatter-config.xml
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<profiles version="8">
+    <profile name="Camel Java Conventions" version="8" 
kind="CodeFormatterProfile">
+        <setting 
id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" 
value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration"
 value="16"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration"
 value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" 
value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" 
value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" 
value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration"
 value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration"
 value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration"
 value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" 
value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" 
value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" 
value="end_of_line"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_block_comments" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_comments" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_header" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_html" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.indent_return_description" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" 
value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.line_length" 
value="120"/>
+        <setting id="org.eclipse.jdt.core.formatter.compact_else_if" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.continuation_indentation" 
value="2"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer"
 value="2"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header"
 value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" 
value="true"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.indentation.size" 
value="8"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do 
not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do 
not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not 
insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized"
 value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" 
value="insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not 
insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" 
value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration"
 value="do not insert"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation"
 value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" 
value="false"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.lineSplit" value="128"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body"
 value="0"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" 
value="1"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.char" 
value="space"/>
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.size" 
value="4"/>
+        <setting 
id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" 
value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" 
value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.disabling_tag" 
value="CHECKSTYLE:OFF"/>
+        <setting id="org.eclipse.jdt.core.formatter.enabling_tag" 
value="CHECKSTYLE:ON"/>
+    </profile>
+</profiles>
diff --git a/langchain4j-ingest-rag/pom.xml b/langchain4j-ingest-rag/pom.xml
new file mode 100644
index 00000000..4848b802
--- /dev/null
+++ b/langchain4j-ingest-rag/pom.xml
@@ -0,0 +1,341 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-quarkus-examples-langchain4j-ingest-rag</artifactId>
+    <groupId>org.apache.camel.quarkus.examples</groupId>
+    <version>3.40.0-SNAPSHOT</version>
+
+    <name>Camel Quarkus :: Examples :: LangChain4j Ingest RAG</name>
+    <description>Camel Quarkus Example :: RAG document Q&amp;A over a folder 
ingested by the langchain4j-ingest extension</description>
+
+    <properties>
+        <quarkus.platform.version>3.40.0</quarkus.platform.version>
+        
<camel-quarkus.platform.version>3.40.0-SNAPSHOT</camel-quarkus.platform.version>
+
+        <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
+        
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
+        
<camel-quarkus.platform.group-id>org.apache.camel.quarkus</camel-quarkus.platform.group-id>
+        
<camel-quarkus.platform.artifact-id>camel-quarkus-bom</camel-quarkus.platform.artifact-id>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <maven.compiler.release>17</maven.compiler.release>
+
+        <formatter-maven-plugin.version>2.29.0</formatter-maven-plugin.version>
+        <impsort-maven-plugin.version>1.13.0</impsort-maven-plugin.version>
+        <license-maven-plugin.version>5.1.2</license-maven-plugin.version>
+        <maven-compiler-plugin.version>3.16.0</maven-compiler-plugin.version>
+        <maven-jar-plugin.version>3.5.1</maven-jar-plugin.version>
+        <maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
+        <maven-surefire-plugin.version>3.5.6</maven-surefire-plugin.version>
+
+        <!-- the Quarkus LangChain4j release Camel Quarkus is built against -->
+        
<quarkiverse-langchain4j.version>1.13.3</quarkiverse-langchain4j.version>
+        <wiremock.version>3.13.2</wiremock.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Import BOM -->
+            <dependency>
+                <groupId>io.quarkiverse.langchain4j</groupId>
+                <artifactId>quarkus-langchain4j-bom</artifactId>
+                <version>${quarkiverse-langchain4j.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>${quarkus.platform.artifact-id}</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>${camel-quarkus.platform.group-id}</groupId>
+                <artifactId>${camel-quarkus.platform.artifact-id}</artifactId>
+                <version>${camel-quarkus.platform.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- the ingestion pipeline: a folder becomes a knowledge base, 
declared in application.properties -->
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-langchain4j-ingest</artifactId>
+        </dependency>
+        <!-- the vector store: pgvector, started by Dev Services in dev and 
test mode -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-jdbc-postgresql</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkiverse.langchain4j</groupId>
+            <artifactId>quarkus-langchain4j-pgvector</artifactId>
+        </dependency>
+        <!-- the chat model behind the @RegisterAiService assistant: a model 
served by Ollama -->
+        <dependency>
+            <groupId>io.quarkiverse.langchain4j</groupId>
+            <artifactId>quarkus-langchain4j-ollama</artifactId>
+        </dependency>
+        <!-- the embedding model: runs in-process, no API key needed -->
+        <dependency>
+            <groupId>dev.langchain4j</groupId>
+            <artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-rest</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-rest-jackson</artifactId>
+        </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.wiremock</groupId>
+            <artifactId>wiremock-standalone</artifactId>
+            <version>${wiremock.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>net.revelc.code.formatter</groupId>
+                    <artifactId>formatter-maven-plugin</artifactId>
+                    <version>${formatter-maven-plugin.version}</version>
+                    <configuration>
+                        
<configFile>${maven.multiModuleProjectDirectory}/eclipse-formatter-config.xml</configFile>
+                        <lineEnding>LF</lineEnding>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>net.revelc.code</groupId>
+                    <artifactId>impsort-maven-plugin</artifactId>
+                    <version>${impsort-maven-plugin.version}</version>
+                    <configuration>
+                        <groups>java.,javax.,org.w3c.,org.xml.,junit.</groups>
+                        <removeUnused>true</removeUnused>
+                        <staticAfter>true</staticAfter>
+                        
<staticGroups>java.,javax.,org.w3c.,org.xml.,junit.</staticGroups>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>${maven-compiler-plugin.version}</version>
+                    <configuration>
+                        <showDeprecation>true</showDeprecation>
+                        <showWarnings>true</showWarnings>
+                        <compilerArgs>
+                            <arg>-Xlint:unchecked</arg>
+                        </compilerArgs>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                    <configuration>
+                        <failIfNoTests>false</failIfNoTests>
+                        <systemPropertyVariables>
+                            
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                        </systemPropertyVariables>
+                    </configuration>
+                </plugin>
+
+                <plugin>
+                    <groupId>${quarkus.platform.group-id}</groupId>
+                    <artifactId>quarkus-maven-plugin</artifactId>
+                    <version>${quarkus.platform.version}</version>
+                    <extensions>true</extensions>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-failsafe-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>${maven-jar-plugin.version}</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>com.mycila</groupId>
+                    <artifactId>license-maven-plugin</artifactId>
+                    <version>${license-maven-plugin.version}</version>
+                    <configuration>
+                        <failIfUnknown>true</failIfUnknown>
+                        
<header>${maven.multiModuleProjectDirectory}/header.txt</header>
+                        <excludes>
+                            <exclude>**/*.adoc</exclude>
+                            <exclude>**/*.md</exclude>
+                            <exclude>**/*.txt</exclude>
+                            <exclude>**/LICENSE.txt</exclude>
+                            <exclude>**/LICENSE</exclude>
+                            <exclude>**/NOTICE.txt</exclude>
+                            <exclude>**/NOTICE</exclude>
+                            <exclude>**/README</exclude>
+                            <exclude>**/pom.xml.versionsBackup</exclude>
+                            <exclude>**/quarkus.log*</exclude>
+                        </excludes>
+                        <mapping>
+                            <java>SLASHSTAR_STYLE</java>
+                            <properties>CAMEL_PROPERTIES_STYLE</properties>
+                            <kt>SLASHSTAR_STYLE</kt>
+                        </mapping>
+                        <headerDefinitions>
+                            
<headerDefinition>${maven.multiModuleProjectDirectory}/license-properties-headerdefinition.xml</headerDefinition>
+                        </headerDefinitions>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>build</id>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code.formatter</groupId>
+                <artifactId>formatter-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>format</id>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>net.revelc.code</groupId>
+                <artifactId>impsort-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>sort-imports</id>
+                        <goals>
+                            <goal>sort</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>com.mycila</groupId>
+                <artifactId>license-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>license-format</id>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                        <phase>process-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.native.enabled>true</quarkus.native.enabled>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>skip-testcontainers-tests</id>
+            <activation>
+                <property>
+                    <name>skip-testcontainers-tests</name>
+                </property>
+            </activation>
+            <properties>
+                <skipTests>true</skipTests>
+            </properties>
+        </profile>
+    </profiles>
+
+</project>
diff --git 
a/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/ChatResource.java
 
b/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/ChatResource.java
new file mode 100644
index 00000000..5b00ad85
--- /dev/null
+++ 
b/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/ChatResource.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.acme.langchain4j.ingest.rag;
+
+import java.util.List;
+
+import dev.langchain4j.data.message.UserMessage;
+import dev.langchain4j.rag.AugmentationRequest;
+import dev.langchain4j.rag.AugmentationResult;
+import dev.langchain4j.rag.RetrievalAugmentor;
+import dev.langchain4j.rag.query.Metadata;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+
+@Path("/chat")
+public class ChatResource {
+
+    @Inject
+    DocumentAssistant assistant;
+
+    @Inject
+    RetrievalAugmentor augmentor;
+
+    /** Asks the assistant; the answer is grounded in the retrieved documents. 
*/
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String chat(@QueryParam("q") String question) {
+        return assistant.answer(question);
+    }
+
+    /** The prompt the assistant would send: the question plus the retrieved 
context. No LLM is called. */
+    @GET
+    @Path("/preview")
+    @Produces(MediaType.TEXT_PLAIN)
+    public String preview(@QueryParam("q") String question) {
+        UserMessage message = UserMessage.from(question);
+        AugmentationResult result = augmentor.augment(
+                new AugmentationRequest(message, Metadata.from(message, 
"preview", List.of())));
+        return ((UserMessage) result.chatMessage()).singleText();
+    }
+}
diff --git 
a/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/DocumentAssistant.java
 
b/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/DocumentAssistant.java
new file mode 100644
index 00000000..7fdf6469
--- /dev/null
+++ 
b/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/DocumentAssistant.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.acme.langchain4j.ingest.rag;
+
+import dev.langchain4j.service.SystemMessage;
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+
+/**
+ * The assistant. There is no RAG code here: the langchain4j-embeddingstore 
extension, which the
+ * ingest extension depends on, produces a RetrievalAugmentor from the 
application's embedding
+ * store and model, and Quarkus LangChain4j picks it up as the default, so 
every question is
+ * augmented with the segments the pipeline ingested.
+ */
+@RegisterAiService
+public interface DocumentAssistant {
+
+    @SystemMessage("""
+            You answer questions about the company's documents.
+            Use ONLY the information provided together with the question.
+            If it does not contain the answer, reply: I don't have complete 
information on that.
+            Be concise.""")
+    String answer(@UserMessage String question);
+}
diff --git 
a/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/SearchResource.java
 
b/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/SearchResource.java
new file mode 100644
index 00000000..c09dbc92
--- /dev/null
+++ 
b/langchain4j-ingest-rag/src/main/java/org/acme/langchain4j/ingest/rag/SearchResource.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.acme.langchain4j.ingest.rag;
+
+import java.util.List;
+import java.util.Map;
+
+import dev.langchain4j.data.segment.TextSegment;
+import dev.langchain4j.model.embedding.EmbeddingModel;
+import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
+import dev.langchain4j.store.embedding.EmbeddingStore;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+import 
org.apache.camel.quarkus.component.langchain4j.ingest.core.IngestService;
+
+@Path("/search")
+public class SearchResource {
+
+    @Inject
+    EmbeddingStore<TextSegment> store;
+
+    @Inject
+    EmbeddingModel model;
+
+    /** The raw retrieval step: the segments closest to the question, with 
their source document. */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public List<Map<String, Object>> search(@QueryParam("q") String question) {
+        return store.search(EmbeddingSearchRequest.builder()
+                .queryEmbedding(model.embed(question).content())
+                .maxResults(3)
+                .build())
+                .matches().stream()
+                .map(match -> Map.<String, Object> of(
+                        "score", match.score(),
+                        "document", 
String.valueOf(match.embedded().metadata().getString(IngestService.METADATA_DOCUMENT_ID)),
+                        "text", match.embedded().text()))
+                .toList();
+    }
+}
diff --git a/langchain4j-ingest-rag/src/main/resources/application.properties 
b/langchain4j-ingest-rag/src/main/resources/application.properties
new file mode 100644
index 00000000..36d990db
--- /dev/null
+++ b/langchain4j-ingest-rag/src/main/resources/application.properties
@@ -0,0 +1,52 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+##      http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+
+# The knowledge base. Every file in the folder is ingested at startup, and a 
file added or
+# edited while the application runs is picked up on the next poll. No route, 
no ingestion
+# code: the langchain4j-ingest extension reads, splits, embeds and stores. The 
pipeline
+# writes to the application's only embedding store, the pgvector store below.
+documents.directory=documents
+quarkus.camel.langchain4j.ingest.docs.source.directory=${documents.directory}
+quarkus.camel.langchain4j.ingest.docs.max-segment-size=500
+quarkus.camel.langchain4j.ingest.docs.max-overlap-size=50
+
+# The vector store: pgvector through Quarkus LangChain4j. Dev Services start a 
pgvector
+# enabled PostgreSQL in dev and test mode; in prod mode point the datasource 
at your own.
+quarkus.datasource.db-kind=postgresql
+quarkus.datasource.devservices.image-name=docker.io/pgvector/pgvector:pg17
+%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/documents
+%prod.quarkus.datasource.username=postgres
+%prod.quarkus.datasource.password=postgres
+# all-MiniLM-L6-v2 produces 384-dimensional vectors
+quarkus.langchain4j.pgvector.dimension=384
+# the vector extension is created on start (the default only does so in dev 
and test mode)
+quarkus.langchain4j.pgvector.register-vector-pg-extension=true
+# the folder is indexed again on every start, so start from an empty table
+quarkus.langchain4j.pgvector.drop-table-first=true
+
+# The chat model behind the assistant: a model served by Ollama, 
https://ollama.com. Pull the
+# model once with `ollama pull llama3.2`; point OLLAMA_BASE_URL at an Ollama 
running elsewhere.
+quarkus.langchain4j.ollama.base-url=${OLLAMA_BASE_URL:http://localhost:11434}
+quarkus.langchain4j.ollama.chat-model.model-id=${OLLAMA_MODEL:llama3.2}
+quarkus.langchain4j.ollama.chat-model.temperature=0.1
+quarkus.langchain4j.ollama.timeout=120s
+# Ollama is run by you rather than by Dev Services: pulling a model is a 
multi-gigabyte download
+quarkus.langchain4j.ollama.devservices.enabled=false
+# Embeddings come from the in-process all-MiniLM-L6-v2 model, which Quarkus 
LangChain4j registers
+# as the EmbeddingModel bean from the langchain4j-embeddings-all-minilm-l6-v2 
dependency. The
+# Ollama embedding model is switched off, so ingestion and retrieval share 
that one bean.
+quarkus.langchain4j.ollama.embedding-model.enabled=false
diff --git 
a/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/Langchain4jIngestRagIT.java
 
b/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/Langchain4jIngestRagIT.java
new file mode 100644
index 00000000..43fff920
--- /dev/null
+++ 
b/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/Langchain4jIngestRagIT.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.acme.langchain4j.ingest.rag;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class Langchain4jIngestRagIT extends Langchain4jIngestRagTest {
+
+}
diff --git 
a/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/Langchain4jIngestRagTest.java
 
b/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/Langchain4jIngestRagTest.java
new file mode 100644
index 00000000..8d9c80d8
--- /dev/null
+++ 
b/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/Langchain4jIngestRagTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.acme.langchain4j.ingest.rag;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@QuarkusTestResource(OllamaTestResource.class)
+@QuarkusTest
+class Langchain4jIngestRagTest {
+
+    private static final String QUESTION = "What is the return policy?";
+
+    @Test
+    void documentsAreIngestedAndSearchable() {
+        List<Map<String, Object>> hits = awaitReturnPolicy();
+        assertTrue(hits.stream().anyMatch(hit -> 
"company-policy.md".equals(hit.get("document"))),
+                "the hit must cite the document it came from, got: " + hits);
+    }
+
+    @Test
+    void assistantAnswersFromTheDocuments() {
+        awaitReturnPolicy();
+
+        // the retrieved context reaches the prompt without any RAG code; no 
LLM involved here
+        String prompt = RestAssured.given()
+                .queryParam("q", QUESTION)
+                .get("/chat/preview")
+                .then().statusCode(200)
+                .extract().asString();
+        assertTrue(prompt.contains("30 days"), "the prompt must contain the 
retrieved context, got: " + prompt);
+
+        String answer = RestAssured.given()
+                .queryParam("q", QUESTION)
+                .get("/chat")
+                .then().statusCode(200)
+                .extract().asString();
+        // the fake model answers correctly only when the retrieved document 
text was in the request
+        assertTrue(answer.contains("30"), "the answer must come from the 
documents, got: " + answer);
+
+    }
+
+    /** Ingestion runs in the background after startup: wait until the return 
policy is searchable. */
+    static List<Map<String, Object>> awaitReturnPolicy() {
+        return await().atMost(3, TimeUnit.MINUTES).pollInterval(1, 
TimeUnit.SECONDS)
+                .until(() -> search("How long do I have to return a product?"),
+                        hits -> hits.stream().anyMatch(hit -> 
String.valueOf(hit.get("text")).contains("30 days")));
+    }
+
+    static List<Map<String, Object>> search(String question) {
+        return RestAssured.given()
+                .queryParam("q", question)
+                .get("/search")
+                .then().statusCode(200)
+                .extract().jsonPath().getList("$");
+    }
+}
diff --git 
a/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/OllamaTestResource.java
 
b/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/OllamaTestResource.java
new file mode 100644
index 00000000..fe71bea3
--- /dev/null
+++ 
b/langchain4j-ingest-rag/src/test/java/org/acme/langchain4j/ingest/rag/OllamaTestResource.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.acme.langchain4j.ingest.rag;
+
+import java.util.Map;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.containing;
+import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static 
com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+
+/**
+ * The chat model of the tests. By default a WireMock server plays the Ollama 
API with canned
+ * answers, so the tests need no model. With {@code OLLAMA_BASE_URL} set, that 
real Ollama is
+ * used instead, with the model named by {@code OLLAMA_MODEL}.
+ */
+public class OllamaTestResource implements QuarkusTestResourceLifecycleManager 
{
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(OllamaTestResource.class);
+    private static final String CHAT = "/api/chat";
+
+    private WireMockServer server;
+
+    @Override
+    public Map<String, String> start() {
+        String realBaseUrl = System.getenv("OLLAMA_BASE_URL");
+        if (realBaseUrl != null && !realBaseUrl.isBlank()) {
+            LOG.info("Using the Ollama server at {}", realBaseUrl);
+            return Map.of();
+        }
+
+        LOG.info("Starting a fake Ollama server backed by WireMock");
+        server = new WireMockServer(options().dynamicPort());
+        server.start();
+        // the "model" knows the answer only when the retrieved document text 
is part of the request,
+        // as it would be after augmentation; a bare question gets the 
fallback below, so a correct
+        // answer in the tests proves that retrieval grounded the prompt
+        server.stubFor(post(urlPathEqualTo(CHAT))
+                .withRequestBody(containing("within 30 days of delivery"))
+                .willReturn(okJson(answer(
+                        "Products can be returned within 30 days of delivery, 
unused and in their original packaging."))));
+        server.stubFor(post(urlPathEqualTo(CHAT))
+                .atPriority(10)
+                .willReturn(okJson(answer("I don't have complete information 
on that."))));
+
+        return Map.of("quarkus.langchain4j.ollama.base-url", server.baseUrl());
+    }
+
+    private static String answer(String content) {
+        return """
+                {
+                  "model": "llama3.2",
+                  "created_at": "2026-01-01T00:00:00Z",
+                  "message": { "role": "assistant", "content": "%s" },
+                  "done": true,
+                  "done_reason": "stop",
+                  "prompt_eval_count": 1,
+                  "eval_count": 1
+                }
+                """.formatted(content);
+    }
+
+    @Override
+    public void stop() {
+        if (server != null) {
+            server.stop();
+            server = null;
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
index abbb4a40..ca094530 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,6 +56,7 @@
         <module>jta-jpa</module>
         <module>kafka</module>
         <module>kamelet-chucknorris</module>
+        <module>langchain4j-ingest-rag</module>
         <module>message-bridge</module>
         <module>netty-custom-correlation</module>
         <module>observability</module>

Reply via email to