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

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

commit 28fe2c786ce7c6c7176354cf5c1901ecd8698674
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Mar 4 14:32:55 2026 +0100

    CAMEL-23126 - Camel-jbang-mcp: Add MCP Prompts for structured multi-step 
workflows to camel-jbang-mcp
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../org/apache/camel/catalog/components/seda.json  |   2 +-
 .../org/apache/camel/catalog/components/stub.json  |   2 +-
 .../jbang/core/commands/mcp/PromptDefinitions.java | 224 +++++++++++++++++++++
 .../core/commands/mcp/PromptDefinitionsTest.java   | 208 +++++++++++++++++++
 4 files changed, 434 insertions(+), 2 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json
index fcafafdfaae0..d2ef0a7fcc49 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/seda.json
@@ -38,7 +38,7 @@
   "properties": {
     "name": { "index": 0, "kind": "path", "displayName": "Name", "group": 
"common", "label": "", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Name of queue" },
     "size": { "index": 1, "kind": "parameter", "displayName": "Size", "group": 
"common", "label": "", "required": false, "type": "integer", "javaType": "int", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, 
"description": "The maximum capacity of the SEDA queue (i.e., the number of 
messages it can hold). Will by default use the queueSize set on the SEDA 
component." },
-    "concurrentConsumers": { "index": 2, "kind": "parameter", "displayName": 
"Concurrent Consumers", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 1, "description": "Number of concurrent 
threads processing exchanges." },
+    "concurrentConsumers": { "index": 2, "kind": "parameter", "displayName": 
"Concurrent Consumers", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 1, "description": "Number of concurrent 
threads processing exchanges. When virtualThreadPerTask is enabled, this 
becomes a concurrency limit (0 = unlimited) and defaults to 0 instead of 1." },
     "bridgeErrorHandler": { "index": 3, "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": 4, "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": 5, "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." },
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/stub.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/stub.json
index 6d33364a3a0c..286a319f9264 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/stub.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/stub.json
@@ -40,7 +40,7 @@
   "properties": {
     "name": { "index": 0, "kind": "path", "displayName": "Name", "group": 
"common", "label": "", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "Name of queue" },
     "size": { "index": 1, "kind": "parameter", "displayName": "Size", "group": 
"common", "label": "", "required": false, "type": "integer", "javaType": "int", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, 
"description": "The maximum capacity of the SEDA queue (i.e., the number of 
messages it can hold). Will by default use the queueSize set on the SEDA 
component." },
-    "concurrentConsumers": { "index": 2, "kind": "parameter", "displayName": 
"Concurrent Consumers", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 1, "description": "Number of concurrent 
threads processing exchanges." },
+    "concurrentConsumers": { "index": 2, "kind": "parameter", "displayName": 
"Concurrent Consumers", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 1, "description": "Number of concurrent 
threads processing exchanges. When virtualThreadPerTask is enabled, this 
becomes a concurrency limit (0 = unlimited) and defaults to 0 instead of 1." },
     "bridgeErrorHandler": { "index": 3, "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": 4, "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": 5, "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." },
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
new file mode 100644
index 000000000000..9bd4c345724e
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitions.java
@@ -0,0 +1,224 @@
+/*
+ * 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.dsl.jbang.core.commands.mcp;
+
+import java.util.List;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+import io.quarkiverse.mcp.server.Prompt;
+import io.quarkiverse.mcp.server.PromptArg;
+import io.quarkiverse.mcp.server.PromptMessage;
+
+/**
+ * MCP Prompt definitions that provide structured multi-step workflows for 
LLMs.
+ * <p>
+ * Prompts guide the LLM through orchestrating multiple existing tools in the 
correct sequence, rather than requiring it
+ * to discover the workflow on its own.
+ */
+@ApplicationScoped
+public class PromptDefinitions {
+
+    /**
+     * Guided workflow for building a Camel integration from requirements.
+     */
+    @Prompt(name = "camel_build_integration",
+            description = "Guided workflow to build a Camel integration: "
+                          + "analyze requirements, discover components and 
EIPs, "
+                          + "generate a YAML route, validate it, and run a 
security check.")
+    public List<PromptMessage> buildIntegration(
+            @PromptArg(name = "requirements",
+                       description = "Natural-language description of what the 
integration should do") String requirements,
+            @PromptArg(name = "runtime", description = "Target runtime: main, 
spring-boot, or quarkus (default: main)",
+                       required = false) String runtime) {
+
+        String resolvedRuntime = runtime != null && !runtime.isBlank() ? 
runtime : "main";
+
+        String instructions = """
+                You are building a Camel integration for the "%s" runtime.
+
+                ## Requirements
+                %s
+
+                ## Workflow
+
+                Follow these steps in order:
+
+                ### Step 1: Identify components
+                Analyze the requirements above and identify the Camel 
components needed.
+                Call `camel_catalog_components` with a relevant filter and 
runtime="%s" to find matching components.
+
+                ### Step 2: Identify EIPs
+                Determine which Enterprise Integration Patterns are needed 
(e.g., split, aggregate, filter, choice).
+                Call `camel_catalog_eips` with a relevant filter to find 
matching patterns.
+
+                ### Step 3: Get component details
+                For each component you selected, call 
`camel_catalog_component_doc` with the component name \
+                and runtime="%s" to get its endpoint options, required 
parameters, and URI syntax.
+
+                ### Step 4: Build the route
+                Using the gathered information, write a complete YAML route 
definition. \
+                Use correct component URI syntax and required options from the 
documentation.
+
+                ### Step 5: Validate
+                Call `camel_validate_yaml_dsl` with the generated YAML route 
to check for syntax errors.
+                If validation fails, fix the issues and re-validate.
+
+                ### Step 6: Security review
+                Call `camel_route_harden_context` with the generated route and 
format="yaml" \
+                to identify security concerns. Address any critical or 
high-severity findings.
+
+                ### Step 7: Present result
+                Present the final YAML route along with:
+                - A brief explanation of each component and EIP used
+                - Any security recommendations from Step 6
+                - Instructions for running the route (e.g., with camel-jbang)
+                """.formatted(resolvedRuntime, requirements, resolvedRuntime, 
resolvedRuntime);
+
+        return List.of(PromptMessage.withUserRole(instructions));
+    }
+
+    /**
+     * Guided workflow for migrating a Camel project to a new version.
+     */
+    @Prompt(name = "camel_migrate_project",
+            description = "Guided workflow to migrate a Camel project: "
+                          + "analyze the pom.xml, check compatibility, "
+                          + "get OpenRewrite recipes, search migration guides, 
"
+                          + "and produce a migration summary.")
+    public List<PromptMessage> migrateProject(
+            @PromptArg(name = "pomContent", description = "The project's 
pom.xml file content") String pomContent,
+            @PromptArg(name = "targetVersion", description = "Target Camel 
version to migrate to (e.g., 4.18.0)",
+                       required = false) String targetVersion) {
+
+        String versionNote = targetVersion != null && !targetVersion.isBlank()
+                ? "Target version: " + targetVersion
+                : "Target version: latest stable (determine from 
camel_version_list)";
+
+        String instructions = """
+                You are migrating a Camel project to a newer version.
+
+                ## %s
+
+                ## Project pom.xml
+                ```xml
+                %s
+                ```
+
+                ## Workflow
+
+                Follow these steps in order:
+
+                ### Step 1: Analyze the project
+                Call `camel_migration_analyze` with the pom.xml content above.
+                This detects the current runtime, Camel version, Java version, 
and component dependencies.
+
+                ### Step 2: Determine target version
+                If no target version was specified, call `camel_version_list` 
with the detected runtime \
+                to find the latest stable version. For LTS releases, filter 
with lts=true.
+
+                ### Step 3: Check compatibility
+                Based on the detected runtime from Step 1:
+                - For **wildfly** or **karaf** runtimes: call 
`camel_migration_wildfly_karaf` with the pom.xml \
+                content, target runtime, and target version.
+                - For **main**, **spring-boot**, or **quarkus** runtimes: call 
`camel_migration_compatibility` \
+                with the detected components, current version, target version, 
runtime, and Java version.
+
+                Review any blockers (e.g., Java version too old) and warnings.
+
+                ### Step 4: Get migration recipes
+                Call `camel_migration_recipes` with the runtime, current 
version, target version, \
+                Java version, and dryRun=true to get the OpenRewrite Maven 
commands.
+
+                ### Step 5: Search for breaking changes
+                For each component detected in Step 1, call 
`camel_migration_guide_search` \
+                with the component name to find relevant breaking changes and 
rename mappings.
+
+                ### Step 6: Produce migration summary
+                Present a structured summary:
+                - **Current state**: runtime, Camel version, Java version, 
component count
+                - **Target state**: target version, required Java version
+                - **Blockers**: issues that must be resolved before migration
+                - **Breaking changes**: component renames, API changes found 
in guides
+                - **Migration commands**: the OpenRewrite commands from Step 4
+                - **Manual steps**: any changes that OpenRewrite cannot 
automate
+                """.formatted(versionNote, pomContent);
+
+        return List.of(PromptMessage.withUserRole(instructions));
+    }
+
+    /**
+     * Guided workflow for a security review of a Camel route.
+     */
+    @Prompt(name = "camel_security_review",
+            description = "Guided workflow to perform a security audit of a 
Camel route: "
+                          + "analyze security-sensitive components, check for 
vulnerabilities, "
+                          + "and produce an actionable audit checklist.")
+    public List<PromptMessage> securityReview(
+            @PromptArg(name = "route", description = "The Camel route content 
to review") String route,
+            @PromptArg(name = "format", description = "Route format: yaml, 
xml, or java (default: yaml)",
+                       required = false) String format) {
+
+        String resolvedFormat = format != null && !format.isBlank() ? format : 
"yaml";
+
+        String instructions = """
+                You are performing a security audit of a Camel route.
+
+                ## Route (format: %s)
+                ```
+                %s
+                ```
+
+                ## Workflow
+
+                Follow these steps in order:
+
+                ### Step 1: Analyze security
+                Call `camel_route_harden_context` with the route above and 
format="%s".
+                This returns security-sensitive components, vulnerabilities, 
and risk levels.
+
+                ### Step 2: Understand route structure
+                Call `camel_route_context` with the route and format="%s".
+                This returns the components and EIPs used, helping you 
understand the full data flow.
+
+                ### Step 3: Produce audit checklist
+                Using the results from Steps 1 and 2, produce a structured 
security audit report:
+
+                **Critical Issues** (must fix before production):
+                - List all critical-severity concerns from the security 
analysis
+                - For each: describe the issue, the affected component, and 
the specific fix
+
+                **Warnings** (should fix):
+                - List all high and medium-severity concerns
+                - For each: describe the risk and the recommended mitigation
+
+                **Positive Findings** (already secured):
+                - List all positive security findings (TLS enabled, property 
placeholders used, etc.)
+
+                **Recommendations**:
+                - Provide actionable, prioritized recommendations based on the 
specific components used
+                - Reference the relevant security best practices for each 
component
+                - Include specific configuration examples where applicable
+
+                **Compliance Notes**:
+                - Note any components that handle PII or sensitive data
+                - Flag any components that communicate over the network 
without encryption
+                """.formatted(resolvedFormat, route, resolvedFormat, 
resolvedFormat);
+
+        return List.of(PromptMessage.withUserRole(instructions));
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
new file mode 100644
index 000000000000..64fd0da9eea2
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PromptDefinitionsTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.dsl.jbang.core.commands.mcp;
+
+import java.util.List;
+
+import io.quarkiverse.mcp.server.PromptMessage;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class PromptDefinitionsTest {
+
+    private final PromptDefinitions prompts = new PromptDefinitions();
+
+    // ---- camel_build_integration ----
+
+    @Test
+    void buildIntegrationReturnsNonEmptyMessages() {
+        List<PromptMessage> result = prompts.buildIntegration("Read from Kafka 
and write to S3", "spring-boot");
+
+        assertThat(result).isNotEmpty();
+    }
+
+    @Test
+    void buildIntegrationContainsRequirements() {
+        List<PromptMessage> result = prompts.buildIntegration("Read from Kafka 
and write to S3", null);
+
+        String text = extractText(result);
+        assertThat(text).contains("Read from Kafka and write to S3");
+    }
+
+    @Test
+    void buildIntegrationReferencesTools() {
+        List<PromptMessage> result = prompts.buildIntegration("poll an FTP 
server", null);
+
+        String text = extractText(result);
+        assertThat(text).contains("camel_catalog_components");
+        assertThat(text).contains("camel_catalog_eips");
+        assertThat(text).contains("camel_catalog_component_doc");
+        assertThat(text).contains("camel_validate_yaml_dsl");
+        assertThat(text).contains("camel_route_harden_context");
+    }
+
+    @Test
+    void buildIntegrationDefaultsRuntimeToMain() {
+        List<PromptMessage> result = prompts.buildIntegration("timer to log", 
null);
+
+        String text = extractText(result);
+        assertThat(text).contains("\"main\" runtime");
+    }
+
+    @Test
+    void buildIntegrationUsesSpecifiedRuntime() {
+        List<PromptMessage> result = prompts.buildIntegration("timer to log", 
"quarkus");
+
+        String text = extractText(result);
+        assertThat(text).contains("\"quarkus\" runtime");
+    }
+
+    @Test
+    void buildIntegrationBlankRuntimeDefaultsToMain() {
+        List<PromptMessage> result = prompts.buildIntegration("timer to log", 
"  ");
+
+        String text = extractText(result);
+        assertThat(text).contains("\"main\" runtime");
+    }
+
+    // ---- camel_migrate_project ----
+
+    @Test
+    void migrateProjectReturnsNonEmptyMessages() {
+        List<PromptMessage> result = prompts.migrateProject("<project/>", 
"4.18.0");
+
+        assertThat(result).isNotEmpty();
+    }
+
+    @Test
+    void migrateProjectContainsPomContent() {
+        String pom = "<project><version>3.20.0</version></project>";
+        List<PromptMessage> result = prompts.migrateProject(pom, null);
+
+        String text = extractText(result);
+        assertThat(text).contains(pom);
+    }
+
+    @Test
+    void migrateProjectReferencesTools() {
+        List<PromptMessage> result = prompts.migrateProject("<project/>", 
"4.18.0");
+
+        String text = extractText(result);
+        assertThat(text).contains("camel_migration_analyze");
+        assertThat(text).contains("camel_migration_compatibility");
+        assertThat(text).contains("camel_migration_wildfly_karaf");
+        assertThat(text).contains("camel_migration_recipes");
+        assertThat(text).contains("camel_migration_guide_search");
+    }
+
+    @Test
+    void migrateProjectIncludesTargetVersion() {
+        List<PromptMessage> result = prompts.migrateProject("<project/>", 
"4.18.0");
+
+        String text = extractText(result);
+        assertThat(text).contains("Target version: 4.18.0");
+    }
+
+    @Test
+    void migrateProjectNullVersionSuggestsLatest() {
+        List<PromptMessage> result = prompts.migrateProject("<project/>", 
null);
+
+        String text = extractText(result);
+        assertThat(text).contains("camel_version_list");
+    }
+
+    @Test
+    void migrateProjectBlankVersionSuggestsLatest() {
+        List<PromptMessage> result = prompts.migrateProject("<project/>", "  
");
+
+        String text = extractText(result);
+        assertThat(text).contains("camel_version_list");
+    }
+
+    // ---- camel_security_review ----
+
+    @Test
+    void securityReviewReturnsNonEmptyMessages() {
+        List<PromptMessage> result = prompts.securityReview("from: 
timer:tick", "yaml");
+
+        assertThat(result).isNotEmpty();
+    }
+
+    @Test
+    void securityReviewContainsRoute() {
+        String route = "from:\n  uri: kafka:topic\n  steps:\n    - to: 
log:out";
+        List<PromptMessage> result = prompts.securityReview(route, null);
+
+        String text = extractText(result);
+        assertThat(text).contains(route);
+    }
+
+    @Test
+    void securityReviewReferencesTools() {
+        List<PromptMessage> result = prompts.securityReview("from: 
timer:tick", null);
+
+        String text = extractText(result);
+        assertThat(text).contains("camel_route_harden_context");
+        assertThat(text).contains("camel_route_context");
+    }
+
+    @Test
+    void securityReviewDefaultsFormatToYaml() {
+        List<PromptMessage> result = prompts.securityReview("from: 
timer:tick", null);
+
+        String text = extractText(result);
+        assertThat(text).contains("format: yaml");
+    }
+
+    @Test
+    void securityReviewUsesSpecifiedFormat() {
+        List<PromptMessage> result = prompts.securityReview("<route/>", "xml");
+
+        String text = extractText(result);
+        assertThat(text).contains("format: xml");
+    }
+
+    @Test
+    void securityReviewBlankFormatDefaultsToYaml() {
+        List<PromptMessage> result = prompts.securityReview("from: 
timer:tick", "  ");
+
+        String text = extractText(result);
+        assertThat(text).contains("format: yaml");
+    }
+
+    @Test
+    void securityReviewContainsAuditSections() {
+        List<PromptMessage> result = prompts.securityReview("from: 
timer:tick", null);
+
+        String text = extractText(result);
+        assertThat(text).contains("Critical Issues");
+        assertThat(text).contains("Warnings");
+        assertThat(text).contains("Positive Findings");
+        assertThat(text).contains("Recommendations");
+    }
+
+    // ---- helper ----
+
+    private String extractText(List<PromptMessage> messages) {
+        StringBuilder sb = new StringBuilder();
+        for (PromptMessage msg : messages) {
+            sb.append(msg.content().asText().text());
+        }
+        return sb.toString();
+    }
+}

Reply via email to