This is an automated email from the ASF dual-hosted git repository.
aldettinger 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 02f3b7e Add an example of data extraction with Quarkus LangChain4j
02f3b7e is described below
commit 02f3b7ebb155588be73ba79d802640bf0f2b4ff4
Author: aldettinger <[email protected]>
AuthorDate: Thu Jul 25 18:30:03 2024 +0200
Add an example of data extraction with Quarkus LangChain4j
---
data-extract-langchain4j/README.adoc | 126 ++++++++
.../eclipse-formatter-config.xml | 276 +++++++++++++++++
data-extract-langchain4j/pom.xml | 326 +++++++++++++++++++++
.../extraction/CustomPojoExtractionService.java | 64 ++++
.../java/org/acme/extraction/CustomPojoStore.java | 50 ++++
.../src/main/java/org/acme/extraction/Routes.java | 49 ++++
.../src/main/resources/application.properties | 36 +++
.../org/acme/extraction/OllamaTestResource.java | 126 ++++++++
.../src/test/java/org/acme/extraction/RouteIT.java | 24 ++
.../test/java/org/acme/extraction/RouteTest.java | 84 ++++++
..._chat-495066d1-9278-4e4b-b8f6-a1c2fa296779.json | 24 ++
..._chat-52961581-e5b1-4309-b62f-1c6e1e0008eb.json | 24 ++
..._chat-5c1f926c-0480-41e9-9ca7-a93e17919e99.json | 24 ++
.../01_sarah-london-10-07-1986-satisfied.json | 4 +
.../02_john-doe-01-11-2001-unsatisfied.json | 4 +
.../03_kate-boss-13-08-1999-satisfied.json | 4 +
docs/modules/ROOT/attachments/examples.json | 5 +
17 files changed, 1250 insertions(+)
diff --git a/data-extract-langchain4j/README.adoc
b/data-extract-langchain4j/README.adoc
new file mode 100644
index 0000000..5260f5a
--- /dev/null
+++ b/data-extract-langchain4j/README.adoc
@@ -0,0 +1,126 @@
+= Unstructured Data Extraction with LangChain4j: A Camel Quarkus example
+:cq-example-description: An example that shows how to convert unstructured
text data to structured Java objects helped with a Large Language Model and
LangChain4j
+
+{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.
+
+Suppose the volume of
https://en.wikipedia.org/wiki/Unstructured_data[unstructured data] grows at a
high pace in a given organization.
+How could one transform those disseminated gold particles into a conform
bullion that could be used in banks.
+For instance, let's imagine an insurance company that would record the
transcripts of the conversation when customers are discussing with the hotline.
+There is probably a lot of valuable information that could be extracted from
those conversation transcripts.
+In this example, we'll convert those text conversations into Java Objects that
could then be used in the rest of the Camel route.
+
+In order to achieve this extraction, we'll need a
https://en.wikipedia.org/wiki/Large_language_model[Large Language Model (LLM)]
that natively supports JSON output.
+Here, we arbitrarily choose https://ollama.com/library/codellama[codellama]
served through https://ollama.com/[ollama].
+In order to invoke the served model, we'll use the high-level LangChain4j APIs
like https://docs.langchain4j.dev/tutorials/ai-services[AiServices].
+As we are using the Quarkus runtime, we can leverage all the advantages of the
https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html[Quarkus
LangChain4j extension].
+
+=== Start the Large Language Model
+
+Let's start a container to serve the LLM with Ollama:
+
+[source,shell]
+----
+docker run -p11434:11434 langchain4j/ollama-codellama:latest
+----
+
+After a moment, a log like below should be output:
+
+[source,shell]
+----
+time=2024-09-03T08:03:15.532Z level=INFO source=types.go:98 msg="inference
compute" id=0 library=cpu compute="" driver=0.0 name="" total="62.5 GiB"
available="54.4 GiB"
+----
+
+That's it, the LLM is now ready to serve our data extraction requests.
+
+=== Package and run the application
+
+You are now ready 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]
+
+==== JVM mode
+
+[source,shell]
+----
+mvn clean package -DskipTests
+java -jar target/quarkus-app/quarkus-run.jar
+----
+
+==== Extracting data from unstructured conversation
+
+Let's atomically copy/move the transcript files to the input folder named
`target/transcripts/`, for instance like below:
+
+[source,shell]
+----
+cp -rf src/test/resources/transcripts/ target/transcripts-tmp
+mv target/transcripts-tmp/*.json target/transcripts/
+----
+
+The Camel route should output a log as below:
+
+[source,shell]
+----
+024-09-03 10:14:34,757 INFO [route1] (Camel (camel-1) thread #1 -
file://target/transcripts) A document has been received by the
camel-quarkus-file extension: {
+ "id": 1,
+ "content": "Operator: Hello, how may I help you ?\nCustomer: Hello, I'm
calling because I need to declare an accident on my main vehicle.\nOperator:
Ok, can you please give me your name ?\nCustomer: My name is Sarah
London.\nOperator: Could you please give me your birth date ?\nCustomer: 1986,
July the 10th.\nOperator: Ok, I've got your contract and I'm happy to share
with you that we'll be able to reimburse all expenses linked to this
accident.\nCustomer: Oh great, many thanks."
+}
+----
+
+In the first log above, we can see that a JSON file handling transcript
related information has been consumed.
+The conversation is present in the JSON field named `content`.
+This content will be injected into the LLM prompt.
+
+After a few seconds or minutes depending on your hardware setup, the LLM
provides an answer strictly conforming to the expected JSON schema.
+It's now easy for LangChain4j to convert the returned JSON into a Java Object.
+At the end, we are provided with a Plain Old Java Object (POJO) handling the
extracted data like below.
+
+[source,shell]
+----
+2024-09-03 10:14:51,284 INFO [org.acm.ext.CustomPojoStore] (Camel (camel-1)
thread #1 - file://target/transcripts) An extracted POJO has been added to the
store:
+{
+ "customerSatisfied": "true",
+ "customerName": "Sarah London",
+ "customerBirthday": "10 July 1986",
+ "summary": "Declare an accident on main vehicle and receive reimbursement
for expenses."
+}
+----
+
+See how the LLM shows its capacity to:
+ * Extract a human friendly sentiment like `customerSatisfied`
+ * Exhibits
https://nlp.stanford.edu/projects/coref.shtml#:~:text=Overview,question%20answering%2C%20and%20information%20extraction.[coreference
resolution], like `customerName` that is deduced from information spread in
the whole conversation
+ * Manage issues related to date format, like the field `customerBirthday`
+ * Mixed structured and unstructured data (semi-structured data) with the
field `summary`.
+
+Cherry on the cake, all those informations are computed simultaneously during
a single LLM inference.
+
+At the end, the application should have extracted 3 POJOs.
+For each of them, it could be interesting to compare the unstructured input
text and the corresponding structured POJO.
+
+More details can be found in the
`src/main/java/org/acme/extraction/CustomPojoExtractionService.java` class.
+
+==== 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].
+
+If the application is still running in JVM mode, please kill it, for instance
with `CTRL+C`.
+
+Now, to prepare a native executable using GraalVM, run the following commands:
+
+[source,shell]
+----
+mvn clean package -DskipTests -Dnative
+./target/*-runner
+----
+
+The compilation is a bit slower. Beyond that, notice how the application
behaves the same way.
+Indeed, you should be able to send the JSON files and see the extracted data
exactly as it was done in JVM mode.
+The only variation compared to the JVM mode is actually that the application
was packaged as a native executable.
+
+== Feedback
+
+Please report bugs and propose improvements via
https://github.com/apache/camel-quarkus/issues[GitHub issues of Camel Quarkus]
project.
diff --git a/data-extract-langchain4j/eclipse-formatter-config.xml
b/data-extract-langchain4j/eclipse-formatter-config.xml
new file mode 100644
index 0000000..2248b2b
--- /dev/null
+++ b/data-extract-langchain4j/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/data-extract-langchain4j/pom.xml b/data-extract-langchain4j/pom.xml
new file mode 100644
index 0000000..576664d
--- /dev/null
+++ b/data-extract-langchain4j/pom.xml
@@ -0,0 +1,326 @@
+<?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-data-extract-langchain4j</artifactId>
+ <groupId>org.apache.camel.quarkus.examples</groupId>
+ <version>3.15.0-SNAPSHOT</version>
+
+ <name>Camel Quarkus :: Examples :: Data Extract LangChain4j
Repository</name>
+ <description>Camel Quarkus Example :: Data Extract LangChain4j
Repository</description>
+
+ <properties>
+
+ <quarkus.platform.version>3.14.1</quarkus.platform.version>
+
<camel-quarkus.platform.version>3.15.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.target>17</maven.compiler.target>
+ <maven.compiler.source>17</maven.compiler.source>
+
<maven.compiler.testTarget>${maven.compiler.target}</maven.compiler.testTarget>
+
<maven.compiler.testSource>${maven.compiler.source}</maven.compiler.testSource>
+
+ <formatter-maven-plugin.version>2.24.1</formatter-maven-plugin.version>
+ <groovy-maven-plugin.version>2.1.1</groovy-maven-plugin.version>
+ <impsort-maven-plugin.version>1.11.0</impsort-maven-plugin.version>
+ <license-maven-plugin.version>4.5</license-maven-plugin.version>
+ <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
+ <maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
+ <maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
+ <maven-surefire-plugin.version>3.4.0</maven-surefire-plugin.version>
+ <quarkus-langchain4j-version>0.17.2</quarkus-langchain4j-version>
+ <wiremock-version>3.9.1</wiremock-version>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <!-- Import BOM -->
+ <dependency>
+ <groupId>io.quarkiverse.langchain4j</groupId>
+ <artifactId>quarkus-langchain4j-bom</artifactId>
+ <version>${quarkus-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>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-bean</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-file</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-jsonpath</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-platform-http</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.quarkiverse.langchain4j</groupId>
+ <artifactId>quarkus-langchain4j-ollama</artifactId>
+ </dependency>
+
+ <!-- Test -->
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-junit5</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.rest-assured</groupId>
+ <artifactId>rest-assured</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers</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>
+ <!-- Specifying -parameters is optional, see
CustomPojoExtractionService.extractFromText javadoc -->
+ <arg>-parameters</arg>
+ <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>
+ </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>**/*.odp</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>
+ </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>
+ </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/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoExtractionService.java
b/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoExtractionService.java
new file mode 100644
index 0000000..098e146
--- /dev/null
+++
b/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoExtractionService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.extraction;
+
+import java.time.LocalDate;
+
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+import io.quarkus.runtime.annotations.RegisterForReflection;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.Handler;
+
+@RegisterAiService
+@ApplicationScoped
+public interface CustomPojoExtractionService {
+
+ @RegisterForReflection
+ static class CustomPojo {
+ public boolean customerSatisfied;
+ public String customerName;
+ public LocalDate customerBirthday;
+ public String summary;
+
+ private final static String FORMAT = "\n{\n"
+ + "\t\"customerSatisfied\": \"%s\",\n"
+ + "\t\"customerName\": \"%s\",\n"
+ + "\t\"customerBirthday\": \"%td %tB %tY\",\n"
+ + "\t\"summary\": \"%s\"\n"
+ + "}\n";
+
+ public String toString() {
+ return String.format(FORMAT, this.customerSatisfied,
this.customerName, this.customerBirthday,
+ this.customerBirthday, this.customerBirthday,
this.summary);
+ }
+ }
+
+ static final String CUSTOM_POJO_EXTRACT_PROMPT = "Extract information
about a customer from the text delimited by triple backticks: ```{text}```."
+ + "The customerBirthday field should be formatted as YYYY-MM-DD."
+ + "The summary field should concisely relate the customer main
ask.";
+
+ /**
+ * The text parameter of this method is automatically injected as {text}
in the CUSTOM_POJO_EXTRACT_PROMPT. This
+ * is made possible as the code is compiled with -parameters argument in
the maven-compiler-plugin related section
+ * of the pom.xml file. Without -parameters, one would need to use the @V
annotation like in the method signature
+ * proposed below: extractFromText(@dev.langchain4j.service.V("text")
String text);
+ */
+ @UserMessage(CUSTOM_POJO_EXTRACT_PROMPT)
+ @Handler
+ CustomPojo extractFromText(String text);
+}
diff --git
a/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoStore.java
b/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoStore.java
new file mode 100644
index 0000000..ee031f9
--- /dev/null
+++
b/data-extract-langchain4j/src/main/java/org/acme/extraction/CustomPojoStore.java
@@ -0,0 +1,50 @@
+/*
+ * 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.extraction;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import org.acme.extraction.CustomPojoExtractionService.CustomPojo;
+import org.apache.camel.Handler;
+import org.jboss.logging.Logger;
+
+@ApplicationScoped
+public class CustomPojoStore {
+
+ private static final Logger LOG = Logger.getLogger(CustomPojoStore.class);
+
+ private List<CustomPojo> pojos = new CopyOnWriteArrayList<>();
+
+ @Handler
+ CustomPojo addPojo(CustomPojo pojo) {
+ LOG.info("An extracted POJO has been added to the store: " + pojo);
+ pojos.add(pojo);
+ return pojo;
+ }
+
+ String asString() {
+ StringBuilder sb = new StringBuilder("{ \"pojos\": [");
+ String pojoString =
pojos.stream().map(CustomPojo::toString).collect(Collectors.joining(","));
+ sb.append(pojoString);
+ sb.append("] }");
+ return sb.toString();
+ }
+
+}
diff --git
a/data-extract-langchain4j/src/main/java/org/acme/extraction/Routes.java
b/data-extract-langchain4j/src/main/java/org/acme/extraction/Routes.java
new file mode 100644
index 0000000..68980be
--- /dev/null
+++ b/data-extract-langchain4j/src/main/java/org/acme/extraction/Routes.java
@@ -0,0 +1,49 @@
+/*
+ * 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.extraction;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import org.apache.camel.builder.RouteBuilder;
+
+@ApplicationScoped
+public class Routes extends RouteBuilder {
+
+ @Inject
+ CustomPojoExtractionService customPojoExtractionService;
+
+ @Inject
+ CustomPojoStore customPojoStore;
+
+ @Override
+ public void configure() {
+
+ // Consumes file documents that contain conversation transcripts (JSON
format)
+ from("file:target/transcripts")
+ .log("A document has been received by the camel-quarkus-file
extension: ${body}")
+ // Retrieves the conversation content from the JSON field
named "content"
+ .setBody(jsonpath("$.content"))
+ // The CustomPojoExtractionService transforms the conversation
transcript into a CustomPojoExtractionService.CustomPojo
+ .bean(customPojoExtractionService)
+ // Store extracted CustomPojoExtractionService.CustomPojos
objects into the CustomPojoStore for later inspection
+ .bean(customPojoStore);
+
+ // This route make it possible to inspect the extracted POJOs, mainly
used for demo and test
+ from("platform-http:/custom-pojo-store?produces=application/json")
+ .bean(customPojoStore, "asString");
+ }
+}
diff --git a/data-extract-langchain4j/src/main/resources/application.properties
b/data-extract-langchain4j/src/main/resources/application.properties
new file mode 100644
index 0000000..633c2db
--- /dev/null
+++ b/data-extract-langchain4j/src/main/resources/application.properties
@@ -0,0 +1,36 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+quarkus.banner.enabled = false
+
+# Configure Quarkus LangChain4j that handle interactions with the Large
Language Model
+quarkus.langchain4j.ollama.base-url = http://localhost:11434
+quarkus.langchain4j.ollama.timeout = 3m
+quarkus.langchain4j.ollama.chat-model.model-id = codellama
+quarkus.langchain4j.ollama.chat-model.format = json
+quarkus.langchain4j.ollama.chat-model.temperature = 0
+# Uncomment lines below to log Ollama client requests and responses
+#quarkus.langchain4j.ollama.log-requests=true
+#quarkus.langchain4j.ollama.log-responses=true
+
+# Or uncomment lines below to log HTTP traffic between LangChain4j & the LLM
API
+#quarkus.rest-client.logging.scope=request-response
+#quarkus.rest-client.logging.body-limit=10000
+#quarkus.log.category."org.jboss.resteasy.reactive.client.logging".level=DEBUG
+
+# Configure Quarkus LangChain4j to keep a single message in memory, forgetting
about previous data extractions
+quarkus.langchain4j.chat-memory.memory-window.max-messages = 1
diff --git
a/data-extract-langchain4j/src/test/java/org/acme/extraction/OllamaTestResource.java
b/data-extract-langchain4j/src/test/java/org/acme/extraction/OllamaTestResource.java
new file mode 100644
index 0000000..2a9489e
--- /dev/null
+++
b/data-extract-langchain4j/src/test/java/org/acme/extraction/OllamaTestResource.java
@@ -0,0 +1,126 @@
+/*
+ * 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.extraction;
+
+import java.util.Arrays;
+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 org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+import static java.lang.String.format;
+
+public class OllamaTestResource implements QuarkusTestResourceLifecycleManager
{
+
+ private static final Logger LOG =
LoggerFactory.getLogger(OllamaTestResource.class);
+
+ // LangChain4j offers only latest tag for ollama-codellama, hence we do
use latest until more tags are introduced
+ private static final String OLLAMA_IMAGE =
"langchain4j/ollama-codellama:latest";
+ private static final int OLLAMA_SERVER_PORT = 11434;
+
+ private GenericContainer<?> ollamaContainer;
+
+ private WireMockServer wireMockServer;
+ private String baseUrl;
+
+ private static final String MODE_MOCK = "mock";
+ private static final String MODE_RECORDING = "record";
+ private static final String MODE_CONTAINER = "container";
+
+ /**
+ * The testMode value could be defined, for instance by invoking:
+ * mvn clean test -DtestMode=mock.
+ *
+ * With the default value "mock", the LLM is faked based on the last
recorded run.
+ * With the value "record", tests are run against a containerized LLM
while the HTTP interactions are recorded.
+ * With the value "container" tests are run against a containerized LLM
without recording.
+ * With any other value, an IllegalArgumentException is thrown.
+ */
+ private boolean isMockMode;
+ private boolean isRecordingMode;
+ private boolean isContainerMode;
+
+ private static final String BASE_URL_FORMAT = "http://%s:%s";
+
+ @Override
+ public Map<String, String> start() {
+
+ // Check the test running mode
+ String testMode = System.getProperty("testMode", MODE_MOCK);
+ isMockMode = MODE_MOCK.equals(testMode);
+ isRecordingMode = MODE_RECORDING.equals(testMode);
+ isContainerMode = MODE_CONTAINER.equals(testMode);
+ if (!isMockMode && !isRecordingMode && !isContainerMode) {
+ throw new IllegalArgumentException(
+ "testMode value should be one of " +
Arrays.asList(MODE_MOCK, MODE_RECORDING, MODE_CONTAINER));
+ }
+
+ if (isMockMode) {
+ LOG.info("Starting a fake Ollama server backed by wiremock");
+ initWireMockServer();
+ } else {
+ LOG.info("Starting an Ollama server backed by testcontainers");
+ ollamaContainer = new GenericContainer<>(OLLAMA_IMAGE)
+ .withExposedPorts(OLLAMA_SERVER_PORT)
+ .withLogConsumer(new
Slf4jLogConsumer(LOG).withPrefix("basicAuthContainer"))
+ .waitingFor(Wait.forLogMessage(".* msg=\"inference
compute\" .*", 1));
+ ollamaContainer.start();
+
+ baseUrl = format(BASE_URL_FORMAT, ollamaContainer.getHost(),
ollamaContainer.getMappedPort(OLLAMA_SERVER_PORT));
+
+ if (isRecordingMode) {
+ LOG.info("Recording interactions with the Ollama server backed
by testcontainers");
+ initWireMockServer();
+ }
+ }
+
+ return Map.of("quarkus.langchain4j.ollama.base-url", baseUrl);
+ }
+
+ private void initWireMockServer() {
+ wireMockServer = new WireMockServer();
+ wireMockServer.start();
+ if (isRecordingMode) {
+ wireMockServer.resetMappings();
+ wireMockServer.startRecording(baseUrl);
+ }
+ baseUrl = format(BASE_URL_FORMAT, "localhost", wireMockServer.port());
+ }
+
+ @Override
+ public void stop() {
+ try {
+ if (ollamaContainer != null) {
+ ollamaContainer.stop();
+ }
+ } catch (Exception ex) {
+ LOG.error("An issue occurred while stopping " +
ollamaContainer.getNetworkAliases(), ex);
+ }
+
+ if (isMockMode) {
+ wireMockServer.stop();
+ } else if (isRecordingMode) {
+ wireMockServer.stopRecording();
+ wireMockServer.saveMappings();
+ }
+ }
+}
diff --git
a/data-extract-langchain4j/src/test/java/org/acme/extraction/RouteIT.java
b/data-extract-langchain4j/src/test/java/org/acme/extraction/RouteIT.java
new file mode 100644
index 0000000..56cf33e
--- /dev/null
+++ b/data-extract-langchain4j/src/test/java/org/acme/extraction/RouteIT.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.extraction;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+public class RouteIT extends RouteTest {
+
+}
diff --git
a/data-extract-langchain4j/src/test/java/org/acme/extraction/RouteTest.java
b/data-extract-langchain4j/src/test/java/org/acme/extraction/RouteTest.java
new file mode 100644
index 0000000..fe466c4
--- /dev/null
+++ b/data-extract-langchain4j/src/test/java/org/acme/extraction/RouteTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.extraction;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.http.ContentType;
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.Test;
+
+import static io.restassured.RestAssured.given;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
+@QuarkusTestResource(OllamaTestResource.class)
+@QuarkusTest
+public class RouteTest {
+
+ @Test
+ void unstructuredFileTranscriptsAreTransformedToPojos() throws IOException
{
+
+ FileUtils.deleteQuietly(new File("target/transcripts-tmp/"));
+ FileUtils.deleteQuietly(new File("target/transcripts/"));
+
+ FileUtils.copyDirectory(new File("src/test/resources/transcripts/"),
new File("target/transcripts-tmp/"));
+ Files.move(Paths.get("target/transcripts-tmp"),
Paths.get("target/transcripts"), StandardCopyOption.ATOMIC_MOVE);
+
+ await().pollInterval(200, TimeUnit.MILLISECONDS).atMost(3,
TimeUnit.MINUTES)
+ .until(
+ () -> {
+ return given()
+ .contentType(ContentType.JSON)
+ .when()
+ .get("/custom-pojo-store")
+ .path("pojos.size()").equals(3);
+ });
+
+ given()
+ .contentType(ContentType.JSON)
+ .when()
+ .get("/custom-pojo-store")
+ .then()
+ .statusCode(200)
+ // Assert values of the first extracted POJO
+ .body("pojos[0].customerSatisfied", is("true"))
+ .body("pojos[0].customerName", is("Sarah London"))
+ .body("pojos[0].customerBirthday", is("10 July 1986"))
+ .body("pojos[0].summary", not(empty()))
+ // Assert values of the second extracted POJO
+ .body("pojos[1].customerSatisfied", is("false"))
+ .body("pojos[1].customerName", is("John Doe"))
+ .body("pojos[1].customerBirthday", is("01 November 2001"))
+ .body("pojos[1].summary", not(empty()))
+ // Assert values of the third extracted POJO
+ .body("pojos[2].customerSatisfied", is("true"))
+ .body("pojos[2].customerName", is("Kate Boss"))
+ .body("pojos[2].customerBirthday", is("13 August 1999"))
+ .body("pojos[2].summary", not(empty()));
+ }
+
+}
diff --git
a/data-extract-langchain4j/src/test/resources/mappings/api_chat-495066d1-9278-4e4b-b8f6-a1c2fa296779.json
b/data-extract-langchain4j/src/test/resources/mappings/api_chat-495066d1-9278-4e4b-b8f6-a1c2fa296779.json
new file mode 100644
index 0000000..ba1c2d3
--- /dev/null
+++
b/data-extract-langchain4j/src/test/resources/mappings/api_chat-495066d1-9278-4e4b-b8f6-a1c2fa296779.json
@@ -0,0 +1,24 @@
+{
+ "id" : "495066d1-9278-4e4b-b8f6-a1c2fa296779",
+ "name" : "api_chat",
+ "request" : {
+ "url" : "/api/chat",
+ "method" : "POST",
+ "bodyPatterns" : [ {
+ "equalToJson" : "{\n \"model\" : \"codellama\",\n \"messages\" : [ {\n
\"role\" : \"assistant\",\n \"content\" :
\"{\\n\\\"customerSatisfied\\\": false,\\n\\\"customerName\\\": \\\"John
Doe\\\",\\n\\\"customerBirthday\\\": \\\"2001-11-01\\\",\\n\\\"summary\\\":
\\\"Insurance company failed to notify customer of automatic cancellation of
full reimbursement option and only provided half reimbursement for
accident.\\\"\\n}\"\n }, {\n \"role\" : \"user\",\n \"content\" : [...]
+ "ignoreArrayOrder" : true,
+ "ignoreExtraElements" : true
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "body" :
"{\"model\":\"codellama\",\"created_at\":\"2024-08-28T16:54:19.439835677Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n\\\"customerSatisfied\\\":
true,\\n\\\"customerName\\\": \\\"Kate Boss\\\",\\n\\\"customerBirthday\\\":
\\\"1999-08-13\\\",\\n\\\"summary\\\": \\\"Customer was unable to find their
insurance contract and had to go through a process of updating their name on
the contract. The operator provided assistance throughout the process, ensuring
that the cu [...]
+ "headers" : {
+ "Date" : "Wed, 28 Aug 2024 16:54:19 GMT",
+ "Content-Type" : "application/json; charset=utf-8"
+ }
+ },
+ "uuid" : "495066d1-9278-4e4b-b8f6-a1c2fa296779",
+ "persistent" : true,
+ "insertionIndex" : 4
+}
\ No newline at end of file
diff --git
a/data-extract-langchain4j/src/test/resources/mappings/api_chat-52961581-e5b1-4309-b62f-1c6e1e0008eb.json
b/data-extract-langchain4j/src/test/resources/mappings/api_chat-52961581-e5b1-4309-b62f-1c6e1e0008eb.json
new file mode 100644
index 0000000..9a626a0
--- /dev/null
+++
b/data-extract-langchain4j/src/test/resources/mappings/api_chat-52961581-e5b1-4309-b62f-1c6e1e0008eb.json
@@ -0,0 +1,24 @@
+{
+ "id" : "52961581-e5b1-4309-b62f-1c6e1e0008eb",
+ "name" : "api_chat",
+ "request" : {
+ "url" : "/api/chat",
+ "method" : "POST",
+ "bodyPatterns" : [ {
+ "equalToJson" : "{\n \"model\" : \"codellama\",\n \"messages\" : [ {\n
\"role\" : \"user\",\n \"content\" : \"Extract information about a
customer from the text delimited by triple backticks: ```Operator: Hello, how
may I help you ?\\nCustomer: Hello, I'm calling because I need to declare an
accident on my main vehicle.\\nOperator: Ok, can you please give me your name
?\\nCustomer: My name is Sarah London.\\nOperator: Could you please give me
your birth date ?\\nCustomer: 1 [...]
+ "ignoreArrayOrder" : true,
+ "ignoreExtraElements" : true
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "body" :
"{\"model\":\"codellama\",\"created_at\":\"2024-08-28T16:52:55.508750951Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n\\\"customerSatisfied\\\":
true,\\n\\\"customerName\\\": \\\"Sarah
London\\\",\\n\\\"customerBirthday\\\": \\\"1986-07-10\\\",\\n\\\"summary\\\":
\\\"Declare an accident on main vehicle and receive reimbursement for
expenses.\\\"\\n}\"},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":22737688834,\"load_duration\":848753776,\"prompt_eval_
[...]
+ "headers" : {
+ "Date" : "Wed, 28 Aug 2024 16:52:55 GMT",
+ "Content-Type" : "application/json; charset=utf-8"
+ }
+ },
+ "uuid" : "52961581-e5b1-4309-b62f-1c6e1e0008eb",
+ "persistent" : true,
+ "insertionIndex" : 6
+}
\ No newline at end of file
diff --git
a/data-extract-langchain4j/src/test/resources/mappings/api_chat-5c1f926c-0480-41e9-9ca7-a93e17919e99.json
b/data-extract-langchain4j/src/test/resources/mappings/api_chat-5c1f926c-0480-41e9-9ca7-a93e17919e99.json
new file mode 100644
index 0000000..9ed4cfe
--- /dev/null
+++
b/data-extract-langchain4j/src/test/resources/mappings/api_chat-5c1f926c-0480-41e9-9ca7-a93e17919e99.json
@@ -0,0 +1,24 @@
+{
+ "id" : "5c1f926c-0480-41e9-9ca7-a93e17919e99",
+ "name" : "api_chat",
+ "request" : {
+ "url" : "/api/chat",
+ "method" : "POST",
+ "bodyPatterns" : [ {
+ "equalToJson" : "{\n \"model\" : \"codellama\",\n \"messages\" : [ {\n
\"role\" : \"assistant\",\n \"content\" :
\"{\\n\\\"customerSatisfied\\\": true,\\n\\\"customerName\\\": \\\"Sarah
London\\\",\\n\\\"customerBirthday\\\": \\\"1986-07-10\\\",\\n\\\"summary\\\":
\\\"Declare an accident on main vehicle and receive reimbursement for
expenses.\\\"\\n}\"\n }, {\n \"role\" : \"user\",\n \"content\" :
\"Extract information about a customer from the text delimited by trip [...]
+ "ignoreArrayOrder" : true,
+ "ignoreExtraElements" : true
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "body" :
"{\"model\":\"codellama\",\"created_at\":\"2024-08-28T16:53:37.033889726Z\",\"message\":{\"role\":\"assistant\",\"content\":\"{\\n\\\"customerSatisfied\\\":
false,\\n\\\"customerName\\\": \\\"John Doe\\\",\\n\\\"customerBirthday\\\":
\\\"2001-11-01\\\",\\n\\\"summary\\\": \\\"Insurance company failed to notify
customer of automatic cancellation of full reimbursement option and only
provided half reimbursement for
accident.\\\"\\n}\"},\"done_reason\":\"stop\",\"done\":true,\" [...]
+ "headers" : {
+ "Date" : "Wed, 28 Aug 2024 16:53:37 GMT",
+ "Content-Type" : "application/json; charset=utf-8"
+ }
+ },
+ "uuid" : "5c1f926c-0480-41e9-9ca7-a93e17919e99",
+ "persistent" : true,
+ "insertionIndex" : 5
+}
\ No newline at end of file
diff --git
a/data-extract-langchain4j/src/test/resources/transcripts/01_sarah-london-10-07-1986-satisfied.json
b/data-extract-langchain4j/src/test/resources/transcripts/01_sarah-london-10-07-1986-satisfied.json
new file mode 100644
index 0000000..d4dab11
--- /dev/null
+++
b/data-extract-langchain4j/src/test/resources/transcripts/01_sarah-london-10-07-1986-satisfied.json
@@ -0,0 +1,4 @@
+{
+ "id": 1,
+ "content": "Operator: Hello, how may I help you ?\nCustomer: Hello, I'm
calling because I need to declare an accident on my main vehicle.\nOperator:
Ok, can you please give me your name ?\nCustomer: My name is Sarah
London.\nOperator: Could you please give me your birth date ?\nCustomer: 1986,
July the 10th.\nOperator: Ok, I've got your contract and I'm happy to share
with you that we'll be able to reimburse all expenses linked to this
accident.\nCustomer: Oh great, many thanks."
+}
\ No newline at end of file
diff --git
a/data-extract-langchain4j/src/test/resources/transcripts/02_john-doe-01-11-2001-unsatisfied.json
b/data-extract-langchain4j/src/test/resources/transcripts/02_john-doe-01-11-2001-unsatisfied.json
new file mode 100644
index 0000000..b7f2d7d
--- /dev/null
+++
b/data-extract-langchain4j/src/test/resources/transcripts/02_john-doe-01-11-2001-unsatisfied.json
@@ -0,0 +1,4 @@
+{
+ "id": 2,
+ "content": "Operator: Hello, how may I help you ?\nCustomer: Hello, I'm
John. I need to share a problem with you. Actually, the insurance has
reimbursed only half the money I have spent due to the accident.\nOperator:
Hello John, could you please give me your last name so that I can find your
contract.\nCustomer: Sure, my surname is Doe.\nOperator: And last thing, I need
to know the date you were born.\nCustomer: Yes, so I was born in 2001, actually
during the first day of November.\nO [...]
+}
\ No newline at end of file
diff --git
a/data-extract-langchain4j/src/test/resources/transcripts/03_kate-boss-13-08-1999-satisfied.json
b/data-extract-langchain4j/src/test/resources/transcripts/03_kate-boss-13-08-1999-satisfied.json
new file mode 100644
index 0000000..af65759
--- /dev/null
+++
b/data-extract-langchain4j/src/test/resources/transcripts/03_kate-boss-13-08-1999-satisfied.json
@@ -0,0 +1,4 @@
+{
+ "id": 3,
+ "content": "Operator: Hello, how may I help you?\nCustomer: Hello, I am
currently at the police station because I've got an accident. The police would
need a proof that I have an insurance. Could you please help me?\nOperator:
Sure, could you please remind me your name and birth date?\nCustomer: Of
course, my name is Kate Hart and I was born on August the thirteen in the year
nineteen ninety nine.\nOperator: I'm sorry Kate, but we don't have any contract
in our records.\nCustomer: Oh, [...]
+}
\ No newline at end of file
diff --git a/docs/modules/ROOT/attachments/examples.json
b/docs/modules/ROOT/attachments/examples.json
index 41b6c4f..758b9b8 100644
--- a/docs/modules/ROOT/attachments/examples.json
+++ b/docs/modules/ROOT/attachments/examples.json
@@ -114,6 +114,11 @@
"description": "Shows how to define a Camel route in XML for tokenizing a
CSV a file.",
"link":
"https://github.com/apache/camel-quarkus-examples/tree/main/file-split-log-xml"
},
+ {
+ "title": "Unstructured Data Extraction with LangChain4j",
+ "description": "Shows how to convert unstructured text data to structured
Java objects helped with a Large Language Model and LangChain4j",
+ "link":
"https://github.com/apache/camel-quarkus-examples/tree/main/data-extract-langchain4j"
+ },
{
"title": "Vertx-Websocket Chat",
"description": "Shows how to configure a WebSocket server and interact
with connected peers.",