You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by fm...@apache.org on 2025/11/19 12:43:48 UTC

(camel) branch main updated: feat: Camel Spring AI components

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

fmariani pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 450821ee552c feat: Camel Spring AI components
450821ee552c is described below

commit 450821ee552c5e32c1c49c3c0ea66c8f6655d2f3
Author: Croway <fe...@gmail.com>
AuthorDate: Wed Nov 12 16:43:07 2025 +0100

    feat: Camel Spring AI components
---
 bom/camel-bom/pom.xml                              |   20 +
 catalog/camel-allcomponents/pom.xml                |   20 +
 .../org/apache/camel/catalog/components.properties |    4 +
 .../camel/catalog/components/spring-ai-chat.json   |   94 ++
 .../catalog/components/spring-ai-embeddings.json   |   45 +
 .../camel/catalog/components/spring-ai-tools.json  |   45 +
 .../catalog/components/spring-ai-vector-store.json |   55 +
 .../camel-spring-ai/camel-spring-ai-chat/pom.xml   |  122 ++
 .../chat/SpringAiChatComponentConfigurer.java      |   63 +
 .../chat/SpringAiChatEndpointConfigurer.java       |  227 ++++
 .../chat/SpringAiChatEndpointUriFactory.java       |   98 ++
 .../component/springai/chat/spring-ai-chat.json    |   94 ++
 .../services/org/apache/camel/component.properties |    7 +
 .../org/apache/camel/component/spring-ai-chat      |    2 +
 .../camel/configurer/spring-ai-chat-component      |    2 +
 .../camel/configurer/spring-ai-chat-endpoint       |    2 +
 .../camel/urifactory/spring-ai-chat-endpoint       |    2 +
 .../src/main/docs/spring-ai-chat-component.adoc    |  607 +++++++++
 .../springai/chat/SpringAiChatComponent.java       |   62 +
 .../springai/chat/SpringAiChatConfiguration.java   |  446 +++++++
 .../springai/chat/SpringAiChatConstants.java       |  124 ++
 .../springai/chat/SpringAiChatEndpoint.java        |   81 ++
 .../springai/chat/SpringAiChatOperations.java      |   35 +
 .../springai/chat/SpringAiChatProducer.java        | 1156 +++++++++++++++++
 .../chat/rag/SpringAiRagAggregatorStrategy.java    |   89 ++
 .../component/springai/chat/OllamaTestSupport.java |   71 ++
 .../springai/chat/SpringAiChatComponentTest.java   |   75 ++
 .../springai/chat/SpringAiChatFileSizeIT.java      |  164 +++
 .../springai/chat/SpringAiChatMemoryIT.java        |  246 ++++
 .../springai/chat/SpringAiChatMetadataIT.java      |  287 +++++
 .../springai/chat/SpringAiChatMultimodalIT.java    |  207 +++
 .../chat/SpringAiChatMultipleMessagesIT.java       |  101 ++
 .../springai/chat/SpringAiChatOllamaIT.java        |   92 ++
 .../chat/SpringAiChatParameterOverrideIT.java      |  127 ++
 .../chat/SpringAiChatPromptTemplateIT.java         |   85 ++
 .../component/springai/chat/SpringAiChatRagIT.java |  106 ++
 .../springai/chat/SpringAiChatSafeguardIT.java     |  124 ++
 .../chat/SpringAiChatStructuredOutputIT.java       |  234 ++++
 .../springai/chat/SpringAiChatSystemMessageIT.java |   91 ++
 .../chat/SpringAiChatToolsDiscoveryTest.java       |  142 +++
 .../chat/SpringAiChatToolsIntegrationIT.java       |  242 ++++
 .../springai/chat/SpringAiChatVectorMemoryIT.java  |  145 +++
 .../springai/chat/SpringAiChatVectorStoreIT.java   |  124 ++
 .../springai/chat/SpringAiChatWrappedFileIT.java   |  170 +++
 .../src/test/resources/log4j2.properties           |   33 +
 .../src/test/resources/test-files/large-file.bin   |  Bin 0 -> 150 bytes
 .../src/test/resources/test-files/medium-file.bin  |  Bin 0 -> 50 bytes
 .../test/resources/test-files/test-document.pdf    |   20 +
 .../src/test/resources/test-files/test-image.jpg   |  Bin 0 -> 74 bytes
 .../src/test/resources/test-files/test-image.png   |  Bin 0 -> 74 bytes
 .../camel-spring-ai-embeddings/pom.xml             |   89 ++
 .../SpringAiEmbeddingsComponentConfigurer.java     |   78 ++
 .../SpringAiEmbeddingsConfigurationConfigurer.java |   51 +
 .../SpringAiEmbeddingsEndpointConfigurer.java      |   62 +
 .../SpringAiEmbeddingsEndpointUriFactory.java      |   72 ++
 .../springai/embeddings/spring-ai-embeddings.json  |   45 +
 .../services/org/apache/camel/component.properties |    7 +
 .../apache/camel/component/spring-ai-embeddings    |    2 +
 ...ngai.embeddings.SpringAiEmbeddingsConfiguration |    2 +
 .../configurer/spring-ai-embeddings-component      |    2 +
 .../camel/configurer/spring-ai-embeddings-endpoint |    2 +
 .../camel/urifactory/spring-ai-embeddings-endpoint |    2 +
 .../main/docs/spring-ai-embeddings-component.adoc  |  103 ++
 .../springai/embeddings/SpringAiEmbeddings.java    |   24 +
 .../embeddings/SpringAiEmbeddingsComponent.java    |   62 +
 .../SpringAiEmbeddingsConfiguration.java           |   52 +
 .../embeddings/SpringAiEmbeddingsEndpoint.java     |   74 ++
 .../embeddings/SpringAiEmbeddingsHeaders.java      |   42 +
 .../embeddings/SpringAiEmbeddingsProducer.java     |   90 ++
 .../springai/embeddings/OllamaTestSupport.java     |   66 +
 .../SpringAiEmbeddingsComponentTest.java           |  137 ++
 .../springai/embeddings/SpringAiEmbeddingsIT.java  |  128 ++
 .../src/test/resources/log4j2.properties           |   33 +
 .../camel-spring-ai/camel-spring-ai-tools/pom.xml  |   73 ++
 .../tools/SpringAiToolsComponentConfigurer.java    |   66 +
 .../tools/SpringAiToolsEndpointConfigurer.java     |  103 ++
 .../tools/SpringAiToolsEndpointUriFactory.java     |   82 ++
 .../component/springai/tools/spring-ai-tools.json  |   45 +
 .../services/org/apache/camel/component.properties |    7 +
 .../org/apache/camel/component/spring-ai-tools     |    2 +
 .../camel/configurer/spring-ai-tools-component     |    2 +
 .../camel/configurer/spring-ai-tools-endpoint      |    2 +
 .../camel/urifactory/spring-ai-tools-endpoint      |    2 +
 .../src/main/docs/spring-ai-tools-component.adoc   |  301 +++++
 .../component/springai/tools/SpringAiTools.java    |   26 +
 .../springai/tools/SpringAiToolsComponent.java     |   82 ++
 .../springai/tools/SpringAiToolsConfiguration.java |   31 +
 .../springai/tools/SpringAiToolsConsumer.java      |   28 +
 .../springai/tools/SpringAiToolsEndpoint.java      |  416 +++++++
 .../camel/component/springai/tools/TagsHelper.java |   33 +
 .../tools/spec/CamelToolExecutorCache.java         |   57 +
 .../tools/spec/CamelToolSpecification.java         |   39 +
 .../component/springai/tools/JsonSchemaTest.java   |  204 +++
 .../springai/tools/SpringAiToolsComponentTest.java |   31 +
 .../camel-spring-ai-vector-store/pom.xml           |  127 ++
 .../SpringAiVectorStoreComponentConfigurer.java    |   99 ++
 ...SpringAiVectorStoreConfigurationConfigurer.java |   72 ++
 .../SpringAiVectorStoreEndpointConfigurer.java     |   83 ++
 .../SpringAiVectorStoreEndpointUriFactory.java     |   76 ++
 .../vectorstore/spring-ai-vector-store.json        |   55 +
 .../services/org/apache/camel/component.properties |    7 +
 .../apache/camel/component/spring-ai-vector-store  |    2 +
 ...ai.vectorstore.SpringAiVectorStoreConfiguration |    2 +
 .../configurer/spring-ai-vector-store-component    |    2 +
 .../configurer/spring-ai-vector-store-endpoint     |    2 +
 .../urifactory/spring-ai-vector-store-endpoint     |    2 +
 .../docs/spring-ai-vector-store-component.adoc     |  200 +++
 .../springai/vectorstore/SpringAiVectorStore.java  |   24 +
 .../vectorstore/SpringAiVectorStoreComponent.java  |   63 +
 .../SpringAiVectorStoreConfiguration.java          |  108 ++
 .../vectorstore/SpringAiVectorStoreEndpoint.java   |   75 ++
 .../vectorstore/SpringAiVectorStoreHeaders.java    |   52 +
 .../vectorstore/SpringAiVectorStoreOperation.java  |   23 +
 .../vectorstore/SpringAiVectorStoreProducer.java   |  246 ++++
 .../springai/vectorstore/QdrantVectorStoreIT.java  |  430 +++++++
 .../SpringAiVectorStoreComponentTest.java          |  356 ++++++
 .../src/test/resources/log4j2.properties           |   33 +
 .../{ => camel-spring-ai}/pom.xml                  |   42 +-
 components/camel-spring-parent/pom.xml             |    1 +
 .../org/apache/camel/main/components.properties    |    4 +
 .../modules/ROOT/examples/json/spring-ai-chat.json |    1 +
 .../ROOT/examples/json/spring-ai-embeddings.json   |    1 +
 .../ROOT/examples/json/spring-ai-tools.json        |    1 +
 .../ROOT/examples/json/spring-ai-vector-store.json |    1 +
 docs/components/modules/ROOT/nav.adoc              |    4 +
 .../ROOT/pages/spring-ai-chat-component.adoc       |    1 +
 .../ROOT/pages/spring-ai-embeddings-component.adoc |    1 +
 .../ROOT/pages/spring-ai-tools-component.adoc      |    1 +
 .../pages/spring-ai-vector-store-component.adoc    |    1 +
 docs/pom.xml                                       |    2 +-
 .../component/ComponentsBuilderFactory.java        |   52 +
 .../dsl/SpringAiChatComponentBuilderFactory.java   |  138 ++
 .../SpringAiEmbeddingsComponentBuilderFactory.java |  160 +++
 .../dsl/SpringAiToolsComponentBuilderFactory.java  |  166 +++
 ...SpringAiVectorStoreComponentBuilderFactory.java |  231 ++++
 .../builder/endpoint/EndpointBuilderFactory.java   |    4 +
 .../camel/builder/endpoint/EndpointBuilders.java   |    4 +
 .../builder/endpoint/StaticEndpointBuilders.java   |  160 +++
 .../dsl/SpringAiChatEndpointBuilderFactory.java    | 1313 ++++++++++++++++++++
 .../SpringAiEmbeddingsEndpointBuilderFactory.java  |  289 +++++
 .../dsl/SpringAiToolsEndpointBuilderFactory.java   |  505 ++++++++
 .../SpringAiVectorStoreEndpointBuilderFactory.java |  429 +++++++
 .../camel-component-known-dependencies.properties  |    4 +
 parent/pom.xml                                     |   21 +
 .../apache/camel/maven/packaging/MojoHelper.java   |    6 +-
 .../camel/maven/packaging/PrepareCatalogMojo.java  |    1 +
 146 files changed, 14909 insertions(+), 19 deletions(-)

diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index b616f902b37c..c285dd4bc4de 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -2047,6 +2047,26 @@
         <artifactId>camel-spring</artifactId>
         <version>4.17.0-SNAPSHOT</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-spring-ai-chat</artifactId>
+        <version>4.17.0-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-spring-ai-embeddings</artifactId>
+        <version>4.17.0-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-spring-ai-tools</artifactId>
+        <version>4.17.0-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-spring-ai-vector-store</artifactId>
+        <version>4.17.0-SNAPSHOT</version>
+      </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
         <artifactId>camel-spring-batch</artifactId>
diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml
index 83437327fe53..685aa2965b41 100644
--- a/catalog/camel-allcomponents/pom.xml
+++ b/catalog/camel-allcomponents/pom.xml
@@ -1841,6 +1841,26 @@
             <artifactId>camel-spring</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-spring-ai-chat</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-spring-ai-embeddings</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-spring-ai-tools</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-spring-ai-vector-store</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-spring-batch</artifactId>
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
index 89b0bc445271..636c1bfb2625 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
@@ -319,6 +319,10 @@ snmp
 solr
 splunk
 splunk-hec
+spring-ai-chat
+spring-ai-embeddings
+spring-ai-tools
+spring-ai-vector-store
 spring-batch
 spring-event
 spring-jdbc
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-chat.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-chat.json
new file mode 100644
index 000000000000..f6589dde1e4a
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-chat.json
@@ -0,0 +1,94 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "spring-ai-chat",
+    "title": "Spring AI Chat",
+    "description": "Perform chat operations using Spring AI.",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "ai",
+    "javaType": "org.apache.camel.component.springai.chat.SpringAiChatComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-spring-ai-chat",
+    "version": "4.17.0-SNAPSHOT",
+    "scheme": "spring-ai-chat",
+    "extendsScheme": "",
+    "syntax": "spring-ai-chat:chatId",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": true,
+    "lenientProperties": false,
+    "browsable": false,
+    "remote": true
+  },
+  "componentProperties": {
+    "lazyStartProducer": { "index": 0, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
+    "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+    "chatModel": { "index": 2, "kind": "property", "displayName": "Chat Model", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.springframework.ai.chat.model.ChatModel", "deprecated": false, "autowired": false, "secret": false, "description": "The ChatModel instance to use for chat operations." }
+  },
+  "headers": {
+    "CamelSpringAiChatResponse": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The response from the chat model", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#CHAT_RESPONSE" },
+    "CamelSpringAiInputTokenCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of input tokens used", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#INPUT_TOKEN_COUNT" },
+    "CamelSpringAiOutputTokenCount": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of output tokens used", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#OUTPUT_TOKEN_COUNT" },
+    "CamelSpringAiTotalTokenCount": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The total number of tokens used", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#TOTAL_TOKEN_COUNT" },
+    "CamelSpringAiChatPromptTemplate": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The prompt template with placeholders for variable substitution", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#PROMPT_TEMPLATE" },
+    "CamelSpringAiChatAugmentedData": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<org.springframework.ai.document.Document>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Augmented data for RAG as List", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#AUGMENTED_DATA" },
+    "CamelSpringAiChatSystemMessage": { "index": 6, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "System message for the conversation", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SYSTEM_MESSAGE" },
+    "CamelSpringAiChatTemperature": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Temperature parameter for response randomness (0.0-2.0)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#TEMPERATURE" },
+    "CamelSpringAiChatMaxTokens": { "index": 8, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Maximum tokens in the response", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MAX_TOKENS" },
+    "CamelSpringAiChatTopP": { "index": 9, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Top P parameter for nucleus sampling", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#TOP_P" },
+    "CamelSpringAiChatTopK": { "index": 10, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Top K parameter for sampling", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#TOP_K" },
+    "CamelSpringAiChatUserMessage": { "index": 11, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "User message text for multimodal requests", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#USER_MESSAGE" },
+    "CamelSpringAiChatMediaData": { "index": 12, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Media data for multimodal requests (image or audio)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MEDIA_DATA" },
+    "CamelSpringAiChatMediaType": { "index": 13, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Media type (MIME type) for multimodal requests (e.g., image\/png, audio\/wav)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MEDIA_TYPE" },
+    "CamelSpringAiChatOutputFormat": { "index": 14, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The output format type for structured output conversion (BEAN, MAP, LIST)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#OUTPUT_FORMAT" },
+    "CamelSpringAiChatOutputClass": { "index": 15, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Class<?>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Java class to use for structured output bean conversion", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#OUTPUT_CLASS" },
+    "CamelSpringAiChatStructuredOutput": { "index": 16, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The structured output converted from the chat response", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#STRUCTURED_OUTPUT" },
+    "CamelSpringAiChatSafeguardSensitiveWords": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Comma-separated list of sensitive words for SafeGuard advisor", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SAFEGUARD_SENSITIVE_WORDS" },
+    "CamelSpringAiChatSafeguardFailureResponse": { "index": 18, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Failure response message for SafeGuard advisor when sensitive content is detected", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SAFEGUARD_FAILURE_RESPONSE" },
+    "CamelSpringAiChatSafeguardOrder": { "index": 19, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Order of execution for SafeGuard advisor", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SAFEGUARD_ORDER" },
+    "CamelSpringAiChatAdvisors": { "index": 20, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<org.springframework.ai.chat.client.advisor.api.Advisor>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of custom advisors to add to the request", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#ADVISORS" },
+    "CamelSpringAiChatEntityClass": { "index": 21, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Class<?>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Java class to use for entity response conversion", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#ENTITY_CLASS" },
+    "CamelSpringAiChatUserMetadata": { "index": 22, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Metadata to attach to user messages", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#USER_METADATA" },
+    "CamelSpringAiChatSystemMetadata": { "index": 23, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Metadata to attach to system messages", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SYSTEM_METADATA" },
+    "CamelSpringAiChatConversationId": { "index": 24, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Conversation ID for managing separate conversation contexts in chat memory", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#CONVERSATION_ID" },
+    "CamelSpringAiChatMaxFileSize": { "index": 25, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Maximum file size in bytes for multimodal content. Overrides endpoint configuration.", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MAX_FILE_SIZE" },
+    "CamelSpringAiChatFinishReason": { "index": 26, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The reason why the chat response generation stopped (e.g., STOP, LENGTH, TOOL_CALLS)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#FINISH_REASON" },
+    "CamelSpringAiChatModelName": { "index": 27, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of the AI model used to generate the response", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MODEL_NAME" },
+    "CamelSpringAiChatResponseId": { "index": 28, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The unique ID of the chat response", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#RESPONSE_ID" },
+    "CamelSpringAiChatResponseMetadata": { "index": 29, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Full response metadata as a Map containing all available metadata fields", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#RESPONSE_METADATA" }
+  },
+  "properties": {
+    "chatId": { "index": 0, "kind": "path", "displayName": "Chat Id", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The ID of the chat endpoint" },
+    "chatClient": { "index": 1, "kind": "parameter", "displayName": "Chat Client", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.springframework.ai.chat.client.ChatClient", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The ChatClient instance to use for chat operations [...]
+    "chatModel": { "index": 2, "kind": "parameter", "displayName": "Chat Model", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.springframework.ai.chat.model.ChatModel", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The ChatModel instance to use for chat operations. Thi [...]
+    "chatOperation": { "index": 3, "kind": "parameter", "displayName": "Chat Operation", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.springai.chat.SpringAiChatOperations", "enum": [ "CHAT_SINGLE_MESSAGE", "CHAT_SINGLE_MESSAGE_WITH_PROMPT", "CHAT_MULTIPLE_MESSAGES" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "CHAT_SINGLE_MESSAGE", "configurationClass": "org.apache.camel. [...]
+    "systemMessage": { "index": 4, "kind": "parameter", "displayName": "System Message", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Default system message to set context for the conversation. Can be overridden by the CamelSprin [...]
+    "tags": { "index": 5, "kind": "parameter", "displayName": "Tags", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Tags for discovering and calling Camel route tools" },
+    "userMessage": { "index": 6, "kind": "parameter", "displayName": "User Message", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Default user message text for multimodal requests. Can be combined with media data in the message b [...]
+    "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+    "advisors": { "index": 8, "kind": "parameter", "displayName": "Advisors", "group": "advanced", "label": "advanced", "required": false, "type": "array", "javaType": "java.util.List<org.springframework.ai.chat.client.advisor.api.Advisor>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "List of custom advisor [...]
+    "chatMemory": { "index": 9, "kind": "parameter", "displayName": "Chat Memory", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.springframework.ai.chat.memory.ChatMemory", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "ChatMemory instance for maintaining conver [...]
+    "chatMemoryVectorStore": { "index": 10, "kind": "parameter", "displayName": "Chat Memory Vector Store", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.springframework.ai.vectorstore.VectorStore", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "VectorStore instance for maintaining c [...]
+    "entityClass": { "index": 11, "kind": "parameter", "displayName": "Entity Class", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.Class<java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The Java class to use for entity response conversion using ChatClient.ent [...]
+    "maxFileSize": { "index": 12, "kind": "parameter", "displayName": "Max File Size", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1048576, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Maximum file size in bytes for multimodal content (images, audio, PDFs, etc [...]
+    "maxTokens": { "index": 13, "kind": "parameter", "displayName": "Max Tokens", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Maximum tokens in the response." },
+    "outputClass": { "index": 14, "kind": "parameter", "displayName": "Output Class", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.Class<java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The Java class to use for BEAN output format conversion. Required when ou [...]
+    "outputFormat": { "index": 15, "kind": "parameter", "displayName": "Output Format", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The output format for structured output conversion (BEAN, MAP, LIST). Used in conjunctio [...]
+    "structuredOutputConverter": { "index": 16, "kind": "parameter", "displayName": "Structured Output Converter", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.springframework.ai.converter.StructuredOutputConverter<java.lang.Object>", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configurati [...]
+    "systemMetadata": { "index": 17, "kind": "parameter", "displayName": "System Metadata", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Metadata to attach to system messages. This metadat [...]
+    "temperature": { "index": 18, "kind": "parameter", "displayName": "Temperature", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "java.lang.Double", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Temperature parameter for response randomness (0.0-2.0)." },
+    "topKSampling": { "index": 19, "kind": "parameter", "displayName": "Top KSampling", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Top K parameter for sampling." },
+    "topP": { "index": 20, "kind": "parameter", "displayName": "Top P", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "java.lang.Double", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Top P parameter for nucleus sampling." },
+    "userMetadata": { "index": 21, "kind": "parameter", "displayName": "User Metadata", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Metadata to attach to user messages. This metadata can  [...]
+    "ragTemplate": { "index": 22, "kind": "parameter", "displayName": "Rag Template", "group": "rag", "label": "rag", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "Context:\\n\\{context}\\n\\nQuestion: \\{question}", "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Template for formatting RAG (R [...]
+    "similarityThreshold": { "index": 23, "kind": "parameter", "displayName": "Similarity Threshold", "group": "rag", "label": "rag", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.7, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Similarity threshold for RAG retrieval (default: 0.7)." },
+    "topK": { "index": 24, "kind": "parameter", "displayName": "Top K", "group": "rag", "label": "rag", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 5, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Number of top documents to retrieve for RAG (default: 5)." },
+    "vectorStore": { "index": 25, "kind": "parameter", "displayName": "Vector Store", "group": "rag", "label": "rag", "required": false, "type": "object", "javaType": "org.springframework.ai.vectorstore.VectorStore", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "VectorStore for automatic RAG retrieval." },
+    "safeguardFailureResponse": { "index": 26, "kind": "parameter", "displayName": "Safeguard Failure Response", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Failure response message for SafeGuard advisor when sensitive c [...]
+    "safeguardSensitiveWords": { "index": 27, "kind": "parameter", "displayName": "Safeguard Sensitive Words", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Comma-separated list of sensitive words for SafeGuard advisor. Wh [...]
+    "safeguardOrder": { "index": 28, "kind": "parameter", "displayName": "Safeguard Order", "group": "security (advanced)", "label": "security,advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Order of execution for SafeGuard advisor. Lower numbers execut [...]
+  }
+}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-embeddings.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-embeddings.json
new file mode 100644
index 000000000000..d13dad0b055c
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-embeddings.json
@@ -0,0 +1,45 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "spring-ai-embeddings",
+    "title": "Spring AI Embeddings",
+    "description": "Spring AI Embeddings",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "ai",
+    "javaType": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-spring-ai-embeddings",
+    "version": "4.17.0-SNAPSHOT",
+    "scheme": "spring-ai-embeddings",
+    "extendsScheme": "",
+    "syntax": "spring-ai-embeddings:embeddingId",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": true,
+    "lenientProperties": false,
+    "browsable": false,
+    "remote": true
+  },
+  "componentProperties": {
+    "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The configuration." },
+    "embeddingModel": { "index": 1, "kind": "property", "displayName": "Embedding Model", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.springframework.ai.embedding.EmbeddingModel", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration", "configurationField": "configuration", "description": "The EmbeddingModel to use for  [...]
+    "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
+    "autowiredEnabled": { "index": 3, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+  },
+  "headers": {
+    "CamelSpringAiEmbeddingMetadata": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.springframework.ai.embedding.EmbeddingResponseMetadata", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The embedding response metadata", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#EMBEDDING_METADATA" },
+    "CamelSpringAiEmbeddingIndex": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The index of the embedding in the response", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#EMBEDDING_INDEX" },
+    "CamelSpringAiEmbedding": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.springframework.ai.embedding.Embedding", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Embedding object", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#EMBEDDING" },
+    "CamelSpringAiEmbeddingInputText": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The input text that was embedded", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#INPUT_TEXT" },
+    "CamelSpringAiEmbeddings": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<org.springframework.ai.embedding.Embedding>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of Embedding objects", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#EMBEDDINGS" },
+    "CamelSpringAiEmbeddingInputTexts": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<String>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of input texts that were embedded", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#INPUT_TEXTS" }
+  },
+  "properties": {
+    "embeddingId": { "index": 0, "kind": "path", "displayName": "Embedding Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The id" },
+    "embeddingModel": { "index": 1, "kind": "parameter", "displayName": "Embedding Model", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.springframework.ai.embedding.EmbeddingModel", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration", "configurationField": "configuration", "description": "The EmbeddingModel to use for [...]
+    "lazyStartProducer": { "index": 2, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+  }
+}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-tools.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-tools.json
new file mode 100644
index 000000000000..ef421ccf04d6
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-tools.json
@@ -0,0 +1,45 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "spring-ai-tools",
+    "title": "Spring AI Tools",
+    "description": "Spring AI Tools and Function Calling Features",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "ai",
+    "javaType": "org.apache.camel.component.springai.tools.SpringAiToolsComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-spring-ai-tools",
+    "version": "4.17.0-SNAPSHOT",
+    "scheme": "spring-ai-tools",
+    "extendsScheme": "",
+    "syntax": "spring-ai-tools:toolId",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": false,
+    "lenientProperties": false,
+    "browsable": false,
+    "remote": true
+  },
+  "componentProperties": {
+    "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "common", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.springai.tools.SpringAiToolsConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The configuration." },
+    "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the like [...]
+    "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
+    "autowiredEnabled": { "index": 3, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+  },
+  "properties": {
+    "toolId": { "index": 0, "kind": "path", "displayName": "Tool Id", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The tool id" },
+    "tags": { "index": 1, "kind": "parameter", "displayName": "Tags", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The tags for the tools" },
+    "description": { "index": 2, "kind": "parameter", "displayName": "Description", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Tool description" },
+    "inputType": { "index": 3, "kind": "parameter", "displayName": "Input Type", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.Class<java.lang.Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Input type class for the tool" },
+    "name": { "index": 4, "kind": "parameter", "displayName": "Name", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Tool name" },
+    "parameters": { "index": 5, "kind": "parameter", "displayName": "Parameters", "group": "consumer", "label": "consumer", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of Tool parameters with optional metadata. Format: parameter.=, parameter..description=, parameter..required=, parameter [...]
+    "returnDirect": { "index": 6, "kind": "parameter", "displayName": "Return Direct", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the tool result should be returned directly or passed back to the model. Default is false, meaning the result is passed back to the model for further processing." },
+    "bridgeErrorHandler": { "index": 7, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming  [...]
+    "exceptionHandler": { "index": 8, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By def [...]
+    "exchangePattern": { "index": 9, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "lazyStartProducer": { "index": 10, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...]
+  }
+}
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-vector-store.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-vector-store.json
new file mode 100644
index 000000000000..997b32c80895
--- /dev/null
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-ai-vector-store.json
@@ -0,0 +1,55 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "spring-ai-vector-store",
+    "title": "Spring AI Vector Store",
+    "description": "Spring AI Vector Store",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "ai",
+    "javaType": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-spring-ai-vector-store",
+    "version": "4.17.0-SNAPSHOT",
+    "scheme": "spring-ai-vector-store",
+    "extendsScheme": "",
+    "syntax": "spring-ai-vector-store:storeId",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": true,
+    "lenientProperties": false,
+    "browsable": false,
+    "remote": true
+  },
+  "componentProperties": {
+    "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The configuration." },
+    "filterExpression": { "index": 1, "kind": "property", "displayName": "Filter Expression", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationField": "configuration", "description": "Filter expression for metadata-based filtering in searches." },
+    "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
+    "operation": { "index": 3, "kind": "property", "displayName": "Operation", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreOperation", "enum": [ "ADD", "DELETE", "SIMILARITY_SEARCH" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "ADD", "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationF [...]
+    "similarityThreshold": { "index": 4, "kind": "property", "displayName": "Similarity Threshold", "group": "producer", "label": "", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationField": "configuration", "description": "The minimum similarity score threshold (0-1) for similarity [...]
+    "topK": { "index": 5, "kind": "property", "displayName": "Top K", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 5, "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationField": "configuration", "description": "The maximum number of similar documents to return for similarity search." },
+    "vectorStore": { "index": 6, "kind": "property", "displayName": "Vector Store", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.springframework.ai.vectorstore.VectorStore", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationField": "configuration", "description": "The VectorStore to use for vector o [...]
+    "autowiredEnabled": { "index": 7, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+  },
+  "headers": {
+    "CamelSpringAiVectorStoreOperation": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreOperation", "enum": [ "ADD", "DELETE", "SIMILARITY_SEARCH" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The operation to perform (ADD, DELETE, SIMILARITY_SEARCH)", "constantName": "org.apache.camel.component.spri [...]
+    "CamelSpringAiVectorStoreTopK": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The maximum number of similar documents to return (topK)", "constantName": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreHeaders#TOP_K" },
+    "CamelSpringAiVectorStoreSimilarityThreshold": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The similarity threshold (0-1)", "constantName": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreHeaders#SIMILARITY_THRESHOLD" },
+    "CamelSpringAiVectorStoreFilterExpression": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Filter expression for metadata-based filtering", "constantName": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreHeaders#FILTER_EXPRESSION" },
+    "CamelSpringAiVectorStoreDocumentIds": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<String>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of document IDs (input for DELETE, output for ADD and SIMILARITY_SEARCH)", "constantName": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreHeaders#DOCUMENT_IDS" },
+    "CamelSpringAiVectorStoreSimilarDocuments": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<org.springframework.ai.document.Document>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of similar documents found", "constantName": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreHeaders#SIMILAR_DOCUMENTS" },
+    "CamelSpringAiVectorStoreDocumentsAdded": { "index": 6, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Number of documents added", "constantName": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreHeaders#DOCUMENTS_ADDED" },
+    "CamelSpringAiVectorStoreDocumentsDeleted": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Number of documents deleted", "constantName": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreHeaders#DOCUMENTS_DELETED" }
+  },
+  "properties": {
+    "storeId": { "index": 0, "kind": "path", "displayName": "Store Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The id" },
+    "filterExpression": { "index": 1, "kind": "parameter", "displayName": "Filter Expression", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationField": "configuration", "description": "Filter expression for metadata-based filtering in searches." },
+    "operation": { "index": 2, "kind": "parameter", "displayName": "Operation", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreOperation", "enum": [ "ADD", "DELETE", "SIMILARITY_SEARCH" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "ADD", "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configuration [...]
+    "similarityThreshold": { "index": 3, "kind": "parameter", "displayName": "Similarity Threshold", "group": "producer", "label": "", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.0, "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationField": "configuration", "description": "The minimum similarity score threshold (0-1) for similarit [...]
+    "topK": { "index": 4, "kind": "parameter", "displayName": "Top K", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 5, "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationField": "configuration", "description": "The maximum number of similar documents to return for similarity search." },
+    "vectorStore": { "index": 5, "kind": "parameter", "displayName": "Vector Store", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.springframework.ai.vectorstore.VectorStore", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.vectorstore.SpringAiVectorStoreConfiguration", "configurationField": "configuration", "description": "The VectorStore to use for vector  [...]
+    "lazyStartProducer": { "index": 6, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+  }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/pom.xml b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/pom.xml
new file mode 100644
index 000000000000..b823e5fbb044
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/pom.xml
@@ -0,0 +1,122 @@
+<?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>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-spring-ai-parent</artifactId>
+        <version>4.17.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-spring-ai-chat</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: Spring AI :: Chat</name>
+    <description>Camel Spring AI Chat component</description>
+
+    <dependencies>
+        <!-- Camel dependencies -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-support</artifactId>
+        </dependency>
+
+        <!-- Camel Spring AI Tools for function calling -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-spring-ai-tools</artifactId>
+            <version>${project.version}</version>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- Spring AI core dependencies -->
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-model</artifactId>
+        </dependency>
+
+        <!-- Spring AI VectorStore for RAG support -->
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-vector-store</artifactId>
+        </dependency>
+
+        <!-- Spring AI Commons for additional utilities -->
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-commons</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-client-chat</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-advisors-vector-store</artifactId>
+        </dependency>
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-spring-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <version>${mockito-version}</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- Spring AI Ollama for integration tests -->
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-ollama</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- Ollama test infrastructure -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-infra-ollama</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-infra-ollama</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatComponentConfigurer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatComponentConfigurer.java
new file mode 100644
index 000000000000..12490e8060e3
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatComponentConfigurer.java
@@ -0,0 +1,63 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.chat;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class SpringAiChatComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        SpringAiChatComponent target = (SpringAiChatComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true;
+        case "chatmodel":
+        case "chatModel": target.setChatModel(property(camelContext, org.springframework.ai.chat.model.ChatModel.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return boolean.class;
+        case "chatmodel":
+        case "chatModel": return org.springframework.ai.chat.model.ChatModel.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        SpringAiChatComponent target = (SpringAiChatComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return target.isAutowiredEnabled();
+        case "chatmodel":
+        case "chatModel": return target.getChatModel();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatEndpointConfigurer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatEndpointConfigurer.java
new file mode 100644
index 000000000000..03254f1979bb
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatEndpointConfigurer.java
@@ -0,0 +1,227 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.chat;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class SpringAiChatEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        SpringAiChatEndpoint target = (SpringAiChatEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "advisors": target.getConfiguration().setAdvisors(property(camelContext, java.util.List.class, value)); return true;
+        case "chatclient":
+        case "chatClient": target.getConfiguration().setChatClient(property(camelContext, org.springframework.ai.chat.client.ChatClient.class, value)); return true;
+        case "chatmemory":
+        case "chatMemory": target.getConfiguration().setChatMemory(property(camelContext, org.springframework.ai.chat.memory.ChatMemory.class, value)); return true;
+        case "chatmemoryvectorstore":
+        case "chatMemoryVectorStore": target.getConfiguration().setChatMemoryVectorStore(property(camelContext, org.springframework.ai.vectorstore.VectorStore.class, value)); return true;
+        case "chatmodel":
+        case "chatModel": target.getConfiguration().setChatModel(property(camelContext, org.springframework.ai.chat.model.ChatModel.class, value)); return true;
+        case "chatoperation":
+        case "chatOperation": target.getConfiguration().setChatOperation(property(camelContext, org.apache.camel.component.springai.chat.SpringAiChatOperations.class, value)); return true;
+        case "entityclass":
+        case "entityClass": target.getConfiguration().setEntityClass(property(camelContext, java.lang.Class.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        case "maxfilesize":
+        case "maxFileSize": target.getConfiguration().setMaxFileSize(property(camelContext, long.class, value)); return true;
+        case "maxtokens":
+        case "maxTokens": target.getConfiguration().setMaxTokens(property(camelContext, java.lang.Integer.class, value)); return true;
+        case "outputclass":
+        case "outputClass": target.getConfiguration().setOutputClass(property(camelContext, java.lang.Class.class, value)); return true;
+        case "outputformat":
+        case "outputFormat": target.getConfiguration().setOutputFormat(property(camelContext, java.lang.String.class, value)); return true;
+        case "ragtemplate":
+        case "ragTemplate": target.getConfiguration().setRagTemplate(property(camelContext, java.lang.String.class, value)); return true;
+        case "safeguardfailureresponse":
+        case "safeguardFailureResponse": target.getConfiguration().setSafeguardFailureResponse(property(camelContext, java.lang.String.class, value)); return true;
+        case "safeguardorder":
+        case "safeguardOrder": target.getConfiguration().setSafeguardOrder(property(camelContext, java.lang.Integer.class, value)); return true;
+        case "safeguardsensitivewords":
+        case "safeguardSensitiveWords": target.getConfiguration().setSafeguardSensitiveWords(property(camelContext, java.lang.String.class, value)); return true;
+        case "similaritythreshold":
+        case "similarityThreshold": target.getConfiguration().setSimilarityThreshold(property(camelContext, double.class, value)); return true;
+        case "structuredoutputconverter":
+        case "structuredOutputConverter": target.getConfiguration().setStructuredOutputConverter(property(camelContext, org.springframework.ai.converter.StructuredOutputConverter.class, value)); return true;
+        case "systemmessage":
+        case "systemMessage": target.getConfiguration().setSystemMessage(property(camelContext, java.lang.String.class, value)); return true;
+        case "systemmetadata":
+        case "systemMetadata": target.getConfiguration().setSystemMetadata(property(camelContext, java.util.Map.class, value)); return true;
+        case "tags": target.getConfiguration().setTags(property(camelContext, java.lang.String.class, value)); return true;
+        case "temperature": target.getConfiguration().setTemperature(property(camelContext, java.lang.Double.class, value)); return true;
+        case "topk":
+        case "topK": target.getConfiguration().setTopK(property(camelContext, int.class, value)); return true;
+        case "topksampling":
+        case "topKSampling": target.getConfiguration().setTopKSampling(property(camelContext, java.lang.Integer.class, value)); return true;
+        case "topp":
+        case "topP": target.getConfiguration().setTopP(property(camelContext, java.lang.Double.class, value)); return true;
+        case "usermessage":
+        case "userMessage": target.getConfiguration().setUserMessage(property(camelContext, java.lang.String.class, value)); return true;
+        case "usermetadata":
+        case "userMetadata": target.getConfiguration().setUserMetadata(property(camelContext, java.util.Map.class, value)); return true;
+        case "vectorstore":
+        case "vectorStore": target.getConfiguration().setVectorStore(property(camelContext, org.springframework.ai.vectorstore.VectorStore.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public String[] getAutowiredNames() {
+        return new String[]{"chatClient", "chatMemory", "chatModel", "structuredOutputConverter", "vectorStore"};
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "advisors": return java.util.List.class;
+        case "chatclient":
+        case "chatClient": return org.springframework.ai.chat.client.ChatClient.class;
+        case "chatmemory":
+        case "chatMemory": return org.springframework.ai.chat.memory.ChatMemory.class;
+        case "chatmemoryvectorstore":
+        case "chatMemoryVectorStore": return org.springframework.ai.vectorstore.VectorStore.class;
+        case "chatmodel":
+        case "chatModel": return org.springframework.ai.chat.model.ChatModel.class;
+        case "chatoperation":
+        case "chatOperation": return org.apache.camel.component.springai.chat.SpringAiChatOperations.class;
+        case "entityclass":
+        case "entityClass": return java.lang.Class.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        case "maxfilesize":
+        case "maxFileSize": return long.class;
+        case "maxtokens":
+        case "maxTokens": return java.lang.Integer.class;
+        case "outputclass":
+        case "outputClass": return java.lang.Class.class;
+        case "outputformat":
+        case "outputFormat": return java.lang.String.class;
+        case "ragtemplate":
+        case "ragTemplate": return java.lang.String.class;
+        case "safeguardfailureresponse":
+        case "safeguardFailureResponse": return java.lang.String.class;
+        case "safeguardorder":
+        case "safeguardOrder": return java.lang.Integer.class;
+        case "safeguardsensitivewords":
+        case "safeguardSensitiveWords": return java.lang.String.class;
+        case "similaritythreshold":
+        case "similarityThreshold": return double.class;
+        case "structuredoutputconverter":
+        case "structuredOutputConverter": return org.springframework.ai.converter.StructuredOutputConverter.class;
+        case "systemmessage":
+        case "systemMessage": return java.lang.String.class;
+        case "systemmetadata":
+        case "systemMetadata": return java.util.Map.class;
+        case "tags": return java.lang.String.class;
+        case "temperature": return java.lang.Double.class;
+        case "topk":
+        case "topK": return int.class;
+        case "topksampling":
+        case "topKSampling": return java.lang.Integer.class;
+        case "topp":
+        case "topP": return java.lang.Double.class;
+        case "usermessage":
+        case "userMessage": return java.lang.String.class;
+        case "usermetadata":
+        case "userMetadata": return java.util.Map.class;
+        case "vectorstore":
+        case "vectorStore": return org.springframework.ai.vectorstore.VectorStore.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        SpringAiChatEndpoint target = (SpringAiChatEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "advisors": return target.getConfiguration().getAdvisors();
+        case "chatclient":
+        case "chatClient": return target.getConfiguration().getChatClient();
+        case "chatmemory":
+        case "chatMemory": return target.getConfiguration().getChatMemory();
+        case "chatmemoryvectorstore":
+        case "chatMemoryVectorStore": return target.getConfiguration().getChatMemoryVectorStore();
+        case "chatmodel":
+        case "chatModel": return target.getConfiguration().getChatModel();
+        case "chatoperation":
+        case "chatOperation": return target.getConfiguration().getChatOperation();
+        case "entityclass":
+        case "entityClass": return target.getConfiguration().getEntityClass();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        case "maxfilesize":
+        case "maxFileSize": return target.getConfiguration().getMaxFileSize();
+        case "maxtokens":
+        case "maxTokens": return target.getConfiguration().getMaxTokens();
+        case "outputclass":
+        case "outputClass": return target.getConfiguration().getOutputClass();
+        case "outputformat":
+        case "outputFormat": return target.getConfiguration().getOutputFormat();
+        case "ragtemplate":
+        case "ragTemplate": return target.getConfiguration().getRagTemplate();
+        case "safeguardfailureresponse":
+        case "safeguardFailureResponse": return target.getConfiguration().getSafeguardFailureResponse();
+        case "safeguardorder":
+        case "safeguardOrder": return target.getConfiguration().getSafeguardOrder();
+        case "safeguardsensitivewords":
+        case "safeguardSensitiveWords": return target.getConfiguration().getSafeguardSensitiveWords();
+        case "similaritythreshold":
+        case "similarityThreshold": return target.getConfiguration().getSimilarityThreshold();
+        case "structuredoutputconverter":
+        case "structuredOutputConverter": return target.getConfiguration().getStructuredOutputConverter();
+        case "systemmessage":
+        case "systemMessage": return target.getConfiguration().getSystemMessage();
+        case "systemmetadata":
+        case "systemMetadata": return target.getConfiguration().getSystemMetadata();
+        case "tags": return target.getConfiguration().getTags();
+        case "temperature": return target.getConfiguration().getTemperature();
+        case "topk":
+        case "topK": return target.getConfiguration().getTopK();
+        case "topksampling":
+        case "topKSampling": return target.getConfiguration().getTopKSampling();
+        case "topp":
+        case "topP": return target.getConfiguration().getTopP();
+        case "usermessage":
+        case "userMessage": return target.getConfiguration().getUserMessage();
+        case "usermetadata":
+        case "userMetadata": return target.getConfiguration().getUserMetadata();
+        case "vectorstore":
+        case "vectorStore": return target.getConfiguration().getVectorStore();
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getCollectionValueType(Object target, String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "advisors": return org.springframework.ai.chat.client.advisor.api.Advisor.class;
+        case "entityclass":
+        case "entityClass": return java.lang.Object.class;
+        case "outputclass":
+        case "outputClass": return java.lang.Object.class;
+        case "structuredoutputconverter":
+        case "structuredOutputConverter": return java.lang.Object.class;
+        case "systemmetadata":
+        case "systemMetadata": return java.lang.Object.class;
+        case "usermetadata":
+        case "userMetadata": return java.lang.Object.class;
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatEndpointUriFactory.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatEndpointUriFactory.java
new file mode 100644
index 000000000000..32ad0c2615d4
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/java/org/apache/camel/component/springai/chat/SpringAiChatEndpointUriFactory.java
@@ -0,0 +1,98 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.chat;
+
+import javax.annotation.processing.Generated;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.spi.EndpointUriFactory;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.GenerateEndpointUriFactoryMojo")
+public class SpringAiChatEndpointUriFactory extends org.apache.camel.support.component.EndpointUriFactorySupport implements EndpointUriFactory {
+
+    private static final String BASE = ":chatId";
+
+    private static final Set<String> PROPERTY_NAMES;
+    private static final Set<String> SECRET_PROPERTY_NAMES;
+    private static final Map<String, String> MULTI_VALUE_PREFIXES;
+    static {
+        Set<String> props = new HashSet<>(29);
+        props.add("advisors");
+        props.add("chatClient");
+        props.add("chatId");
+        props.add("chatMemory");
+        props.add("chatMemoryVectorStore");
+        props.add("chatModel");
+        props.add("chatOperation");
+        props.add("entityClass");
+        props.add("lazyStartProducer");
+        props.add("maxFileSize");
+        props.add("maxTokens");
+        props.add("outputClass");
+        props.add("outputFormat");
+        props.add("ragTemplate");
+        props.add("safeguardFailureResponse");
+        props.add("safeguardOrder");
+        props.add("safeguardSensitiveWords");
+        props.add("similarityThreshold");
+        props.add("structuredOutputConverter");
+        props.add("systemMessage");
+        props.add("systemMetadata");
+        props.add("tags");
+        props.add("temperature");
+        props.add("topK");
+        props.add("topKSampling");
+        props.add("topP");
+        props.add("userMessage");
+        props.add("userMetadata");
+        props.add("vectorStore");
+        PROPERTY_NAMES = Collections.unmodifiableSet(props);
+        SECRET_PROPERTY_NAMES = Collections.emptySet();
+        MULTI_VALUE_PREFIXES = Collections.emptyMap();
+    }
+
+    @Override
+    public boolean isEnabled(String scheme) {
+        return "spring-ai-chat".equals(scheme);
+    }
+
+    @Override
+    public String buildUri(String scheme, Map<String, Object> properties, boolean encode) throws URISyntaxException {
+        String syntax = scheme + BASE;
+        String uri = syntax;
+
+        Map<String, Object> copy = new HashMap<>(properties);
+
+        uri = buildPathParameter(syntax, uri, "chatId", null, false, copy);
+        uri = buildQueryParameters(uri, copy, encode);
+        return uri;
+    }
+
+    @Override
+    public Set<String> propertyNames() {
+        return PROPERTY_NAMES;
+    }
+
+    @Override
+    public Set<String> secretPropertyNames() {
+        return SECRET_PROPERTY_NAMES;
+    }
+
+    @Override
+    public Map<String, String> multiValuePrefixes() {
+        return MULTI_VALUE_PREFIXES;
+    }
+
+    @Override
+    public boolean isLenientProperties() {
+        return false;
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/org/apache/camel/component/springai/chat/spring-ai-chat.json b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/org/apache/camel/component/springai/chat/spring-ai-chat.json
new file mode 100644
index 000000000000..f6589dde1e4a
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/org/apache/camel/component/springai/chat/spring-ai-chat.json
@@ -0,0 +1,94 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "spring-ai-chat",
+    "title": "Spring AI Chat",
+    "description": "Perform chat operations using Spring AI.",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "ai",
+    "javaType": "org.apache.camel.component.springai.chat.SpringAiChatComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-spring-ai-chat",
+    "version": "4.17.0-SNAPSHOT",
+    "scheme": "spring-ai-chat",
+    "extendsScheme": "",
+    "syntax": "spring-ai-chat:chatId",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": true,
+    "lenientProperties": false,
+    "browsable": false,
+    "remote": true
+  },
+  "componentProperties": {
+    "lazyStartProducer": { "index": 0, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
+    "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+    "chatModel": { "index": 2, "kind": "property", "displayName": "Chat Model", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.springframework.ai.chat.model.ChatModel", "deprecated": false, "autowired": false, "secret": false, "description": "The ChatModel instance to use for chat operations." }
+  },
+  "headers": {
+    "CamelSpringAiChatResponse": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The response from the chat model", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#CHAT_RESPONSE" },
+    "CamelSpringAiInputTokenCount": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of input tokens used", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#INPUT_TOKEN_COUNT" },
+    "CamelSpringAiOutputTokenCount": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The number of output tokens used", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#OUTPUT_TOKEN_COUNT" },
+    "CamelSpringAiTotalTokenCount": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The total number of tokens used", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#TOTAL_TOKEN_COUNT" },
+    "CamelSpringAiChatPromptTemplate": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The prompt template with placeholders for variable substitution", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#PROMPT_TEMPLATE" },
+    "CamelSpringAiChatAugmentedData": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<org.springframework.ai.document.Document>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Augmented data for RAG as List", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#AUGMENTED_DATA" },
+    "CamelSpringAiChatSystemMessage": { "index": 6, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "System message for the conversation", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SYSTEM_MESSAGE" },
+    "CamelSpringAiChatTemperature": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Temperature parameter for response randomness (0.0-2.0)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#TEMPERATURE" },
+    "CamelSpringAiChatMaxTokens": { "index": 8, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Maximum tokens in the response", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MAX_TOKENS" },
+    "CamelSpringAiChatTopP": { "index": 9, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Top P parameter for nucleus sampling", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#TOP_P" },
+    "CamelSpringAiChatTopK": { "index": 10, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Top K parameter for sampling", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#TOP_K" },
+    "CamelSpringAiChatUserMessage": { "index": 11, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "User message text for multimodal requests", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#USER_MESSAGE" },
+    "CamelSpringAiChatMediaData": { "index": 12, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "byte[]", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Media data for multimodal requests (image or audio)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MEDIA_DATA" },
+    "CamelSpringAiChatMediaType": { "index": 13, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Media type (MIME type) for multimodal requests (e.g., image\/png, audio\/wav)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MEDIA_TYPE" },
+    "CamelSpringAiChatOutputFormat": { "index": 14, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The output format type for structured output conversion (BEAN, MAP, LIST)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#OUTPUT_FORMAT" },
+    "CamelSpringAiChatOutputClass": { "index": 15, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Class<?>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Java class to use for structured output bean conversion", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#OUTPUT_CLASS" },
+    "CamelSpringAiChatStructuredOutput": { "index": 16, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The structured output converted from the chat response", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#STRUCTURED_OUTPUT" },
+    "CamelSpringAiChatSafeguardSensitiveWords": { "index": 17, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Comma-separated list of sensitive words for SafeGuard advisor", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SAFEGUARD_SENSITIVE_WORDS" },
+    "CamelSpringAiChatSafeguardFailureResponse": { "index": 18, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Failure response message for SafeGuard advisor when sensitive content is detected", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SAFEGUARD_FAILURE_RESPONSE" },
+    "CamelSpringAiChatSafeguardOrder": { "index": 19, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Order of execution for SafeGuard advisor", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SAFEGUARD_ORDER" },
+    "CamelSpringAiChatAdvisors": { "index": 20, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<org.springframework.ai.chat.client.advisor.api.Advisor>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of custom advisors to add to the request", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#ADVISORS" },
+    "CamelSpringAiChatEntityClass": { "index": 21, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Class<?>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Java class to use for entity response conversion", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#ENTITY_CLASS" },
+    "CamelSpringAiChatUserMetadata": { "index": 22, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Metadata to attach to user messages", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#USER_METADATA" },
+    "CamelSpringAiChatSystemMetadata": { "index": 23, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Metadata to attach to system messages", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#SYSTEM_METADATA" },
+    "CamelSpringAiChatConversationId": { "index": 24, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Conversation ID for managing separate conversation contexts in chat memory", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#CONVERSATION_ID" },
+    "CamelSpringAiChatMaxFileSize": { "index": 25, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Maximum file size in bytes for multimodal content. Overrides endpoint configuration.", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MAX_FILE_SIZE" },
+    "CamelSpringAiChatFinishReason": { "index": 26, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The reason why the chat response generation stopped (e.g., STOP, LENGTH, TOOL_CALLS)", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#FINISH_REASON" },
+    "CamelSpringAiChatModelName": { "index": 27, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of the AI model used to generate the response", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#MODEL_NAME" },
+    "CamelSpringAiChatResponseId": { "index": 28, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The unique ID of the chat response", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#RESPONSE_ID" },
+    "CamelSpringAiChatResponseMetadata": { "index": 29, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Full response metadata as a Map containing all available metadata fields", "constantName": "org.apache.camel.component.springai.chat.SpringAiChatConstants#RESPONSE_METADATA" }
+  },
+  "properties": {
+    "chatId": { "index": 0, "kind": "path", "displayName": "Chat Id", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The ID of the chat endpoint" },
+    "chatClient": { "index": 1, "kind": "parameter", "displayName": "Chat Client", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.springframework.ai.chat.client.ChatClient", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The ChatClient instance to use for chat operations [...]
+    "chatModel": { "index": 2, "kind": "parameter", "displayName": "Chat Model", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.springframework.ai.chat.model.ChatModel", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The ChatModel instance to use for chat operations. Thi [...]
+    "chatOperation": { "index": 3, "kind": "parameter", "displayName": "Chat Operation", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.springai.chat.SpringAiChatOperations", "enum": [ "CHAT_SINGLE_MESSAGE", "CHAT_SINGLE_MESSAGE_WITH_PROMPT", "CHAT_MULTIPLE_MESSAGES" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "CHAT_SINGLE_MESSAGE", "configurationClass": "org.apache.camel. [...]
+    "systemMessage": { "index": 4, "kind": "parameter", "displayName": "System Message", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Default system message to set context for the conversation. Can be overridden by the CamelSprin [...]
+    "tags": { "index": 5, "kind": "parameter", "displayName": "Tags", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Tags for discovering and calling Camel route tools" },
+    "userMessage": { "index": 6, "kind": "parameter", "displayName": "User Message", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Default user message text for multimodal requests. Can be combined with media data in the message b [...]
+    "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+    "advisors": { "index": 8, "kind": "parameter", "displayName": "Advisors", "group": "advanced", "label": "advanced", "required": false, "type": "array", "javaType": "java.util.List<org.springframework.ai.chat.client.advisor.api.Advisor>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "List of custom advisor [...]
+    "chatMemory": { "index": 9, "kind": "parameter", "displayName": "Chat Memory", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.springframework.ai.chat.memory.ChatMemory", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "ChatMemory instance for maintaining conver [...]
+    "chatMemoryVectorStore": { "index": 10, "kind": "parameter", "displayName": "Chat Memory Vector Store", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.springframework.ai.vectorstore.VectorStore", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "VectorStore instance for maintaining c [...]
+    "entityClass": { "index": 11, "kind": "parameter", "displayName": "Entity Class", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.Class<java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The Java class to use for entity response conversion using ChatClient.ent [...]
+    "maxFileSize": { "index": 12, "kind": "parameter", "displayName": "Max File Size", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1048576, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Maximum file size in bytes for multimodal content (images, audio, PDFs, etc [...]
+    "maxTokens": { "index": 13, "kind": "parameter", "displayName": "Max Tokens", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Maximum tokens in the response." },
+    "outputClass": { "index": 14, "kind": "parameter", "displayName": "Output Class", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.Class<java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The Java class to use for BEAN output format conversion. Required when ou [...]
+    "outputFormat": { "index": 15, "kind": "parameter", "displayName": "Output Format", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "The output format for structured output conversion (BEAN, MAP, LIST). Used in conjunctio [...]
+    "structuredOutputConverter": { "index": 16, "kind": "parameter", "displayName": "Structured Output Converter", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.springframework.ai.converter.StructuredOutputConverter<java.lang.Object>", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configurati [...]
+    "systemMetadata": { "index": 17, "kind": "parameter", "displayName": "System Metadata", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Metadata to attach to system messages. This metadat [...]
+    "temperature": { "index": 18, "kind": "parameter", "displayName": "Temperature", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "java.lang.Double", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Temperature parameter for response randomness (0.0-2.0)." },
+    "topKSampling": { "index": 19, "kind": "parameter", "displayName": "Top KSampling", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Top K parameter for sampling." },
+    "topP": { "index": 20, "kind": "parameter", "displayName": "Top P", "group": "advanced", "label": "advanced", "required": false, "type": "number", "javaType": "java.lang.Double", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Top P parameter for nucleus sampling." },
+    "userMetadata": { "index": 21, "kind": "parameter", "displayName": "User Metadata", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.Object>", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Metadata to attach to user messages. This metadata can  [...]
+    "ragTemplate": { "index": 22, "kind": "parameter", "displayName": "Rag Template", "group": "rag", "label": "rag", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "defaultValue": "Context:\\n\\{context}\\n\\nQuestion: \\{question}", "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Template for formatting RAG (R [...]
+    "similarityThreshold": { "index": 23, "kind": "parameter", "displayName": "Similarity Threshold", "group": "rag", "label": "rag", "required": false, "type": "number", "javaType": "double", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 0.7, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Similarity threshold for RAG retrieval (default: 0.7)." },
+    "topK": { "index": 24, "kind": "parameter", "displayName": "Top K", "group": "rag", "label": "rag", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 5, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Number of top documents to retrieve for RAG (default: 5)." },
+    "vectorStore": { "index": 25, "kind": "parameter", "displayName": "Vector Store", "group": "rag", "label": "rag", "required": false, "type": "object", "javaType": "org.springframework.ai.vectorstore.VectorStore", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "VectorStore for automatic RAG retrieval." },
+    "safeguardFailureResponse": { "index": 26, "kind": "parameter", "displayName": "Safeguard Failure Response", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Failure response message for SafeGuard advisor when sensitive c [...]
+    "safeguardSensitiveWords": { "index": 27, "kind": "parameter", "displayName": "Safeguard Sensitive Words", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Comma-separated list of sensitive words for SafeGuard advisor. Wh [...]
+    "safeguardOrder": { "index": 28, "kind": "parameter", "displayName": "Safeguard Order", "group": "security (advanced)", "label": "security,advanced", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.springai.chat.SpringAiChatConfiguration", "configurationField": "configuration", "description": "Order of execution for SafeGuard advisor. Lower numbers execut [...]
+  }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/component.properties
new file mode 100644
index 000000000000..a7e7c4caf618
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/component.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+components=spring-ai-chat
+groupId=org.apache.camel
+artifactId=camel-spring-ai-chat
+version=4.17.0-SNAPSHOT
+projectName=Camel :: Spring AI :: Chat
+projectDescription=Camel Spring AI Chat component
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-chat b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-chat
new file mode 100644
index 000000000000..a25ebae10f72
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-chat
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.chat.SpringAiChatComponent
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-chat-component b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-chat-component
new file mode 100644
index 000000000000..384d011abc66
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-chat-component
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.chat.SpringAiChatComponentConfigurer
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-chat-endpoint b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-chat-endpoint
new file mode 100644
index 000000000000..01ffb658259e
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-chat-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.chat.SpringAiChatEndpointConfigurer
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-chat-endpoint b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-chat-endpoint
new file mode 100644
index 000000000000..b1dfe0e525cb
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-chat-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.chat.SpringAiChatEndpointUriFactory
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/docs/spring-ai-chat-component.adoc b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/docs/spring-ai-chat-component.adoc
new file mode 100644
index 000000000000..49d2378e10ab
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/docs/spring-ai-chat-component.adoc
@@ -0,0 +1,607 @@
+= Spring AI Chat Component
+:doctitle: Spring AI Chat
+:shortname: spring-ai-chat
+:artifactid: camel-spring-ai-chat
+:description: Perform chat operations using Spring AI.
+:since: 4.17
+:supportlevel: Preview
+:tabs-sync-option:
+:component-header: Only producer is supported
+//Manually maintained attributes
+:camel-spring-boot-name: spring-ai-chat
+
+*Since Camel {since}*
+
+*{component-header}*
+
+The Spring AI Chat component allows you to integrate with any Large Language Model (LLM)
+supported by https://docs.spring.io/spring-ai/reference/[Spring AI].
+
+Maven users will need to add the following dependency to their `pom.xml`:
+
+[source,xml]
+----
+<dependency>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>camel-spring-ai-chat</artifactId>
+    <version>x.x.x</version>
+</dependency>
+----
+
+== URI format
+
+----
+spring-ai-chat:chatId[?options]
+----
+
+Where *chatId* can be any string to uniquely identify the endpoint.
+
+// component-configure options: START
+
+// component-configure options: END
+
+// component options: START
+include::partial$component-configure-options.adoc[]
+include::partial$component-endpoint-options.adoc[]
+// component options: END
+
+// endpoint options: START
+
+// endpoint options: END
+
+// component headers: START
+include::partial$component-endpoint-headers.adoc[]
+// component headers: END
+
+== Usage
+
+=== Configuring the ChatModel
+
+The component requires a Spring AI `ChatModel` to be configured. The component automatically creates a `ChatClient` internally and handles all the advanced features for you, including:
+
+- **ChatMemory**: Automatic conversation history management via MessageChatMemoryAdvisor or VectorStoreChatMemoryAdvisor
+- **VectorStore RAG**: Automatic context retrieval from vector databases via QuestionAnswerAdvisor
+- **SafeGuard**: Content filtering based on sensitive words
+- **Custom Advisors**: Any additional advisors you configure
+- **System Messages**: Default system prompts
+
+Simply provide a `ChatModel` bean in your configuration, and the component will take care of the rest.
+
+=== Chat Operations
+
+The component supports three types of chat operations:
+
+1. **CHAT_SINGLE_MESSAGE**: Send a simple text message to the LLM
+2. **CHAT_SINGLE_MESSAGE_WITH_PROMPT**: Use templates with variable substitution
+3. **CHAT_MULTIPLE_MESSAGES**: Support conversation history with multiple messages
+
+==== Simple Chat (Single Message)
+
+[source,java]
+----
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel");
+
+// Usage:
+String response = template.requestBody("direct:chat", "What is the capital of Italy?", String.class);
+----
+
+==== Chat with Prompt Templates
+
+Use the `CHAT_SINGLE_MESSAGE_WITH_PROMPT` operation to leverage Spring AI's template engine:
+
+[source,java]
+----
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel&chatOperation=CHAT_SINGLE_MESSAGE_WITH_PROMPT");
+
+// Usage:
+String promptTemplate = "Create a recipe for {{dish}} using {{ingredients}}";
+Map<String, Object> variables = Map.of(
+    "dish", "pasta carbonara",
+    "ingredients", "eggs, cheek lard, parmesan, black pepper"
+);
+
+String response = template.requestBodyAndHeader(
+    "direct:chat",
+    variables,
+    SpringAiChatConstants.PROMPT_TEMPLATE,
+    promptTemplate,
+    String.class
+);
+----
+
+==== Conversation History (Multiple Messages)
+
+Maintain conversation context by sending multiple messages:
+
+[source,java]
+----
+import org.springframework.ai.chat.messages.*;
+
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel&chatOperation=CHAT_MULTIPLE_MESSAGES");
+
+// Usage:
+List<Message> conversation = new ArrayList<>();
+conversation.add(new SystemMessage("You are a helpful cooking assistant."));
+conversation.add(new UserMessage("What's a good pasta recipe?"));
+conversation.add(new AssistantMessage("I recommend pasta carbonara..."));
+conversation.add(new UserMessage("Can I make it vegetarian?"));
+
+String response = template.requestBody("direct:chat", conversation, String.class);
+----
+
+=== Retrieval Augmented Generation (RAG)
+
+RAG allows you to augment prompts with relevant context from your data sources, enabling the LLM to answer questions based on your specific information.
+
+==== Automatic RAG with VectorStore
+
+Configure a VectorStore for automatic context retrieval:
+
+[source,java]
+----
+import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.ai.vectorstore.SimpleVectorStore;
+
+// Create and populate vector store
+VectorStore vectorStore = new SimpleVectorStore();
+// ... add documents to vector store ...
+
+context.getRegistry().bind("vectorStore", vectorStore);
+
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel&vectorStore=#vectorStore&topK=5");
+----
+
+The component will automatically:
+
+1. Take the user's question
+2. Query the VectorStore for the most similar documents (top 5 by default)
+3. Augment the prompt with the retrieved context
+4. Send the enriched prompt to the LLM
+
+==== RAG with Content Enricher Pattern
+
+Use the `SpringAiRagAggregatorStrategy` with Camel's Content Enricher EIP:
+
+[source,java]
+----
+import org.apache.camel.component.springai.chat.rag.SpringAiRagAggregatorStrategy;
+
+SpringAiRagAggregatorStrategy aggregatorStrategy = new SpringAiRagAggregatorStrategy();
+
+from("direct:chat")
+    .enrich("direct:retrieve-context", aggregatorStrategy)
+    .to("spring-ai-chat:test?chatModel=#chatModel");
+
+from("direct:retrieve-context")
+    .setBody(simple("Retrieved context based on question ${body}"));
+----
+
+==== RAG with Headers
+
+Directly provide augmented data via the `AUGMENTED_DATA` header:
+
+[source,java]
+----
+import org.springframework.ai.document.Document;
+import static org.apache.camel.component.springai.chat.SpringAiChatConstants.AUGMENTED_DATA;
+
+List<Document> context = List.of(
+    new Document("The Eiffel Tower is located in Paris, France."),
+    new Document("It was built in 1889 for the World's Fair.")
+);
+
+String response = template.requestBodyAndHeader(
+    "direct:chat",
+    "Where is the Eiffel Tower located?",
+    AUGMENTED_DATA,
+    context,
+    String.class
+);
+----
+
+=== Controlling Response Parameters
+
+==== Via Endpoint Configuration
+
+[source,java]
+----
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel&temperature=0.8&maxTokens=500");
+----
+
+=== Adding System Messages
+
+System messages provide instructions or context to the LLM. You can configure them via endpoint parameters for default behavior:
+
+[source,java]
+----
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel&systemMessage=You are a professional chef specializing in Italian cuisine.");
+
+// Usage:
+String response = template.requestBody("direct:chat",
+    "What should I cook tonight?", String.class);
+----
+
+=== Response Headers and Metadata
+
+The component automatically tracks token usage and response metadata, providing them via headers. You can access these headers in your Camel routes:
+
+[source,java]
+----
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel")
+    .log("Token usage - Input: ${header.CamelSpringAiInputTokenCount}")
+    .log("Token usage - Output: ${header.CamelSpringAiOutputTokenCount}")
+    .log("Token usage - Total: ${header.CamelSpringAiTotalTokenCount}")
+    .log("Finish reason: ${header.CamelSpringAiChatFinishReason}")
+    .log("Model used: ${header.CamelSpringAiChatModelName}");
+
+// Usage:
+template.requestBody("direct:chat", "Explain quantum computing", String.class);
+----
+
+The following headers are set after each chat operation:
+
+**Token Usage Headers:**
+
+* `CamelSpringAiInputTokenCount` - Number of tokens in the prompt
+* `CamelSpringAiOutputTokenCount` - Number of tokens in the response
+* `CamelSpringAiTotalTokenCount` - Total tokens used (input + output)
+
+**Response Metadata Headers:**
+
+* `CamelSpringAiChatFinishReason` - Why the generation stopped (e.g., "STOP", "LENGTH", "TOOL_CALLS")
+* `CamelSpringAiChatModelName` - The name of the AI model used for the response
+* `CamelSpringAiChatResponseId` - Unique identifier for the response
+* `CamelSpringAiChatResponseMetadata` - Full metadata Map containing all available fields
+
+NOTE: Not all AI models provide all metadata fields. The availability of specific metadata depends on the underlying model and provider. Most models provide at least finish reason and token counts.
+
+=== Message Metadata
+
+The component supports attaching metadata to both user and system messages. This is useful for tracking message identifiers, conversation context, priority levels, or other application-specific data.
+
+==== User Message Metadata
+
+You can attach metadata to user messages via headers or endpoint configuration:
+
+[source,java]
+----
+// Using URL parameters - nested property syntax
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel"
+        + "&userMetadata.messageId=msg-123"
+        + "&userMetadata.userId=user-456"
+        + "&userMetadata.priority=high");
+----
+
+==== System Message Metadata
+
+Similarly, you can attach metadata to system messages:
+
+[source,java]
+----
+// Using URL parameters - nested property syntax
+from("direct:chat")
+    .to("spring-ai-chat:test?chatModel=#chatModel"
+        + "&systemMetadata.version=1.0"
+        + "&systemMetadata.model=gpt-4"
+        + "&systemMetadata.promptVersion=2024-01");
+----
+
+=== Conversation Memory
+
+The component provides automatic conversation memory management via Spring AI's ChatClient and ChatMemory. Two memory strategies are supported:
+
+* **MessageChatMemoryAdvisor** - Traditional message-based memory using ChatMemory implementations
+* **VectorStoreChatMemoryAdvisor** - Vector-based semantic memory using VectorStore for long-term retrieval
+
+IMPORTANT: Do not configure both `chatMemory` and `chatMemoryVectorStore` on the same endpoint. If both are provided, `chatMemory` (MessageChatMemoryAdvisor) will take precedence.
+
+==== Message-based Memory (ChatMemory)
+
+Configure a `ChatMemory` on the endpoint for automatic conversation tracking using traditional message window approach. This strategy keeps a configurable number of recent messages in memory.
+
+[source,java]
+----
+// Create chat memory with message window
+ChatMemory chatMemory = MessageWindowChatMemory.builder()
+    .chatMemoryRepository(new InMemoryChatMemoryRepository())
+    .maxMessages(10)  // Keep last 10 messages
+    .build();
+
+context.getRegistry().bind("chatMemory", chatMemory);
+
+// Configure endpoint with memory
+from("direct:chat")
+    .to("spring-ai-chat:memory?chatModel=#chatModel&chatMemory=#chatMemory");
+
+// Conversation ID for isolation
+template.requestBodyAndHeader("direct:chat", "My name is Alice",
+    SpringAiChatConstants.CONVERSATION_ID, "user-123", String.class);
+
+template.requestBodyAndHeader("direct:chat", "What's my name?",
+    SpringAiChatConstants.CONVERSATION_ID, "user-123", String.class); // Will remember: "Alice"
+----
+
+==== Vector-based Semantic Memory (ChatMemoryVectorStore)
+
+For long-term memory with semantic retrieval capabilities, configure a `VectorStore` for chat memory. This strategy uses vector embeddings to enable semantic search over conversation history, making it ideal for retrieving contextually relevant information rather than just recent messages.
+
+[source,java]
+----
+OllamaApi ollamaApi = OllamaApi.builder()
+                .baseUrl(OLLAMA.baseUrl())
+                .build();
+
+EmbeddingModel embeddingModel = OllamaEmbeddingModel.builder()
+        .ollamaApi(ollamaApi)
+        .defaultOptions(OllamaEmbeddingOptions.builder()
+                .model("embeddinggemma:300m")
+                .build())
+        .build();
+
+// Create vector store for chat memory
+chatMemoryVectorStore = SimpleVectorStore.builder(embeddingModel)
+        .build();
+
+// Create vector store for chat memory
+VectorStore chatMemoryVectorStore = SimpleVectorStore.builder(embeddingModel)
+    .build();
+
+context.getRegistry().bind("chatMemoryVectorStore", chatMemoryVectorStore);
+
+// Configure endpoint with vector memory
+from("direct:vector-memory")
+    .to("spring-ai-chat:vmem?chatModel=#chatModel&chatMemoryVectorStore=#chatMemoryVectorStore");
+
+// Memory is managed automatically with semantic search
+template.requestBodyAndHeader("direct:vector-memory",
+    "My favorite programming language is Java",
+    SpringAiChatConstants.CONVERSATION_ID, "user-123", String.class);
+
+template.requestBodyAndHeader("direct:vector-memory",
+    "What programming language did I mention?",
+    SpringAiChatConstants.CONVERSATION_ID, "user-123", String.class); // Will use semantic search to retrieve: "Java"
+----
+
+NOTE: The component automatically configures conversation isolation using metadata filters, ensuring each conversation ID maintains separate memory context. However, conversation isolation via metadata filtering requires a VectorStore implementation that supports filter expressions.
+
+=== Function Calling / Tool Integration
+
+The component integrates with the xref:spring-ai-tools-component.adoc[Spring AI Tools Component] to enable LLMs to call Camel routes as functions/tools. This extends the LLM's capabilities with custom logic, external APIs, database queries, and more.
+
+When you configure the `tags` parameter, the chat component discovers all matching tools from `spring-ai-tools` routes, registers them with Spring AI's ChatClient, and allows the LLM to call them during conversation.
+
+[source,java]
+----
+// Define tools
+from("spring-ai-tools:weather?tags=weather&description=Get current weather for a location")
+    .setBody(constant("Sunny"));
+
+from("spring-ai-tools:calculator?tags=math&description=Evaluate mathematical expressions")
+    .setBody(constant("42"));
+
+from("spring-ai-tools:stock?tags=finance&description=Get current stock price")
+    .setBody(constant("42"));
+
+// Chat endpoints with different tool combinations
+from("direct:weatherChat")
+    .to("spring-ai-chat:chat?tags=weather&chatClient=#chatClient");
+
+from("direct:mathChat")
+    .to("spring-ai-chat:chat?tags=math&chatClient=#chatClient");
+
+from("direct:multiToolChat")
+    .to("spring-ai-chat:chat?tags=weather,math,finance&chatClient=#chatClient");
+----
+
+**How it works:**
+
+1. LLM analyzes the question and decides which tools to call
+2. LLM generates function call requests with parameters
+3. Component executes Camel routes with parameters as headers
+4. Tool results are sent back to the LLM
+5. LLM processes results and generates final response
+6. Multiple tool calls can happen in sequence if needed
+
+**Tool Definition Parameters:**
+
+* `tags`: Comma-separated tags for organizing tools (required)
+* `description`: What the tool does - shown to the LLM (required for consumer)
+* `name`: Tool name (auto-generated from description if not provided)
+* `inputType`: Java class for input validation (optional)
+
+**Best Practices:**
+
+1. **Clear Descriptions**: Provide detailed tool descriptions so the LLM knows when to use them
+2. **Appropriate Tags**: Organize tools with meaningful tags
+3. **Error Handling**: Implement proper error handling in tool routes
+4. **Parameter Validation**: Validate input parameters in tool processors
+5. **Response Format**: Return clear, concise text responses from tools
+
+=== Multimodal Support (Images, Audio, and Documents)
+
+The component supports multimodal input for models that support vision, audio, and document capabilities.
+
+The component seamlessly integrates with Camel file components (file, ftp, sftp, smb) through `WrappedFile` support, enabling direct routing from file sources to AI chat. Media can also be sent as byte arrays or as lists of files with appropriate headers.
+
+[source,java]
+----
+// Direct file-to-AI pipeline (MIME type auto-detected)
+from("file:input-images?noop=true")
+    .setHeader(SpringAiChatConstants.USER_MESSAGE, constant("What's in this image?"))
+    .to("spring-ai-chat:vision?chatModel=#chatModel");
+
+// With default user message configured
+from("direct:vision")
+    .to("spring-ai-chat:vision?chatModel=#chatModel&userMessage=Describe this image in detail");
+
+// Byte array input with headers
+byte[] imageData = Files.readAllBytes(Paths.get("photo.jpg"));
+template.requestBodyAndHeaders("direct:vision", imageData,
+    Map.of(USER_MESSAGE, "What objects do you see?", MEDIA_TYPE, "image/jpeg"),
+    String.class);
+
+// Multiple files via aggregation (as List<WrappedFile>)
+from("file:comparison-images?noop=true")
+    .aggregate(constant(true), new GroupedBodyAggregationStrategy())
+    .completionSize(2)
+    .setHeader(USER_MESSAGE, constant("Compare these images"))
+    .to("spring-ai-chat:comparator?chatModel=#chatModel");
+
+// UserMessage with Media for maximum control
+Media media = new Media(MimeTypeUtils.IMAGE_PNG, imageData);
+UserMessage message = new UserMessage("Analyze this scan", media);
+template.requestBody("direct:scan-analyzer", message, String.class);
+----
+
+**Supported Input Types:**
+
+* `String` - Simple text message
+* `byte[]` - Single media file (image, audio, etc.)
+* `WrappedFile` - Single file from file components
+* `List<WrappedFile>` - Multiple files for comparison or analysis
+* `UserMessage` - Spring AI UserMessage with text and/or media
+* `Message` - Any Spring AI Message type
+* `List<Message>` - Full conversation history (CHAT_MULTIPLE_MESSAGES operation)
+
+**MIME Type Detection Priority:**
+
+1. `CamelSpringAiChatMediaType` header (highest priority)
+2. `CamelFileContentType` header from file components
+3. Auto-detection from file extension
+4. Default to `image/png` if not recognized
+
+**Supported File Formats (Auto-Detection):**
+
+* **Images**: PNG, JPEG, GIF, WebP, BMP, TIFF, SVG
+* **Videos**: MP4, WebM, MOV, MKV, FLV, MPEG, WMV, 3GP
+* **Audio**: WAV, MP3, OGG, M4A, FLAC
+* **Documents**: PDF, CSV, DOC, DOCX, XLS, XLSX, HTML, TXT, Markdown
+
+**File Size Validation:**
+
+To prevent out-of-memory errors with large media files, the component validates file sizes before loading them into memory. The default maximum file size is 1MB, configurable via:
+
+[source,java]
+----
+// Configure via endpoint parameter (in bytes)
+from("file:uploads")
+    .to("spring-ai-chat:vision?chatModel=#chatModel&maxFileSize=5242880"); // 5MB
+
+// Override via header for dynamic control
+from("direct:upload")
+    .setHeader(SpringAiChatConstants.MAX_FILE_SIZE, constant(10485760L)) // 10MB
+    .to("spring-ai-chat:vision?chatModel=#chatModel");
+
+// Disable size checking (not recommended)
+from("file:trusted")
+    .to("spring-ai-chat:vision?chatModel=#chatModel&maxFileSize=0");
+----
+
+Files exceeding the configured limit will be rejected with an `IllegalArgumentException`.
+
+=== SafeGuard - Content Filtering
+
+The component provides content filtering capabilities through Spring AI's SafeGuard advisor. SafeGuard monitors LLM responses for sensitive words and blocks responses containing restricted content, returning a customizable failure message instead.
+
+This feature is useful for:
+* Preventing disclosure of sensitive information (passwords, secrets, API keys)
+* Enforcing content policies in customer-facing applications
+* Compliance with data protection requirements
+* Filtering inappropriate or restricted topics
+
+[source,java]
+----
+// Configure safeguard with sensitive words
+from("direct:safeguard")
+    .to("spring-ai-chat:test?chatModel=#chatModel"
+        + "&safeguardSensitiveWords=password,secret,confidential,api-key"
+        + "&safeguardFailureResponse=I cannot provide information containing sensitive words");
+----
+
+**Configuration Options:**
+
+* `safeguardSensitiveWords`: Comma-separated list of words to block in responses
+* `safeguardFailureResponse`: Message returned when sensitive content is detected
+* Headers: `CamelSpringAiChatSafeguardSensitiveWords`, `CamelSpringAiChatSafeguardFailureResponse`
+
+NOTE: SafeGuard checks the LLM's response content. If any configured sensitive word is found in the response, the entire response is replaced with the failure message. The check is case-insensitive.
+
+=== Structured Output Conversion
+
+The component supports structured output conversion, allowing you to convert LLM responses into typed Java objects, Maps, or Lists. This feature is based on https://docs.spring.io/spring-ai/reference/api/structured-output-converter.html[Spring AI's Structured Output Converter].
+
+Three output formats are supported: **BEAN** (Java records/POJOs), **MAP** (key-value structures), and **LIST** (arrays). Each can be configured via converter bean, endpoint parameters, or headers.
+
+[source,java]
+----
+// Bean output - convert to typed Java objects
+@JsonPropertyOrder({ "actor", "movies" })
+public record ActorFilms(String actor, List<String> movies) {}
+
+from("direct:bean")
+    .to("spring-ai-chat:test?chatModel=#chatModel&outputFormat=BEAN&outputClass="
+        + ActorFilms.class.getName());
+
+// Map output - convert to key-value structure
+from("direct:map")
+    .to("spring-ai-chat:test?chatModel=#chatModel&outputFormat=MAP");
+
+// List output - convert to array
+from("direct:list")
+    .to("spring-ai-chat:test?chatModel=#chatModel&outputFormat=LIST");
+
+// Dynamic conversion via headers
+template.requestBodyAndHeader("direct:chat", "List programming languages",
+    SpringAiChatConstants.OUTPUT_FORMAT, "LIST", List.class);
+----
+
+**Best Practices:**
+
+1. Use `@JsonPropertyOrder` for consistent property ordering
+2. Keep structures simple for better LLM accuracy
+3. Validate output (LLMs may occasionally produce unexpected formats)
+4. Use lower temperatures (0.1-0.3) for consistent structured output
+
+==== Advanced: Using ChatClient Directly
+
+For maximum control, you can provide a pre-configured `ChatClient` instead of a `ChatModel`:
+
+[source,java]
+----
+ChatClient chatClient = ChatClient.builder(chatModel)
+    .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
+    .defaultSystem("You are a helpful assistant")
+    .build();
+
+from("direct:chat")
+    .to("spring-ai-chat:client?chatClient=#chatClient");
+----
+
+NOTE: Most users should use `ChatModel` and let the component handle `ChatClient` creation automatically. Only use this approach if you have specific requirements that cannot be met with standard configuration options.
+
+=== Request/Response Logging
+
+The component automatically adds Spring AI's `SimpleLoggerAdvisor` to log requests and responses for debugging. Enable it by setting the logger `org.springframework.ai.chat.client.advisor` to DEBUG level:
+
+[source,properties]
+----
+logging.level.org.springframework.ai.chat.client.advisor=DEBUG
+----
+
+== See Also
+
+* https://docs.spring.io/spring-ai/reference/[Spring AI Documentation]
+* https://docs.spring.io/spring-ai/reference/api/tools.html[Spring AI Function Calling]
+* xref:spring-ai-tools-component.adoc[Spring AI Tools Component]
+* xref:spring-ai-embeddings-component.adoc[Spring AI Embeddings Component]
+* xref:spring-ai-vector-store-component.adoc[Spring AI VectorStore Component]
+
+// include::spring-boot:partial$starter.adoc[]
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatComponent.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatComponent.java
new file mode 100644
index 000000000000..ff16333f442c
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatComponent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.Map;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.support.DefaultComponent;
+import org.springframework.ai.chat.model.ChatModel;
+
+/**
+ * Camel component for Spring AI Chat integration.
+ */
+@Component("spring-ai-chat")
+public class SpringAiChatComponent extends DefaultComponent {
+
+    @Metadata(label = "advanced")
+    private ChatModel chatModel;
+
+    public SpringAiChatComponent() {
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+        SpringAiChatConfiguration configuration = new SpringAiChatConfiguration();
+        SpringAiChatEndpoint endpoint = new SpringAiChatEndpoint(uri, this, configuration, remaining);
+        setProperties(endpoint, parameters);
+
+        if (chatModel != null) {
+            configuration.setChatModel(chatModel);
+        }
+
+        return endpoint;
+    }
+
+    public ChatModel getChatModel() {
+        return chatModel;
+    }
+
+    /**
+     * The ChatModel instance to use for chat operations.
+     */
+    public void setChatModel(ChatModel chatModel) {
+        this.chatModel = chatModel;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatConfiguration.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatConfiguration.java
new file mode 100644
index 000000000000..a9dd2341bc14
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatConfiguration.java
@@ -0,0 +1,446 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.client.advisor.api.Advisor;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.converter.StructuredOutputConverter;
+import org.springframework.ai.vectorstore.VectorStore;
+
+@UriParams
+public class SpringAiChatConfiguration implements Cloneable {
+
+    @UriParam
+    @Metadata(autowired = true)
+    private ChatClient chatClient;
+
+    @UriParam
+    @Metadata(autowired = true)
+    private ChatModel chatModel;
+
+    @UriParam(label = "advanced")
+    @Metadata(autowired = true)
+    private ChatMemory chatMemory;
+
+    @UriParam(label = "advanced")
+    private VectorStore chatMemoryVectorStore;
+
+    @UriParam(defaultValue = "CHAT_SINGLE_MESSAGE")
+    @Metadata(required = true)
+    private SpringAiChatOperations chatOperation = SpringAiChatOperations.CHAT_SINGLE_MESSAGE;
+
+    @UriParam(label = "rag")
+    @Metadata(autowired = true)
+    private VectorStore vectorStore;
+
+    @UriParam(label = "rag", defaultValue = "5")
+    private int topK = 5;
+
+    @UriParam(label = "rag", defaultValue = "0.7")
+    private double similarityThreshold = 0.7;
+
+    @UriParam(label = "advanced")
+    private Double temperature;
+
+    @UriParam(label = "advanced")
+    private Integer maxTokens;
+
+    @UriParam(label = "advanced")
+    private Double topP;
+
+    @UriParam(label = "advanced")
+    private Integer topKSampling;
+
+    @UriParam
+    private String userMessage;
+
+    @UriParam
+    private String systemMessage;
+
+    @UriParam(label = "advanced")
+    @Metadata(autowired = true)
+    private StructuredOutputConverter<?> structuredOutputConverter;
+
+    @UriParam(label = "advanced")
+    private String outputFormat;
+
+    @UriParam(label = "advanced")
+    private Class<?> outputClass;
+
+    @UriParam(description = "Tags for discovering and calling Camel route tools")
+    private String tags;
+
+    @UriParam(label = "security")
+    private String safeguardSensitiveWords;
+
+    @UriParam(label = "security")
+    private String safeguardFailureResponse;
+
+    @UriParam(label = "security,advanced")
+    private Integer safeguardOrder;
+
+    @UriParam(label = "advanced")
+    @Metadata(autowired = false)
+    private List<Advisor> advisors;
+
+    @UriParam(label = "advanced")
+    private Class<?> entityClass;
+
+    @UriParam(label = "advanced")
+    private Map<String, Object> userMetadata;
+
+    @UriParam(label = "advanced")
+    private Map<String, Object> systemMetadata;
+
+    @UriParam(label = "rag", defaultValue = "Context:\\n\\{context}\\n\\nQuestion: \\{question}")
+    private String ragTemplate = "Context:\n{context}\n\nQuestion: {question}";
+
+    @UriParam(label = "advanced", defaultValue = "1048576")
+    private long maxFileSize = 1024 * 1024; // 1MB default
+
+    public ChatClient getChatClient() {
+        return chatClient;
+    }
+
+    /**
+     * The ChatClient instance to use for chat operations. ChatClient provides advanced features like memory management
+     * and advisors. If not provided, a ChatClient will be created from the ChatModel.
+     */
+    public void setChatClient(ChatClient chatClient) {
+        this.chatClient = chatClient;
+    }
+
+    public ChatModel getChatModel() {
+        return chatModel;
+    }
+
+    /**
+     * The ChatModel instance to use for chat operations. This is used to create a ChatClient if one is not explicitly
+     * provided.
+     */
+    public void setChatModel(ChatModel chatModel) {
+        this.chatModel = chatModel;
+    }
+
+    public ChatMemory getChatMemory() {
+        return chatMemory;
+    }
+
+    /**
+     * ChatMemory instance for maintaining conversation history across requests. When provided, conversation context
+     * will be automatically managed using MessageChatMemoryAdvisor.
+     */
+    public void setChatMemory(ChatMemory chatMemory) {
+        this.chatMemory = chatMemory;
+    }
+
+    public VectorStore getChatMemoryVectorStore() {
+        return chatMemoryVectorStore;
+    }
+
+    /**
+     * VectorStore instance for maintaining conversation history using semantic search. When provided, conversation
+     * context will be automatically managed using VectorStoreChatMemoryAdvisor. This is an alternative to chatMemory
+     * that uses vector embeddings for long-term memory with semantic retrieval.
+     */
+    public void setChatMemoryVectorStore(VectorStore chatMemoryVectorStore) {
+        this.chatMemoryVectorStore = chatMemoryVectorStore;
+    }
+
+    public SpringAiChatOperations getChatOperation() {
+        return chatOperation;
+    }
+
+    /**
+     * The chat operation to perform.
+     */
+    public void setChatOperation(SpringAiChatOperations chatOperation) {
+        this.chatOperation = chatOperation;
+    }
+
+    public VectorStore getVectorStore() {
+        return vectorStore;
+    }
+
+    /**
+     * VectorStore for automatic RAG retrieval.
+     */
+    public void setVectorStore(VectorStore vectorStore) {
+        this.vectorStore = vectorStore;
+    }
+
+    public int getTopK() {
+        return topK;
+    }
+
+    /**
+     * Number of top documents to retrieve for RAG (default: 5).
+     */
+    public void setTopK(int topK) {
+        this.topK = topK;
+    }
+
+    public double getSimilarityThreshold() {
+        return similarityThreshold;
+    }
+
+    /**
+     * Similarity threshold for RAG retrieval (default: 0.7).
+     */
+    public void setSimilarityThreshold(double similarityThreshold) {
+        this.similarityThreshold = similarityThreshold;
+    }
+
+    public Double getTemperature() {
+        return temperature;
+    }
+
+    /**
+     * Temperature parameter for response randomness (0.0-2.0).
+     */
+    public void setTemperature(Double temperature) {
+        this.temperature = temperature;
+    }
+
+    public Integer getMaxTokens() {
+        return maxTokens;
+    }
+
+    /**
+     * Maximum tokens in the response.
+     */
+    public void setMaxTokens(Integer maxTokens) {
+        this.maxTokens = maxTokens;
+    }
+
+    public Double getTopP() {
+        return topP;
+    }
+
+    /**
+     * Top P parameter for nucleus sampling.
+     */
+    public void setTopP(Double topP) {
+        this.topP = topP;
+    }
+
+    public Integer getTopKSampling() {
+        return topKSampling;
+    }
+
+    /**
+     * Top K parameter for sampling.
+     */
+    public void setTopKSampling(Integer topKSampling) {
+        this.topKSampling = topKSampling;
+    }
+
+    public String getUserMessage() {
+        return userMessage;
+    }
+
+    /**
+     * Default user message text for multimodal requests. Can be combined with media data in the message body.
+     */
+    public void setUserMessage(String userMessage) {
+        this.userMessage = userMessage;
+    }
+
+    public String getSystemMessage() {
+        return systemMessage;
+    }
+
+    /**
+     * Default system message to set context for the conversation. Can be overridden by the
+     * CamelSpringAiChatSystemMessage header.
+     */
+    public void setSystemMessage(String systemMessage) {
+        this.systemMessage = systemMessage;
+    }
+
+    public StructuredOutputConverter<?> getStructuredOutputConverter() {
+        return structuredOutputConverter;
+    }
+
+    /**
+     * A StructuredOutputConverter for converting the chat response to structured output (e.g., BeanOutputConverter,
+     * MapOutputConverter, ListOutputConverter). When provided, the converter will be used to transform the response
+     * into the desired format.
+     */
+    public void setStructuredOutputConverter(StructuredOutputConverter<?> structuredOutputConverter) {
+        this.structuredOutputConverter = structuredOutputConverter;
+    }
+
+    public String getOutputFormat() {
+        return outputFormat;
+    }
+
+    /**
+     * The output format for structured output conversion (BEAN, MAP, LIST). Used in conjunction with outputClass for
+     * BEAN format.
+     */
+    public void setOutputFormat(String outputFormat) {
+        this.outputFormat = outputFormat;
+    }
+
+    public Class<?> getOutputClass() {
+        return outputClass;
+    }
+
+    /**
+     * The Java class to use for BEAN output format conversion. Required when outputFormat is BEAN.
+     */
+    public void setOutputClass(Class<?> outputClass) {
+        this.outputClass = outputClass;
+    }
+
+    public String getTags() {
+        return tags;
+    }
+
+    /**
+     * Tags for discovering and calling Camel route tools. When provided, the chat component will automatically register
+     * tools from camel-spring-ai-tools routes matching these tags.
+     */
+    public void setTags(String tags) {
+        this.tags = tags;
+    }
+
+    public String getSafeguardSensitiveWords() {
+        return safeguardSensitiveWords;
+    }
+
+    /**
+     * Comma-separated list of sensitive words for SafeGuard advisor. When provided, the SafeGuard advisor will be
+     * enabled to prevent generation of content containing these words.
+     */
+    public void setSafeguardSensitiveWords(String safeguardSensitiveWords) {
+        this.safeguardSensitiveWords = safeguardSensitiveWords;
+    }
+
+    public String getSafeguardFailureResponse() {
+        return safeguardFailureResponse;
+    }
+
+    /**
+     * Failure response message for SafeGuard advisor when sensitive content is detected. If not specified, a default
+     * message will be used.
+     */
+    public void setSafeguardFailureResponse(String safeguardFailureResponse) {
+        this.safeguardFailureResponse = safeguardFailureResponse;
+    }
+
+    public Integer getSafeguardOrder() {
+        return safeguardOrder;
+    }
+
+    /**
+     * Order of execution for SafeGuard advisor. Lower numbers execute first. Default is 0.
+     */
+    public void setSafeguardOrder(Integer safeguardOrder) {
+        this.safeguardOrder = safeguardOrder;
+    }
+
+    public List<Advisor> getAdvisors() {
+        return advisors;
+    }
+
+    /**
+     * List of custom advisors to add to the ChatClient. These advisors will be added after the built-in advisors
+     * (SimpleLogger, SafeGuard, ChatMemory, QuestionAnswer) in the order they are provided in the list.
+     */
+    public void setAdvisors(List<Advisor> advisors) {
+        this.advisors = advisors;
+    }
+
+    public Class<?> getEntityClass() {
+        return entityClass;
+    }
+
+    /**
+     * The Java class to use for entity response conversion using ChatClient.entity(Class). When specified, the response
+     * will be automatically converted to this type instead of returning a String.
+     */
+    public void setEntityClass(Class<?> entityClass) {
+        this.entityClass = entityClass;
+    }
+
+    public Map<String, Object> getUserMetadata() {
+        return userMetadata;
+    }
+
+    /**
+     * Metadata to attach to user messages. This metadata can be used for tracking conversation context, message
+     * identifiers, or other application-specific data.
+     */
+    public void setUserMetadata(Map<String, Object> userMetadata) {
+        this.userMetadata = userMetadata;
+    }
+
+    public Map<String, Object> getSystemMetadata() {
+        return systemMetadata;
+    }
+
+    /**
+     * Metadata to attach to system messages. This metadata can be used for tracking system prompt versions, model
+     * configurations, or other application-specific data.
+     */
+    public void setSystemMetadata(Map<String, Object> systemMetadata) {
+        this.systemMetadata = systemMetadata;
+    }
+
+    public String getRagTemplate() {
+        return ragTemplate;
+    }
+
+    /**
+     * Template for formatting RAG (Retrieval Augmented Generation) prompts when augmented data is provided. The
+     * template supports two placeholders: {context} for the retrieved documents and {question} for the user's question.
+     * Default template is "Context:\n{context}\n\nQuestion: {question}".
+     */
+    public void setRagTemplate(String ragTemplate) {
+        this.ragTemplate = ragTemplate;
+    }
+
+    public long getMaxFileSize() {
+        return maxFileSize;
+    }
+
+    /**
+     * Maximum file size in bytes for multimodal content (images, audio, PDFs, etc.). Files exceeding this size will be
+     * rejected with an exception. Default is 1048576 bytes (1MB). Set to 0 to disable size checking (not recommended).
+     */
+    public void setMaxFileSize(long maxFileSize) {
+        this.maxFileSize = maxFileSize;
+    }
+
+    public SpringAiChatConfiguration copy() {
+        try {
+            return (SpringAiChatConfiguration) clone();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatConstants.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatConstants.java
new file mode 100644
index 000000000000..dd43ea80fa17
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatConstants.java
@@ -0,0 +1,124 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.spi.Metadata;
+
+/**
+ * Constants for Spring AI Chat component headers.
+ */
+public final class SpringAiChatConstants {
+
+    @Metadata(description = "The response from the chat model", javaType = "String")
+    public static final String CHAT_RESPONSE = "CamelSpringAiChatResponse";
+
+    @Metadata(description = "The number of input tokens used", javaType = "Integer")
+    public static final String INPUT_TOKEN_COUNT = "CamelSpringAiInputTokenCount";
+
+    @Metadata(description = "The number of output tokens used", javaType = "Integer")
+    public static final String OUTPUT_TOKEN_COUNT = "CamelSpringAiOutputTokenCount";
+
+    @Metadata(description = "The total number of tokens used", javaType = "Integer")
+    public static final String TOTAL_TOKEN_COUNT = "CamelSpringAiTotalTokenCount";
+
+    @Metadata(description = "The prompt template with placeholders for variable substitution", javaType = "String")
+    public static final String PROMPT_TEMPLATE = "CamelSpringAiChatPromptTemplate";
+
+    @Metadata(description = "Augmented data for RAG as List<org.springframework.ai.document.Document>",
+              javaType = "java.util.List<org.springframework.ai.document.Document>")
+    public static final String AUGMENTED_DATA = "CamelSpringAiChatAugmentedData";
+
+    @Metadata(description = "System message for the conversation", javaType = "String")
+    public static final String SYSTEM_MESSAGE = "CamelSpringAiChatSystemMessage";
+
+    @Metadata(description = "Temperature parameter for response randomness (0.0-2.0)", javaType = "Double")
+    public static final String TEMPERATURE = "CamelSpringAiChatTemperature";
+
+    @Metadata(description = "Maximum tokens in the response", javaType = "Integer")
+    public static final String MAX_TOKENS = "CamelSpringAiChatMaxTokens";
+
+    @Metadata(description = "Top P parameter for nucleus sampling", javaType = "Double")
+    public static final String TOP_P = "CamelSpringAiChatTopP";
+
+    @Metadata(description = "Top K parameter for sampling", javaType = "Integer")
+    public static final String TOP_K = "CamelSpringAiChatTopK";
+
+    @Metadata(description = "User message text for multimodal requests", javaType = "String")
+    public static final String USER_MESSAGE = "CamelSpringAiChatUserMessage";
+
+    @Metadata(description = "Media data for multimodal requests (image or audio)", javaType = "byte[]")
+    public static final String MEDIA_DATA = "CamelSpringAiChatMediaData";
+
+    @Metadata(description = "Media type (MIME type) for multimodal requests (e.g., image/png, audio/wav)", javaType = "String")
+    public static final String MEDIA_TYPE = "CamelSpringAiChatMediaType";
+
+    @Metadata(description = "The output format type for structured output conversion (BEAN, MAP, LIST)", javaType = "String")
+    public static final String OUTPUT_FORMAT = "CamelSpringAiChatOutputFormat";
+
+    @Metadata(description = "The Java class to use for structured output bean conversion", javaType = "Class<?>")
+    public static final String OUTPUT_CLASS = "CamelSpringAiChatOutputClass";
+
+    @Metadata(description = "The structured output converted from the chat response", javaType = "Object")
+    public static final String STRUCTURED_OUTPUT = "CamelSpringAiChatStructuredOutput";
+
+    @Metadata(description = "Comma-separated list of sensitive words for SafeGuard advisor", javaType = "String")
+    public static final String SAFEGUARD_SENSITIVE_WORDS = "CamelSpringAiChatSafeguardSensitiveWords";
+
+    @Metadata(description = "Failure response message for SafeGuard advisor when sensitive content is detected",
+              javaType = "String")
+    public static final String SAFEGUARD_FAILURE_RESPONSE = "CamelSpringAiChatSafeguardFailureResponse";
+
+    @Metadata(description = "Order of execution for SafeGuard advisor", javaType = "Integer")
+    public static final String SAFEGUARD_ORDER = "CamelSpringAiChatSafeguardOrder";
+
+    @Metadata(description = "List of custom advisors to add to the request",
+              javaType = "java.util.List<org.springframework.ai.chat.client.advisor.api.Advisor>")
+    public static final String ADVISORS = "CamelSpringAiChatAdvisors";
+
+    @Metadata(description = "The Java class to use for entity response conversion", javaType = "Class<?>")
+    public static final String ENTITY_CLASS = "CamelSpringAiChatEntityClass";
+
+    @Metadata(description = "Metadata to attach to user messages", javaType = "java.util.Map<String, Object>")
+    public static final String USER_METADATA = "CamelSpringAiChatUserMetadata";
+
+    @Metadata(description = "Metadata to attach to system messages", javaType = "java.util.Map<String, Object>")
+    public static final String SYSTEM_METADATA = "CamelSpringAiChatSystemMetadata";
+
+    @Metadata(description = "Conversation ID for managing separate conversation contexts in chat memory", javaType = "String")
+    public static final String CONVERSATION_ID = "CamelSpringAiChatConversationId";
+
+    @Metadata(description = "Maximum file size in bytes for multimodal content. Overrides endpoint configuration.",
+              javaType = "Long")
+    public static final String MAX_FILE_SIZE = "CamelSpringAiChatMaxFileSize";
+
+    @Metadata(description = "The reason why the chat response generation stopped (e.g., STOP, LENGTH, TOOL_CALLS)",
+              javaType = "String")
+    public static final String FINISH_REASON = "CamelSpringAiChatFinishReason";
+
+    @Metadata(description = "The name of the AI model used to generate the response", javaType = "String")
+    public static final String MODEL_NAME = "CamelSpringAiChatModelName";
+
+    @Metadata(description = "The unique ID of the chat response", javaType = "String")
+    public static final String RESPONSE_ID = "CamelSpringAiChatResponseId";
+
+    @Metadata(description = "Full response metadata as a Map containing all available metadata fields",
+              javaType = "java.util.Map<String, Object>")
+    public static final String RESPONSE_METADATA = "CamelSpringAiChatResponseMetadata";
+
+    private SpringAiChatConstants() {
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatEndpoint.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatEndpoint.java
new file mode 100644
index 000000000000..82ff1c2df23e
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatEndpoint.java
@@ -0,0 +1,81 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.Category;
+import org.apache.camel.Component;
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.spi.EndpointServiceLocation;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.support.DefaultEndpoint;
+
+/**
+ * Perform chat operations using Spring AI.
+ */
+@UriEndpoint(firstVersion = "4.17.0", scheme = "spring-ai-chat", title = "Spring AI Chat",
+             syntax = "spring-ai-chat:chatId", producerOnly = true, category = { Category.AI },
+             headersClass = SpringAiChatConstants.class)
+public class SpringAiChatEndpoint extends DefaultEndpoint implements EndpointServiceLocation {
+
+    @UriPath(description = "The ID of the chat endpoint")
+    private String chatId;
+
+    @UriParam
+    private SpringAiChatConfiguration configuration;
+
+    public SpringAiChatEndpoint(String uri, Component component, SpringAiChatConfiguration configuration, String chatId) {
+        super(uri, component);
+        this.configuration = configuration;
+        this.chatId = chatId;
+    }
+
+    @Override
+    public Producer createProducer() throws Exception {
+        return new SpringAiChatProducer(this);
+    }
+
+    @Override
+    public Consumer createConsumer(Processor processor) throws Exception {
+        throw new UnsupportedOperationException("Consumer not supported for Spring AI Chat endpoint");
+    }
+
+    public SpringAiChatConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    public String getChatId() {
+        return chatId;
+    }
+
+    public void setChatId(String chatId) {
+        this.chatId = chatId;
+    }
+
+    @Override
+    public String getServiceUrl() {
+        return "spring-ai-chat";
+    }
+
+    @Override
+    public String getServiceProtocol() {
+        return "spring-ai";
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatOperations.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatOperations.java
new file mode 100644
index 000000000000..844b0739e48e
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatOperations.java
@@ -0,0 +1,35 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+/**
+ * Spring AI Chat Operations.
+ */
+public enum SpringAiChatOperations {
+    /**
+     * Send a single message to the chat model.
+     */
+    CHAT_SINGLE_MESSAGE,
+    /**
+     * Send a message using a prompt template with variable substitution.
+     */
+    CHAT_SINGLE_MESSAGE_WITH_PROMPT,
+    /**
+     * Send multiple messages for conversation history support.
+     */
+    CHAT_MULTIPLE_MESSAGES
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatProducer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatProducer.java
new file mode 100644
index 000000000000..3ff76ccef2fa
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/SpringAiChatProducer.java
@@ -0,0 +1,1156 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.NoSuchHeaderException;
+import org.apache.camel.WrappedFile;
+import org.apache.camel.component.springai.tools.TagsHelper;
+import org.apache.camel.component.springai.tools.spec.CamelToolExecutorCache;
+import org.apache.camel.component.springai.tools.spec.CamelToolSpecification;
+import org.apache.camel.support.DefaultProducer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
+import org.springframework.ai.chat.client.advisor.SafeGuardAdvisor;
+import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
+import org.springframework.ai.chat.client.advisor.api.Advisor;
+import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
+import org.springframework.ai.chat.client.advisor.vectorstore.VectorStoreChatMemoryAdvisor;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.prompt.ChatOptions;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.chat.prompt.PromptTemplate;
+import org.springframework.ai.content.Media;
+import org.springframework.ai.converter.BeanOutputConverter;
+import org.springframework.ai.converter.ListOutputConverter;
+import org.springframework.ai.converter.MapOutputConverter;
+import org.springframework.ai.converter.StructuredOutputConverter;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.model.tool.ToolCallingChatOptions;
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.ai.vectorstore.SearchRequest;
+import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.core.convert.support.DefaultConversionService;
+import org.springframework.util.MimeType;
+
+/**
+ * Producer for Spring AI Chat operations using ChatClient.
+ */
+public class SpringAiChatProducer extends DefaultProducer {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SpringAiChatProducer.class);
+
+    private ChatClient chatClient;
+
+    public SpringAiChatProducer(SpringAiChatEndpoint endpoint) {
+        super(endpoint);
+    }
+
+    @Override
+    public SpringAiChatEndpoint getEndpoint() {
+        return (SpringAiChatEndpoint) super.getEndpoint();
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        // Initialize ChatClient
+        ChatClient configuredClient = getEndpoint().getConfiguration().getChatClient();
+        if (configuredClient != null) {
+            // Use the provided ChatClient
+            this.chatClient = configuredClient;
+        } else {
+            // Create ChatClient from ChatModel
+            ChatModel chatModel = getEndpoint().getConfiguration().getChatModel();
+            if (chatModel == null) {
+                throw new IllegalArgumentException(
+                        "Either ChatClient or ChatModel must be configured");
+            }
+
+            ChatClient.Builder builder = ChatClient.builder(chatModel);
+
+            // Add default advisors based on configuration
+            List<Advisor> advisors = buildDefaultAdvisors();
+            if (!advisors.isEmpty()) {
+                builder.defaultAdvisors(advisors);
+            }
+
+            // Set default system message if configured
+            String systemMessage = getEndpoint().getConfiguration().getSystemMessage();
+            if (systemMessage != null) {
+                builder.defaultSystem(systemMessage);
+            }
+
+            this.chatClient = builder.build();
+        }
+    }
+
+    @Override
+    public void process(Exchange exchange) throws Exception {
+        SpringAiChatOperations operation = getEndpoint().getConfiguration().getChatOperation();
+
+        switch (operation) {
+            case CHAT_SINGLE_MESSAGE:
+                processSingleMessage(exchange);
+                break;
+            case CHAT_SINGLE_MESSAGE_WITH_PROMPT:
+                processSingleMessageWithPrompt(exchange);
+                break;
+            case CHAT_MULTIPLE_MESSAGES:
+                processMultipleMessages(exchange);
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported operation: " + operation);
+        }
+    }
+
+    private void processSingleMessage(Exchange exchange) throws Exception {
+        // Get the message body
+        final Object messageBody = exchange.getIn().getMandatoryBody();
+
+        ChatClient.ChatClientRequestSpec request = chatClient.prompt();
+
+        // Check for manual augmented data for RAG
+        String userMessageText = null;
+        List<Document> augmentedData = exchange.getIn().getHeader(SpringAiChatConstants.AUGMENTED_DATA, List.class);
+
+        // Warn if both automatic RAG (QuestionAnswerAdvisor) and manual RAG (AUGMENTED_DATA header) are configured
+        if (augmentedData != null && !augmentedData.isEmpty() && isQuestionAnswerAdvisorConfigured()) {
+            LOG.warn("Both AUGMENTED_DATA header and QuestionAnswerAdvisor (VectorStore) are configured. " +
+                     "This may result in redundant or conflicting context being added to the prompt. " +
+                     "Consider using only one RAG method: either automatic (QuestionAnswerAdvisor) or manual (AUGMENTED_DATA header).");
+        }
+
+        // Handle different body types
+        if (messageBody instanceof String) {
+            userMessageText = (String) messageBody;
+        } else if (messageBody instanceof UserMessage) {
+            UserMessage userMsg = (UserMessage) messageBody;
+            userMessageText = userMsg.getText();
+            // If there's media, we need to handle it differently using additive builder
+            if (!userMsg.getMedia().isEmpty()) {
+                applyUserMessageWithMedia(request, exchange, userMsg.getText(), userMsg.getMedia());
+                userMessageText = null; // Already handled
+            }
+        } else if (messageBody instanceof Message) {
+            // Note: Using request.messages() here replaces the entire message list,
+            // which will ignore any default system message configured in doStart() or via headers.
+            // This is intentional for the CHAT_SINGLE_MESSAGE operation when a complete Message is provided.
+            request.messages((Message) messageBody);
+        } else if (messageBody instanceof WrappedFile) {
+            // Handle single WrappedFile for multimodal content
+            WrappedFile<?> wrappedFile = (WrappedFile<?>) messageBody;
+            List<Media> mediaList = convertWrappedFilesToMedia(exchange, List.of(wrappedFile));
+            UserMessage multimodalMessage = createMultimodalMessageWithMedia(exchange, mediaList);
+            applyUserMessageWithMedia(request, exchange, multimodalMessage.getText(), multimodalMessage.getMedia());
+        } else if (messageBody instanceof List) {
+            // Handle List<WrappedFile> for multimodal content with multiple files
+            List<?> list = (List<?>) messageBody;
+            if (!list.isEmpty() && list.get(0) instanceof WrappedFile) {
+                @SuppressWarnings("unchecked")
+                List<WrappedFile<?>> wrappedFiles = (List<WrappedFile<?>>) list;
+                List<Media> mediaList = convertWrappedFilesToMedia(exchange, wrappedFiles);
+                UserMessage multimodalMessage = createMultimodalMessageWithMedia(exchange, mediaList);
+                applyUserMessageWithMedia(request, exchange, multimodalMessage.getText(), multimodalMessage.getMedia());
+            } else {
+                throw new IllegalArgumentException(
+                        "List body must contain WrappedFile objects. Got: "
+                                                   + (list.isEmpty() ? "empty list" : list.get(0).getClass().getName()));
+            }
+        } else if (messageBody instanceof byte[]) {
+            // Handle multimodal content (image or audio)
+            UserMessage multimodalMessage = createMultimodalMessage(exchange, (byte[]) messageBody);
+            applyUserMessageWithMedia(request, exchange, multimodalMessage.getText(), multimodalMessage.getMedia());
+        } else {
+            throw new IllegalArgumentException(
+                    "Unsupported message type: " + messageBody.getClass().getName()
+                                               + ". Expected String, byte[], WrappedFile, List<WrappedFile>, or org.springframework.ai.chat.messages.Message");
+        }
+
+        // Apply augmented data to user message if provided
+        if (userMessageText != null && augmentedData != null && !augmentedData.isEmpty()) {
+            String context = augmentedData.stream()
+                    .map(Document::getText)
+                    .collect(Collectors.joining("\n\n"));
+            userMessageText = formatRagPrompt(context, userMessageText);
+        }
+
+        // Set the user message if we have one
+        if (userMessageText != null) {
+            applyUserMessageWithText(request, exchange, userMessageText);
+        }
+
+        // Apply configuration and headers (augmented data is already handled above)
+        applyRequestOptions(request, exchange);
+
+        // Execute the request with entity or structured output handling
+        executeRequest(request, exchange);
+    }
+
+    /**
+     * Process a single message using a PromptTemplate.
+     * <p>
+     * This method passes ALL messages from the rendered PromptTemplate to the ChatClient, including SystemMessage and
+     * UserMessage. This allows templates to define both system roles and user messages.
+     * </p>
+     */
+    private void processSingleMessageWithPrompt(Exchange exchange) throws NoSuchHeaderException, InvalidPayloadException {
+        // Get the prompt template from header
+        final String promptTemplateText
+                = exchange.getIn().getHeader(SpringAiChatConstants.PROMPT_TEMPLATE, String.class);
+        if (promptTemplateText == null) {
+            throw new NoSuchHeaderException(
+                    "The promptTemplate is a required header for CHAT_SINGLE_MESSAGE_WITH_PROMPT operation",
+                    exchange,
+                    SpringAiChatConstants.PROMPT_TEMPLATE);
+        }
+
+        // Get variables from message body
+        Map<String, Object> variables = exchange.getIn().getMandatoryBody(Map.class);
+
+        // Create prompt using Spring AI's PromptTemplate
+        PromptTemplate template = new PromptTemplate(promptTemplateText);
+        Prompt prompt = template.create(variables);
+
+        // Pass ALL messages from the prompt template (including SystemMessage, UserMessage, etc.)
+        ChatClient.ChatClientRequestSpec request = chatClient.prompt()
+                .messages(prompt.getInstructions());
+
+        // Apply configuration and headers
+        applyRequestOptions(request, exchange);
+
+        // Execute the request with entity or structured output handling
+        executeRequest(request, exchange);
+    }
+
+    private void processMultipleMessages(Exchange exchange) throws InvalidPayloadException {
+        // Get messages from body
+        List<Message> messages = exchange.getIn().getMandatoryBody(List.class);
+
+        ChatClient.ChatClientRequestSpec request = chatClient.prompt().messages(messages);
+
+        // Apply configuration and headers
+        applyRequestOptions(request, exchange);
+
+        // Execute the request with entity or structured output handling
+        executeRequest(request, exchange);
+    }
+
+    private UserMessage createMultimodalMessage(Exchange exchange, byte[] mediaData) {
+        String userMessageText = getUserMessageText(exchange);
+
+        // Get media type from header or try to detect
+        String mediaTypeStr = exchange.getIn().getHeader(SpringAiChatConstants.MEDIA_TYPE, String.class);
+        if (mediaTypeStr == null) {
+            // Default to image/png if not specified
+            mediaTypeStr = "image/png";
+        }
+
+        MimeType mimeType = MimeType.valueOf(mediaTypeStr);
+        Media media = Media.builder()
+                .mimeType(mimeType)
+                .data(mediaData)
+                .build();
+
+        return UserMessage.builder()
+                .text(userMessageText)
+                .media(media)
+                .build();
+    }
+
+    /**
+     * Create a multimodal message with multiple media items
+     */
+    private UserMessage createMultimodalMessageWithMedia(Exchange exchange, List<Media> mediaList) {
+        String userMessageText = getUserMessageText(exchange);
+
+        return UserMessage.builder()
+                .text(userMessageText)
+                .media(mediaList)
+                .build();
+    }
+
+    /**
+     * Get user message text from header or configuration, with empty string as fallback
+     */
+    private String getUserMessageText(Exchange exchange) {
+        String userMessageText = exchange.getIn().getHeader(SpringAiChatConstants.USER_MESSAGE, String.class);
+        if (userMessageText == null) {
+            userMessageText = getEndpoint().getConfiguration().getUserMessage();
+        }
+        if (userMessageText == null) {
+            userMessageText = ""; // Empty text for media-only messages
+        }
+        return userMessageText;
+    }
+
+    /**
+     * Convert a list of WrappedFile objects to Media objects
+     */
+    private List<Media> convertWrappedFilesToMedia(Exchange exchange, List<WrappedFile<?>> wrappedFiles) {
+        List<Media> mediaList = new ArrayList<>();
+
+        for (WrappedFile<?> wrappedFile : wrappedFiles) {
+            // Unwrap the file
+            Object fileObj = wrappedFile.getFile();
+            if (fileObj == null) {
+                throw new IllegalArgumentException("WrappedFile contains null file");
+            }
+            if (!(fileObj instanceof File)) {
+                throw new IllegalArgumentException(
+                        "WrappedFile must contain a java.io.File instance, got: " + fileObj.getClass().getName());
+            }
+
+            File file = (File) fileObj;
+
+            // Detect MIME type
+            String mimeTypeStr = detectMimeType(file, exchange);
+            MimeType mimeType = MimeType.valueOf(mimeTypeStr);
+
+            // Convert file to byte array with size validation
+            byte[] fileData = fileToByteArray(file, exchange);
+
+            // Create Media object
+            Media media = Media.builder()
+                    .mimeType(mimeType)
+                    .data(fileData)
+                    .build();
+
+            mediaList.add(media);
+        }
+
+        return mediaList;
+    }
+
+    /**
+     * Detect MIME type from file extension and component headers Priority: 1) CamelSpringAiChatMediaType header, 2)
+     * Component-specific headers (FILE_CONTENT_TYPE, etc.), 3) Auto-detection from extension, 4) Default to image/png
+     */
+    private String detectMimeType(File file, Exchange exchange) {
+        // First check if MIME type is provided in spring-ai-chat specific header (highest priority)
+        String mediaTypeStr = exchange.getIn().getHeader(SpringAiChatConstants.MEDIA_TYPE, String.class);
+        if (mediaTypeStr != null) {
+            return mediaTypeStr;
+        }
+
+        // Check for file component's content type header (camel-file, camel-ftp, camel-sftp, camel-smb)
+        String fileContentType = exchange.getIn().getHeader(Exchange.FILE_CONTENT_TYPE, String.class);
+        if (fileContentType != null) {
+            return fileContentType;
+        }
+
+        // Auto-detect from file extension
+        String fileName = file.getName().toLowerCase();
+
+        // Image formats (matching Spring AI Media.Format constants)
+        if (fileName.endsWith(".png")) {
+            return "image/png";
+        } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
+            return "image/jpeg";
+        } else if (fileName.endsWith(".gif")) {
+            return "image/gif";
+        } else if (fileName.endsWith(".webp")) {
+            return "image/webp";
+        } else if (fileName.endsWith(".bmp")) {
+            return "image/bmp";
+        } else if (fileName.endsWith(".tiff") || fileName.endsWith(".tif")) {
+            return "image/tiff";
+        } else if (fileName.endsWith(".svg")) {
+            return "image/svg+xml";
+        }
+        // Video formats (matching Spring AI Media.Format constants)
+        else if (fileName.endsWith(".mp4")) {
+            return "video/mp4";
+        } else if (fileName.endsWith(".webm")) {
+            return "video/webm";
+        } else if (fileName.endsWith(".mov")) {
+            return "video/quicktime";
+        } else if (fileName.endsWith(".mkv")) {
+            return "video/x-matroska";
+        } else if (fileName.endsWith(".flv")) {
+            return "video/x-flv";
+        } else if (fileName.endsWith(".mpeg") || fileName.endsWith(".mpg")) {
+            return "video/mpeg";
+        } else if (fileName.endsWith(".wmv")) {
+            return "video/x-ms-wmv";
+        } else if (fileName.endsWith(".3gp")) {
+            return "video/3gpp";
+        }
+        // Audio formats (using standard MIME types)
+        else if (fileName.endsWith(".wav")) {
+            return "audio/wav";
+        } else if (fileName.endsWith(".mp3")) {
+            return "audio/mpeg"; // Correct MIME type for MP3
+        } else if (fileName.endsWith(".ogg")) {
+            return "audio/ogg";
+        } else if (fileName.endsWith(".m4a")) {
+            return "audio/mp4"; // Standard MIME type for M4A
+        } else if (fileName.endsWith(".flac")) {
+            return "audio/flac";
+        }
+        // Document formats (matching Spring AI Media.Format constants)
+        else if (fileName.endsWith(".pdf")) {
+            return "application/pdf";
+        } else if (fileName.endsWith(".csv")) {
+            return "text/csv";
+        } else if (fileName.endsWith(".doc")) {
+            return "application/msword";
+        } else if (fileName.endsWith(".docx")) {
+            return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
+        } else if (fileName.endsWith(".xls")) {
+            return "application/vnd.ms-excel";
+        } else if (fileName.endsWith(".xlsx")) {
+            return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+        } else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) {
+            return "text/html";
+        } else if (fileName.endsWith(".txt")) {
+            return "text/plain";
+        } else if (fileName.endsWith(".md")) {
+            return "text/markdown";
+        }
+
+        // Default to image/png if extension not recognized
+        LOG.debug("Could not detect MIME type from file extension: {}, defaulting to image/png", fileName);
+        return "image/png";
+    }
+
+    /**
+     * Convert a File to byte array with size validation
+     */
+    private byte[] fileToByteArray(File file) {
+        return fileToByteArray(file, null);
+    }
+
+    /**
+     * Convert a File to byte array with size validation
+     */
+    private byte[] fileToByteArray(File file, Exchange exchange) {
+        long fileSize = file.length();
+        long maxSize = getMaxFileSize(exchange);
+
+        // Validate file size if max size is set (0 means no limit)
+        if (maxSize > 0 && fileSize > maxSize) {
+            throw new IllegalArgumentException(
+                    String.format("File size (%d bytes) exceeds maximum allowed size (%d bytes): %s",
+                            fileSize, maxSize, file.getAbsolutePath()));
+        }
+
+        try {
+            return Files.readAllBytes(file.toPath());
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed to read file: " + file.getAbsolutePath(), e);
+        }
+    }
+
+    /**
+     * Get max file size from header (priority) or configuration
+     */
+    private long getMaxFileSize(Exchange exchange) {
+        if (exchange != null) {
+            Long headerMaxSize = exchange.getIn().getHeader(SpringAiChatConstants.MAX_FILE_SIZE, Long.class);
+            if (headerMaxSize != null) {
+                return headerMaxSize;
+            }
+        }
+        return getEndpoint().getConfiguration().getMaxFileSize();
+    }
+
+    private void applyRequestOptions(ChatClient.ChatClientRequestSpec request, Exchange exchange) {
+        // Apply system message from header (overrides configuration)
+        String systemMessage = exchange.getIn().getHeader(SpringAiChatConstants.SYSTEM_MESSAGE, String.class);
+        if (systemMessage != null) {
+            Map<String, Object> systemMetadata = getSystemMetadata(exchange);
+            final String finalSystemMessage = systemMessage;
+            request.system(s -> {
+                s.text(finalSystemMessage);
+                if (systemMetadata != null && !systemMetadata.isEmpty()) {
+                    s.metadata(systemMetadata);
+                }
+            });
+        }
+
+        // Note: Augmented data is handled in the calling method for better control
+
+        // Get tool callbacks first if tags are configured
+        List<ToolCallback> toolCallbacks = getToolCallbacksForTags(getEndpoint().getConfiguration().getTags());
+
+        // Apply chat options from configuration and headers using ToolCallingChatOptions
+        // This ensures tool callbacks are properly included in the options
+        ToolCallingChatOptions.Builder optionsBuilder = ToolCallingChatOptions.builder();
+
+        // Add tool callbacks to the options builder if present
+        if (!toolCallbacks.isEmpty()) {
+            LOG.debug("Adding {} tool callbacks to ToolCallingChatOptions", toolCallbacks.size());
+            optionsBuilder.toolCallbacks(toolCallbacks);
+        }
+
+        // Check configuration first
+        if (getEndpoint().getConfiguration().getTemperature() != null) {
+            optionsBuilder.temperature(getEndpoint().getConfiguration().getTemperature());
+        }
+        if (getEndpoint().getConfiguration().getMaxTokens() != null) {
+            optionsBuilder.maxTokens(getEndpoint().getConfiguration().getMaxTokens());
+        }
+        if (getEndpoint().getConfiguration().getTopP() != null) {
+            optionsBuilder.topP(getEndpoint().getConfiguration().getTopP());
+        }
+        if (getEndpoint().getConfiguration().getTopKSampling() != null) {
+            optionsBuilder.topK(getEndpoint().getConfiguration().getTopKSampling());
+        }
+
+        // Headers override configuration
+        Double temperature = exchange.getIn().getHeader(SpringAiChatConstants.TEMPERATURE, Double.class);
+        if (temperature != null) {
+            optionsBuilder.temperature(temperature);
+        }
+
+        Integer maxTokens = exchange.getIn().getHeader(SpringAiChatConstants.MAX_TOKENS, Integer.class);
+        if (maxTokens != null) {
+            optionsBuilder.maxTokens(maxTokens);
+        }
+
+        Double topP = exchange.getIn().getHeader(SpringAiChatConstants.TOP_P, Double.class);
+        if (topP != null) {
+            optionsBuilder.topP(topP);
+        }
+
+        Integer topK = exchange.getIn().getHeader(SpringAiChatConstants.TOP_K, Integer.class);
+        if (topK != null) {
+            optionsBuilder.topK(topK);
+        }
+
+        ToolCallingChatOptions options = optionsBuilder.build();
+        request.options(options);
+
+        // Apply conversation ID for chat memory if provided
+        String conversationId = exchange.getIn().getHeader(SpringAiChatConstants.CONVERSATION_ID, String.class);
+        if (conversationId != null) {
+            request.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId));
+        }
+
+        // Apply SafeGuard advisor overrides if provided via headers
+        applySafeguardHeaderOverrides(request, exchange);
+
+        // Apply custom advisors from headers if provided
+        applyCustomAdvisorsFromHeaders(request, exchange);
+    }
+
+    /**
+     * Apply SafeGuard advisor configuration overrides from headers
+     */
+    private void applySafeguardHeaderOverrides(ChatClient.ChatClientRequestSpec request, Exchange exchange) {
+        String sensitiveWords = exchange.getIn().getHeader(SpringAiChatConstants.SAFEGUARD_SENSITIVE_WORDS, String.class);
+        String failureResponse = exchange.getIn().getHeader(SpringAiChatConstants.SAFEGUARD_FAILURE_RESPONSE, String.class);
+        Integer order = exchange.getIn().getHeader(SpringAiChatConstants.SAFEGUARD_ORDER, Integer.class);
+
+        // If any SafeGuard header is provided, we need to configure/override the SafeGuard advisor for this request
+        if (sensitiveWords != null || failureResponse != null || order != null) {
+            // Start with configuration values, then override with headers
+            String finalSensitiveWords = sensitiveWords != null
+                    ? sensitiveWords
+                    : getEndpoint().getConfiguration().getSafeguardSensitiveWords();
+            String finalFailureResponse = failureResponse != null
+                    ? failureResponse
+                    : getEndpoint().getConfiguration().getSafeguardFailureResponse();
+            Integer finalOrder = order != null
+                    ? order
+                    : getEndpoint().getConfiguration().getSafeguardOrder();
+
+            // Build and add SafeGuard advisor if we have sensitive words
+            SafeGuardAdvisor safeguardAdvisor
+                    = buildSafeguardAdvisor(finalSensitiveWords, finalFailureResponse, finalOrder, true);
+            if (safeguardAdvisor != null) {
+                request.advisors(safeguardAdvisor);
+            }
+        }
+    }
+
+    /**
+     * Apply custom advisors from headers to the request
+     */
+    @SuppressWarnings("unchecked")
+    private void applyCustomAdvisorsFromHeaders(ChatClient.ChatClientRequestSpec request, Exchange exchange) {
+        List<Advisor> customAdvisors = exchange.getIn().getHeader(SpringAiChatConstants.ADVISORS, List.class);
+
+        if (customAdvisors != null && !customAdvisors.isEmpty()) {
+            // Add each custom advisor individually to the request
+            for (Advisor advisor : customAdvisors) {
+                request.advisors(advisor);
+            }
+            LOG.debug("Added {} custom advisors from headers", customAdvisors.size());
+        }
+    }
+
+    private void populateResponse(ChatResponse response, Exchange exchange) {
+        String responseText = response.getResult().getOutput().getText();
+        exchange.getMessage().setBody(responseText);
+        exchange.getMessage().setHeader(SpringAiChatConstants.CHAT_RESPONSE, responseText);
+
+        populateTokenUsage(response, exchange);
+        populateResponseMetadata(response, exchange);
+    }
+
+    private void populateTokenUsage(ChatResponse response, Exchange exchange) {
+        // Add token usage information if available
+        if (response.getMetadata() != null && response.getMetadata().getUsage() != null) {
+            var usage = response.getMetadata().getUsage();
+            exchange.getMessage().setHeader(SpringAiChatConstants.INPUT_TOKEN_COUNT,
+                    usage.getPromptTokens());
+            exchange.getMessage().setHeader(SpringAiChatConstants.OUTPUT_TOKEN_COUNT,
+                    usage.getCompletionTokens());
+            exchange.getMessage().setHeader(SpringAiChatConstants.TOTAL_TOKEN_COUNT,
+                    usage.getTotalTokens());
+        }
+    }
+
+    private void populateResponseMetadata(ChatResponse response, Exchange exchange) {
+        // Add response metadata if available
+        if (response.getMetadata() != null) {
+            var responseMetadata = response.getMetadata();
+
+            // Model name
+            if (responseMetadata.getModel() != null) {
+                exchange.getMessage().setHeader(SpringAiChatConstants.MODEL_NAME,
+                        responseMetadata.getModel());
+            }
+
+            // Response ID
+            if (responseMetadata.getId() != null) {
+                exchange.getMessage().setHeader(SpringAiChatConstants.RESPONSE_ID,
+                        responseMetadata.getId());
+            }
+
+            // Finish reason is on the Generation metadata, not the Response metadata
+            if (response.getResult() != null && response.getResult().getMetadata() != null) {
+                String finishReason = response.getResult().getMetadata().getFinishReason();
+                if (finishReason != null) {
+                    exchange.getMessage().setHeader(SpringAiChatConstants.FINISH_REASON, finishReason);
+                }
+            }
+
+            // Full metadata map for advanced users
+            // This includes all metadata fields in a single map
+            Map<String, Object> metadataMap = new java.util.HashMap<>();
+            if (responseMetadata.getId() != null) {
+                metadataMap.put("id", responseMetadata.getId());
+            }
+            if (responseMetadata.getModel() != null) {
+                metadataMap.put("model", responseMetadata.getModel());
+            }
+            // Add finish reason from generation metadata
+            if (response.getResult() != null && response.getResult().getMetadata() != null) {
+                String finishReason = response.getResult().getMetadata().getFinishReason();
+                if (finishReason != null) {
+                    metadataMap.put("finishReason", finishReason);
+                }
+            }
+            if (responseMetadata.getUsage() != null) {
+                metadataMap.put("usage", responseMetadata.getUsage());
+            }
+            // Add rate limit info if available
+            if (responseMetadata.getRateLimit() != null) {
+                metadataMap.put("rateLimit", responseMetadata.getRateLimit());
+            }
+            // Add prompt metadata if available
+            if (responseMetadata.getPromptMetadata() != null) {
+                metadataMap.put("promptMetadata", responseMetadata.getPromptMetadata());
+            }
+
+            if (!metadataMap.isEmpty()) {
+                exchange.getMessage().setHeader(SpringAiChatConstants.RESPONSE_METADATA, metadataMap);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> getUserMetadata(Exchange exchange) {
+        // Check if metadata is provided via header (takes highest priority)
+        Map<String, Object> metadata = exchange.getIn().getHeader(SpringAiChatConstants.USER_METADATA, Map.class);
+
+        if (metadata != null) {
+            return metadata;
+        }
+
+        // Check configuration for metadata
+        return getEndpoint().getConfiguration().getUserMetadata();
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> getSystemMetadata(Exchange exchange) {
+        // Check if metadata is provided via header (takes highest priority)
+        Map<String, Object> metadata = exchange.getIn().getHeader(SpringAiChatConstants.SYSTEM_METADATA, Map.class);
+
+        if (metadata != null) {
+            return metadata;
+        }
+
+        // Check configuration for metadata
+        return getEndpoint().getConfiguration().getSystemMetadata();
+    }
+
+    private Class<?> getEntityClass(Exchange exchange) {
+        // Check if entity class is provided via header (takes highest priority)
+        Class<?> entityClass = exchange.getIn().getHeader(SpringAiChatConstants.ENTITY_CLASS, Class.class);
+
+        if (entityClass != null) {
+            return entityClass;
+        }
+
+        // Check configuration for entity class
+        return getEndpoint().getConfiguration().getEntityClass();
+    }
+
+    /**
+     * Process a chat request with entity conversion.
+     * <p>
+     * This method executes the chat request and automatically converts the AI model's response into a Java entity
+     * object using Spring AI's entity conversion mechanism. The conversion leverages the model's ability to generate
+     * structured output that matches the entity class structure.
+     * </p>
+     *
+     * @param request     the configured chat client request
+     * @param exchange    the Camel exchange
+     * @param entityClass the target class to convert the response into
+     * @param <T>         the type of the entity
+     */
+    private <T> void processEntityRequest(
+            ChatClient.ChatClientRequestSpec request, Exchange exchange, Class<T> entityClass) {
+        // Execute the request and convert to entity
+        T entity = request.call().entity(entityClass);
+
+        // Set the entity as the body
+        exchange.getMessage().setBody(entity);
+
+        LOG.debug("Converted response to entity of type: {}", entityClass.getName());
+    }
+
+    private StructuredOutputConverter<?> getStructuredOutputConverter(Exchange exchange) {
+        // Check if converter is provided via header (takes highest priority)
+        StructuredOutputConverter<?> converter
+                = exchange.getIn().getHeader(SpringAiChatConstants.OUTPUT_FORMAT, StructuredOutputConverter.class);
+
+        if (converter != null) {
+            return converter;
+        }
+
+        // Check configuration for pre-configured converter
+        if (getEndpoint().getConfiguration().getStructuredOutputConverter() != null) {
+            return getEndpoint().getConfiguration().getStructuredOutputConverter();
+        }
+
+        // Check if we should create a converter based on headers or configuration
+        String outputFormat = exchange.getIn().getHeader(SpringAiChatConstants.OUTPUT_FORMAT, String.class);
+        Class<?> outputClass = exchange.getIn().getHeader(SpringAiChatConstants.OUTPUT_CLASS, Class.class);
+
+        // If not in headers, check configuration
+        if (outputFormat == null) {
+            outputFormat = getEndpoint().getConfiguration().getOutputFormat();
+        }
+        if (outputClass == null) {
+            outputClass = getEndpoint().getConfiguration().getOutputClass();
+        }
+
+        if (outputFormat != null) {
+            switch (outputFormat.toUpperCase()) {
+                case "BEAN":
+                    if (outputClass == null) {
+                        throw new IllegalArgumentException(
+                                "OUTPUT_CLASS header or configuration is required when OUTPUT_FORMAT is BEAN");
+                    }
+                    return new BeanOutputConverter<>(outputClass);
+                case "MAP":
+                    return new MapOutputConverter();
+                case "LIST":
+                    return new ListOutputConverter(new DefaultConversionService());
+                default:
+                    throw new IllegalArgumentException(
+                            "Unsupported OUTPUT_FORMAT: " + outputFormat + ". Supported values: BEAN, MAP, LIST");
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Process a chat request with structured output conversion.
+     * <p>
+     * This method executes the chat request and converts the AI model's response into a structured format using a
+     * {@link StructuredOutputConverter}. The converter appends format instructions to the user message, guiding the
+     * model to generate output in the desired format (e.g., JSON, XML, CSV).
+     * </p>
+     * <p>
+     * The method performs the following steps:
+     * </p>
+     * <ol>
+     * <li>Retrieves format instructions from the converter</li>
+     * <li>Appends format instructions to the user message</li>
+     * <li>Executes the chat request</li>
+     * <li>Converts the raw response text using the converter</li>
+     * <li>Sets the structured output as the message body</li>
+     * <li>Populates response headers with both raw and structured output</li>
+     * </ol>
+     * <p>
+     * Supported converters include:
+     * </p>
+     * <ul>
+     * <li>{@link BeanOutputConverter} - Converts response to a Java bean</li>
+     * <li>{@link MapOutputConverter} - Converts response to a Map</li>
+     * <li>{@link ListOutputConverter} - Converts response to a List</li>
+     * </ul>
+     *
+     * @param request   the configured chat client request
+     * @param exchange  the Camel exchange
+     * @param converter the structured output converter to use
+     * @param <T>       the type of the structured output
+     */
+    @SuppressWarnings("unchecked")
+    private <T> void processStructuredOutputRequest(
+            ChatClient.ChatClientRequestSpec request, Exchange exchange,
+            StructuredOutputConverter<T> converter) {
+        // Get format instructions from the converter
+        String format = converter.getFormat();
+
+        // Append format instructions to the request
+        request.user(u -> u.text(format));
+
+        // Execute the request
+        ChatResponse response = request.call().chatResponse();
+
+        // Get the raw response text
+        String responseText = response.getResult().getOutput().getText();
+
+        // Convert the response using the converter
+        T structuredOutput = converter.convert(responseText);
+
+        // Set the structured output as the body
+        exchange.getMessage().setBody(structuredOutput);
+
+        // Also set headers
+        exchange.getMessage().setHeader(SpringAiChatConstants.CHAT_RESPONSE, responseText);
+        exchange.getMessage().setHeader(SpringAiChatConstants.STRUCTURED_OUTPUT, structuredOutput);
+
+        populateTokenUsage(response, exchange);
+    }
+
+    /**
+     * Register tools from camel-spring-ai-tools routes based on configured tags
+     *
+     * Tools are registered via ChatOptions.toolCallbacks() which is the correct way to pass ToolCallback instances in
+     * Spring AI. The tools() method expects objects with @Tool annotated methods, not ToolCallback instances.
+     */
+    private List<ToolCallback> getToolCallbacksForTags(String tags) {
+        if (tags == null || tags.trim().isEmpty()) {
+            LOG.debug("No tags configured, skipping tool discovery");
+            return List.of();
+        }
+
+        // Discover tools from Camel Spring AI Tools routes
+        List<ToolCallback> toolCallbacks = discoverTools(tags);
+
+        if (!toolCallbacks.isEmpty()) {
+            // Collect tool names for enhanced logging
+            String toolNames = toolCallbacks.stream()
+                    .map(callback -> callback.getToolDefinition().name())
+                    .collect(Collectors.joining(", "));
+
+            LOG.debug("Discovered {} unique tools for tags: {} - Tools: [{}]", toolCallbacks.size(), tags, toolNames);
+
+            // Detailed logging for each tool
+            for (ToolCallback callback : toolCallbacks) {
+                LOG.debug("Tool found: {} - {} - Schema: {}", callback.getToolDefinition().name(),
+                        callback.getToolDefinition().description(),
+                        callback.getToolDefinition().inputSchema());
+            }
+        } else {
+            LOG.warn("No tools found for tags: {}", tags);
+        }
+
+        return toolCallbacks;
+    }
+
+    /**
+     * Discover tools by tags and return a list of ToolCallback instances
+     */
+    private List<ToolCallback> discoverTools(String tags) {
+        final CamelToolExecutorCache toolCache = CamelToolExecutorCache.getInstance();
+        final Map<String, Set<CamelToolSpecification>> tools = toolCache.getTools();
+        final String[] tagArray = TagsHelper.splitTags(tags);
+
+        final List<ToolCallback> toolCallbacks = Arrays.stream(tagArray)
+                .flatMap(tag -> tools.entrySet().stream()
+                        .filter(entry -> entry.getKey().equals(tag))
+                        .flatMap(entry -> entry.getValue().stream()))
+                .map(CamelToolSpecification::getToolCallback)
+                .collect(Collectors.toList());
+
+        LOG.debug("Discovered {} unique tools for tags: {}", toolCallbacks.size(), tags);
+        return toolCallbacks;
+    }
+
+    /**
+     * Format the RAG prompt using the configured template
+     */
+    private String formatRagPrompt(String context, String question) {
+        String template = getEndpoint().getConfiguration().getRagTemplate();
+        if (template == null || template.trim().isEmpty()) {
+            // Fallback to default template if not configured
+            template = "Context:\n{context}\n\nQuestion: {question}";
+        }
+        return template
+                .replace("{context}", context)
+                .replace("{question}", question);
+    }
+
+    /**
+     * Check if QuestionAnswerAdvisor is configured (VectorStore is present)
+     */
+    private boolean isQuestionAnswerAdvisorConfigured() {
+        return getEndpoint().getConfiguration().getVectorStore() != null;
+    }
+
+    /**
+     * Build a SafeGuard advisor with the given configuration
+     *
+     * @param  sensitiveWords  comma-separated list of sensitive words
+     * @param  failureResponse response to return when sensitive words are detected
+     * @param  order           order of the advisor
+     * @param  isPerRequest    whether this is a per-request advisor (affects logging)
+     * @return                 SafeGuardAdvisor instance or null if sensitiveWords is null or empty
+     */
+    private SafeGuardAdvisor buildSafeguardAdvisor(
+            String sensitiveWords, String failureResponse, Integer order, boolean isPerRequest) {
+        if (sensitiveWords == null || sensitiveWords.trim().isEmpty()) {
+            return null;
+        }
+
+        SafeGuardAdvisor.Builder safeguardBuilder = SafeGuardAdvisor.builder();
+
+        // Parse sensitive words from comma-separated list
+        List<String> wordList = Arrays.stream(sensitiveWords.split(","))
+                .map(String::trim)
+                .filter(s -> !s.isEmpty())
+                .collect(Collectors.toList());
+        safeguardBuilder.sensitiveWords(wordList);
+
+        // Set failure response if configured
+        if (failureResponse != null && !failureResponse.trim().isEmpty()) {
+            safeguardBuilder.failureResponse(failureResponse);
+        }
+
+        // Set order if configured
+        if (order != null) {
+            safeguardBuilder.order(order);
+        }
+
+        String logMessage = isPerRequest
+                ? "Per-request SafeGuardAdvisor configured with {} sensitive words"
+                : "SafeGuardAdvisor enabled with {} sensitive words";
+        LOG.debug(logMessage, wordList.size());
+
+        return safeguardBuilder.build();
+    }
+
+    /**
+     * Apply a text-only user message to the chat request.
+     * <p>
+     * This method configures the user message part of the chat request with the provided text content. It also applies
+     * any user metadata from the exchange headers or endpoint configuration. User metadata can be used to pass
+     * additional context or information that should be associated with the user message.
+     * </p>
+     *
+     * @param request  the chat client request to configure
+     * @param exchange the Camel exchange containing headers and configuration
+     * @param text     the user message text content
+     */
+    private void applyUserMessageWithText(ChatClient.ChatClientRequestSpec request, Exchange exchange, String text) {
+        Map<String, Object> userMetadata = getUserMetadata(exchange);
+        request.user(u -> {
+            u.text(text);
+            if (userMetadata != null && !userMetadata.isEmpty()) {
+                u.metadata(userMetadata);
+            }
+        });
+    }
+
+    /**
+     * Apply a multimodal user message with media attachments to the chat request.
+     * <p>
+     * This method configures the user message part of the chat request with both text content and media attachments
+     * (such as images, audio, or documents). This enables multimodal AI interactions where the model can process and
+     * analyze visual or audio content alongside text. It also applies any user metadata from the exchange headers or
+     * endpoint configuration.
+     * </p>
+     * <p>
+     * Supported media types include:
+     * </p>
+     * <ul>
+     * <li>Images: PNG, JPEG, GIF, WebP, BMP, TIFF, SVG</li>
+     * <li>Audio: WAV, MP3, OGG, M4A, FLAC</li>
+     * <li>Documents: PDF</li>
+     * </ul>
+     *
+     * @param request  the chat client request to configure
+     * @param exchange the Camel exchange containing headers and configuration
+     * @param text     the user message text content (can be empty for media-only messages)
+     * @param media    the list of media attachments to include with the message
+     */
+    private void applyUserMessageWithMedia(
+            ChatClient.ChatClientRequestSpec request, Exchange exchange, String text, List<Media> media) {
+        Map<String, Object> userMetadata = getUserMetadata(exchange);
+        request.user(u -> {
+            u.text(text).media(media.toArray(new Media[0]));
+            if (userMetadata != null && !userMetadata.isEmpty()) {
+                u.metadata(userMetadata);
+            }
+        });
+    }
+
+    /**
+     * Execute the request with entity or structured output handling
+     */
+    private void executeRequest(ChatClient.ChatClientRequestSpec request, Exchange exchange) {
+        // Check for entity class (takes precedence over structured output converter)
+        Class<?> entityClass = getEntityClass(exchange);
+
+        // Check for structured output converter
+        StructuredOutputConverter<?> converter = getStructuredOutputConverter(exchange);
+
+        // Execute based on entity class and structured output
+        if (entityClass != null) {
+            processEntityRequest(request, exchange, entityClass);
+        } else if (converter != null) {
+            processStructuredOutputRequest(request, exchange, converter);
+        } else {
+            ChatResponse response = request.call().chatResponse();
+            populateResponse(response, exchange);
+        }
+    }
+
+    /**
+     * Build the list of default advisors based on endpoint configuration.
+     * <p>
+     * Advisors are Spring AI components that intercept and modify chat requests and responses. They enable features
+     * like logging, memory, RAG (Retrieval-Augmented Generation), content filtering, and custom processing. Advisors
+     * are executed in the order they are added to the list.
+     * </p>
+     * <p>
+     * The following advisors may be added based on configuration:
+     * </p>
+     * <ol>
+     * <li><strong>SimpleLoggerAdvisor</strong> - Always added first. Logs chat requests and responses for debugging and
+     * monitoring purposes.</li>
+     * <li><strong>SafeGuardAdvisor</strong> - Added if sensitive words are configured. Blocks requests or responses
+     * containing configured sensitive words and returns a custom failure response.</li>
+     * <li><strong>MessageChatMemoryAdvisor</strong> - Added if ChatMemory is configured. Maintains conversation history
+     * using an in-memory or persistent chat memory store, enabling contextual multi-turn conversations.</li>
+     * <li><strong>VectorStoreChatMemoryAdvisor</strong> - Added if chatMemoryVectorStore is configured (alternative to
+     * MessageChatMemoryAdvisor). Stores conversation history in a vector store with automatic conversation isolation by
+     * ID.</li>
+     * <li><strong>QuestionAnswerAdvisor</strong> - Added if VectorStore is configured. Implements RAG by automatically
+     * retrieving relevant context from the vector store and augmenting the user's question with it.</li>
+     * <li><strong>Custom Advisors</strong> - Any user-provided advisors from the endpoint configuration are added
+     * last.</li>
+     * </ol>
+     * <p>
+     * <strong>Note:</strong> If both ChatMemory and chatMemoryVectorStore are configured, only ChatMemory will be used
+     * and a warning will be logged.
+     * </p>
+     *
+     * @return the list of configured advisors to be applied to the ChatClient
+     */
+    private List<Advisor> buildDefaultAdvisors() {
+        List<Advisor> advisors = new ArrayList<>();
+
+        // Always add SimpleLoggerAdvisor first
+        advisors.add(new SimpleLoggerAdvisor());
+
+        // Add SafeGuardAdvisor if configured
+        SafeGuardAdvisor safeguardAdvisor = buildSafeguardAdvisor(
+                getEndpoint().getConfiguration().getSafeguardSensitiveWords(),
+                getEndpoint().getConfiguration().getSafeguardFailureResponse(),
+                getEndpoint().getConfiguration().getSafeguardOrder(),
+                false);
+        if (safeguardAdvisor != null) {
+            advisors.add(safeguardAdvisor);
+        }
+
+        // Add ChatMemory advisor if configured
+        ChatMemory chatMemory = getEndpoint().getConfiguration().getChatMemory();
+        VectorStore chatMemoryVectorStore = getEndpoint().getConfiguration().getChatMemoryVectorStore();
+
+        if (chatMemory != null && chatMemoryVectorStore != null) {
+            LOG.warn("Both chatMemory and chatMemoryVectorStore are configured. Using MessageChatMemoryAdvisor (chatMemory). " +
+                     "Configure only one memory type.");
+        }
+
+        if (chatMemory != null) {
+            advisors.add(MessageChatMemoryAdvisor.builder(chatMemory).build());
+            LOG.debug("MessageChatMemoryAdvisor enabled");
+        } else if (chatMemoryVectorStore != null) {
+            // Configure VectorStoreChatMemoryAdvisor with conversation isolation
+            // The conversationId parameter enables automatic filtering by conversation ID
+            advisors.add(VectorStoreChatMemoryAdvisor.builder(chatMemoryVectorStore)
+                    .conversationId(ChatMemory.CONVERSATION_ID)
+                    .defaultTopK(getEndpoint().getConfiguration().getTopK())
+                    .build());
+            LOG.debug("VectorStoreChatMemoryAdvisor enabled with conversation isolation and topK={}",
+                    getEndpoint().getConfiguration().getTopK());
+        }
+
+        // Add QuestionAnswerAdvisor if VectorStore is configured
+        VectorStore vectorStore = getEndpoint().getConfiguration().getVectorStore();
+        if (vectorStore != null) {
+            advisors.add(QuestionAnswerAdvisor.builder(vectorStore)
+                    .searchRequest(SearchRequest.builder()
+                            .topK(getEndpoint().getConfiguration().getTopK())
+                            .similarityThreshold(getEndpoint().getConfiguration().getSimilarityThreshold())
+                            .build())
+                    .build());
+            LOG.debug("QuestionAnswerAdvisor enabled with topK={}, similarityThreshold={}",
+                    getEndpoint().getConfiguration().getTopK(),
+                    getEndpoint().getConfiguration().getSimilarityThreshold());
+        }
+
+        // Add custom advisors if configured
+        List<Advisor> customAdvisors = getEndpoint().getConfiguration().getAdvisors();
+        if (customAdvisors != null && !customAdvisors.isEmpty()) {
+            advisors.addAll(customAdvisors);
+            LOG.debug("Added {} custom advisors", customAdvisors.size());
+        }
+
+        LOG.debug("Built {} total advisors", advisors.size());
+        return advisors;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/rag/SpringAiRagAggregatorStrategy.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/rag/SpringAiRagAggregatorStrategy.java
new file mode 100644
index 000000000000..0f6f05027841
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/main/java/org/apache/camel/component/springai/chat/rag/SpringAiRagAggregatorStrategy.java
@@ -0,0 +1,89 @@
+/*
+ * 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.apache.camel.component.springai.chat.rag;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.camel.AggregationStrategy;
+import org.apache.camel.Exchange;
+import org.springframework.ai.document.Document;
+
+import static org.apache.camel.component.springai.chat.SpringAiChatConstants.AUGMENTED_DATA;
+
+/**
+ * Aggregation strategy for Retrieval Augmented Generation (RAG) with Spring AI.
+ * <p>
+ * This strategy can be used with Camel's Content Enricher EIP to augment prompts with relevant context from external
+ * data sources.
+ * </p>
+ * <p>
+ * Usage example:
+ *
+ * <pre>
+ * SpringAiRagAggregatorStrategy aggregatorStrategy = new SpringAiRagAggregatorStrategy();
+ *
+ * from("direct:chat")
+ *         .enrich("direct:rag", aggregatorStrategy)
+ *         .to("spring-ai-chat:test?chatOperation=CHAT_SINGLE_MESSAGE");
+ *
+ * from("direct:rag")
+ *         .process(exchange -> {
+ *             // Retrieve relevant documents
+ *             List&lt;String&gt; documents = retrieveDocuments(exchange.getIn().getBody(String.class));
+ *             exchange.getIn().setBody(documents);
+ *         });
+ * </pre>
+ * </p>
+ */
+public class SpringAiRagAggregatorStrategy implements AggregationStrategy {
+
+    @Override
+    public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
+        if (oldExchange == null) {
+            return newExchange;
+        }
+
+        // Extract new data from enricher response
+        Optional<List> newAugmentedData = Optional.ofNullable(newExchange.getIn().getBody(List.class));
+        if (newAugmentedData.isEmpty()) {
+            return oldExchange;
+        }
+
+        // Convert to Spring AI Documents
+        List<Document> newDocuments = new ArrayList<>();
+        for (Object item : newAugmentedData.get()) {
+            if (item instanceof String) {
+                newDocuments.add(new Document((String) item));
+            } else if (item instanceof Document) {
+                newDocuments.add((Document) item);
+            } else {
+                // Try to convert to string
+                newDocuments.add(new Document(item.toString()));
+            }
+        }
+
+        // Get or create augmented data list in header
+        List<Document> augmentedData
+                = Optional.ofNullable(oldExchange.getIn().getHeader(AUGMENTED_DATA, List.class)).orElse(new ArrayList<>());
+        augmentedData.addAll(newDocuments);
+
+        oldExchange.getIn().setHeader(AUGMENTED_DATA, augmentedData);
+        return oldExchange;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/OllamaTestSupport.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/OllamaTestSupport.java
new file mode 100644
index 000000000000..a6e5bdf6ef46
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/OllamaTestSupport.java
@@ -0,0 +1,71 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.test.infra.ollama.services.OllamaService;
+import org.apache.camel.test.infra.ollama.services.OllamaServiceFactory;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.ollama.OllamaChatModel;
+import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaChatOptions;
+
+/**
+ * Base test support class for Spring AI Ollama integration tests.
+ */
+public class OllamaTestSupport extends CamelTestSupport {
+
+    protected ChatModel chatModel;
+
+    @RegisterExtension
+    static OllamaService OLLAMA = OllamaServiceFactory.createSingletonService();
+
+    @Override
+    protected void setupResources() throws Exception {
+        super.setupResources();
+
+        chatModel = createModel();
+    }
+
+    protected void bindChatModel(CamelContext camelContext) {
+        camelContext.getRegistry().bind("chatModel", chatModel);
+    }
+
+    protected String modelName() {
+        return OLLAMA.modelName();
+    }
+
+    protected ChatModel createModel() {
+        OllamaApi ollamaApi = OllamaApi.builder()
+                .baseUrl(OLLAMA.baseUrl())
+                .build();
+
+        OllamaChatOptions ollamaOptions = OllamaChatOptions.builder()
+                .model(modelName())
+                .temperature(0.3)
+                .build();
+
+        ChatModel chatModel = OllamaChatModel.builder()
+                .ollamaApi(ollamaApi)
+                .defaultOptions(ollamaOptions)
+                .build();
+
+        return chatModel;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatComponentTest.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatComponentTest.java
new file mode 100644
index 000000000000..78dccedc4d98
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatComponentTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.chat.messages.AssistantMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.ai.chat.model.Generation;
+import org.springframework.ai.chat.prompt.Prompt;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SpringAiChatComponentTest extends CamelTestSupport {
+
+    private ChatModel mockChatModel;
+
+    @Override
+    protected void doPreSetup() throws Exception {
+        super.doPreSetup();
+
+        // Create a mock ChatModel
+        mockChatModel = mock(ChatModel.class);
+
+        // Mock response
+        AssistantMessage assistantMessage = new AssistantMessage("Hello! I'm a mock AI assistant.");
+        Generation generation = new Generation(assistantMessage);
+        ChatResponse chatResponse = new ChatResponse(java.util.List.of(generation));
+
+        when(mockChatModel.call(any(Prompt.class))).thenReturn(chatResponse);
+    }
+
+    @Test
+    public void testChatComponent() throws Exception {
+        String response = template.requestBody("direct:chat", "Hello, AI!", String.class);
+
+        assertNotNull(response);
+        assertEquals("Hello! I'm a mock AI assistant.", response);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                SpringAiChatComponent component = new SpringAiChatComponent();
+                component.setChatModel(mockChatModel);
+                context.addComponent("spring-ai-chat", component);
+
+                from("direct:chat")
+                        .to("spring-ai-chat:test");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatFileSizeIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatFileSizeIT.java
new file mode 100644
index 000000000000..a08024453d38
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatFileSizeIT.java
@@ -0,0 +1,164 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * Integration test for file size validation functionality.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatFileSizeIT extends OllamaTestSupport {
+
+    private String getTestResourcePath(String resource) {
+        URL url = getClass().getClassLoader().getResource(resource);
+        if (url == null) {
+            throw new RuntimeException("Resource not found: " + resource);
+        }
+        return url.getPath();
+    }
+
+    @Test
+    public void testFileSizeWithinLimit() throws IOException {
+        // test-image.png is 74 bytes - well within the 1MB default limit
+        String testFilePath = getTestResourcePath("test-files/test-image.png");
+        byte[] imageData = Files.readAllBytes(Paths.get(testFilePath));
+        assertThat(imageData.length).isLessThan(1024 * 1024); // Less than 1MB
+
+        var exchange = template().request("direct:file-size-limit", e -> {
+            e.getIn().setBody(imageData);
+            e.getIn().setHeader(SpringAiChatConstants.USER_MESSAGE, "What color is this image?");
+            e.getIn().setHeader(SpringAiChatConstants.MEDIA_TYPE, "image/png");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testFileSizeExceedsConfiguredLimit() throws IOException {
+        // large-file.bin is 150 bytes - exceeds 100 byte limit configured on endpoint
+        String testFilesPath = getTestResourcePath("test-files");
+
+        assertThatThrownBy(() -> {
+            template().requestBody("direct:file-size-small-limit", null, String.class);
+        }).isInstanceOf(CamelExecutionException.class)
+                .cause()
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("File size")
+                .hasMessageContaining("exceeds maximum allowed size");
+    }
+
+    @Test
+    public void testFileSizeHeaderOverridesConfiguration() throws IOException {
+        // medium-file.bin is 50 bytes - exceeds endpoint config (40 bytes) but within header override (60 bytes)
+        String testFilesPath = getTestResourcePath("test-files");
+
+        var exchange = template().request("direct:file-size-header-override", e -> {
+            e.getIn().setHeader(SpringAiChatConstants.USER_MESSAGE, "Process this file.");
+            // Override the endpoint's 40 byte limit with a 60 byte limit via header
+            e.getIn().setHeader(SpringAiChatConstants.MAX_FILE_SIZE, 60L);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testFileSizeZeroMeansNoLimit() throws IOException {
+        // large-file.bin is 150 bytes - would exceed normal limits but maxFileSize=0 disables checking
+        String testFilesPath = getTestResourcePath("test-files");
+
+        var exchange = template().request("direct:file-size-no-limit", e -> {
+            e.getIn().setHeader(SpringAiChatConstants.USER_MESSAGE, "Describe this file.");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testFileSizeValidationWithWrappedFile() throws IOException {
+        // large-file.bin is 150 bytes - exceeds 100 byte limit
+        String testFilesPath = getTestResourcePath("test-files");
+
+        assertThatThrownBy(() -> {
+            template().requestBody("direct:wrapped-file-size-limit", null, String.class);
+        }).isInstanceOf(CamelExecutionException.class)
+                .cause()
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("File size")
+                .hasMessageContaining("exceeds maximum allowed size");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                String testFilesPath = getTestResourcePath("test-files");
+
+                // Route 1: Default limit (1MB from configuration defaults)
+                // Uses small test-image.png (74 bytes)
+                from("direct:file-size-limit")
+                        .to("spring-ai-chat:default?chatModel=#chatModel");
+
+                // Route 2: Small limit (100 bytes) configured on endpoint
+                // Uses large-file.bin (150 bytes) - should fail
+                from("direct:file-size-small-limit")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=large-file.bin&noop=true&idempotent=false")
+                        .setHeader(SpringAiChatConstants.USER_MESSAGE, constant("What is in this file?"))
+                        .to("spring-ai-chat:small?chatModel=#chatModel&maxFileSize=100");
+
+                // Route 3: Header override test
+                // Uses medium-file.bin (50 bytes) - exceeds endpoint config (40 bytes) but header allows it (60 bytes)
+                from("direct:file-size-header-override")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=medium-file.bin&noop=true&idempotent=false")
+                        .to("spring-ai-chat:small?chatModel=#chatModel&maxFileSize=40");
+
+                // Route 4: No limit (maxFileSize=0)
+                // Uses large-file.bin (150 bytes) - should succeed because limit is disabled
+                from("direct:file-size-no-limit")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=large-file.bin&noop=true&idempotent=false")
+                        .to("spring-ai-chat:nolimit?chatModel=#chatModel&maxFileSize=0");
+
+                // Route 5: WrappedFile with size limit
+                // Uses large-file.bin (150 bytes) - should fail with 100 byte limit
+                from("direct:wrapped-file-size-limit")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=large-file.bin&noop=true&idempotent=false")
+                        .setHeader(SpringAiChatConstants.USER_MESSAGE, constant("What is in this file?"))
+                        .to("spring-ai-chat:wrapped?chatModel=#chatModel&maxFileSize=100");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMemoryIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMemoryIT.java
new file mode 100644
index 000000000000..154a0d25b6da
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMemoryIT.java
@@ -0,0 +1,246 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
+import org.springframework.ai.chat.memory.MessageWindowChatMemory;
+import org.springframework.ai.chat.messages.AssistantMessage;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.UserMessage;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test demonstrating how to use Spring AI's ChatMemory with Camel.
+ *
+ * This test shows how to configure and use MessageWindowChatMemory (InMemoryChatMemory) to maintain conversation
+ * history across multiple interactions.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatMemoryIT extends OllamaTestSupport {
+
+    private static final String CONVERSATION_ID = "user-123";
+
+    @Test
+    public void testChatMemoryRetainsConversationHistory() {
+        // Create an in-memory chat memory
+        ChatMemory chatMemory = createChatMemory();
+
+        // First interaction - tell the assistant something
+        List<Message> messages1 = new ArrayList<>();
+        messages1.add(new UserMessage("My favorite color is blue."));
+
+        String response1 = template().requestBody("direct:chat", messages1, String.class);
+        assertThat(response1).isNotNull();
+
+        // Store the conversation in memory
+        chatMemory.add(CONVERSATION_ID, messages1.get(0));
+        chatMemory.add(CONVERSATION_ID, new AssistantMessage(response1));
+
+        // Second interaction - ask about what we told it before
+        List<Message> messages2 = new ArrayList<>();
+        // Retrieve conversation history
+        List<Message> history = chatMemory.get(CONVERSATION_ID); // Get messages
+        messages2.addAll(history);
+        messages2.add(new UserMessage("What did I say my favorite color was? Answer in one word."));
+
+        String response2 = template().requestBody("direct:chat", messages2, String.class);
+        assertThat(response2).isNotNull();
+        assertThat(response2.toLowerCase()).contains("blue");
+
+        // Store this interaction too
+        chatMemory.add(CONVERSATION_ID, messages2.get(messages2.size() - 1));
+        chatMemory.add(CONVERSATION_ID, new AssistantMessage(response2));
+    }
+
+    private static @NotNull ChatMemory createChatMemory() {
+        InMemoryChatMemoryRepository inMemoryChatMemoryRepository = new InMemoryChatMemoryRepository();
+
+        ChatMemory chatMemory = MessageWindowChatMemory.builder()
+                .chatMemoryRepository(inMemoryChatMemoryRepository)
+                .maxMessages(10)
+                .build();
+        return chatMemory;
+    }
+
+    @Test
+    public void testMultipleConversationIds() {
+        ChatMemory chatMemory = createChatMemory();
+
+        // User 1 conversation
+        chatMemory.add("user-1", new UserMessage("I like pizza"));
+        chatMemory.add("user-1", new AssistantMessage("Pizza is delicious!"));
+
+        // User 2 conversation
+        chatMemory.add("user-2", new UserMessage("I like sushi"));
+        chatMemory.add("user-2", new AssistantMessage("Sushi is great!"));
+
+        // Verify conversations are isolated
+        List<Message> user1History = chatMemory.get("user-1");
+        List<Message> user2History = chatMemory.get("user-2");
+
+        assertThat(user1History).hasSize(2);
+        assertThat(user1History.get(0).getText()).contains("pizza");
+
+        assertThat(user2History).hasSize(2);
+        assertThat(user2History.get(0).getText()).contains("sushi");
+    }
+
+    @Test
+    public void testChatMemoryWithCamelProcessor() {
+        ChatMemory chatMemory = createChatMemory();
+        String conversationId = "conversation-with-processor";
+
+        // First message
+        var exchange1 = template().request("direct:chat-with-memory", e -> {
+            e.getIn().setBody("My name is Alice.");
+            e.getIn().setHeader("conversationId", conversationId);
+            e.getIn().setHeader("chatMemory", chatMemory);
+        });
+
+        String response1 = exchange1.getMessage().getBody(String.class);
+        assertThat(response1).isNotNull();
+
+        // Second message - should remember the name
+        var exchange2 = template().request("direct:chat-with-memory", e -> {
+            e.getIn().setBody("What is my name? Answer in one word.");
+            e.getIn().setHeader("conversationId", conversationId);
+            e.getIn().setHeader("chatMemory", chatMemory);
+        });
+
+        String response2 = exchange2.getMessage().getBody(String.class);
+        assertThat(response2).isNotNull();
+        assertThat(response2.toLowerCase()).contains("alice");
+    }
+
+    @Test
+    public void testClearingChatMemory() {
+        ChatMemory chatMemory = createChatMemory();
+
+        // Add some messages
+        chatMemory.add(CONVERSATION_ID, new UserMessage("Remember this"));
+        chatMemory.add(CONVERSATION_ID, new AssistantMessage("I will remember"));
+
+        // Verify messages exist
+        List<Message> before = chatMemory.get(CONVERSATION_ID);
+        assertThat(before).hasSize(2);
+
+        // Clear the conversation
+        chatMemory.clear(CONVERSATION_ID);
+
+        // Verify conversation is cleared
+        List<Message> after = chatMemory.get(CONVERSATION_ID);
+        assertThat(after).isEmpty();
+    }
+
+    @Test
+    public void testAutomaticChatMemoryWithChatClient() {
+        String conversationId = "auto-memory-test";
+
+        // Note: This test demonstrates ChatMemory with ChatClient
+        // The MessageChatMemoryAdvisor stores conversation in memory automatically
+
+        // First message
+        var exchange1 = template().request("direct:chat-with-auto-memory", e -> {
+            e.getIn().setBody("My favorite number is 42. Please remember this.");
+            e.getIn().setHeader("conversationId", conversationId);
+        });
+
+        String response1 = exchange1.getMessage().getBody(String.class);
+        assertThat(response1).isNotNull();
+
+        // Second message - the advisor should remember the context
+        var exchange2 = template().request("direct:chat-with-auto-memory", e -> {
+            e.getIn().setBody("What is my favorite number? Answer with just the number.");
+            e.getIn().setHeader("conversationId", conversationId);
+        });
+
+        String response2 = exchange2.getMessage().getBody(String.class);
+        assertThat(response2).isNotNull();
+
+        // The MessageChatMemoryAdvisor should maintain context
+        // If this fails, it means the advisor is not storing messages properly
+        assertThat(response2).contains("42");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                // Create and register ChatMemory for automatic memory management
+                ChatMemory chatMemory = createChatMemory();
+                this.getCamelContext().getRegistry().bind("chatMemory", chatMemory);
+
+                // Simple route for multiple messages
+                from("direct:chat")
+                        .to("spring-ai-chat:memory?chatModel=#chatModel&chatOperation=CHAT_MULTIPLE_MESSAGES");
+
+                // Route with automatic memory management via ChatClient
+                from("direct:chat-with-auto-memory")
+                        .to("spring-ai-chat:auto-memory?chatModel=#chatModel&chatMemory=#chatMemory");
+
+                // Route with memory management in processor (manual approach)
+                from("direct:chat-with-memory")
+                        .process(exchange -> {
+                            ChatMemory memory = exchange.getIn().getHeader("chatMemory", ChatMemory.class);
+                            String convId = exchange.getIn().getHeader("conversationId", String.class);
+                            String userMessage = exchange.getIn().getBody(String.class);
+
+                            // Retrieve conversation history
+                            List<Message> messages = new ArrayList<>();
+                            if (memory != null && convId != null) {
+                                List<Message> history = memory.get(convId);
+                                messages.addAll(history);
+                            }
+
+                            // Add current user message
+                            UserMessage currentMessage = new UserMessage(userMessage);
+                            messages.add(currentMessage);
+
+                            // Store user message in memory
+                            if (memory != null && convId != null) {
+                                memory.add(convId, currentMessage);
+                            }
+
+                            exchange.getIn().setBody(messages);
+                        })
+                        .to("spring-ai-chat:memory?chatModel=#chatModel&chatOperation=CHAT_MULTIPLE_MESSAGES")
+                        .process(exchange -> {
+                            // Store assistant response in memory
+                            ChatMemory memory = exchange.getIn().getHeader("chatMemory", ChatMemory.class);
+                            String convId = exchange.getIn().getHeader("conversationId", String.class);
+                            String response = exchange.getIn().getBody(String.class);
+
+                            if (memory != null && convId != null) {
+                                memory.add(convId, new AssistantMessage(response));
+                            }
+                        });
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMetadataIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMetadataIT.java
new file mode 100644
index 000000000000..c379344b9cb9
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMetadataIT.java
@@ -0,0 +1,287 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for message metadata functionality.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatMetadataIT extends OllamaTestSupport {
+
+    @Test
+    public void testUserMetadataViaHeader() {
+        Map<String, Object> userMetadata = new HashMap<>();
+        userMetadata.put("messageId", "msg-123");
+        userMetadata.put("userId", "user-456");
+        userMetadata.put("priority", "high");
+
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody("What is the capital of France?");
+            e.getIn().setHeader(SpringAiChatConstants.USER_METADATA, userMetadata);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).contains("paris");
+    }
+
+    @Test
+    public void testSystemMetadataViaHeader() {
+        Map<String, Object> systemMetadata = new HashMap<>();
+        systemMetadata.put("version", "1.0");
+        systemMetadata.put("promptVersion", "2024-01");
+
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody("Tell me one exercise.");
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_MESSAGE,
+                    "You are a fitness coach. Recommend one physical activity.");
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_METADATA, systemMetadata);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testBothUserAndSystemMetadata() {
+        Map<String, Object> userMetadata = new HashMap<>();
+        userMetadata.put("messageId", "msg-789");
+        userMetadata.put("customerId", "cust-123");
+
+        Map<String, Object> systemMetadata = new HashMap<>();
+        systemMetadata.put("model", "test-model");
+        systemMetadata.put("config", "standard");
+
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody("What is 2 + 2?");
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_MESSAGE,
+                    "You are a math tutor. Provide clear, concise answers.");
+            e.getIn().setHeader(SpringAiChatConstants.USER_METADATA, userMetadata);
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_METADATA, systemMetadata);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).contains("4");
+    }
+
+    @Test
+    public void testUserMetadataViaEndpointConfiguration() {
+        // Metadata is bound in the route builder
+        String response = template().requestBody("direct:chat-with-metadata",
+                "What is the capital of Italy?", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).contains("rome");
+    }
+
+    @Test
+    public void testMetadataWithPromptTemplate() {
+        Map<String, Object> userMetadata = new HashMap<>();
+        userMetadata.put("requestId", "req-456");
+        userMetadata.put("source", "web");
+
+        String template = "What is the capital of {country}?";
+        Map<String, Object> variables = new HashMap<>();
+        variables.put("country", "Germany");
+
+        var exchange = template().request("direct:chat-prompt", e -> {
+            e.getIn().setBody(variables);
+            e.getIn().setHeader(SpringAiChatConstants.PROMPT_TEMPLATE, template);
+            e.getIn().setHeader(SpringAiChatConstants.USER_METADATA, userMetadata);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).contains("berlin");
+    }
+
+    @Test
+    public void testMetadataOverridesBetweenHeaderAndConfig() {
+        // Config has default metadata, header should override
+        Map<String, Object> headerMetadata = new HashMap<>();
+        headerMetadata.put("messageId", "override-123");
+        headerMetadata.put("source", "header");
+
+        var exchange = template().request("direct:chat-with-metadata", e -> {
+            e.getIn().setBody("What is 10 + 5?");
+            e.getIn().setHeader(SpringAiChatConstants.USER_METADATA, headerMetadata);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).contains("15");
+    }
+
+    @Test
+    public void testMetadataViaUrlParameters() {
+        // Test using URL parameter syntax: userMetadata.key=value
+        // This demonstrates Camel's nested property binding for Map types
+        var exchange = template().request("direct:chat-url-params", e -> {
+            e.getIn().setBody("What is the capital of Spain?");
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_MESSAGE,
+                    "You are a geography expert. Provide short, accurate answers.");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).contains("madrid");
+    }
+
+    @Test
+    public void testMetadataUrlParametersWithoutSystemMessage() {
+        // Test metadata via URL parameters without system message
+        // Only userMetadata should be applied
+        String response = template().requestBody("direct:chat-url-params",
+                "What is 5 * 5?", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response).contains("25");
+    }
+
+    @Test
+    public void testResponseMetadataHeaders() {
+        // Test that response metadata headers are populated
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody("What is the capital of France?");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+
+        // Check that response metadata headers are set
+        // Note: Not all models provide all metadata fields
+        Object finishReason = exchange.getMessage().getHeader(SpringAiChatConstants.FINISH_REASON);
+        Object modelName = exchange.getMessage().getHeader(SpringAiChatConstants.MODEL_NAME);
+        Object responseId = exchange.getMessage().getHeader(SpringAiChatConstants.RESPONSE_ID);
+        Object responseMetadata = exchange.getMessage().getHeader(SpringAiChatConstants.RESPONSE_METADATA);
+
+        // At minimum, we should have some metadata
+        // Most models provide finish reason
+        assertThat(finishReason != null || modelName != null || responseId != null || responseMetadata != null)
+                .as("At least one metadata field should be present")
+                .isTrue();
+
+        // Token usage should also be set
+        Integer inputTokens = exchange.getMessage().getHeader(SpringAiChatConstants.INPUT_TOKEN_COUNT, Integer.class);
+        Integer outputTokens = exchange.getMessage().getHeader(SpringAiChatConstants.OUTPUT_TOKEN_COUNT, Integer.class);
+        Integer totalTokens = exchange.getMessage().getHeader(SpringAiChatConstants.TOTAL_TOKEN_COUNT, Integer.class);
+
+        // Most models provide token counts
+        if (inputTokens != null) {
+            assertThat(inputTokens).isGreaterThan(0);
+        }
+        if (outputTokens != null) {
+            assertThat(outputTokens).isGreaterThan(0);
+        }
+        if (totalTokens != null) {
+            assertThat(totalTokens).isGreaterThan(0);
+        }
+    }
+
+    @Test
+    public void testResponseMetadataMap() {
+        // Test that the full response metadata map is populated correctly
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody("Tell me a fun fact.");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> metadata
+                = exchange.getMessage().getHeader(SpringAiChatConstants.RESPONSE_METADATA, Map.class);
+
+        if (metadata != null) {
+            assertThat(metadata).isNotEmpty();
+
+            // If finish reason is present in individual header, it should also be in map
+            Object finishReason = exchange.getMessage().getHeader(SpringAiChatConstants.FINISH_REASON);
+            if (finishReason != null) {
+                assertThat(metadata).containsKey("finishReason");
+                assertThat(metadata.get("finishReason")).isEqualTo(finishReason);
+            }
+
+            // If model name is present in individual header, it should also be in map
+            Object modelName = exchange.getMessage().getHeader(SpringAiChatConstants.MODEL_NAME);
+            if (modelName != null) {
+                assertThat(metadata).containsKey("model");
+                assertThat(metadata.get("model")).isEqualTo(modelName);
+            }
+
+            // If response ID is present in individual header, it should also be in map
+            Object responseId = exchange.getMessage().getHeader(SpringAiChatConstants.RESPONSE_ID);
+            if (responseId != null) {
+                assertThat(metadata).containsKey("id");
+                assertThat(metadata.get("id")).isEqualTo(responseId);
+            }
+        }
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                // Create default metadata maps
+                Map<String, Object> defaultUserMetadata = new HashMap<>();
+                defaultUserMetadata.put("application", "test-app");
+                defaultUserMetadata.put("environment", "test");
+
+                Map<String, Object> defaultSystemMetadata = new HashMap<>();
+                defaultSystemMetadata.put("version", "1.0");
+                defaultSystemMetadata.put("model", "default");
+
+                getCamelContext().getRegistry().bind("defaultUserMetadata", defaultUserMetadata);
+                getCamelContext().getRegistry().bind("defaultSystemMetadata", defaultSystemMetadata);
+
+                from("direct:chat")
+                        .to("spring-ai-chat:test?chatModel=#chatModel");
+
+                from("direct:chat-with-metadata")
+                        .to("spring-ai-chat:metadata?chatModel=#chatModel"
+                            + "&userMetadata=#defaultUserMetadata"
+                            + "&systemMetadata=#defaultSystemMetadata");
+
+                from("direct:chat-prompt")
+                        .to("spring-ai-chat:prompt?chatModel=#chatModel"
+                            + "&chatOperation=CHAT_SINGLE_MESSAGE_WITH_PROMPT");
+
+                // Route demonstrating URL parameter syntax for metadata
+                // This shows how to set metadata using nested properties in the endpoint URI
+                from("direct:chat-url-params")
+                        .to("spring-ai-chat:urlparams?chatModel=#chatModel"
+                            + "&userMetadata.test=t1"
+                            + "&userMetadata.test2=t2"
+                            + "&systemMetadata.test3=r3");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMultimodalIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMultimodalIT.java
new file mode 100644
index 000000000000..f1cd551d8d6c
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMultimodalIT.java
@@ -0,0 +1,207 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.content.Media;
+import org.springframework.util.MimeTypeUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for multimodal capabilities (image and audio input).
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatMultimodalIT extends OllamaTestSupport {
+
+    @Test
+    public void testImageInputWithByteArray() throws IOException {
+        // Create a simple test image (red square)
+        byte[] imageData = createTestImage(Color.RED, 100, 100);
+
+        var exchange = template().request("direct:multimodal", e -> {
+            e.getIn().setBody(imageData);
+            e.getIn().setHeader(SpringAiChatConstants.USER_MESSAGE, "What color is this image?");
+            e.getIn().setHeader(SpringAiChatConstants.MEDIA_TYPE, "image/png");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+        // Note: Response quality depends on model's multimodal capabilities
+    }
+
+    @Test
+    public void testImageInputWithConfiguredUserMessage() throws IOException {
+        byte[] imageData = createTestImage(Color.BLUE, 150, 150);
+
+        var exchange = template().request("direct:multimodal-configured", e -> {
+            e.getIn().setBody(imageData);
+            e.getIn().setHeader(SpringAiChatConstants.MEDIA_TYPE, "image/png");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testImageInputWithSystemMessage() throws IOException {
+        byte[] imageData = createTestImage(Color.GREEN, 200, 200);
+
+        var exchange = template().request("direct:multimodal-system", e -> {
+            e.getIn().setBody(imageData);
+            e.getIn().setHeader(SpringAiChatConstants.USER_MESSAGE, "Describe this image.");
+            e.getIn().setHeader(SpringAiChatConstants.MEDIA_TYPE, "image/png");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testMultimodalWithUserMessageObject() throws IOException {
+        // Create a UserMessage directly with media
+        byte[] imageData = createTestImage(Color.YELLOW, 100, 100);
+        Media media = Media.builder()
+                .mimeType(MimeTypeUtils.IMAGE_PNG)
+                .data(imageData)
+                .build();
+
+        UserMessage userMessage = UserMessage.builder()
+                .text("What is in this image?")
+                .media(media)
+                .build();
+
+        String response = template().requestBody("direct:multimodal", userMessage, String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testMultimodalDefaultImageType() throws IOException {
+        // Test without specifying media type (should default to image/png)
+        byte[] imageData = createTestImage(Color.MAGENTA, 120, 120);
+
+        var exchange = template().request("direct:multimodal", e -> {
+            e.getIn().setBody(imageData);
+            e.getIn().setHeader(SpringAiChatConstants.USER_MESSAGE, "Analyze this image.");
+            // No MEDIA_TYPE header - should default to image/png
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testMultimodalWithTokenTracking() throws IOException {
+        byte[] imageData = createTestImage(Color.CYAN, 100, 100);
+
+        var exchange = template().request("direct:multimodal", e -> {
+            e.getIn().setBody(imageData);
+            e.getIn().setHeader(SpringAiChatConstants.USER_MESSAGE, "Describe this.");
+            e.getIn().setHeader(SpringAiChatConstants.MEDIA_TYPE, "image/png");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+
+        // Verify token usage is tracked for multimodal requests
+        Integer totalTokens = exchange.getMessage().getHeader(SpringAiChatConstants.TOTAL_TOKEN_COUNT, Integer.class);
+        // Note: Some models may not report token counts for multimodal requests
+        if (totalTokens != null) {
+            assertThat(totalTokens).isGreaterThan(0);
+        }
+    }
+
+    @Test
+    public void testMultimodalWithJpegImage() throws IOException {
+        byte[] imageData = createTestImageJpeg(Color.ORANGE, 150, 150);
+
+        var exchange = template().request("direct:multimodal", e -> {
+            e.getIn().setBody(imageData);
+            e.getIn().setHeader(SpringAiChatConstants.USER_MESSAGE, "What do you see?");
+            e.getIn().setHeader(SpringAiChatConstants.MEDIA_TYPE, "image/jpeg");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    /**
+     * Helper method to create a test image as PNG
+     */
+    private byte[] createTestImage(Color color, int width, int height) throws IOException {
+        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        Graphics2D graphics = image.createGraphics();
+        graphics.setColor(color);
+        graphics.fillRect(0, 0, width, height);
+        graphics.dispose();
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ImageIO.write(image, "png", baos);
+        return baos.toByteArray();
+    }
+
+    /**
+     * Helper method to create a test image as JPEG
+     */
+    private byte[] createTestImageJpeg(Color color, int width, int height) throws IOException {
+        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        Graphics2D graphics = image.createGraphics();
+        graphics.setColor(color);
+        graphics.fillRect(0, 0, width, height);
+        graphics.dispose();
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ImageIO.write(image, "jpg", baos);
+        return baos.toByteArray();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                from("direct:multimodal")
+                        .to("spring-ai-chat:multimodal?chatModel=#chatModel");
+
+                from("direct:multimodal-configured")
+                        .to("spring-ai-chat:multimodal-config?chatModel=#chatModel&userMessage=Describe what you see in this image.");
+
+                from("direct:multimodal-system")
+                        .to("spring-ai-chat:multimodal-sys?chatModel=#chatModel&systemMessage=You are an expert image analyst. Provide detailed descriptions.");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMultipleMessagesIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMultipleMessagesIT.java
new file mode 100644
index 000000000000..e0570b332061
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatMultipleMessagesIT.java
@@ -0,0 +1,101 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.springframework.ai.chat.messages.AssistantMessage;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for Spring AI Chat multiple messages (conversation history) operation.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatMultipleMessagesIT extends OllamaTestSupport {
+
+    @Test
+    public void testConversationHistoryWithContext() {
+        List<Message> conversation = new ArrayList<>();
+        conversation.add(new SystemMessage("You are a helpful assistant that answers questions about geography."));
+        conversation.add(new UserMessage("What is the capital of Italy?"));
+        conversation.add(new AssistantMessage("Rome"));
+        conversation.add(new UserMessage("What is its population? Answer with just the approximate number in millions."));
+
+        String response = template().requestBody("direct:conversation", conversation, String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+        // The assistant should understand "its" refers to Rome from context
+        assertThat(response).containsAnyOf("2", "3", "4", "million");
+    }
+
+    @Test
+    public void testMultipleMessagesWithTokenTracking() {
+        List<Message> messages = new ArrayList<>();
+        messages.add(new UserMessage("Say 'hello world' in one word"));
+
+        var exchange = template().request("direct:conversation", e -> {
+            e.getIn().setBody(messages);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+
+        // Verify token usage
+        Integer inputTokens = exchange.getMessage().getHeader(SpringAiChatConstants.INPUT_TOKEN_COUNT, Integer.class);
+        Integer outputTokens = exchange.getMessage().getHeader(SpringAiChatConstants.OUTPUT_TOKEN_COUNT, Integer.class);
+        Integer totalTokens = exchange.getMessage().getHeader(SpringAiChatConstants.TOTAL_TOKEN_COUNT, Integer.class);
+
+        assertThat(inputTokens).isNotNull().isGreaterThan(0);
+        assertThat(outputTokens).isNotNull().isGreaterThan(0);
+        assertThat(totalTokens).isEqualTo(inputTokens + outputTokens);
+    }
+
+    @Test
+    public void testMultiTurnConversation() {
+        List<Message> conversation = new ArrayList<>();
+        conversation.add(new UserMessage("My favorite color is blue."));
+        conversation.add(new AssistantMessage("That's nice! Blue is a calming color."));
+        conversation.add(new UserMessage("What did I say my favorite color was? Answer in one word."));
+
+        String response = template().requestBody("direct:conversation", conversation, String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).contains("blue");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                from("direct:conversation")
+                        .to("spring-ai-chat:conversation?chatModel=#chatModel&chatOperation=CHAT_MULTIPLE_MESSAGES");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatOllamaIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatOllamaIT.java
new file mode 100644
index 000000000000..920c3b2397ea
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatOllamaIT.java
@@ -0,0 +1,92 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for Spring AI Chat component using Ollama.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatOllamaIT extends OllamaTestSupport {
+
+    @Test
+    public void testSimpleChatWithOllama() {
+        String response
+                = template.requestBody("direct:chat", "What is the capital of Italy? Answer in one word.", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+        assertThat(response.toLowerCase()).contains("rome")
+                .as("Expected to contain rome but was " + response.toLowerCase());
+    }
+
+    @Test
+    public void testChatWithTokenUsageHeaders() {
+        var exchange = template.request("direct:chat", e -> {
+            e.getIn().setBody("Say 'hello' in one word");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+
+        // Verify token usage headers are set
+        Integer inputTokens = exchange.getMessage().getHeader(SpringAiChatConstants.INPUT_TOKEN_COUNT, Integer.class);
+        Integer outputTokens = exchange.getMessage().getHeader(SpringAiChatConstants.OUTPUT_TOKEN_COUNT, Integer.class);
+        Integer totalTokens = exchange.getMessage().getHeader(SpringAiChatConstants.TOTAL_TOKEN_COUNT, Integer.class);
+
+        assertThat(inputTokens).isNotNull();
+        assertThat(outputTokens).isNotNull();
+        assertThat(totalTokens).isNotNull();
+        assertThat(totalTokens).isEqualTo(inputTokens + outputTokens);
+    }
+
+    @Test
+    public void testMultipleChatRequests() {
+        String response1 = template.requestBody("direct:chat", "What is 2+2? Answer with just the number.", String.class);
+        String response2
+                = template.requestBody("direct:chat", "What is the color of the sky? Answer in one word.", String.class);
+
+        assertThat(response1).isNotNull();
+        assertThat(response1).contains("4")
+                .as("Expected to contain 4 " + response1);
+
+        assertThat(response2).isNotNull();
+        assertThat(response2.toLowerCase()).containsAnyOf("blue", "azure")
+                .as("Expected to contain any of blue or azure, but was " + response2);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                SpringAiChatComponent component = new SpringAiChatComponent();
+                component.setChatModel(chatModel);
+                context.addComponent("spring-ai-chat", component);
+
+                from("direct:chat")
+                        .to("spring-ai-chat:test");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatParameterOverrideIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatParameterOverrideIT.java
new file mode 100644
index 000000000000..8ef46256bd4e
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatParameterOverrideIT.java
@@ -0,0 +1,127 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for parameter override via headers.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatParameterOverrideIT extends OllamaTestSupport {
+
+    @Test
+    public void testTemperatureOverrideViaHeader() {
+        // Higher temperature should produce more varied responses
+        var exchange = template().request("direct:chat-with-config", e -> {
+            e.getIn().setBody("Say a random number between 1 and 100.");
+            e.getIn().setHeader(SpringAiChatConstants.TEMPERATURE, 1.5);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testMaxTokensOverrideViaHeader() {
+        var exchange = template().request("direct:chat-with-config", e -> {
+            e.getIn().setBody("Write a story about a cat.");
+            e.getIn().setHeader(SpringAiChatConstants.MAX_TOKENS, 50);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+
+        // Verify the response is limited by tokens
+        Integer outputTokens = exchange.getMessage().getHeader(SpringAiChatConstants.OUTPUT_TOKEN_COUNT, Integer.class);
+        assertThat(outputTokens).isNotNull();
+        // Output should be around or less than max tokens
+        assertThat(outputTokens).isLessThanOrEqualTo(60); // Allow some buffer
+    }
+
+    @Test
+    public void testTopPOverrideViaHeader() {
+        var exchange = template().request("direct:chat-with-config", e -> {
+            e.getIn().setBody("Complete this: The sky is");
+            e.getIn().setHeader(SpringAiChatConstants.TOP_P, 0.1);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        // With low top_p, should get more deterministic responses
+        assertThat(response.toLowerCase()).containsAnyOf("blue", "above", "high");
+    }
+
+    @Test
+    public void testMultipleParameterOverrides() {
+        var exchange = template().request("direct:chat-with-config", e -> {
+            e.getIn().setBody("What is 2+2? Answer with just the number.");
+            e.getIn().setHeader(SpringAiChatConstants.TEMPERATURE, 0.1);
+            e.getIn().setHeader(SpringAiChatConstants.MAX_TOKENS, 10);
+            e.getIn().setHeader(SpringAiChatConstants.TOP_P, 0.9);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).contains("4");
+    }
+
+    @Test
+    public void testHeadersOverrideEndpointConfiguration() {
+        // Endpoint has temperature=0.3, we override with header
+        var exchange = template().request("direct:chat-with-config", e -> {
+            e.getIn().setBody("Say hello in one word.");
+            e.getIn().setHeader(SpringAiChatConstants.TEMPERATURE, 0.9);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+
+        // Verify token tracking still works with parameter overrides
+        Integer totalTokens = exchange.getMessage().getHeader(SpringAiChatConstants.TOTAL_TOKEN_COUNT, Integer.class);
+        assertThat(totalTokens).isNotNull().isGreaterThan(0);
+    }
+
+    @Test
+    public void testUsingEndpointConfigurationWithoutHeaders() {
+        // This should use the endpoint's configured temperature=0.3
+        String response = template().requestBody("direct:chat-with-config",
+                "What is the capital of France? Answer in one word.", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).contains("paris");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                from("direct:chat-with-config")
+                        .to("spring-ai-chat:configured?chatModel=#chatModel&temperature=0.3&maxTokens=500");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatPromptTemplateIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatPromptTemplateIT.java
new file mode 100644
index 000000000000..c482505a5032
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatPromptTemplateIT.java
@@ -0,0 +1,85 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for Spring AI Chat prompt template operation.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatPromptTemplateIT extends OllamaTestSupport {
+
+    @Test
+    public void testPromptTemplateWithVariables() {
+        String template = "What is the capital of {country}? Answer in one word.";
+        Map<String, Object> variables = new HashMap<>();
+        variables.put("country", "France");
+
+        var exchange = template().request("direct:prompt", e -> {
+            e.getIn().setBody(variables);
+            e.getIn().setHeader(SpringAiChatConstants.PROMPT_TEMPLATE, template);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+        assertThat(response.toLowerCase()).contains("paris");
+    }
+
+    @Test
+    public void testPromptTemplateWithMultipleVariables() {
+        String template = "Write a {length} sentence about {subject} in {language}.";
+        Map<String, Object> variables = new HashMap<>();
+        variables.put("length", "short");
+        variables.put("subject", "technology");
+        variables.put("language", "English");
+
+        var exchange = template().request("direct:prompt", e -> {
+            e.getIn().setBody(variables);
+            e.getIn().setHeader(SpringAiChatConstants.PROMPT_TEMPLATE, template);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+
+        // Verify token usage is tracked
+        Integer totalTokens = exchange.getMessage().getHeader(SpringAiChatConstants.TOTAL_TOKEN_COUNT, Integer.class);
+        assertThat(totalTokens).isNotNull().isGreaterThan(0);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                from("direct:prompt")
+                        .to("spring-ai-chat:prompt?chatModel=#chatModel&chatOperation=CHAT_SINGLE_MESSAGE_WITH_PROMPT");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatRagIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatRagIT.java
new file mode 100644
index 000000000000..439aeb7eb0bf
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatRagIT.java
@@ -0,0 +1,106 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.springai.chat.rag.SpringAiRagAggregatorStrategy;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.springframework.ai.document.Document;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for RAG (Retrieval Augmented Generation) features.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatRagIT extends OllamaTestSupport {
+
+    @Test
+    public void testRagWithManualAugmentedData() {
+        List<Document> context = new ArrayList<>();
+        context.add(
+                new Document("Apache Camel is an open-source integration framework based on Enterprise Integration Patterns."));
+        context.add(new Document("Camel was created in 2007 and is part of the Apache Software Foundation."));
+
+        var exchange = template().request("direct:rag", e -> {
+            e.getIn().setBody("When was Camel created? Answer with just the year.");
+            e.getIn().setHeader(SpringAiChatConstants.AUGMENTED_DATA, context);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).contains("2007");
+    }
+
+    @Test
+    public void testRagWithAggregatorStrategy() {
+        String response = template().requestBody("direct:rag-enricher",
+                "What framework is mentioned? Answer in two words.", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).containsAnyOf("camel", "apache");
+    }
+
+    @Test
+    public void testRagWithSystemMessage() {
+        List<Document> context = new ArrayList<>();
+        context.add(new Document("The product price is $99.99"));
+
+        var exchange = template().request("direct:rag", e -> {
+            e.getIn().setBody("What is the price?");
+            e.getIn().setHeader(SpringAiChatConstants.AUGMENTED_DATA, context);
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_MESSAGE,
+                    "You are a helpful assistant. Answer based only on the provided context.");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).containsAnyOf("99", "$");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                SpringAiRagAggregatorStrategy aggregatorStrategy = new SpringAiRagAggregatorStrategy();
+
+                from("direct:rag")
+                        .to("spring-ai-chat:rag?chatModel=#chatModel");
+
+                from("direct:rag-enricher")
+                        .enrich("direct:retrieve-context", aggregatorStrategy)
+                        .to("spring-ai-chat:rag?chatModel=#chatModel");
+
+                from("direct:retrieve-context")
+                        .process(exchange -> {
+                            // Simulate document retrieval
+                            List<String> documents = new ArrayList<>();
+                            documents.add("Apache Camel is a powerful integration framework.");
+                            documents.add("Camel supports over 300 components for integration.");
+                            exchange.getIn().setBody(documents);
+                        });
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatSafeguardIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatSafeguardIT.java
new file mode 100644
index 000000000000..2e9bc0ae9d4e
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatSafeguardIT.java
@@ -0,0 +1,124 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for Spring AI Chat component SafeGuard advisor.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatSafeguardIT extends OllamaTestSupport {
+
+    @Test
+    public void testSafeguardBlocksSensitiveWords() {
+        // Request that would normally return something containing "password"
+        String response = template.requestBody("direct:safeguard",
+                "Tell me a story about a user who forgot their password", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+
+        // The SafeGuard advisor should have blocked the response and returned the failure message
+        assertThat(response).contains("I cannot provide information containing sensitive words");
+    }
+
+    @Test
+    public void testSafeguardAllowsNonSensitiveContent() {
+        // Request that doesn't contain sensitive words
+        String response = template.requestBody("direct:safeguard",
+                "What is the capital of France? Answer in one word.", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+
+        // Should get a normal response without being blocked
+        assertThat(response.toLowerCase()).contains("paris");
+        assertThat(response).doesNotContain("I cannot provide information containing sensitive words");
+    }
+
+    @Test
+    public void testSafeguardWithHeaderOverride() {
+        // Override safeguard settings via headers
+        var exchange = template.request("direct:safeguardWithoutConfig", e -> {
+            e.getIn().setBody("Tell me about API keys and secrets");
+            e.getIn().setHeader(SpringAiChatConstants.SAFEGUARD_SENSITIVE_WORDS, "secret,api,key");
+            e.getIn().setHeader(SpringAiChatConstants.SAFEGUARD_FAILURE_RESPONSE,
+                    "Custom blocked message: This topic is restricted");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).contains("Custom blocked message: This topic is restricted");
+    }
+
+    @Test
+    public void testSafeguardWithCustomFailureResponse() {
+        String response = template.requestBody("direct:safeguardCustom",
+                "How do I reset my password?", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response).contains("Security policy violation detected");
+    }
+
+    @Test
+    public void testMultipleSensitiveWords() {
+        // Test with one of multiple configured sensitive words
+        String response1 = template.requestBody("direct:safeguard",
+                "Tell me about confidential data", String.class);
+
+        assertThat(response1).isNotNull();
+        assertThat(response1).contains("I cannot provide information containing sensitive words");
+
+        // Test with another sensitive word
+        String response2 = template.requestBody("direct:safeguard",
+                "What are the secret codes?", String.class);
+
+        assertThat(response2).isNotNull();
+        assertThat(response2).contains("I cannot provide information containing sensitive words");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                SpringAiChatComponent component = new SpringAiChatComponent();
+                component.setChatModel(chatModel);
+                context.addComponent("spring-ai-chat", component);
+
+                // Route with safeguard configured on endpoint
+                from("direct:safeguard")
+                        .to("spring-ai-chat:test?safeguardSensitiveWords=password,secret,confidential"
+                            + "&safeguardFailureResponse=I cannot provide information containing sensitive words");
+
+                // Route without safeguard config - will use headers
+                from("direct:safeguardWithoutConfig")
+                        .to("spring-ai-chat:test");
+
+                // Route with custom failure response
+                from("direct:safeguardCustom")
+                        .to("spring-ai-chat:test?safeguardSensitiveWords=password,secret"
+                            + "&safeguardFailureResponse=Security policy violation detected");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatStructuredOutputIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatStructuredOutputIT.java
new file mode 100644
index 000000000000..d4815077ef23
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatStructuredOutputIT.java
@@ -0,0 +1,234 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.springframework.ai.converter.BeanOutputConverter;
+import org.springframework.ai.converter.ListOutputConverter;
+import org.springframework.ai.converter.MapOutputConverter;
+import org.springframework.core.convert.support.DefaultConversionService;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test demonstrating Spring AI's Structured Output Converter with Camel.
+ *
+ * This test shows how to convert LLM responses into typed Java objects, Maps, or Lists using Spring AI's
+ * StructuredOutputConverter API.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatStructuredOutputIT extends OllamaTestSupport {
+
+    @Test
+    public void testBeanStructuredOutputWithHeaders() {
+        String userMessage = "Generate filmography for Tom Hanks";
+
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody(userMessage);
+            e.getIn().setHeader(SpringAiChatConstants.OUTPUT_FORMAT, "BEAN");
+            e.getIn().setHeader(SpringAiChatConstants.OUTPUT_CLASS, ActorFilms.class);
+        });
+
+        assertThat(exchange).isNotNull();
+        Object body = exchange.getMessage().getBody();
+        assertThat(body).isInstanceOf(ActorFilms.class);
+
+        ActorFilms actorFilms = (ActorFilms) body;
+        assertThat(actorFilms.actor()).isNotNull();
+        assertThat(actorFilms.movies()).isNotNull().isNotEmpty();
+    }
+
+    @Test
+    public void testBeanStructuredOutputWithConverter() {
+        String userMessage = "Generate filmography for Steven Spielberg";
+
+        ActorFilms result = template().requestBody("direct:bean-output", userMessage, ActorFilms.class);
+
+        assertThat(result).isNotNull();
+        assertThat(result.actor()).isNotNull();
+        assertThat(result.movies()).isNotNull().isNotEmpty();
+    }
+
+    @Test
+    public void testMapStructuredOutputWithHeaders() {
+        String userMessage = "Tell me about the weather in San Francisco";
+
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody(userMessage);
+            e.getIn().setHeader(SpringAiChatConstants.OUTPUT_FORMAT, "MAP");
+        });
+
+        assertThat(exchange).isNotNull();
+        Object body = exchange.getMessage().getBody();
+        assertThat(body).isInstanceOf(Map.class);
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> resultMap = (Map<String, Object>) body;
+        assertThat(resultMap).isNotNull().isNotEmpty();
+    }
+
+    @Test
+    public void testMapStructuredOutputWithConverter() {
+        String userMessage = "Provide information about Paris";
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> result = template().requestBody("direct:map-output", userMessage, Map.class);
+
+        assertThat(result).isNotNull().isNotEmpty();
+    }
+
+    @Test
+    public void testListStructuredOutputWithHeaders() {
+        String userMessage = "List 5 popular programming languages";
+
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody(userMessage);
+            e.getIn().setHeader(SpringAiChatConstants.OUTPUT_FORMAT, "LIST");
+        });
+
+        assertThat(exchange).isNotNull();
+        Object body = exchange.getMessage().getBody();
+        assertThat(body).isInstanceOf(List.class);
+
+        @SuppressWarnings("unchecked")
+        List<String> resultList = (List<String>) body;
+        assertThat(resultList).isNotNull().isNotEmpty();
+    }
+
+    @Test
+    public void testListStructuredOutputWithConverter() {
+        String userMessage = "List 3 European capital cities";
+
+        @SuppressWarnings("unchecked")
+        List<String> result = template().requestBody("direct:list-output", userMessage, List.class);
+
+        assertThat(result).isNotNull().isNotEmpty();
+    }
+
+    @Test
+    public void testStructuredOutputAccessRawResponse() {
+        String userMessage = "Generate filmography for Meryl Streep";
+
+        var exchange = template().request("direct:bean-output", e -> {
+            e.getIn().setBody(userMessage);
+        });
+
+        assertThat(exchange).isNotNull();
+
+        // Get structured output from body
+        ActorFilms structured = exchange.getMessage().getBody(ActorFilms.class);
+        assertThat(structured).isNotNull();
+        assertThat(structured.actor()).isNotNull();
+        assertThat(structured.movies()).isNotNull().isNotEmpty();
+
+        // Get raw response from header
+        String rawResponse = exchange.getMessage().getHeader(SpringAiChatConstants.CHAT_RESPONSE, String.class);
+        assertThat(rawResponse).isNotNull().isNotEmpty();
+
+        // Get structured output from header as well
+        ActorFilms alsoStructured
+                = exchange.getMessage().getHeader(SpringAiChatConstants.STRUCTURED_OUTPUT, ActorFilms.class);
+        assertThat(alsoStructured).isNotNull().isEqualTo(structured);
+    }
+
+    @Test
+    public void testBeanOutputWithEndpointConfiguration() {
+        String userMessage = "Generate filmography for Christopher Nolan";
+
+        ActorFilms result = template().requestBody("direct:bean-output-via-config", userMessage, ActorFilms.class);
+
+        assertThat(result).isNotNull();
+        assertThat(result.actor()).isNotNull();
+        assertThat(result.movies()).isNotNull().isNotEmpty();
+    }
+
+    @Test
+    public void testMapOutputWithEndpointConfiguration() {
+        String userMessage = "Tell me about Rome";
+
+        @SuppressWarnings("unchecked")
+        Map<String, Object> result = template().requestBody("direct:map-output-via-config", userMessage, Map.class);
+
+        assertThat(result).isNotNull().isNotEmpty();
+    }
+
+    @Test
+    public void testListOutputWithEndpointConfiguration() {
+        String userMessage = "List 3 colors";
+
+        @SuppressWarnings("unchecked")
+        List<String> result = template().requestBody("direct:list-output-via-config", userMessage, List.class);
+
+        assertThat(result).isNotNull().isNotEmpty();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                // Create and register converters
+                BeanOutputConverter<ActorFilms> beanConverter = new BeanOutputConverter<>(ActorFilms.class);
+                MapOutputConverter mapConverter = new MapOutputConverter();
+                ListOutputConverter listConverter = new ListOutputConverter(new DefaultConversionService());
+
+                this.getCamelContext().getRegistry().bind("beanConverter", beanConverter);
+                this.getCamelContext().getRegistry().bind("mapConverter", mapConverter);
+                this.getCamelContext().getRegistry().bind("listConverter", listConverter);
+
+                // Simple route for header-based conversion
+                from("direct:chat")
+                        .to("spring-ai-chat:test?chatModel=#chatModel");
+
+                // Route with pre-configured bean converter
+                from("direct:bean-output")
+                        .to("spring-ai-chat:test?chatModel=#chatModel&structuredOutputConverter=#beanConverter");
+
+                // Route with pre-configured map converter
+                from("direct:map-output")
+                        .to("spring-ai-chat:test?chatModel=#chatModel&structuredOutputConverter=#mapConverter");
+
+                // Route with pre-configured list converter
+                from("direct:list-output")
+                        .to("spring-ai-chat:test?chatModel=#chatModel&structuredOutputConverter=#listConverter");
+
+                // Routes using endpoint configuration for outputFormat and outputClass
+                from("direct:bean-output-via-config")
+                        .to("spring-ai-chat:test?chatModel=#chatModel&outputFormat=BEAN&outputClass="
+                            + ActorFilms.class.getName());
+
+                from("direct:map-output-via-config")
+                        .to("spring-ai-chat:test?chatModel=#chatModel&outputFormat=MAP");
+
+                from("direct:list-output-via-config")
+                        .to("spring-ai-chat:test?chatModel=#chatModel&outputFormat=LIST");
+            }
+        };
+    }
+
+    @JsonPropertyOrder({ "actor", "movies" })
+    public record ActorFilms(String actor, List<String> movies) {
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatSystemMessageIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatSystemMessageIT.java
new file mode 100644
index 000000000000..c4fe55d2fca5
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatSystemMessageIT.java
@@ -0,0 +1,91 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for system message functionality.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatSystemMessageIT extends OllamaTestSupport {
+
+    @Test
+    public void testSystemMessageInfluencesResponse() {
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody("What should I do today?");
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_MESSAGE,
+                    "You are a fitness coach. Always recommend physical activities.");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).containsAnyOf("exercise", "workout", "run", "walk", "activity", "physical");
+    }
+
+    @Test
+    public void testSystemMessageWithSpecificFormat() {
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody("What is 5 + 3?");
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_MESSAGE,
+                    "You are a math teacher. Always explain your answer step by step.");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response).contains("8");
+    }
+
+    @Test
+    public void testSystemMessageAsExpert() {
+        var exchange = template().request("direct:chat", e -> {
+            e.getIn().setBody("Tell me about integration patterns in one sentence.");
+            e.getIn().setHeader(SpringAiChatConstants.SYSTEM_MESSAGE,
+                    "You are an expert in Enterprise Integration Patterns and Apache Camel.");
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).containsAnyOf("integration", "pattern", "system", "message");
+    }
+
+    @Test
+    public void testWithoutSystemMessage() {
+        String response = template().requestBody("direct:chat",
+                "Say hello in one word.", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).containsAnyOf("hello", "hi");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                from("direct:chat")
+                        .to("spring-ai-chat:test?chatModel=#chatModel");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatToolsDiscoveryTest.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatToolsDiscoveryTest.java
new file mode 100644
index 000000000000..30427e0bcbe4
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatToolsDiscoveryTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.springai.tools.spec.CamelToolExecutorCache;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit test for tool discovery mechanism in camel-spring-ai-chat component.
+ *
+ * This test verifies that tools are properly registered in the cache when defined using the spring-ai-tools component.
+ */
+public class SpringAiChatToolsDiscoveryTest extends CamelTestSupport {
+
+    @AfterEach
+    public void cleanupToolCache() {
+        // Clean up the tool cache after each test
+        CamelToolExecutorCache.getInstance().getTools().clear();
+    }
+
+    @Test
+    public void testToolsAreRegisteredWithCache() throws Exception {
+        // Wait for routes to start and tools to be registered
+        Thread.sleep(500);
+
+        var toolCache = CamelToolExecutorCache.getInstance();
+        var tools = toolCache.getTools();
+
+        // Verify that tools are registered with correct tags
+        assertThat(tools).isNotEmpty();
+        assertThat(tools).containsKey("weather");
+        assertThat(tools).containsKey("math");
+        assertThat(tools).containsKey("database");
+
+        // Verify weather tools
+        assertThat(tools.get("weather")).isNotEmpty();
+        assertThat(tools.get("weather")).hasSize(1);
+
+        // Verify math tools
+        assertThat(tools.get("math")).isNotEmpty();
+        assertThat(tools.get("math")).hasSize(1);
+
+        // Verify database tools
+        assertThat(tools.get("database")).isNotEmpty();
+        assertThat(tools.get("database")).hasSize(1);
+    }
+
+    @Test
+    public void testToolExecutionWithParameters() throws Exception {
+        // Wait for routes to start
+        Thread.sleep(500);
+
+        // Test that a tool can be executed directly via the tool route
+        var exchange = template().request("direct:testWeatherTool", e -> {
+            e.getIn().setHeader("city", "Paris");
+        });
+
+        assertThat(exchange.getException()).isNull();
+        String result = exchange.getMessage().getBody(String.class);
+        assertThat(result).contains("Paris");
+    }
+
+    @Test
+    public void testMultipleToolsWithSameTag() throws Exception {
+        // Wait for routes to start
+        Thread.sleep(500);
+
+        var toolCache = CamelToolExecutorCache.getInstance();
+        var tools = toolCache.getTools();
+
+        // Verify that the weather tag has exactly one tool
+        assertThat(tools.get("weather")).isNotNull();
+        assertThat(tools.get("weather")).hasSize(1);
+
+        // Verify the tool can be executed
+        var exchange = template().request("direct:testWeatherTool", e -> {
+            e.getIn().setHeader("city", "London");
+        });
+
+        assertThat(exchange.getException()).isNull();
+        String result = exchange.getMessage().getBody(String.class);
+        assertThat(result).contains("London");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // Define weather tool
+                from("spring-ai-tools:weather?tags=weather&description=Get weather for a city")
+                        .process(exchange -> {
+                            String city = exchange.getIn().getHeader("city", String.class);
+                            String weather = "The weather in " + city + " is sunny";
+                            exchange.getIn().setBody(weather);
+                        });
+
+                // Define calculator tool
+                from("spring-ai-tools:calculator?tags=math&description=Calculate expressions")
+                        .process(exchange -> {
+                            String expression = exchange.getIn().getHeader("expression", String.class);
+                            exchange.getIn().setBody("Result: " + expression);
+                        });
+
+                // Define database tool
+                from("spring-ai-tools:queryDb?tags=database&description=Query database")
+                        .process(exchange -> {
+                            String query = exchange.getIn().getHeader("query", String.class);
+                            exchange.getIn().setBody("Query result: " + query);
+                        });
+
+                // Direct route for testing tool execution
+                from("direct:testWeatherTool")
+                        .setHeader("city", simple("${header.city}"))
+                        .process(exchange -> {
+                            String city = exchange.getIn().getHeader("city", String.class);
+                            String weather = "The weather in " + city + " is sunny";
+                            exchange.getIn().setBody(weather);
+                        });
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatToolsIntegrationIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatToolsIntegrationIT.java
new file mode 100644
index 000000000000..b29c6a63b444
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatToolsIntegrationIT.java
@@ -0,0 +1,242 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test demonstrating interaction between camel-spring-ai-chat and camel-spring-ai-tools components.
+ *
+ * This test shows how Camel routes can be exposed as tools/functions that LLMs can call during conversations.
+ *
+ * NOTE: Tool invocation depends on the LLM's capability and willingness to use tools. Some models or configurations may
+ * not invoke tools even when available. The tests verify that: 1. Tools are properly registered in the cache 2. The
+ * chat endpoint has access to the tools 3. If tools are invoked, they execute correctly
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatToolsIntegrationIT extends OllamaTestSupport {
+
+    private static final String WEATHER_PARIS = "The weather in Paris is sunny with a temperature of 22°C";
+    private static final String WEATHER_LONDON = "The weather in London is cloudy with a temperature of 15°C";
+    private static final String CALCULATOR_RESULT = "The result is: 8";
+
+    @Test
+    public void testToolsAreRegistered() throws Exception {
+        // Verify that tools are registered in the cache before testing
+        var toolCache = org.apache.camel.component.springai.tools.spec.CamelToolExecutorCache.getInstance();
+        var tools = toolCache.getTools();
+
+        // Give routes time to start and register tools
+        Thread.sleep(1000);
+
+        assertThat(tools).isNotEmpty();
+        assertThat(tools).containsKey("weather");
+        assertThat(tools).containsKey("math");
+        assertThat(tools.get("weather")).hasSize(1);
+        assertThat(tools.get("math")).hasSize(1);
+    }
+
+    @Test
+    public void testChatWithSingleTool() throws InterruptedException {
+        MockEndpoint weatherMock = getMockEndpoint("mock:weather");
+        weatherMock.reset();
+        weatherMock.expectedMinimumMessageCount(1);
+        weatherMock.expectedHeaderReceived("city", "Paris");
+
+        // Test that the LLM can call a single tool (weather) during conversation
+        String response = template().requestBody("direct:weatherChat",
+                "What's the weather in Paris?", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).containsAnyOf("paris", "sunny", "22", "weather");
+
+        // Verify the weather tool was actually invoked
+        weatherMock.assertIsSatisfied();
+        assertThat(weatherMock.getReceivedCounter()).isGreaterThanOrEqualTo(1);
+    }
+
+    @Test
+    public void testChatWithMultipleTools() throws InterruptedException {
+        // Test that the LLM can choose between multiple tools based on the question
+
+        // Test weather tool
+        MockEndpoint weatherMock = getMockEndpoint("mock:weather");
+        weatherMock.reset();
+        weatherMock.expectedMinimumMessageCount(1);
+
+        String weatherResponse = template().requestBody("direct:multiToolChat",
+                "What's the weather in London?", String.class);
+
+        assertThat(weatherResponse).isNotNull();
+        assertThat(weatherResponse.toLowerCase()).containsAnyOf("london", "cloudy", "15", "weather");
+
+        // Verify the weather tool was actually invoked
+        weatherMock.assertIsSatisfied();
+        assertThat(weatherMock.getReceivedCounter()).isGreaterThanOrEqualTo(1);
+
+        // Test calculator tool
+        MockEndpoint calculatorMock = getMockEndpoint("mock:calculator");
+        calculatorMock.reset();
+        calculatorMock.expectedMinimumMessageCount(1);
+
+        String mathResponse = template().requestBody("direct:multiToolChat",
+                "What is 5 plus 3?", String.class);
+
+        assertThat(mathResponse).isNotNull();
+        assertThat(mathResponse).containsAnyOf("8", "eight");
+
+        // Verify the calculator tool was actually invoked
+        calculatorMock.assertIsSatisfied();
+        assertThat(calculatorMock.getReceivedCounter()).isGreaterThanOrEqualTo(1);
+    }
+
+    @Test
+    public void testToolWithParameters() throws InterruptedException {
+        MockEndpoint weatherMock = getMockEndpoint("mock:weather");
+        weatherMock.reset();
+        weatherMock.expectedMinimumMessageCount(1);
+
+        // Test that tool parameters are properly passed from LLM to Camel route
+        String response = template().requestBody("direct:weatherChat",
+                "Tell me about the weather in Paris", String.class);
+
+        assertThat(response).isNotNull();
+        // The LLM should have called the weather tool with city="Paris"
+        assertThat(response.toLowerCase()).containsAnyOf("paris", "sunny", "22");
+
+        // Verify the tool was invoked and received the correct parameter
+        weatherMock.assertIsSatisfied();
+        assertThat(weatherMock.getReceivedCounter()).isGreaterThanOrEqualTo(1);
+
+        // Verify the city parameter was passed correctly
+        String cityParam = weatherMock.getExchanges().get(0).getIn().getHeader("city", String.class);
+        assertThat(cityParam).isNotNull();
+        assertThat(cityParam.toLowerCase()).contains("paris");
+    }
+
+    @Test
+    public void testChatWithSpecificTagsOnly() throws InterruptedException {
+        // Test that only tools with matching tags are available
+
+        // Weather chat should only have access to weather tools
+        MockEndpoint weatherMock = getMockEndpoint("mock:weather");
+        weatherMock.reset();
+        weatherMock.expectedMinimumMessageCount(1);
+
+        String weatherResponse = template().requestBody("direct:weatherChat",
+                "What's the weather in Paris?", String.class);
+        assertThat(weatherResponse).isNotNull();
+
+        weatherMock.assertIsSatisfied();
+
+        // Math chat should only have access to calculator tools
+        MockEndpoint calculatorMock = getMockEndpoint("mock:calculator");
+        calculatorMock.reset();
+        calculatorMock.expectedMinimumMessageCount(1);
+
+        String mathResponse = template().requestBody("direct:mathChat",
+                "Calculate the following mathematical expression 5 plus 3", String.class);
+        assertThat(mathResponse).isNotNull();
+        assertThat(mathResponse).containsAnyOf("8", "eight");
+
+        calculatorMock.assertIsSatisfied();
+    }
+
+    @Test
+    public void testConversationWithToolCalls() throws InterruptedException {
+        MockEndpoint weatherMock = getMockEndpoint("mock:weather");
+        weatherMock.reset();
+        weatherMock.expectedMinimumMessageCount(1);
+
+        // Test a conversation where tools might be called
+        String response = template().requestBody("direct:weatherChat",
+                "What's the weather in Paris and London? Please check both cities.", String.class);
+
+        assertThat(response).isNotNull();
+        // The LLM should respond with something about weather
+        assertThat(response.toLowerCase()).containsAnyOf("paris", "london", "weather", "sunny", "cloudy", "temperature");
+
+        // Verify the weather tool was invoked at least once
+        weatherMock.assertIsSatisfied();
+        int actualCalls = weatherMock.getReceivedCounter();
+        assertThat(actualCalls).isGreaterThanOrEqualTo(1);
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(getCamelContext());
+
+                // Define weather tool - gets weather for a specified city
+                from("spring-ai-tools:weather?tags=weather&description=Get current weather for a city&parameter.city=string")
+                        .log("Weather tool called with city: ${header.city}")
+                        .process(exchange -> {
+                            String city = exchange.getIn().getHeader("city", String.class);
+                            String weather;
+
+                            if (city != null && city.toLowerCase().contains("paris")) {
+                                weather = WEATHER_PARIS;
+                            } else if (city != null && city.toLowerCase().contains("london")) {
+                                weather = WEATHER_LONDON;
+                            } else {
+                                weather = "Weather information not available for " + city;
+                            }
+
+                            exchange.getIn().setBody(weather);
+                        })
+                        .log("Weather tool returning: ${body}")
+                        .to("mock:weather");
+
+                // Define calculator tool - performs simple calculations
+                from("spring-ai-tools:calculator?tags=math&description=Calculate mathematical expressions&parameter.expression=string")
+                        .process(exchange -> {
+                            String expression = exchange.getIn().getHeader("expression", String.class);
+                            String result;
+
+                            // Simple calculator for demonstration
+                            if (expression != null && expression.contains("5") && expression.contains("3")) {
+                                result = CALCULATOR_RESULT;
+                            } else {
+                                result = "Unable to calculate: " + expression;
+                            }
+
+                            exchange.getIn().setBody(result);
+                        })
+                        .to("mock:calculator");
+
+                // Chat endpoint with weather tools only
+                from("direct:weatherChat")
+                        .to("spring-ai-chat:weatherChat?tags=weather&chatModel=#chatModel");
+
+                // Chat endpoint with math tools only
+                from("direct:mathChat")
+                        .to("spring-ai-chat:mathChat?tags=math&chatModel=#chatModel");
+
+                // Chat endpoint with multiple tools (weather + math)
+                from("direct:multiToolChat")
+                        .to("spring-ai-chat:multiToolChat?tags=weather,math&chatModel=#chatModel");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatVectorMemoryIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatVectorMemoryIT.java
new file mode 100644
index 000000000000..6f7205b289ef
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatVectorMemoryIT.java
@@ -0,0 +1,145 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.ollama.OllamaEmbeddingModel;
+import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaEmbeddingOptions;
+import org.springframework.ai.vectorstore.SimpleVectorStore;
+import org.springframework.ai.vectorstore.VectorStore;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test demonstrating how to use Spring AI's VectorStoreChatMemoryAdvisor with Camel.
+ *
+ * This test shows how to configure and use VectorStoreChatMemoryAdvisor to maintain conversation history using vector
+ * embeddings for long-term memory with semantic retrieval capabilities.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatVectorMemoryIT extends OllamaTestSupport {
+
+    private VectorStore chatMemoryVectorStore;
+
+    @Override
+    protected void setupResources() throws Exception {
+        super.setupResources();
+
+        // Create embedding model for vector store
+        OllamaApi ollamaApi = OllamaApi.builder()
+                .baseUrl(OLLAMA.baseUrl())
+                .build();
+
+        EmbeddingModel embeddingModel = OllamaEmbeddingModel.builder()
+                .ollamaApi(ollamaApi)
+                .defaultOptions(OllamaEmbeddingOptions.builder()
+                        .model("embeddinggemma:300m")
+                        .build())
+                .build();
+
+        // Create vector store for chat memory
+        chatMemoryVectorStore = SimpleVectorStore.builder(embeddingModel)
+                .build();
+    }
+
+    @Test
+    public void testVectorMemoryRetainsConversationHistory() {
+        String conversationId = "vector-memory-test-1";
+
+        // First interaction - tell the assistant something
+        var exchange1 = template().request("direct:chat-with-vector-memory", e -> {
+            e.getIn().setBody("My favorite programming language is Java.");
+            e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, conversationId);
+        });
+
+        String response1 = exchange1.getMessage().getBody(String.class);
+        assertThat(response1).isNotNull();
+
+        // Second interaction - ask about what we told it before
+        // The VectorStoreChatMemoryAdvisor should retrieve relevant context using semantic search
+        var exchange2 = template().request("direct:chat-with-vector-memory", e -> {
+            e.getIn().setBody("What programming language did I mention? Answer in one word.");
+            e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, conversationId);
+        });
+
+        String response2 = exchange2.getMessage().getBody(String.class);
+        assertThat(response2).isNotNull();
+        assertThat(response2.toLowerCase()).contains("java");
+    }
+
+    // NOTE: This test is disabled because SimpleVectorStore does not support metadata filtering.
+    // Conversation isolation requires a VectorStore that supports filter expressions
+    // (e.g., Qdrant, Weaviate, Pinecone, Milvus, ChromaDB).
+    // For production use with multiple conversations, use a VectorStore implementation that supports filtering.
+    //
+    // @Test
+    // public void testVectorMemoryWithMultipleConversations() {
+    //     // This test would verify conversation isolation if using a filtering-capable VectorStore
+    // }
+
+    @Test
+    public void testVectorMemorySemanticRetrieval() {
+        String conversationId = "semantic-test";
+
+        // Add multiple pieces of information
+        template().request("direct:chat-with-vector-memory", e -> {
+            e.getIn().setBody("I work as a software engineer at a tech company.");
+            e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, conversationId);
+        });
+
+        template().request("direct:chat-with-vector-memory", e -> {
+            e.getIn().setBody("My hobbies include hiking and photography.");
+            e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, conversationId);
+        });
+
+        template().request("direct:chat-with-vector-memory", e -> {
+            e.getIn().setBody("I graduated from university in 2015.");
+            e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, conversationId);
+        });
+
+        // Ask a semantic question that should retrieve relevant context
+        var exchange = template().request("direct:chat-with-vector-memory", e -> {
+            e.getIn().setBody("What is my profession? Answer in 2-3 words.");
+            e.getIn().setHeader(SpringAiChatConstants.CONVERSATION_ID, conversationId);
+        });
+
+        String response = exchange.getMessage().getBody(String.class);
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).containsAnyOf("software", "engineer", "developer");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                // Register VectorStore for chat memory
+                this.getCamelContext().getRegistry().bind("chatMemoryVectorStore", chatMemoryVectorStore);
+
+                // Route with VectorStoreChatMemoryAdvisor
+                from("direct:chat-with-vector-memory")
+                        .to("spring-ai-chat:vector-memory?chatModel=#chatModel&chatMemoryVectorStore=#chatMemoryVectorStore");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatVectorStoreIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatVectorStoreIT.java
new file mode 100644
index 000000000000..fa4604ecffeb
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatVectorStoreIT.java
@@ -0,0 +1,124 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.ollama.OllamaEmbeddingModel;
+import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaEmbeddingOptions;
+import org.springframework.ai.vectorstore.SimpleVectorStore;
+import org.springframework.ai.vectorstore.VectorStore;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for VectorStore automatic RAG retrieval.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiChatVectorStoreIT extends OllamaTestSupport {
+
+    private VectorStore vectorStore;
+
+    @Override
+    protected void setupResources() throws Exception {
+        super.setupResources();
+
+        // Create embedding model for vector store
+        OllamaApi ollamaApi = OllamaApi.builder()
+                .baseUrl(OLLAMA.baseUrl())
+                .build();
+
+        EmbeddingModel embeddingModel = OllamaEmbeddingModel.builder()
+                .ollamaApi(ollamaApi)
+                .defaultOptions(OllamaEmbeddingOptions.builder()
+                        .model("embeddinggemma:300m")
+                        .build())
+                .build();
+
+        // Create and populate vector store
+        vectorStore = SimpleVectorStore.builder(embeddingModel)
+                .build();
+
+        List<Document> documents = List.of(
+                new Document(
+                        "Apache Camel is an integration framework created in 2007.",
+                        Map.of("source", "camel-docs")),
+                new Document(
+                        "Camel supports Enterprise Integration Patterns.",
+                        Map.of("source", "camel-docs")),
+                new Document(
+                        "Spring AI provides AI integration for Spring applications.",
+                        Map.of("source", "spring-ai-docs")),
+                new Document(
+                        "Vector stores are used for similarity search in RAG applications.",
+                        Map.of("source", "ai-docs")));
+
+        vectorStore.add(documents);
+    }
+
+    @Test
+    public void testVectorStoreAutoRetrieval() {
+        String response = template().requestBody("direct:vector-rag",
+                "When was Apache Camel created? Answer with just the year.", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response).contains("2007");
+    }
+
+    @Test
+    public void testVectorStoreWithCustomTopK() {
+        String response = template().requestBody("direct:vector-rag-top-k",
+                "What patterns does Camel support? Answer in 3 words.", String.class);
+
+        assertThat(response).isNotNull();
+        assertThat(response.toLowerCase()).containsAnyOf("integration", "enterprise", "pattern");
+    }
+
+    @Test
+    public void testVectorStoreDoesNotRetrieveIrrelevantContext() {
+        // Query about something not in the vector store
+        String response = template().requestBody("direct:vector-rag",
+                "What is quantum computing? If you don't know, say 'I don't know'.", String.class);
+
+        assertThat(response).isNotNull();
+        // The response might still try to answer, but should not contain specific details from our docs
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+                this.getCamelContext().getRegistry().bind("vectorStore", vectorStore);
+
+                from("direct:vector-rag")
+                        .to("spring-ai-chat:vectorstore?chatModel=#chatModel&vectorStore=#vectorStore&similarityThreshold=0.4");
+
+                from("direct:vector-rag-top-k")
+                        .to("spring-ai-chat:vectorstore?chatModel=#chatModel&vectorStore=#vectorStore&topK=2&similarityThreshold=0.4");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatWrappedFileIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatWrappedFileIT.java
new file mode 100644
index 000000000000..ad2ca8efad68
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/java/org/apache/camel/component/springai/chat/SpringAiChatWrappedFileIT.java
@@ -0,0 +1,170 @@
+/*
+ * 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.apache.camel.component.springai.chat;
+
+import java.net.URL;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ai.ollama.OllamaChatModel;
+import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaChatOptions;
+import org.springframework.retry.support.RetryTemplate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for WrappedFile support in multimodal capabilities.
+ *
+ * Tests the ability to use files directly from the file component with spring-ai-chat. This enables patterns like:
+ * from("file:...").to("spring-ai-chat:...")
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+@Disabled("TODO: Find a model that support images and pdf")
+public class SpringAiChatWrappedFileIT extends OllamaTestSupport {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SpringAiChatWrappedFileIT.class);
+
+    private String getTestResourcePath(String resource) {
+        URL url = getClass().getClassLoader().getResource(resource);
+        if (url == null) {
+            throw new RuntimeException("Resource not found: " + resource);
+        }
+        return url.getPath();
+    }
+
+    @Override
+    protected String modelName() {
+        return "qwen3-vl:2b";
+    }
+
+    @Override
+    protected org.springframework.ai.chat.model.ChatModel createModel() {
+        OllamaApi ollamaApi = OllamaApi.builder()
+                .baseUrl(OLLAMA.baseUrl())
+                .build();
+
+        OllamaChatOptions ollamaOptions
+                = OllamaChatOptions.builder()
+                        .model(modelName())
+                        .temperature(0.3)
+                        .build();
+
+        return OllamaChatModel.builder()
+                .ollamaApi(ollamaApi)
+                .defaultOptions(ollamaOptions)
+                .retryTemplate(RetryTemplate.builder()
+                        .maxAttempts(1)
+                        .build())
+                .build();
+    }
+
+    @Test
+    public void testWrappedFileWithAutoDetectedMimeType() throws Exception {
+        String response = template().requestBody("direct:auto-detect", null, String.class);
+        LOG.info("LLM Response (auto-detected MIME): {}", response);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testWrappedFileWithHeaderSpecifiedMimeType() throws Exception {
+        String response = template().requestBody("direct:header-mime", null, String.class);
+        LOG.info("LLM Response (header-specified MIME): {}", response);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testWrappedFileWithConfiguredUserMessage() throws Exception {
+        String response = template().requestBody("direct:configured", null, String.class);
+        LOG.info("LLM Response (configured user message): {}", response);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testWrappedFileWithUserMessageHeader() throws Exception {
+        String response = template().requestBody("direct:header-msg", null, String.class);
+        LOG.info("LLM Response (user message header): {}", response);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Test
+    public void testWrappedFileWithFileContentTypeHeader() throws Exception {
+        String response = template().requestBody("direct:content-type", null, String.class);
+        LOG.info("LLM Response (FILE_CONTENT_TYPE header): {}", response);
+        assertThat(response).isNotNull();
+        assertThat(response).isNotEmpty();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                bindChatModel(this.getCamelContext());
+
+                String testFilesPath = getTestResourcePath("test-files");
+
+                // Route 1: Auto-detect MIME type from file extension (PNG)
+                from("direct:auto-detect")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=test-image.png&noop=true&idempotent=false")
+                        .setHeader(SpringAiChatConstants.USER_MESSAGE, constant("What color is this image?"))
+                        .to("spring-ai-chat:wrapped-file?chatModel=#chatModel");
+
+                // Route 2: Header-specified MIME type (JPEG)
+                from("direct:header-mime")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=test-image.jpg&noop=true&idempotent=false")
+                        .setHeader(SpringAiChatConstants.USER_MESSAGE, constant("Describe this image."))
+                        .setHeader(SpringAiChatConstants.MEDIA_TYPE, constant("image/jpeg"))
+                        .to("spring-ai-chat:wrapped-file-header?chatModel=#chatModel");
+
+                // Route 3: Configured user message on endpoint
+                from("direct:configured")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=test-image.png&noop=true&idempotent=false")
+                        .to("spring-ai-chat:wrapped-file-config?chatModel=#chatModel&userMessage=Analyze this image in detail.");
+
+                // Route 4: User message from header
+                from("direct:header-msg")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=test-image.png&noop=true&idempotent=false")
+                        .setHeader(SpringAiChatConstants.USER_MESSAGE, constant("What do you see in this picture?"))
+                        .to("spring-ai-chat:wrapped-file-msg?chatModel=#chatModel");
+
+                // Route 5: PDF file auto-detection
+                from("direct:pdf")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=test-document.pdf&noop=true&idempotent=false")
+                        .setHeader(SpringAiChatConstants.USER_MESSAGE,
+                                constant("Summarize the content of this PDF document."))
+                        .to("spring-ai-chat:wrapped-file-pdf?chatModel=#chatModel");
+
+                // Route 6: FILE_CONTENT_TYPE header from file component
+                from("direct:content-type")
+                        .pollEnrich("file:" + testFilesPath + "?fileName=test-image.png&noop=true&idempotent=false")
+                        .setHeader(SpringAiChatConstants.USER_MESSAGE, constant("Describe this image."))
+                        // Simulate FILE_CONTENT_TYPE header that would be set by file component with probeContentType=true
+                        .setHeader(org.apache.camel.Exchange.FILE_CONTENT_TYPE, constant("image/png"))
+                        .to("spring-ai-chat:wrapped-file-ct?chatModel=#chatModel");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/log4j2.properties b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/log4j2.properties
new file mode 100644
index 000000000000..3ea9d3beaea2
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/log4j2.properties
@@ -0,0 +1,33 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-spring-ai-chat-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
+
+logger.spring.name=org.springframework.ai.chat.client.advisor
+logger.spring.level=DEBUG
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/large-file.bin b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/large-file.bin
new file mode 100644
index 000000000000..187b57431089
Binary files /dev/null and b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/large-file.bin differ
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/medium-file.bin b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/medium-file.bin
new file mode 100644
index 000000000000..445c8963c00f
Binary files /dev/null and b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/medium-file.bin differ
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-document.pdf b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-document.pdf
new file mode 100644
index 000000000000..3cedc4da23fb
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-document.pdf
@@ -0,0 +1,20 @@
+%PDF-1.4
+1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj
+2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
+3 0 obj << /Type /Page /Parent 2 0 R /Resources << /Font << /F1 4 0 R >> >> /MediaBox [0 0 612 792] /Contents 5 0 R >> endobj
+4 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj
+5 0 obj << /Length 44 >> stream
+BT /F1 12 Tf 100 700 Td (Test PDF) Tj ET
+endstream endobj
+xref
+0 6
+0000000000 65535 f 
+0000000009 00000 n 
+0000000058 00000 n 
+0000000115 00000 n 
+0000000261 00000 n 
+0000000330 00000 n 
+trailer << /Size 6 /Root 1 0 R >>
+startxref
+422
+%%EOF
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-image.jpg b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-image.jpg
new file mode 100644
index 000000000000..896a2078ff7f
Binary files /dev/null and b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-image.jpg differ
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-image.png b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-image.png
new file mode 100644
index 000000000000..896a2078ff7f
Binary files /dev/null and b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-chat/src/test/resources/test-files/test-image.png differ
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/pom.xml b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/pom.xml
new file mode 100644
index 000000000000..c895676bf666
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/pom.xml
@@ -0,0 +1,89 @@
+<?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>
+
+    <parent>
+        <artifactId>camel-spring-ai-parent</artifactId>
+        <groupId>org.apache.camel</groupId>
+        <version>4.17.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-spring-ai-embeddings</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: Spring AI :: Embeddings</name>
+    <description>Spring AI Embeddings</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-support</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-model</artifactId>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-transformers</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- Spring AI Ollama for integration tests -->
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-ollama</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- Ollama test infrastructure -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-infra-ollama</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-infra-ollama</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+    </dependencies>
+</project>
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponentConfigurer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponentConfigurer.java
new file mode 100644
index 000000000000..b70effcd451c
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponentConfigurer.java
@@ -0,0 +1,78 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.embeddings;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class SpringAiEmbeddingsComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    private org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration getOrCreateConfiguration(SpringAiEmbeddingsComponent target) {
+        if (target.getConfiguration() == null) {
+            target.setConfiguration(new org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration());
+        }
+        return target.getConfiguration();
+    }
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        SpringAiEmbeddingsComponent target = (SpringAiEmbeddingsComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true;
+        case "configuration": target.setConfiguration(property(camelContext, org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration.class, value)); return true;
+        case "embeddingmodel":
+        case "embeddingModel": getOrCreateConfiguration(target).setEmbeddingModel(property(camelContext, org.springframework.ai.embedding.EmbeddingModel.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public String[] getAutowiredNames() {
+        return new String[]{"embeddingModel"};
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return boolean.class;
+        case "configuration": return org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration.class;
+        case "embeddingmodel":
+        case "embeddingModel": return org.springframework.ai.embedding.EmbeddingModel.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        SpringAiEmbeddingsComponent target = (SpringAiEmbeddingsComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return target.isAutowiredEnabled();
+        case "configuration": return target.getConfiguration();
+        case "embeddingmodel":
+        case "embeddingModel": return getOrCreateConfiguration(target).getEmbeddingModel();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsConfigurationConfigurer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsConfigurationConfigurer.java
new file mode 100644
index 000000000000..b623b1e4dfaa
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsConfigurationConfigurer.java
@@ -0,0 +1,51 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.embeddings;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.GenerateConfigurerMojo")
+@SuppressWarnings("unchecked")
+public class SpringAiEmbeddingsConfigurationConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration target = (org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "embeddingmodel":
+        case "embeddingModel": target.setEmbeddingModel(property(camelContext, org.springframework.ai.embedding.EmbeddingModel.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "embeddingmodel":
+        case "embeddingModel": return org.springframework.ai.embedding.EmbeddingModel.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration target = (org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "embeddingmodel":
+        case "embeddingModel": return target.getEmbeddingModel();
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpointConfigurer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpointConfigurer.java
new file mode 100644
index 000000000000..4d9645808b18
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpointConfigurer.java
@@ -0,0 +1,62 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.embeddings;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class SpringAiEmbeddingsEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        SpringAiEmbeddingsEndpoint target = (SpringAiEmbeddingsEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "embeddingmodel":
+        case "embeddingModel": target.getConfiguration().setEmbeddingModel(property(camelContext, org.springframework.ai.embedding.EmbeddingModel.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public String[] getAutowiredNames() {
+        return new String[]{"embeddingModel"};
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "embeddingmodel":
+        case "embeddingModel": return org.springframework.ai.embedding.EmbeddingModel.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        SpringAiEmbeddingsEndpoint target = (SpringAiEmbeddingsEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "embeddingmodel":
+        case "embeddingModel": return target.getConfiguration().getEmbeddingModel();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpointUriFactory.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpointUriFactory.java
new file mode 100644
index 000000000000..592438141d43
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpointUriFactory.java
@@ -0,0 +1,72 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.embeddings;
+
+import javax.annotation.processing.Generated;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.spi.EndpointUriFactory;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.GenerateEndpointUriFactoryMojo")
+public class SpringAiEmbeddingsEndpointUriFactory extends org.apache.camel.support.component.EndpointUriFactorySupport implements EndpointUriFactory {
+
+    private static final String BASE = ":embeddingId";
+
+    private static final Set<String> PROPERTY_NAMES;
+    private static final Set<String> SECRET_PROPERTY_NAMES;
+    private static final Map<String, String> MULTI_VALUE_PREFIXES;
+    static {
+        Set<String> props = new HashSet<>(3);
+        props.add("embeddingId");
+        props.add("embeddingModel");
+        props.add("lazyStartProducer");
+        PROPERTY_NAMES = Collections.unmodifiableSet(props);
+        SECRET_PROPERTY_NAMES = Collections.emptySet();
+        MULTI_VALUE_PREFIXES = Collections.emptyMap();
+    }
+
+    @Override
+    public boolean isEnabled(String scheme) {
+        return "spring-ai-embeddings".equals(scheme);
+    }
+
+    @Override
+    public String buildUri(String scheme, Map<String, Object> properties, boolean encode) throws URISyntaxException {
+        String syntax = scheme + BASE;
+        String uri = syntax;
+
+        Map<String, Object> copy = new HashMap<>(properties);
+
+        uri = buildPathParameter(syntax, uri, "embeddingId", null, true, copy);
+        uri = buildQueryParameters(uri, copy, encode);
+        return uri;
+    }
+
+    @Override
+    public Set<String> propertyNames() {
+        return PROPERTY_NAMES;
+    }
+
+    @Override
+    public Set<String> secretPropertyNames() {
+        return SECRET_PROPERTY_NAMES;
+    }
+
+    @Override
+    public Map<String, String> multiValuePrefixes() {
+        return MULTI_VALUE_PREFIXES;
+    }
+
+    @Override
+    public boolean isLenientProperties() {
+        return false;
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/org/apache/camel/component/springai/embeddings/spring-ai-embeddings.json b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/org/apache/camel/component/springai/embeddings/spring-ai-embeddings.json
new file mode 100644
index 000000000000..d13dad0b055c
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/org/apache/camel/component/springai/embeddings/spring-ai-embeddings.json
@@ -0,0 +1,45 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "spring-ai-embeddings",
+    "title": "Spring AI Embeddings",
+    "description": "Spring AI Embeddings",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "ai",
+    "javaType": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-spring-ai-embeddings",
+    "version": "4.17.0-SNAPSHOT",
+    "scheme": "spring-ai-embeddings",
+    "extendsScheme": "",
+    "syntax": "spring-ai-embeddings:embeddingId",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": true,
+    "lenientProperties": false,
+    "browsable": false,
+    "remote": true
+  },
+  "componentProperties": {
+    "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The configuration." },
+    "embeddingModel": { "index": 1, "kind": "property", "displayName": "Embedding Model", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.springframework.ai.embedding.EmbeddingModel", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration", "configurationField": "configuration", "description": "The EmbeddingModel to use for  [...]
+    "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
+    "autowiredEnabled": { "index": 3, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+  },
+  "headers": {
+    "CamelSpringAiEmbeddingMetadata": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.springframework.ai.embedding.EmbeddingResponseMetadata", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The embedding response metadata", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#EMBEDDING_METADATA" },
+    "CamelSpringAiEmbeddingIndex": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The index of the embedding in the response", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#EMBEDDING_INDEX" },
+    "CamelSpringAiEmbedding": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.springframework.ai.embedding.Embedding", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Embedding object", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#EMBEDDING" },
+    "CamelSpringAiEmbeddingInputText": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The input text that was embedded", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#INPUT_TEXT" },
+    "CamelSpringAiEmbeddings": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<org.springframework.ai.embedding.Embedding>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of Embedding objects", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#EMBEDDINGS" },
+    "CamelSpringAiEmbeddingInputTexts": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "java.util.List<String>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of input texts that were embedded", "constantName": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsHeaders#INPUT_TEXTS" }
+  },
+  "properties": {
+    "embeddingId": { "index": 0, "kind": "path", "displayName": "Embedding Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The id" },
+    "embeddingModel": { "index": 1, "kind": "parameter", "displayName": "Embedding Model", "group": "producer", "label": "", "required": true, "type": "object", "javaType": "org.springframework.ai.embedding.EmbeddingModel", "deprecated": false, "deprecationNote": "", "autowired": true, "secret": false, "configurationClass": "org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration", "configurationField": "configuration", "description": "The EmbeddingModel to use for [...]
+    "lazyStartProducer": { "index": 2, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...]
+  }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/component.properties
new file mode 100644
index 000000000000..73c52f5ea74d
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/component.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+components=spring-ai-embeddings
+groupId=org.apache.camel
+artifactId=camel-spring-ai-embeddings
+version=4.17.0-SNAPSHOT
+projectName=Camel :: Spring AI :: Embeddings
+projectDescription=Spring AI Embeddings
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-embeddings b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-embeddings
new file mode 100644
index 000000000000..8cdafd4c6486
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-embeddings
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsComponent
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration
new file mode 100644
index 000000000000..d84cc82c3606
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfiguration
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsConfigurationConfigurer
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-embeddings-component b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-embeddings-component
new file mode 100644
index 000000000000..e780c7224640
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-embeddings-component
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsComponentConfigurer
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-embeddings-endpoint b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-embeddings-endpoint
new file mode 100644
index 000000000000..a22199774dc4
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-embeddings-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsEndpointConfigurer
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-embeddings-endpoint b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-embeddings-endpoint
new file mode 100644
index 000000000000..e59aac4444ee
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-embeddings-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.embeddings.SpringAiEmbeddingsEndpointUriFactory
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/docs/spring-ai-embeddings-component.adoc b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/docs/spring-ai-embeddings-component.adoc
new file mode 100644
index 000000000000..89a522c5d510
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/docs/spring-ai-embeddings-component.adoc
@@ -0,0 +1,103 @@
+= Spring AI Embeddings Component
+:doctitle: Spring AI Embeddings
+:shortname: spring-ai-embeddings
+:artifactid: camel-spring-ai-embeddings
+:description: Spring AI Embeddings
+:since: 4.17
+:supportlevel: Preview
+:tabs-sync-option:
+:component-header: Only producer is supported
+//Manually maintained attributes
+:group: AI
+:camel-spring-boot-name: spring-ai-embeddings
+
+*Since Camel {since}*
+
+*{component-header}*
+
+The Spring AI Embeddings component provides support for computing embeddings using https://docs.spring.io/spring-ai/reference/api/embeddings.html[Spring AI embedding models].
+
+Embeddings are numerical vector representations of text that capture semantic relationships between inputs. They are useful for:
+
+* Semantic similarity search
+* Text clustering and classification
+* Retrieval-Augmented Generation (RAG)
+* Recommendation systems
+
+[NOTE]
+====
+This is a low-level component for direct embedding computation. For most use cases, you can use the `camel-spring-ai-chat` component with appropriate configuration for RAG scenarios, or the `camel-spring-ai-vector-store` component which can handle embeddings automatically without requiring this component directly.
+====
+
+== URI format
+
+----
+spring-ai-embeddings:embeddingId[?options]
+----
+
+Where *embeddingId* can be any string to uniquely identify the endpoint
+
+
+// component-configure options: START
+
+// component-configure options: END
+
+// component options: START
+include::partial$component-configure-options.adoc[]
+include::partial$component-endpoint-options.adoc[]
+// component options: END
+
+// endpoint options: START
+
+// endpoint options: END
+
+// component headers: START
+include::partial$component-endpoint-headers.adoc[]
+// component headers: END
+
+== Usage
+
+The component accepts two types of input:
+
+* **Single text**: A String containing the text to embed
+* **Batch processing**: A List<String> containing multiple texts to embed at once
+
+The embedding vectors are returned in the message body, with additional metadata available in headers.
+
+=== Single Text Example
+
+[source,java]
+----
+from("direct:start")
+    .to("spring-ai-embeddings:myEmbedding")
+    .log("Embedding vector: ${body}");
+----
+
+When processing a single String, the component returns:
+
+* **Message Body**: The embedding vector as `float[]`
+* **Headers**:
+** `CamelSpringAiEmbedding` - The Embedding object
+** `CamelSpringAiEmbeddingIndex` - The index (0 for single embeddings)
+** `CamelSpringAiEmbeddingInputText` - The original input text
+** `CamelSpringAiEmbeddingMetadata` - Response metadata
+
+=== Batch Processing Example
+
+[source,java]
+----
+from("direct:batch")
+    .setBody(constant(List.of("text 1", "text 2", "text 3")))
+    .to("spring-ai-embeddings:myEmbedding")
+    .log("Number of embeddings: ${body.size()}");
+----
+
+When processing a List<String>, the component returns:
+
+* **Message Body**: List of embedding vectors as `List<float[]>`
+* **Headers**:
+** `CamelSpringAiEmbeddings` - List of Embedding objects
+** `CamelSpringAiEmbeddingInputTexts` - The original input texts as List<String>
+** `CamelSpringAiEmbeddingMetadata` - Response metadata
+
+// include::spring-boot:partial$starter.adoc[]
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddings.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddings.java
new file mode 100644
index 000000000000..1a109b390871
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddings.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.apache.camel.component.springai.embeddings;
+
+public final class SpringAiEmbeddings {
+    public static final String SCHEME = "spring-ai-embeddings";
+
+    private SpringAiEmbeddings() {
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponent.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponent.java
new file mode 100644
index 000000000000..9bc75d89bb26
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.apache.camel.component.springai.embeddings;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.support.DefaultComponent;
+
+@Component(SpringAiEmbeddings.SCHEME)
+public class SpringAiEmbeddingsComponent extends DefaultComponent {
+    @Metadata
+    private SpringAiEmbeddingsConfiguration configuration;
+
+    public SpringAiEmbeddingsComponent() {
+        this(null);
+    }
+
+    public SpringAiEmbeddingsComponent(CamelContext context) {
+        super(context);
+
+        this.configuration = new SpringAiEmbeddingsConfiguration();
+    }
+
+    public SpringAiEmbeddingsConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    /**
+     * The configuration.
+     */
+    public void setConfiguration(SpringAiEmbeddingsConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+        SpringAiEmbeddingsConfiguration configuration = this.configuration.copy();
+
+        SpringAiEmbeddingsEndpoint endpoint = new SpringAiEmbeddingsEndpoint(uri, this, remaining, configuration);
+        setProperties(endpoint, parameters);
+
+        return endpoint;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsConfiguration.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsConfiguration.java
new file mode 100644
index 000000000000..8ef815b9e858
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsConfiguration.java
@@ -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.
+ */
+package org.apache.camel.component.springai.embeddings;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.Configurer;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriParams;
+import org.springframework.ai.embedding.EmbeddingModel;
+
+@Configurer
+@UriParams
+public class SpringAiEmbeddingsConfiguration implements Cloneable {
+
+    @Metadata(required = true, autowired = true)
+    @UriParam
+    private EmbeddingModel embeddingModel;
+
+    public EmbeddingModel getEmbeddingModel() {
+        return embeddingModel;
+    }
+
+    /**
+     * The {@link EmbeddingModel} to use for generating embeddings.
+     */
+    public void setEmbeddingModel(EmbeddingModel embeddingModel) {
+        this.embeddingModel = embeddingModel;
+    }
+
+    public SpringAiEmbeddingsConfiguration copy() {
+        try {
+            return (SpringAiEmbeddingsConfiguration) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeCamelException(e);
+        }
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpoint.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpoint.java
new file mode 100644
index 000000000000..081d641e61cd
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsEndpoint.java
@@ -0,0 +1,74 @@
+/*
+ * 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.apache.camel.component.springai.embeddings;
+
+import org.apache.camel.Category;
+import org.apache.camel.Component;
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.support.DefaultEndpoint;
+
+/**
+ * Spring AI Embeddings
+ */
+@UriEndpoint(firstVersion = "4.17.0", scheme = SpringAiEmbeddings.SCHEME, title = "Spring AI Embeddings",
+             syntax = "spring-ai-embeddings:embeddingId", producerOnly = true, category = {
+                     Category.AI
+             }, headersClass = SpringAiEmbeddingsHeaders.class)
+public class SpringAiEmbeddingsEndpoint extends DefaultEndpoint {
+    @Metadata(required = true)
+    @UriPath(description = "The id")
+    private final String embeddingId;
+
+    @UriParam
+    private SpringAiEmbeddingsConfiguration configuration;
+
+    public SpringAiEmbeddingsEndpoint(
+                                      String endpointUri,
+                                      Component component,
+                                      String embeddingId,
+                                      SpringAiEmbeddingsConfiguration configuration) {
+
+        super(endpointUri, component);
+
+        this.embeddingId = embeddingId;
+        this.configuration = configuration;
+    }
+
+    public SpringAiEmbeddingsConfiguration getConfiguration() {
+        return this.configuration;
+    }
+
+    public String getEmbeddingId() {
+        return this.embeddingId;
+    }
+
+    @Override
+    public Producer createProducer() throws Exception {
+        return new SpringAiEmbeddingsProducer(this);
+    }
+
+    @Override
+    public Consumer createConsumer(Processor processor) throws Exception {
+        throw new UnsupportedOperationException("Consumer is not implemented for this component");
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsHeaders.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsHeaders.java
new file mode 100644
index 000000000000..575be7d50868
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsHeaders.java
@@ -0,0 +1,42 @@
+/*
+ * 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.apache.camel.component.springai.embeddings;
+
+import org.apache.camel.spi.Metadata;
+
+public class SpringAiEmbeddingsHeaders {
+
+    @Metadata(description = "The embedding response metadata",
+              javaType = "org.springframework.ai.embedding.EmbeddingResponseMetadata")
+    public static final String EMBEDDING_METADATA = "CamelSpringAiEmbeddingMetadata";
+
+    @Metadata(description = "The index of the embedding in the response", javaType = "Integer")
+    public static final String EMBEDDING_INDEX = "CamelSpringAiEmbeddingIndex";
+
+    @Metadata(description = "The Embedding object", javaType = "org.springframework.ai.embedding.Embedding")
+    public static final String EMBEDDING = "CamelSpringAiEmbedding";
+
+    @Metadata(description = "The input text that was embedded", javaType = "String")
+    public static final String INPUT_TEXT = "CamelSpringAiEmbeddingInputText";
+
+    @Metadata(description = "List of Embedding objects",
+              javaType = "java.util.List<org.springframework.ai.embedding.Embedding>")
+    public static final String EMBEDDINGS = "CamelSpringAiEmbeddings";
+
+    @Metadata(description = "List of input texts that were embedded", javaType = "java.util.List<String>")
+    public static final String INPUT_TEXTS = "CamelSpringAiEmbeddingInputTexts";
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsProducer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsProducer.java
new file mode 100644
index 000000000000..ef401fa5a019
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/main/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsProducer.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.apache.camel.component.springai.embeddings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.support.DefaultProducer;
+import org.springframework.ai.embedding.Embedding;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.embedding.EmbeddingOptions;
+import org.springframework.ai.embedding.EmbeddingRequest;
+import org.springframework.ai.embedding.EmbeddingResponse;
+
+public class SpringAiEmbeddingsProducer extends DefaultProducer {
+    public SpringAiEmbeddingsProducer(SpringAiEmbeddingsEndpoint endpoint) {
+        super(endpoint);
+    }
+
+    @Override
+    public SpringAiEmbeddingsEndpoint getEndpoint() {
+        return (SpringAiEmbeddingsEndpoint) super.getEndpoint();
+    }
+
+    @Override
+    public void process(Exchange exchange) throws Exception {
+        final Message message = exchange.getMessage();
+        final EmbeddingModel model = getEndpoint().getConfiguration().getEmbeddingModel();
+
+        // Support both single String and List<String> as input
+        List<String> inputTexts;
+        Object body = message.getBody();
+
+        if (body instanceof String) {
+            inputTexts = List.of((String) body);
+        } else if (body instanceof List) {
+            inputTexts = (List<String>) body;
+        } else {
+            throw new IllegalArgumentException(
+                    "Message body must be a String or List<String>, but was: "
+                                               + (body != null ? body.getClass().getName() : "null"));
+        }
+
+        final EmbeddingResponse response = model.call(new EmbeddingRequest(
+                inputTexts,
+                EmbeddingOptions.builder().build()));
+
+        if (response.getResults() != null && !response.getResults().isEmpty()) {
+            if (inputTexts.size() == 1) {
+                Embedding embedding = response.getResults().get(0);
+                message.setBody(embedding.getOutput());
+                message.setHeader(SpringAiEmbeddingsHeaders.EMBEDDING_INDEX, embedding.getIndex());
+                message.setHeader(SpringAiEmbeddingsHeaders.EMBEDDING, embedding);
+                message.setHeader(SpringAiEmbeddingsHeaders.INPUT_TEXT, inputTexts.get(0));
+            } else {
+                List<float[]> vectors = new ArrayList<>();
+                List<Embedding> embeddings = new ArrayList<>();
+
+                for (Embedding embedding : response.getResults()) {
+                    vectors.add(embedding.getOutput());
+                    embeddings.add(embedding);
+                }
+
+                message.setBody(vectors);
+                message.setHeader(SpringAiEmbeddingsHeaders.EMBEDDINGS, embeddings);
+                message.setHeader(SpringAiEmbeddingsHeaders.INPUT_TEXTS, inputTexts);
+            }
+        }
+
+        if (response.getMetadata() != null) {
+            message.setHeader(SpringAiEmbeddingsHeaders.EMBEDDING_METADATA, response.getMetadata());
+        }
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/OllamaTestSupport.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/OllamaTestSupport.java
new file mode 100644
index 000000000000..b605c3925dfc
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/OllamaTestSupport.java
@@ -0,0 +1,66 @@
+/*
+ * 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.apache.camel.component.springai.embeddings;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.test.infra.ollama.services.OllamaService;
+import org.apache.camel.test.infra.ollama.services.OllamaServiceFactory;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.ollama.OllamaEmbeddingModel;
+import org.springframework.ai.ollama.api.OllamaApi;
+import org.springframework.ai.ollama.api.OllamaEmbeddingOptions;
+
+/**
+ * Base test support class for Spring AI Ollama integration tests.
+ */
+public class OllamaTestSupport extends CamelTestSupport {
+
+    protected EmbeddingModel embeddingModel;
+
+    @RegisterExtension
+    static OllamaService OLLAMA = OllamaServiceFactory.createSingletonService();
+
+    @Override
+    protected void setupResources() throws Exception {
+        super.setupResources();
+
+        embeddingModel = createModel();
+    }
+
+    protected void bindEmbeddingModel(CamelContext camelContext) {
+        camelContext.getRegistry().bind("embeddingModel", embeddingModel);
+    }
+
+    protected EmbeddingModel createModel() {
+        OllamaApi ollamaApi = OllamaApi.builder()
+                .baseUrl(OLLAMA.baseUrl())
+                .build();
+
+        OllamaEmbeddingOptions ollamaOptions = OllamaEmbeddingOptions.builder()
+                .model(OLLAMA.modelName())
+                .build();
+
+        EmbeddingModel embeddingModel = OllamaEmbeddingModel.builder()
+                .ollamaApi(ollamaApi)
+                .defaultOptions(ollamaOptions)
+                .build();
+
+        return embeddingModel;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponentTest.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponentTest.java
new file mode 100644
index 000000000000..627555d5136e
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsComponentTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.apache.camel.component.springai.embeddings;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Message;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.embedding.Embedding;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.embedding.EmbeddingRequest;
+import org.springframework.ai.embedding.EmbeddingResponse;
+import org.springframework.ai.embedding.EmbeddingResponseMetadata;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SpringAiEmbeddingsComponentTest extends CamelTestSupport {
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+
+        SpringAiEmbeddingsComponent component
+                = context.getComponent(SpringAiEmbeddings.SCHEME, SpringAiEmbeddingsComponent.class);
+
+        // Use a simple mock embedding model for testing
+        EmbeddingModel embeddingModel = new EmbeddingModel() {
+            @Override
+            public EmbeddingResponse call(EmbeddingRequest request) {
+                List<Embedding> embeddings = new ArrayList<>();
+                int index = 0;
+                for (String text : request.getInstructions()) {
+                    float[] vector = new float[] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };
+                    embeddings.add(new Embedding(vector, index++));
+                }
+                return new EmbeddingResponse(embeddings, new EmbeddingResponseMetadata());
+            }
+
+            @Override
+            public float[] embed(Document document) {
+                return new float[] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };
+            }
+
+            @Override
+            public List<float[]> embed(List<String> texts) {
+                List<float[]> results = new ArrayList<>();
+                for (int i = 0; i < texts.size(); i++) {
+                    results.add(new float[] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f });
+                }
+                return results;
+            }
+
+            @Override
+            public int dimensions() {
+                return 5;
+            }
+        };
+
+        component.getConfiguration().setEmbeddingModel(embeddingModel);
+
+        return context;
+    }
+
+    @Test
+    public void testSimpleEmbedding() {
+
+        Message first = fluentTemplate.to("spring-ai-embeddings:first")
+                .withBody("hi")
+                .request(Message.class);
+
+        float[] firstEmbedding = first.getBody(float[].class);
+        assertThat(firstEmbedding).isNotNull();
+        assertThat(firstEmbedding).hasSize(5);
+        assertThat(firstEmbedding).containsExactly(0.1f, 0.2f, 0.3f, 0.4f, 0.5f);
+
+        String firstInputText = first.getHeader(SpringAiEmbeddingsHeaders.INPUT_TEXT, String.class);
+        assertThat(firstInputText).isEqualTo("hi");
+
+        Integer embeddingIndex = first.getHeader(SpringAiEmbeddingsHeaders.EMBEDDING_INDEX, Integer.class);
+        assertThat(embeddingIndex).isEqualTo(0);
+
+        Message second = fluentTemplate.to("spring-ai-embeddings:second")
+                .withBody("hello")
+                .request(Message.class);
+
+        float[] secondEmbedding = second.getBody(float[].class);
+        ;
+        assertThat(secondEmbedding).isNotNull();
+        assertThat(secondEmbedding).hasSize(5);
+        assertThat(secondEmbedding).containsExactly(0.1f, 0.2f, 0.3f, 0.4f, 0.5f);
+
+        String secondInputText = second.getHeader(SpringAiEmbeddingsHeaders.INPUT_TEXT, String.class);
+        assertThat(secondInputText).isEqualTo("hello");
+
+        // Verify both embeddings have the same dimensionality
+        assertThat(firstEmbedding).hasSameSizeAs(secondEmbedding);
+    }
+
+    @Test
+    public void testBatchEmbedding() {
+        List<String> texts = Arrays.asList("hello", "world", "test");
+
+        Message result = fluentTemplate.to("spring-ai-embeddings:batch")
+                .withBody(texts)
+                .request(Message.class);
+
+        List<float[]> vectors = result.getBody(List.class);
+        assertThat(vectors).isNotNull();
+        assertThat(vectors).hasSize(3);
+
+        List<String> inputTexts = result.getHeader(SpringAiEmbeddingsHeaders.INPUT_TEXTS, List.class);
+        assertThat(inputTexts).isEqualTo(texts);
+
+        // Verify all vectors have the same dimensionality
+        assertThat(vectors.get(0)).hasSameSizeAs(vectors.get(1));
+        assertThat(vectors.get(0)).hasSameSizeAs(vectors.get(2));
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsIT.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsIT.java
new file mode 100644
index 000000000000..6e359814be19
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/java/org/apache/camel/component/springai/embeddings/SpringAiEmbeddingsIT.java
@@ -0,0 +1,128 @@
+/*
+ * 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.apache.camel.component.springai.embeddings;
+
+import java.util.List;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Integration test for Spring AI Embeddings component using Ollama.
+ */
+@DisabledIfSystemProperty(named = "ci.env.name", matches = ".*", disabledReason = "Disabled unless running in CI")
+public class SpringAiEmbeddingsIT extends OllamaTestSupport {
+
+    @Test
+    public void testSimpleEmbeddingWithOllama() {
+        var exchange = template.request("direct:embeddings", e -> {
+            e.getIn().setBody("The quick brown fox jumps over the lazy dog");
+        });
+
+        float[] embeddingVector = exchange.getMessage().getBody(float[].class);
+
+        assertThat(embeddingVector).isNotNull();
+        assertThat(embeddingVector).isNotEmpty();
+        assertThat(embeddingVector.length).isGreaterThan(0);
+    }
+
+    @Test
+    public void testEmbeddingWithInputTextHeader() {
+        var exchange = template.request("direct:embeddings", e -> {
+            e.getIn().setBody("Hello world");
+        });
+
+        String inputText = exchange.getMessage().getHeader(SpringAiEmbeddingsHeaders.INPUT_TEXT, String.class);
+        assertThat(inputText).isEqualTo("Hello world");
+
+        float[] embeddingVector = exchange.getMessage().getBody(float[].class);
+        assertThat(embeddingVector).isNotNull();
+    }
+
+    @Test
+    public void testMultipleEmbeddingRequests() {
+        var exchange1 = template.request("direct:embeddings", e -> {
+            e.getIn().setBody("cat");
+        });
+
+        var exchange2 = template.request("direct:embeddings", e -> {
+            e.getIn().setBody("dog");
+        });
+
+        float[] embedding1 = exchange1.getMessage().getBody(float[].class);
+        float[] embedding2 = exchange2.getMessage().getBody(float[].class);
+
+        assertThat(embedding1).isNotNull();
+        assertThat(embedding2).isNotNull();
+
+        // Both embeddings should have the same dimensionality
+        assertThat(embedding1.length).isEqualTo(embedding2.length);
+
+        // The embeddings should be different (not identical vectors)
+        assertThat(embedding1).isNotEqualTo(embedding2);
+    }
+
+    @Test
+    public void testEmbeddingMetadata() {
+        var exchange = template.request("direct:embeddings", e -> {
+            e.getIn().setBody("test embedding");
+        });
+
+        Integer embeddingIndex = exchange.getMessage().getHeader(SpringAiEmbeddingsHeaders.EMBEDDING_INDEX, Integer.class);
+        assertThat(embeddingIndex).isNotNull();
+        assertThat(embeddingIndex).isEqualTo(0);
+    }
+
+    @Test
+    public void testBatchEmbeddings() {
+        List<String> texts = List.of("cat", "dog", "bird");
+
+        var exchange = template.request("direct:embeddings", e -> {
+            e.getIn().setBody(texts);
+        });
+
+        List<float[]> vectors = exchange.getMessage().getBody(List.class);
+        assertThat(vectors).isNotNull();
+        assertThat(vectors).hasSize(3);
+
+        List<String> inputTexts = exchange.getMessage().getHeader(SpringAiEmbeddingsHeaders.INPUT_TEXTS, List.class);
+        assertThat(inputTexts).isEqualTo(texts);
+
+        // Verify all vectors have the same dimensionality
+        assertThat(vectors.get(0).length).isEqualTo(vectors.get(1).length);
+        assertThat(vectors.get(0).length).isEqualTo(vectors.get(2).length);
+
+        // Verify vectors are not identical
+        assertThat(vectors.get(0)).isNotEqualTo(vectors.get(1));
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                this.getCamelContext().getRegistry().bind("embeddingModel", embeddingModel);
+
+                from("direct:embeddings")
+                        .to("spring-ai-embeddings:test?embeddingModel=#embeddingModel");
+            }
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/resources/log4j2.properties b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/resources/log4j2.properties
new file mode 100644
index 000000000000..ff650fb94072
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-embeddings/src/test/resources/log4j2.properties
@@ -0,0 +1,33 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-spring-ai-embeddings.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+appender.out.type = Console
+appender.out.name = out
+appender.out.layout.type = PatternLayout
+appender.out.layout.pattern = [%30.30t] %-30.30c{1} %-5p %m%n
+
+rootLogger.level = INFO
+rootLogger.appenderRef.file.ref = file
+
+logger.spring.name=org.springframework.ai.chat.client.advisor
+logger.spring.level=DEBUG
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/pom.xml b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/pom.xml
new file mode 100644
index 000000000000..ab43e9baed63
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/pom.xml
@@ -0,0 +1,73 @@
+<?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>
+
+    <parent>
+        <groupId>org.apache.camel</groupId>
+        <artifactId>camel-spring-ai-parent</artifactId>
+        <version>4.17.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>camel-spring-ai-tools</artifactId>
+    <packaging>jar</packaging>
+    <name>Camel :: Spring AI :: Tools</name>
+    <description>Spring AI Tools and Function Calling Features</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-support</artifactId>
+        </dependency>
+
+        <!-- Spring AI core dependencies -->
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-model</artifactId>
+        </dependency>
+
+        <!-- test -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- for testing -->
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test-spring-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsComponentConfigurer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsComponentConfigurer.java
new file mode 100644
index 000000000000..42fde1e49730
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsComponentConfigurer.java
@@ -0,0 +1,66 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.tools;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class SpringAiToolsComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        SpringAiToolsComponent target = (SpringAiToolsComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true;
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true;
+        case "configuration": target.setConfiguration(property(camelContext, org.apache.camel.component.springai.tools.SpringAiToolsConfiguration.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return boolean.class;
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": return boolean.class;
+        case "configuration": return org.apache.camel.component.springai.tools.SpringAiToolsConfiguration.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        SpringAiToolsComponent target = (SpringAiToolsComponent) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "autowiredenabled":
+        case "autowiredEnabled": return target.isAutowiredEnabled();
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+        case "configuration": return target.getConfiguration();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpointConfigurer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpointConfigurer.java
new file mode 100644
index 000000000000..e7d91f7260ef
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpointConfigurer.java
@@ -0,0 +1,103 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.tools;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class SpringAiToolsEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+        SpringAiToolsEndpoint target = (SpringAiToolsEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true;
+        case "description": target.setDescription(property(camelContext, java.lang.String.class, value)); return true;
+        case "exceptionhandler":
+        case "exceptionHandler": target.setExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true;
+        case "exchangepattern":
+        case "exchangePattern": target.setExchangePattern(property(camelContext, org.apache.camel.ExchangePattern.class, value)); return true;
+        case "inputtype":
+        case "inputType": target.setInputType(property(camelContext, java.lang.Class.class, value)); return true;
+        case "lazystartproducer":
+        case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true;
+        case "name": target.setName(property(camelContext, java.lang.String.class, value)); return true;
+        case "parameters": target.setParameters(property(camelContext, java.util.Map.class, value)); return true;
+        case "returndirect":
+        case "returnDirect": target.setReturnDirect(property(camelContext, boolean.class, value)); return true;
+        case "tags": target.setTags(property(camelContext, java.lang.String.class, value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": return boolean.class;
+        case "description": return java.lang.String.class;
+        case "exceptionhandler":
+        case "exceptionHandler": return org.apache.camel.spi.ExceptionHandler.class;
+        case "exchangepattern":
+        case "exchangePattern": return org.apache.camel.ExchangePattern.class;
+        case "inputtype":
+        case "inputType": return java.lang.Class.class;
+        case "lazystartproducer":
+        case "lazyStartProducer": return boolean.class;
+        case "name": return java.lang.String.class;
+        case "parameters": return java.util.Map.class;
+        case "returndirect":
+        case "returnDirect": return boolean.class;
+        case "tags": return java.lang.String.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        SpringAiToolsEndpoint target = (SpringAiToolsEndpoint) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "bridgeerrorhandler":
+        case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+        case "description": return target.getDescription();
+        case "exceptionhandler":
+        case "exceptionHandler": return target.getExceptionHandler();
+        case "exchangepattern":
+        case "exchangePattern": return target.getExchangePattern();
+        case "inputtype":
+        case "inputType": return target.getInputType();
+        case "lazystartproducer":
+        case "lazyStartProducer": return target.isLazyStartProducer();
+        case "name": return target.getName();
+        case "parameters": return target.getParameters();
+        case "returndirect":
+        case "returnDirect": return target.isReturnDirect();
+        case "tags": return target.getTags();
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getCollectionValueType(Object target, String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "inputtype":
+        case "inputType": return java.lang.Object.class;
+        case "parameters": return java.lang.String.class;
+        default: return null;
+        }
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpointUriFactory.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpointUriFactory.java
new file mode 100644
index 000000000000..99bff566ceda
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpointUriFactory.java
@@ -0,0 +1,82 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.springai.tools;
+
+import javax.annotation.processing.Generated;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.spi.EndpointUriFactory;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.GenerateEndpointUriFactoryMojo")
+public class SpringAiToolsEndpointUriFactory extends org.apache.camel.support.component.EndpointUriFactorySupport implements EndpointUriFactory {
+
+    private static final String BASE = ":toolId";
+
+    private static final Set<String> PROPERTY_NAMES;
+    private static final Set<String> SECRET_PROPERTY_NAMES;
+    private static final Map<String, String> MULTI_VALUE_PREFIXES;
+    static {
+        Set<String> props = new HashSet<>(11);
+        props.add("bridgeErrorHandler");
+        props.add("description");
+        props.add("exceptionHandler");
+        props.add("exchangePattern");
+        props.add("inputType");
+        props.add("lazyStartProducer");
+        props.add("name");
+        props.add("parameters");
+        props.add("returnDirect");
+        props.add("tags");
+        props.add("toolId");
+        PROPERTY_NAMES = Collections.unmodifiableSet(props);
+        SECRET_PROPERTY_NAMES = Collections.emptySet();
+        Map<String, String> prefixes = new HashMap<>(1);
+        prefixes.put("parameters", "parameter.");
+        MULTI_VALUE_PREFIXES = Collections.unmodifiableMap(prefixes);
+    }
+
+    @Override
+    public boolean isEnabled(String scheme) {
+        return "spring-ai-tools".equals(scheme);
+    }
+
+    @Override
+    public String buildUri(String scheme, Map<String, Object> properties, boolean encode) throws URISyntaxException {
+        String syntax = scheme + BASE;
+        String uri = syntax;
+
+        Map<String, Object> copy = new HashMap<>(properties);
+
+        uri = buildPathParameter(syntax, uri, "toolId", null, true, copy);
+        uri = buildQueryParameters(uri, copy, encode);
+        return uri;
+    }
+
+    @Override
+    public Set<String> propertyNames() {
+        return PROPERTY_NAMES;
+    }
+
+    @Override
+    public Set<String> secretPropertyNames() {
+        return SECRET_PROPERTY_NAMES;
+    }
+
+    @Override
+    public Map<String, String> multiValuePrefixes() {
+        return MULTI_VALUE_PREFIXES;
+    }
+
+    @Override
+    public boolean isLenientProperties() {
+        return false;
+    }
+}
+
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/org/apache/camel/component/springai/tools/spring-ai-tools.json b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/org/apache/camel/component/springai/tools/spring-ai-tools.json
new file mode 100644
index 000000000000..ef421ccf04d6
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/org/apache/camel/component/springai/tools/spring-ai-tools.json
@@ -0,0 +1,45 @@
+{
+  "component": {
+    "kind": "component",
+    "name": "spring-ai-tools",
+    "title": "Spring AI Tools",
+    "description": "Spring AI Tools and Function Calling Features",
+    "deprecated": false,
+    "firstVersion": "4.17.0",
+    "label": "ai",
+    "javaType": "org.apache.camel.component.springai.tools.SpringAiToolsComponent",
+    "supportLevel": "Preview",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-spring-ai-tools",
+    "version": "4.17.0-SNAPSHOT",
+    "scheme": "spring-ai-tools",
+    "extendsScheme": "",
+    "syntax": "spring-ai-tools:toolId",
+    "async": false,
+    "api": false,
+    "consumerOnly": false,
+    "producerOnly": false,
+    "lenientProperties": false,
+    "browsable": false,
+    "remote": true
+  },
+  "componentProperties": {
+    "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "common", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.springai.tools.SpringAiToolsConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The configuration." },
+    "bridgeErrorHandler": { "index": 1, "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming messages, or the like [...]
+    "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
+    "autowiredEnabled": { "index": 3, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
+  },
+  "properties": {
+    "toolId": { "index": 0, "kind": "path", "displayName": "Tool Id", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The tool id" },
+    "tags": { "index": 1, "kind": "parameter", "displayName": "Tags", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The tags for the tools" },
+    "description": { "index": 2, "kind": "parameter", "displayName": "Description", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Tool description" },
+    "inputType": { "index": 3, "kind": "parameter", "displayName": "Input Type", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.Class<java.lang.Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Input type class for the tool" },
+    "name": { "index": 4, "kind": "parameter", "displayName": "Name", "group": "consumer", "label": "consumer", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Tool name" },
+    "parameters": { "index": 5, "kind": "parameter", "displayName": "Parameters", "group": "consumer", "label": "consumer", "required": false, "type": "object", "javaType": "java.util.Map<java.lang.String, java.lang.String>", "prefix": "parameter.", "multiValue": true, "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "List of Tool parameters with optional metadata. Format: parameter.=, parameter..description=, parameter..required=, parameter [...]
+    "returnDirect": { "index": 6, "kind": "parameter", "displayName": "Return Direct", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the tool result should be returned directly or passed back to the model. Default is false, meaning the result is passed back to the model for further processing." },
+    "bridgeErrorHandler": { "index": 7, "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions (if possible) occurred while the Camel consumer is trying to pickup incoming  [...]
+    "exceptionHandler": { "index": 8, "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By def [...]
+    "exchangePattern": { "index": 9, "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "enum", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], "deprecated": false, "autowired": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." },
+    "lazyStartProducer": { "index": 10, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produ [...]
+  }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/component.properties
new file mode 100644
index 000000000000..8665369249e5
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/component.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+components=spring-ai-tools
+groupId=org.apache.camel
+artifactId=camel-spring-ai-tools
+version=4.17.0-SNAPSHOT
+projectName=Camel :: Spring AI :: Tools
+projectDescription=Spring AI Tools and Function Calling Features
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-tools b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-tools
new file mode 100644
index 000000000000..b4fb0978c931
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/component/spring-ai-tools
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.tools.SpringAiToolsComponent
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-tools-component b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-tools-component
new file mode 100644
index 000000000000..e23dd8433224
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-tools-component
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.tools.SpringAiToolsComponentConfigurer
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-tools-endpoint b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-tools-endpoint
new file mode 100644
index 000000000000..bbc9a5a863b4
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/configurer/spring-ai-tools-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.tools.SpringAiToolsEndpointConfigurer
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-tools-endpoint b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-tools-endpoint
new file mode 100644
index 000000000000..0e8bdb1b617a
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/generated/resources/META-INF/services/org/apache/camel/urifactory/spring-ai-tools-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.springai.tools.SpringAiToolsEndpointUriFactory
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/docs/spring-ai-tools-component.adoc b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/docs/spring-ai-tools-component.adoc
new file mode 100644
index 000000000000..3e59ce053595
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/docs/spring-ai-tools-component.adoc
@@ -0,0 +1,301 @@
+= Spring AI Tools Component
+:doctitle: Spring AI Tools
+:shortname: spring-ai-tools
+:artifactid: camel-spring-ai-tools
+:description: Spring AI Tools and Function Calling Features
+:since: 4.17
+:supportlevel: Preview
+:tabs-sync-option:
+:component-header: Both producer and consumer are supported
+//Manually maintained attributes
+:group: AI
+:camel-spring-boot-name: spring-ai-tools
+
+*Since Camel {since}*
+
+*{component-header}*
+
+The Spring AI Tools component provides support for integrating function calling capabilities with https://docs.spring.io/spring-ai/reference/api/tools.html[Spring AI] chat models. This component enables AI models to invoke Camel routes as tools/functions, allowing the model to perform actions, retrieve data, or interact with external systems during conversations.
+
+Function calling (also known as tool use) allows AI models to:
+
+* Execute Camel routes as callable functions
+* Retrieve data from external systems during conversations
+* Perform actions based on conversation context
+* Extend AI capabilities with custom business logic
+* Build Agentic AI applications
+
+== URI format
+
+----
+spring-ai-tools:toolId[?options]
+----
+
+Where *toolId* is a unique identifier for the tool or tool set
+
+
+// component-configure options: START
+
+// component-configure options: END
+
+// component options: START
+include::partial$component-configure-options.adoc[]
+include::partial$component-endpoint-options.adoc[]
+// component options: END
+
+// endpoint options: START
+
+// endpoint options: END
+
+// component headers: START
+include::partial$component-endpoint-headers.adoc[]
+// component headers: END
+
+== Usage
+
+The Camel Spring AI Tools component is used as a consumer to expose Camel routes as tools that AI models can call. The route processes tool invocations and returns results to the model.
+
+IMPORTANT: This component is designed to work with the xref:spring-ai-chat-component.adoc[Spring AI Chat Component]. Define tools using `spring-ai-tools` consumer endpoints, then use them via `spring-ai-chat` with the `tags` parameter. See the <<Integration with Spring AI Chat Component>> section below for details.
+
+=== Basic Tool Definition
+
+[source,java]
+----
+from("spring-ai-tools:weatherTool?tags=weather&description=Get current weather for a location")
+    .log("Getting weather for ${header.location}")
+    .to("https://api.weather.example.com/current")
+    .convertBodyTo(String.class);
+----
+
+The AI model can invoke this tool when it needs weather information, and the route will execute to retrieve the data.
+
+=== Tool with Parameters
+
+Tools can define typed parameters using the `parameter.*` prefix:
+
+[source,java]
+----
+from("spring-ai-tools:weatherTool?tags=weather" +
+    "&description=Get weather forecast" +
+    "&name=getWeather" +
+    "&parameter.location=string" +
+    "&parameter.location.description=The city and state" +
+    "&parameter.location.required=true" +
+    "&parameter.unit=string" +
+    "&parameter.unit.description=Temperature unit" +
+    "&parameter.unit.enum=C,F" +
+    "&parameter.unit.required=false")
+    .log("Location: ${header.location}, Unit: ${header.unit}")
+    .process(exchange -> {
+        String location = exchange.getIn().getHeader("location", String.class);
+        String unit = exchange.getIn().getHeader("unit", String.class);
+        // Fetch and process weather data
+        exchange.getIn().setBody("Weather data for " + location);
+    });
+----
+
+Parameter metadata is passed to the AI model to help it understand how to call the tool.
+
+=== Using Java Classes for Input
+
+Instead of parameter maps, you can use a Java class to define the tool's input schema:
+
+[source,java]
+----
+public class WeatherRequest {
+    private String location;
+    private String unit;
+    // getters and setters
+}
+
+from("spring-ai-tools:weatherTool?tags=weather" +
+    "&description=Get weather forecast" +
+    "&inputType=com.example.WeatherRequest")
+    .log("Processing weather request")
+    .process(exchange -> {
+        // Headers are set from the input object fields
+        String location = exchange.getIn().getHeader("location", String.class);
+        exchange.getIn().setBody("Weather for " + location);
+    });
+----
+
+=== Return Direct Mode
+
+By default, tool results are passed back to the AI model for further processing. Use `returnDirect=true` to return the tool's result directly to the user:
+
+[source,java]
+----
+from("spring-ai-tools:calculator?tags=math" +
+    "&description=Calculate mathematical expressions" +
+    "&returnDirect=true")
+    .process(exchange -> {
+        // Calculate and return result immediately to user
+        exchange.getIn().setBody("Result: 42");
+    });
+----
+
+=== Tool Organization with Tags
+
+Tags are used to organize and group tools. The xref:spring-ai-chat-component.adoc[Spring AI Chat Component] uses tags to discover which tools should be available to the AI model:
+
+[source,java]
+----
+// Define tools with tags
+from("spring-ai-tools:weatherTool?tags=weather,external-api" +
+    "&description=Get current weather for a location")
+    .to("https://weather-api.example.com");
+
+from("spring-ai-tools:timeTool?tags=time,utility" +
+    "&description=Get current time")
+    .process(exchange -> exchange.getIn().setBody(LocalDateTime.now().toString()));
+
+// Use tools via spring-ai-chat component with tags
+from("direct:weatherChat")
+    .to("spring-ai-chat:chat?tags=weather,time&chatClient=#chatClient"); // Both tools available
+
+from("direct:utilityChat")
+    .to("spring-ai-chat:chat?tags=utility&chatClient=#chatClient"); // Only time tool available
+----
+
+A single tool can have multiple tags, allowing flexible tool organization and reuse across different chat contexts.
+
+=== Error Handling
+
+If a tool execution fails, the error is propagated back to the AI model, which can handle it or report it to the user:
+
+[source,java]
+----
+from("spring-ai-tools:riskyOperation?tags=ops&description=Perform risky operation")
+    .onException(Exception.class)
+        .handled(true)
+        .setBody(simple("Error occurred: ${exception.message}"))
+    .end()
+    .to("direct:performOperation");
+----
+
+== Complete Example
+
+Here's a complete example showing tool registration and usage:
+
+[source,java]
+----
+// Register a database query tool
+from("spring-ai-tools:queryDb?tags=database" +
+    "&description=Query the customer database" +
+    "&parameter.query=string" +
+    "&parameter.query.description=SQL query to execute" +
+    "&parameter.query.required=true")
+    .to("jdbc:dataSource")
+    .convertBodyTo(String.class);
+
+// Register a notification tool
+from("spring-ai-tools:sendNotification?tags=notifications" +
+    "&description=Send notification to user" +
+    "&parameter.message=string" +
+    "&parameter.message.required=true" +
+    "&returnDirect=true")
+    .to("telegram:bots")
+    .setBody(constant("Notification sent"));
+
+// Chat endpoint with access to both tools
+from("direct:assistantChat")
+    .to("spring-ai-chat:assistant?tags=database,notifications&chatClient=#chatClient")
+    .log("Response: ${body}");
+----
+
+When a user sends "Show me all customers and notify me when done" to the assistant endpoint:
+1. The model may call the `queryDb` tool to fetch customers
+2. The model may then call `sendNotification` to inform the user
+3. The final response is returned
+
+== Headers
+
+When a tool is invoked, all tool parameters are set as message headers. The parameter names correspond to header names:
+
+[source,java]
+----
+// Tool definition with parameter.location=string
+from("spring-ai-tools:myTool?tags=t&parameter.location=string")
+    .process(exchange -> {
+        String location = exchange.getIn().getHeader("location", String.class);
+        // Process with location parameter
+    });
+----
+
+== Integration with Spring AI Chat Component
+
+The Spring AI Tools component is designed to work seamlessly with the xref:spring-ai-chat-component.adoc[Spring AI Chat Component]. Define tools using `spring-ai-tools` consumer endpoints, then reference them in `spring-ai-chat` endpoints using the `tags` parameter to enable function calling capabilities.
+
+=== How the Integration Works
+
+When you configure the `tags` parameter on a `spring-ai-chat` endpoint:
+
+1. The chat component discovers all `spring-ai-tools` consumer endpoints registered with matching tags
+2. These tools are automatically registered with Spring AI's ChatClient as available functions
+3. When a user sends a message, the LLM can decide to call these tools based on the conversation context
+4. The chat component executes the corresponding Camel routes and sends results back to the LLM
+5. The LLM processes tool results and generates the final response
+6. Multiple tools can be called in sequence if needed
+
+=== Complete Integration Example
+
+[source,java]
+----
+public class ChatWithToolsConfig {
+
+    public RouteBuilder chatWithToolsRoutes() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                // Define tools using spring-ai-tools consumer
+                from("spring-ai-tools:queryCustomers?tags=database" +
+                     "&description=Search the customer database" +
+                     "&parameter.searchTerm=string" +
+                     "&parameter.searchTerm.description=Name or company to search for" +
+                     "&parameter.searchTerm.required=true")
+                    .to("sql:SELECT * FROM customers WHERE name LIKE '%${header.searchTerm}%'?dataSource=#dataSource")
+                    .marshal().json();
+
+                from("spring-ai-tools:sendEmail?tags=email" +
+                     "&description=Send an email to a customer" +
+                     "&parameter.email=string" +
+                     "&parameter.subject=string" +
+                     "&parameter.body=string")
+                    .to("smtp://mailserver");
+
+                from("spring-ai-tools:createInvoice?tags=billing" +
+                     "&description=Create an invoice for a customer" +
+                     "&parameter.customerId=string" +
+                     "&parameter.amount=number")
+                    .to("bean:invoiceService?method=create");
+
+                // Chat endpoint with access to all tools
+                from("direct:assistantChat")
+                    .to("spring-ai-chat:assistant?tags=database,email,billing&chatClient=#chatClient");
+
+                // Chat with specific tool subsets
+                from("direct:customerServiceChat")
+                    .to("spring-ai-chat:support?tags=database,email&chatClient=#chatClient");
+
+                from("direct:billingChat")
+                    .to("spring-ai-chat:billing?tags=database,billing&chatClient=#chatClient");
+            }
+        };
+    }
+}
+
+// Usage examples
+String response1 = template.requestBody("direct:assistantChat",
+    "Find customers with 'Smith' in their name and send them a promotional email", String.class);
+
+String response2 = template.requestBody("direct:billingChat",
+    "Create an invoice for customer ABC123 for $500", String.class);
+----
+
+== See Also
+
+* xref:spring-ai-chat-component.adoc[Spring AI Chat Component]
+* https://docs.spring.io/spring-ai/reference/[Spring AI Documentation]
+* https://docs.spring.io/spring-ai/reference/api/tools.html[Spring AI Function Calling]
+
+// include::spring-boot:partial$starter.adoc[]
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiTools.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiTools.java
new file mode 100644
index 000000000000..741bddfedf7c
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiTools.java
@@ -0,0 +1,26 @@
+/*
+ * 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.apache.camel.component.springai.tools;
+
+public final class SpringAiTools {
+
+    public static final String SCHEME = "spring-ai-tools";
+    public static final String NO_TOOLS_CALLED_HEADER = "SpringAiToolsNoToolsCalled";
+
+    private SpringAiTools() {
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsComponent.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsComponent.java
new file mode 100644
index 000000000000..d4badfac1bae
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsComponent.java
@@ -0,0 +1,82 @@
+/*
+ * 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.apache.camel.component.springai.tools;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Endpoint;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.Component;
+import org.apache.camel.support.DefaultComponent;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.PropertiesHelper;
+
+import static org.apache.camel.component.springai.tools.SpringAiTools.SCHEME;
+
+@Component(SCHEME)
+public class SpringAiToolsComponent extends DefaultComponent {
+
+    @Metadata
+    SpringAiToolsConfiguration configuration;
+
+    public SpringAiToolsComponent() {
+        this(null);
+    }
+
+    public SpringAiToolsComponent(CamelContext context) {
+        super(context);
+        this.configuration = new SpringAiToolsConfiguration();
+    }
+
+    public SpringAiToolsConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    /**
+     * The configuration.
+     */
+    public void setConfiguration(SpringAiToolsConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    @Override
+    protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
+        if (ObjectHelper.isEmpty(remaining)) {
+            throw new IllegalArgumentException(
+                    "Tool ID must be configured on the endpoint using syntax spring-ai-tools:toolId");
+        }
+
+        final String toolId = remaining;
+        final String tagList = parameters.get("tags").toString();
+        if (ObjectHelper.isEmpty(tagList)) {
+            throw new IllegalArgumentException("At least one tag must be specified");
+        }
+
+        SpringAiToolsConfiguration springAiToolsConfiguration = this.configuration.copy();
+
+        Map<String, Object> toolParameters = PropertiesHelper.extractProperties(parameters, "parameter.");
+        SpringAiToolsEndpoint endpoint
+                = new SpringAiToolsEndpoint(uri, this, toolId, tagList, springAiToolsConfiguration);
+        endpoint.setParameters(toolParameters.entrySet().stream()
+                .collect(Collectors.toMap(Map.Entry::getKey, e -> (String) e.getValue())));
+
+        setProperties(endpoint, parameters);
+        return endpoint;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsConfiguration.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsConfiguration.java
new file mode 100644
index 000000000000..841a437a4cbb
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsConfiguration.java
@@ -0,0 +1,31 @@
+/*
+ * 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.apache.camel.component.springai.tools;
+
+import org.apache.camel.spi.UriParams;
+
+@UriParams
+public class SpringAiToolsConfiguration implements Cloneable {
+
+    public SpringAiToolsConfiguration copy() {
+        try {
+            return (SpringAiToolsConfiguration) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsConsumer.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsConsumer.java
new file mode 100644
index 000000000000..680a45495f66
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * 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.apache.camel.component.springai.tools;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.Processor;
+import org.apache.camel.support.DefaultConsumer;
+
+public class SpringAiToolsConsumer extends DefaultConsumer {
+
+    public SpringAiToolsConsumer(Endpoint endpoint, Processor processor) {
+        super(endpoint, processor);
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpoint.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpoint.java
new file mode 100644
index 000000000000..21e1209f1544
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpoint.java
@@ -0,0 +1,416 @@
+/*
+ * 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.apache.camel.component.springai.tools;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.camel.Category;
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.component.springai.tools.spec.CamelToolExecutorCache;
+import org.apache.camel.component.springai.tools.spec.CamelToolSpecification;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.support.DefaultEndpoint;
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.ai.tool.function.FunctionToolCallback;
+import org.springframework.ai.tool.metadata.ToolMetadata;
+
+import static org.apache.camel.component.springai.tools.SpringAiTools.SCHEME;
+
+@UriEndpoint(firstVersion = "4.17.0", scheme = SCHEME,
+             title = "Spring AI Tools",
+             syntax = "spring-ai-tools:toolId",
+             category = { Category.AI })
+public class SpringAiToolsEndpoint extends DefaultEndpoint {
+
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    @Metadata(required = true)
+    @UriPath(description = "The tool id")
+    private final String toolId;
+
+    @Metadata(required = true)
+    @UriParam(description = "The tags for the tools")
+    private String tags;
+
+    @UriParam
+    private SpringAiToolsConfiguration configuration;
+
+    @Metadata(label = "consumer")
+    @UriParam(description = "Tool description")
+    private String description;
+
+    @Metadata(label = "consumer")
+    @UriParam(description = "Tool name")
+    private String name;
+
+    @Metadata(label = "consumer")
+    @UriParam(description = "List of Tool parameters with optional metadata. "
+                            + "Format: parameter.<name>=<type>, parameter.<name>.description=<text>, "
+                            + "parameter.<name>.required=<true|false>, parameter.<name>.enum=<value1,value2>. "
+                            + "Example: parameter.location=string, parameter.location.description=The city and state, "
+                            + "parameter.location.required=true, parameter.unit.enum=C,F",
+              prefix = "parameter.", multiValue = true)
+    private Map<String, String> parameters;
+
+    @Metadata(label = "consumer")
+    @UriParam(description = "Input type class for the tool")
+    private Class<?> inputType;
+
+    @Metadata(label = "consumer")
+    @UriParam(description = "Whether the tool result should be returned directly or passed back to the model. "
+                            + "Default is false, meaning the result is passed back to the model for further processing.",
+              defaultValue = "false")
+    private boolean returnDirect;
+
+    public SpringAiToolsEndpoint(String uri, SpringAiToolsComponent component, String toolId, String tags,
+                                 SpringAiToolsConfiguration configuration) {
+        super(uri, component);
+        this.toolId = toolId;
+        this.tags = tags;
+        this.configuration = configuration;
+    }
+
+    @Override
+    public Producer createProducer() throws Exception {
+        throw new UnsupportedOperationException(
+                "The spring-ai-tools component does not support producer mode. "
+                                                + "Use the spring-ai-chat component with tags parameter to invoke tools. "
+                                                + "Example: spring-ai-chat:chat?tags=weather&chatClient=#chatClient");
+    }
+
+    @Override
+    public Consumer createConsumer(Processor processor) throws Exception {
+        if (description == null) {
+            throw new IllegalArgumentException(
+                    "In order to use the spring-ai-tools component as a consumer, you need to specify at least description");
+        }
+
+        final String toolName;
+        if (name != null) {
+            toolName = name;
+        } else {
+            toolName = toolId;
+        }
+
+        final SpringAiToolsConsumer springAiToolsConsumer = new SpringAiToolsConsumer(this, processor);
+        configureConsumer(springAiToolsConsumer);
+
+        // Create a function that executes the Camel route
+        java.util.function.Function<java.util.Map<String, Object>, String> function = args -> {
+            try {
+                org.apache.camel.Exchange exchange = createExchange();
+                // Set arguments as headers
+                for (java.util.Map.Entry<String, Object> entry : args.entrySet()) {
+                    exchange.getMessage().setHeader(entry.getKey(), entry.getValue());
+                }
+
+                // Execute the consumer route
+                springAiToolsConsumer.getProcessor().process(exchange);
+
+                // Return the result
+                return exchange.getIn().getBody(String.class);
+            } catch (Exception e) {
+                throw new RuntimeException("Error executing tool", e);
+            }
+        };
+
+        // Build the tool callback using FunctionToolCallback
+        FunctionToolCallback.Builder builder = FunctionToolCallback.builder(toolName, function)
+                .description(description);
+
+        if (inputType != null) {
+            builder.inputType(inputType);
+        } else if (parameters != null && !parameters.isEmpty()) {
+            // Build JSON schema from parameters map
+            String inputSchema = buildJsonSchemaFromParameters(parameters);
+            builder.inputSchema(inputSchema);
+            builder.inputType(java.util.Map.class);
+        } else {
+            builder.inputType(java.util.Map.class);
+        }
+
+        // Configure tool metadata
+        if (returnDirect) {
+            builder.toolMetadata(ToolMetadata.builder().returnDirect(true).build());
+        }
+
+        ToolCallback toolCallback = builder.build();
+
+        CamelToolSpecification camelToolSpecification
+                = new CamelToolSpecification(toolCallback, springAiToolsConsumer);
+        final CamelToolExecutorCache executorCache = CamelToolExecutorCache.getInstance();
+
+        String[] splitTags = TagsHelper.splitTags(tags);
+        for (String tag : splitTags) {
+            executorCache.put(tag, camelToolSpecification);
+        }
+
+        return camelToolSpecification.getConsumer();
+    }
+
+    /**
+     * A freely named tool ID (prefer to use something unique)
+     *
+     * @return
+     */
+    public String getToolId() {
+        return toolId;
+    }
+
+    /**
+     * The tool configuration
+     *
+     * @return
+     */
+    public SpringAiToolsConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    /**
+     * A description of the tool. This is passed to the LLM, so it should be descriptive of the tool capabilities
+     *
+     * @return
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * The tool name. This is passed to the LLM, so it should conform to any LLM restrictions.
+     *
+     * @return
+     */
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The input parameters for the tool
+     *
+     * @return
+     */
+    public Map<String, String> getParameters() {
+        return parameters;
+    }
+
+    public void setParameters(Map<String, String> parameters) {
+        this.parameters = parameters;
+    }
+
+    public void setTags(String tags) {
+        this.tags = tags;
+    }
+
+    /**
+     * The tags associated with the tool
+     *
+     * @return
+     */
+    public String getTags() {
+        return tags;
+    }
+
+    public Class<?> getInputType() {
+        return inputType;
+    }
+
+    public void setInputType(Class<?> inputType) {
+        this.inputType = inputType;
+    }
+
+    /**
+     * Whether the tool result should be returned directly or passed back to the model
+     *
+     * @return
+     */
+    public boolean isReturnDirect() {
+        return returnDirect;
+    }
+
+    public void setReturnDirect(boolean returnDirect) {
+        this.returnDirect = returnDirect;
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+
+        CamelToolExecutorCache.getInstance().getTools().clear();
+    }
+
+    /**
+     * Builds a JSON schema from the parameters map in the format expected by openai function call API
+     * https://platform.openai.com/docs/guides/function-calling.
+     * <p>
+     * Supports the following parameter formats:
+     * <ul>
+     * <li>parameter.&lt;name&gt;=&lt;type&gt; - defines the parameter type (e.g., parameter.location=string)</li>
+     * <li>parameter.&lt;name&gt;.description=&lt;text&gt; - defines the parameter description</li>
+     * <li>parameter.&lt;name&gt;.required=&lt;true|false&gt; - defines if parameter is required (default: false)</li>
+     * <li>parameter.&lt;name&gt;.enum=&lt;value1,value2,...&gt; - defines allowed values (comma-separated)</li>
+     * </ul>
+     *
+     * @param  parameters the map of parameter configurations
+     * @return            JSON schema string conforming to JSON Schema Draft 2020-12
+     */
+    private String buildJsonSchemaFromParameters(Map<String, String> parameters) {
+        try {
+            // Parse parameters into structured format
+            Map<String, ParameterMetadata> paramMetadata = parseParameterMetadata(parameters);
+
+            // Build JSON schema
+            ObjectNode schema = OBJECT_MAPPER.createObjectNode();
+            schema.put("type", "object");
+
+            ObjectNode properties = schema.putObject("properties");
+            List<String> requiredParams = new ArrayList<>();
+
+            for (Map.Entry<String, ParameterMetadata> entry : paramMetadata.entrySet()) {
+                String paramName = entry.getKey();
+                ParameterMetadata metadata = entry.getValue();
+
+                ObjectNode property = properties.putObject(paramName);
+                property.put("type", mapTypeToJsonSchemaType(metadata.type));
+
+                if (metadata.description != null) {
+                    property.put("description", metadata.description);
+                }
+
+                if (metadata.enumValues != null && !metadata.enumValues.isEmpty()) {
+                    ArrayNode enumArray = property.putArray("enum");
+                    for (String enumValue : metadata.enumValues) {
+                        enumArray.add(enumValue.trim());
+                    }
+                }
+
+                if (metadata.required) {
+                    requiredParams.add(paramName);
+                }
+            }
+
+            // Add required array if there are required parameters
+            if (!requiredParams.isEmpty()) {
+                ArrayNode requiredArray = schema.putArray("required");
+                for (String requiredParam : requiredParams) {
+                    requiredArray.add(requiredParam);
+                }
+            }
+
+            return OBJECT_MAPPER.writeValueAsString(schema);
+        } catch (Exception e) {
+            throw new RuntimeException("Error building JSON schema from parameters", e);
+        }
+    }
+
+    /**
+     * Parses the flat parameter map into structured metadata.
+     * <p>
+     * Handles parameter configurations like:
+     * <ul>
+     * <li>parameter.location=string</li>
+     * <li>parameter.location.description=The city and state</li>
+     * <li>parameter.location.required=true</li>
+     * <li>parameter.location.enum=C,F</li>
+     * </ul>
+     *
+     * @param  parameters the flat parameter map
+     * @return            map of parameter names to their metadata
+     */
+    private Map<String, ParameterMetadata> parseParameterMetadata(Map<String, String> parameters) {
+        Map<String, ParameterMetadata> metadata = new HashMap<>();
+
+        for (Map.Entry<String, String> entry : parameters.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+
+            if (key.contains(".")) {
+                // Handle nested properties like "location.description"
+                String[] parts = key.split("\\.", 2);
+                String paramName = parts[0];
+                String propertyName = parts[1];
+
+                ParameterMetadata meta = metadata.computeIfAbsent(paramName, k -> new ParameterMetadata());
+
+                switch (propertyName) {
+                    case "description":
+                        meta.description = value;
+                        break;
+                    case "required":
+                        meta.required = Boolean.parseBoolean(value);
+                        break;
+                    case "enum":
+                        meta.enumValues = List.of(value.split(","));
+                        break;
+                    default:
+                        // Ignore unknown properties
+                        break;
+                }
+            } else {
+                // Handle direct parameter type like "location=string"
+                ParameterMetadata meta = metadata.computeIfAbsent(key, k -> new ParameterMetadata());
+                meta.type = value;
+            }
+        }
+
+        return metadata;
+    }
+
+    /**
+     * Internal class to hold parameter metadata.
+     */
+    private static class ParameterMetadata {
+        String type = "string"; // default type
+        String description;
+        boolean required = false; // default not required
+        List<String> enumValues;
+    }
+
+    /**
+     * Maps Camel parameter types to JSON schema types.
+     *
+     * @param  type the Camel parameter type
+     * @return      the corresponding JSON schema type
+     */
+    private String mapTypeToJsonSchemaType(String type) {
+        return switch (type.toLowerCase()) {
+            case "string" -> "string";
+            case "integer", "int", "long" -> "integer";
+            case "number", "double", "float" -> "number";
+            case "boolean", "bool" -> "boolean";
+            default -> "string"; // fallback for unknown types
+        };
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/TagsHelper.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/TagsHelper.java
new file mode 100644
index 000000000000..ad6da81aafc5
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/TagsHelper.java
@@ -0,0 +1,33 @@
+/*
+ * 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.apache.camel.component.springai.tools;
+
+public final class TagsHelper {
+    private TagsHelper() {
+    }
+
+    /**
+     * Split the list of tags
+     *
+     * @param  tagList
+     * @return
+     */
+    public static String[] splitTags(String tagList) {
+        return tagList.split(",");
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/spec/CamelToolExecutorCache.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/spec/CamelToolExecutorCache.java
new file mode 100644
index 000000000000..17cc021ce3c4
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/spec/CamelToolExecutorCache.java
@@ -0,0 +1,57 @@
+/*
+ * 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.apache.camel.component.springai.tools.spec;
+
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Caches Tools Specification and Consumer route reference by the chatId, so that different chats can have different
+ * Tool implementation
+ */
+public final class CamelToolExecutorCache {
+
+    private Map<String, Set<CamelToolSpecification>> tools;
+
+    private CamelToolExecutorCache() {
+        tools = new ConcurrentHashMap<>();
+    }
+
+    private static final class SingletonHolder {
+        private static final CamelToolExecutorCache INSTANCE = new CamelToolExecutorCache();
+    }
+
+    public static CamelToolExecutorCache getInstance() {
+        return SingletonHolder.INSTANCE;
+    }
+
+    public void put(String chatId, CamelToolSpecification specification) {
+        if (tools.get(chatId) != null) {
+            tools.get(chatId).add(specification);
+        } else {
+            Set<CamelToolSpecification> camelToolSpecifications = new LinkedHashSet<>();
+            camelToolSpecifications.add(specification);
+            tools.put(chatId, camelToolSpecifications);
+        }
+    }
+
+    public Map<String, Set<CamelToolSpecification>> getTools() {
+        return tools;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/spec/CamelToolSpecification.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/spec/CamelToolSpecification.java
new file mode 100644
index 000000000000..cff7c32ee0e8
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/spec/CamelToolSpecification.java
@@ -0,0 +1,39 @@
+/*
+ * 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.apache.camel.component.springai.tools.spec;
+
+import org.apache.camel.component.springai.tools.SpringAiToolsConsumer;
+import org.springframework.ai.tool.ToolCallback;
+
+public class CamelToolSpecification {
+
+    private final ToolCallback toolCallback;
+    private final SpringAiToolsConsumer consumer;
+
+    public CamelToolSpecification(ToolCallback toolCallback, SpringAiToolsConsumer consumer) {
+        this.toolCallback = toolCallback;
+        this.consumer = consumer;
+    }
+
+    public ToolCallback getToolCallback() {
+        return toolCallback;
+    }
+
+    public SpringAiToolsConsumer getConsumer() {
+        return consumer;
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/test/java/org/apache/camel/component/springai/tools/JsonSchemaTest.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/test/java/org/apache/camel/component/springai/tools/JsonSchemaTest.java
new file mode 100644
index 000000000000..791e3b4aa5c2
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/test/java/org/apache/camel/component/springai/tools/JsonSchemaTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.apache.camel.component.springai.tools;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.camel.Consumer;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class JsonSchemaTest extends CamelTestSupport {
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    @Test
+    public void testJsonSchemaGenerationWithFullMetadata() throws Exception {
+        // Create parameters with full metadata
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("location", "string");
+        parameters.put("location.description", "The city and state e.g. San Francisco, CA");
+        parameters.put("location.required", "true");
+        parameters.put("unit", "string");
+        parameters.put("unit.description", "The temperature unit");
+        parameters.put("unit.enum", "C,F");
+        parameters.put("unit.required", "true");
+        parameters.put("threshold", "number");
+        parameters.put("threshold.description", "Temperature threshold");
+
+        // Create endpoint and test schema generation
+        SpringAiToolsComponent component = new SpringAiToolsComponent(context);
+        SpringAiToolsEndpoint endpoint = new SpringAiToolsEndpoint(
+                "spring-ai-tools:test",
+                component,
+                "test-tool",
+                "test-tag",
+                new SpringAiToolsConfiguration());
+
+        endpoint.setParameters(parameters);
+        endpoint.setDescription("Test tool");
+
+        // Access the private method via reflection for testing
+        java.lang.reflect.Method method
+                = SpringAiToolsEndpoint.class.getDeclaredMethod("buildJsonSchemaFromParameters", Map.class);
+        method.setAccessible(true);
+        String schema = (String) method.invoke(endpoint, parameters);
+
+        System.out.println("Generated Schema:");
+        System.out.println(schema);
+
+        // Parse and validate the schema
+        JsonNode schemaNode = MAPPER.readTree(schema);
+
+        // Verify basic structure
+        assertEquals("object", schemaNode.get("type").asText());
+        assertNotNull(schemaNode.get("properties"));
+
+        // Verify location property
+        JsonNode locationProp = schemaNode.get("properties").get("location");
+        assertNotNull(locationProp);
+        assertEquals("string", locationProp.get("type").asText());
+        assertEquals("The city and state e.g. San Francisco, CA", locationProp.get("description").asText());
+
+        // Verify unit property with enum
+        JsonNode unitProp = schemaNode.get("properties").get("unit");
+        assertNotNull(unitProp);
+        assertEquals("string", unitProp.get("type").asText());
+        assertEquals("The temperature unit", unitProp.get("description").asText());
+        assertTrue(unitProp.has("enum"));
+        assertEquals("C", unitProp.get("enum").get(0).asText());
+        assertEquals("F", unitProp.get("enum").get(1).asText());
+
+        // Verify threshold property
+        JsonNode thresholdProp = schemaNode.get("properties").get("threshold");
+        assertNotNull(thresholdProp);
+        assertEquals("number", thresholdProp.get("type").asText());
+        assertEquals("Temperature threshold", thresholdProp.get("description").asText());
+
+        // Verify required array
+        JsonNode required = schemaNode.get("required");
+        assertNotNull(required);
+        assertEquals(2, required.size());
+        assertTrue(required.toString().contains("location"));
+        assertTrue(required.toString().contains("unit"));
+    }
+
+    @Test
+    public void testJsonSchemaGenerationWithMinimalMetadata() throws Exception {
+        // Create parameters with minimal metadata
+        Map<String, String> parameters = new HashMap<>();
+        parameters.put("name", "string");
+        parameters.put("age", "integer");
+
+        // Create endpoint and test schema generation
+        SpringAiToolsComponent component = new SpringAiToolsComponent(context);
+        SpringAiToolsEndpoint endpoint = new SpringAiToolsEndpoint(
+                "spring-ai-tools:test",
+                component,
+                "test-tool",
+                "test-tag",
+                new SpringAiToolsConfiguration());
+
+        endpoint.setParameters(parameters);
+        endpoint.setDescription("Test tool");
+
+        // Access the private method via reflection for testing
+        java.lang.reflect.Method method
+                = SpringAiToolsEndpoint.class.getDeclaredMethod("buildJsonSchemaFromParameters", Map.class);
+        method.setAccessible(true);
+        String schema = (String) method.invoke(endpoint, parameters);
+
+        System.out.println("Generated Minimal Schema:");
+        System.out.println(schema);
+
+        // Parse and validate the schema
+        JsonNode schemaNode = MAPPER.readTree(schema);
+
+        // Verify basic structure
+        assertEquals("object", schemaNode.get("type").asText());
+        assertNotNull(schemaNode.get("properties"));
+
+        // Verify properties
+        JsonNode nameProp = schemaNode.get("properties").get("name");
+        assertNotNull(nameProp);
+        assertEquals("string", nameProp.get("type").asText());
+
+        JsonNode ageProp = schemaNode.get("properties").get("age");
+        assertNotNull(ageProp);
+        assertEquals("integer", ageProp.get("type").asText());
+
+        // Verify no required array (since no required=true was specified)
+        // In JSON, if required is empty, it might not be present or be an empty array
+        assertTrue(schemaNode.get("required") == null || schemaNode.get("required").size() == 0);
+    }
+
+    @Test
+    public void testToolMetadataReturnDirectConfiguration() throws Exception {
+        // Create endpoint with returnDirect=true
+        SpringAiToolsComponent component = new SpringAiToolsComponent(context);
+        SpringAiToolsEndpoint endpoint = new SpringAiToolsEndpoint(
+                "spring-ai-tools:directTool?returnDirect=true",
+                component,
+                "directTool",
+                "test-tag",
+                new SpringAiToolsConfiguration());
+
+        endpoint.setDescription("Tool that returns directly");
+        endpoint.setReturnDirect(true);
+
+        // Create a simple consumer to test
+        Consumer consumer = endpoint.createConsumer(exchange -> {
+            exchange.getIn().setBody("Direct result");
+        });
+
+        assertNotNull(consumer);
+
+        // Verify the returnDirect property is set
+        assertTrue(endpoint.isReturnDirect());
+    }
+
+    @Test
+    public void testToolMetadataDefaultReturnDirect() throws Exception {
+        // Create endpoint without returnDirect (should default to false)
+        SpringAiToolsComponent component = new SpringAiToolsComponent(context);
+        SpringAiToolsEndpoint endpoint = new SpringAiToolsEndpoint(
+                "spring-ai-tools:normalTool",
+                component,
+                "normalTool",
+                "test-tag",
+                new SpringAiToolsConfiguration());
+
+        endpoint.setDescription("Normal tool");
+
+        // Create a simple consumer to test
+        Consumer consumer = endpoint.createConsumer(exchange -> {
+            exchange.getIn().setBody("Normal result");
+        });
+
+        assertNotNull(consumer);
+
+        // Verify the returnDirect property defaults to false
+        assertTrue(!endpoint.isReturnDirect());
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/test/java/org/apache/camel/component/springai/tools/SpringAiToolsComponentTest.java b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/test/java/org/apache/camel/component/springai/tools/SpringAiToolsComponentTest.java
new file mode 100644
index 000000000000..8b788cd8fa0d
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/test/java/org/apache/camel/component/springai/tools/SpringAiToolsComponentTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.apache.camel.component.springai.tools;
+
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class SpringAiToolsComponentTest extends CamelTestSupport {
+
+    @Test
+    public void testComponentExists() throws Exception {
+        SpringAiToolsComponent component = context.getComponent("spring-ai-tools", SpringAiToolsComponent.class);
+        assertNotNull(component);
+    }
+}
diff --git a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-vector-store/pom.xml b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-vector-store/pom.xml
new file mode 100644
index 000000000000..396bf29950d8
--- /dev/null
+++ b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-vector-store/pom.xml
@@ -0,0 +1,127 @@
+<?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"
... 6073 lines suppressed ...