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

oscerd 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 addf51d8d706 CAMEL-24219: Add camel_ai_pipeline_scaffold MCP tool 
(#25048)
addf51d8d706 is described below

commit addf51d8d706837f114b15755c8ac4b96aac759c
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Jul 23 15:34:32 2026 +0200

    CAMEL-24219: Add camel_ai_pipeline_scaffold MCP tool (#25048)
    
    * CAMEL-24219: Add camel_ai_pipeline_scaffold MCP tool
    
    New MCP tool that generates YAML DSL Camel routes for AI
    document-processing pipelines combining Docling/Textract with Bedrock.
    
    Supports 4 pipeline types (summarization, extraction, RAG,
    classification), 3 document processors (docling, textract, combined),
    3 source types (file, s3, url), and configurable Bedrock model/region.
    Returns a runnable YAML route and matching application.properties.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
    
    * CAMEL-24219: Address review feedback
    
    - Use modelId as endpoint parameter instead of fabricated headers
    - Remove non-existent CamelAwsBedrockModelId/InputType headers
    - Fix Docling params: serverUrl -> doclingServeUrl, add useDoclingServe
    - Fix default model ID prefix: us.anthropic -> anthropic
    - Make ScaffoldResult record public
    - Strengthen test assertions for correct parameter names
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
    
    ---------
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
---
 .../core/commands/mcp/AiPipelineScaffoldTools.java | 361 +++++++++++++++++++++
 .../commands/mcp/AiPipelineScaffoldToolsTest.java  | 201 ++++++++++++
 2 files changed, 562 insertions(+)

diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/AiPipelineScaffoldTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/AiPipelineScaffoldTools.java
new file mode 100644
index 000000000000..1a392ff181dd
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/AiPipelineScaffoldTools.java
@@ -0,0 +1,361 @@
+/*
+ * 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 jakarta.enterprise.context.ApplicationScoped;
+
+import io.quarkiverse.mcp.server.Tool;
+import io.quarkiverse.mcp.server.ToolArg;
+import io.quarkiverse.mcp.server.ToolCallException;
+
+/**
+ * MCP Tool for generating AI document-processing pipeline scaffolds.
+ * <p>
+ * Generates runnable YAML DSL routes that combine document processing 
(Docling, Textract) with AI services (Bedrock)
+ * for common use cases: summarization, extraction, RAG, and classification.
+ */
+@ApplicationScoped
+public class AiPipelineScaffoldTools {
+
+    private static final String DEFAULT_MODEL = 
"anthropic.claude-sonnet-4-20250514-v1:0";
+    private static final String DEFAULT_REGION = "us-east-1";
+
+    @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
+          description = "Generate a YAML DSL Camel route for an AI 
document-processing pipeline. "
+                        + "Combines document processors (Docling for 
open-source/on-prem, Textract for AWS-managed) "
+                        + "with Bedrock LLM services for summarization, 
extraction, RAG, or classification. "
+                        + "Returns a runnable route and matching 
application.properties template.")
+    public ScaffoldResult camel_ai_pipeline_scaffold(
+            @ToolArg(description = "Pipeline type: summarization, extraction, 
rag, or classification") String pipelineType,
+            @ToolArg(description = "Document processor: docling (open-source, 
default), textract (AWS), "
+                                   + "or combined (Docling for text + Textract 
for tables)") String documentProcessor,
+            @ToolArg(description = "Document source: file (local path, 
default), s3 (AWS S3 bucket), "
+                                   + "or url (HTTP/HTTPS URL)") String 
documentSource,
+            @ToolArg(description = "Bedrock model ID (default: Claude Sonnet 
4). "
+                                   + "Examples: 
anthropic.claude-sonnet-4-20250514-v1:0, "
+                                   + "anthropic.claude-opus-4-20250514-v1:0, "
+                                   + "amazon.nova-pro-v1:0") String modelId,
+            @ToolArg(description = "AWS region for Bedrock and Textract 
(default: us-east-1)") String region) {
+
+        if (pipelineType == null || pipelineType.isBlank()) {
+            throw new ToolCallException(
+                    "pipelineType is required. Use: summarization, extraction, 
rag, or classification", null);
+        }
+
+        String resolvedType = pipelineType.toLowerCase().trim();
+        String resolvedProcessor = documentProcessor != null && 
!documentProcessor.isBlank()
+                ? documentProcessor.toLowerCase().trim() : "docling";
+        String resolvedSource = documentSource != null && 
!documentSource.isBlank()
+                ? documentSource.toLowerCase().trim() : "file";
+        String resolvedModel = modelId != null && !modelId.isBlank() ? 
modelId.trim() : DEFAULT_MODEL;
+        String resolvedRegion = region != null && !region.isBlank() ? 
region.trim() : DEFAULT_REGION;
+
+        validateInputs(resolvedType, resolvedProcessor, resolvedSource);
+
+        String yamlRoute = generateRoute(resolvedType, resolvedProcessor, 
resolvedSource, resolvedModel, resolvedRegion);
+        String properties = generateProperties(resolvedProcessor, 
resolvedSource, resolvedModel, resolvedRegion);
+        String description = generateDescription(resolvedType, 
resolvedProcessor, resolvedSource);
+
+        return new ScaffoldResult(yamlRoute, properties, description);
+    }
+
+    private void validateInputs(String type, String processor, String source) {
+        switch (type) {
+            case "summarization", "extraction", "rag", "classification" -> {
+            }
+            default -> throw new ToolCallException(
+                    "Unknown pipeline type: " + type + ". Use: summarization, 
extraction, rag, or classification", null);
+        }
+        switch (processor) {
+            case "docling", "textract", "combined" -> {
+            }
+            default -> throw new ToolCallException(
+                    "Unknown document processor: " + processor + ". Use: 
docling, textract, or combined", null);
+        }
+        switch (source) {
+            case "file", "s3", "url" -> {
+            }
+            default -> throw new ToolCallException(
+                    "Unknown document source: " + source + ". Use: file, s3, 
or url", null);
+        }
+    }
+
+    // ---- Route generation ----
+
+    private String generateRoute(String type, String processor, String source, 
String model, String region) {
+        return switch (type) {
+            case "summarization" -> generateSummarizationRoute(processor, 
source, model, region);
+            case "extraction" -> generateExtractionRoute(processor, source, 
model, region);
+            case "rag" -> generateRagRoute(processor, source, model, region);
+            case "classification" -> generateClassificationRoute(processor, 
source, model, region);
+            default -> throw new ToolCallException("Unsupported pipeline type: 
" + type, null);
+        };
+    }
+
+    private String generateSummarizationRoute(String processor, String source, 
String model, String region) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("# AI Document Summarization Pipeline\n");
+        sb.append("# Extracts text from documents and generates summaries via 
Bedrock\n");
+        sb.append("- route:\n");
+        sb.append("    id: ai-summarization\n");
+        sb.append("    from:\n");
+        appendSourceEndpoint(sb, source);
+        sb.append("    steps:\n");
+        appendDocumentProcessing(sb, processor);
+        sb.append("      - process:\n");
+        sb.append("          ref: \"#buildSummarizationPrompt\"\n");
+        appendBedrockConverse(sb, model, region);
+        sb.append("      - log:\n");
+        sb.append("          message: \"Summary generated: ${body}\"\n");
+        return sb.toString();
+    }
+
+    private String generateExtractionRoute(String processor, String source, 
String model, String region) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("# AI Structured Data Extraction Pipeline\n");
+        sb.append("# Extracts text from documents and pulls structured data 
via Bedrock\n");
+        sb.append("- route:\n");
+        sb.append("    id: ai-extraction\n");
+        sb.append("    from:\n");
+        appendSourceEndpoint(sb, source);
+        sb.append("    steps:\n");
+        appendDocumentProcessing(sb, processor);
+        sb.append("      - process:\n");
+        sb.append("          ref: \"#buildExtractionPrompt\"\n");
+        appendBedrockConverse(sb, model, region);
+        sb.append("      - unmarshal:\n");
+        sb.append("          json:\n");
+        sb.append("            unmarshalType: java.util.Map\n");
+        sb.append("      - log:\n");
+        sb.append("          message: \"Extracted data: ${body}\"\n");
+        return sb.toString();
+    }
+
+    private String generateRagRoute(String processor, String source, String 
model, String region) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("# RAG (Retrieval-Augmented Generation) Pipeline\n");
+        sb.append("# Ingests documents into a vector store, then answers 
queries using Bedrock\n");
+        sb.append("#\n");
+        sb.append("# This pipeline has two routes:\n");
+        sb.append("# 1. Ingestion: documents -> chunk -> embed -> vector 
store\n");
+        sb.append("# 2. Query: user question -> retrieve context -> Bedrock 
answer\n\n");
+
+        // Ingestion route
+        sb.append("- route:\n");
+        sb.append("    id: rag-ingestion\n");
+        sb.append("    from:\n");
+        appendSourceEndpoint(sb, source);
+        sb.append("    steps:\n");
+        appendDocumentProcessing(sb, processor);
+        sb.append("      # Chunk the extracted text for embedding\n");
+        sb.append("      - split:\n");
+        sb.append("          tokenize: \"\\n\\n\"\n");
+        sb.append("          streaming: true\n");
+        sb.append("        steps:\n");
+        sb.append("          - to:\n");
+        sb.append("              uri: \"langchain4j-embeddings:embed\"\n");
+        sb.append("              parameters:\n");
+        sb.append("                embeddingModelId: \"#bedrockEmbedding\"\n");
+        sb.append("          # TODO: Configure your vector store endpoint\n");
+        sb.append("          - to: 
\"log:ingested?showBody=false&showHeaders=true\"\n\n");
+
+        // Query route
+        sb.append("- route:\n");
+        sb.append("    id: rag-query\n");
+        sb.append("    from:\n");
+        sb.append("      uri: \"direct:query\"\n");
+        sb.append("    steps:\n");
+        sb.append("      # TODO: Retrieve relevant chunks from vector 
store\n");
+        sb.append("      # - to: \"langchain4j-embeddings:embed\" # embed the 
query\n");
+        sb.append("      # - to: \"qdrant:search\"                # search 
vector store\n");
+        sb.append("      - process:\n");
+        sb.append("          ref: \"#buildRagPrompt\"\n");
+        appendBedrockConverse(sb, model, region);
+        sb.append("      - log:\n");
+        sb.append("          message: \"RAG answer: ${body}\"\n");
+        return sb.toString();
+    }
+
+    private String generateClassificationRoute(String processor, String 
source, String model, String region) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("# AI Document Classification Pipeline\n");
+        sb.append("# Extracts text and classifies documents into categories 
via Bedrock\n");
+        sb.append("- route:\n");
+        sb.append("    id: ai-classification\n");
+        sb.append("    from:\n");
+        appendSourceEndpoint(sb, source);
+        sb.append("    steps:\n");
+        appendDocumentProcessing(sb, processor);
+        sb.append("      - process:\n");
+        sb.append("          ref: \"#buildClassificationPrompt\"\n");
+        appendBedrockConverse(sb, model, region);
+        sb.append("      - choice:\n");
+        sb.append("          when:\n");
+        sb.append("            - simple: \"${body} contains 'invoice'\"\n");
+        sb.append("              steps:\n");
+        sb.append("                - to: \"direct:handle-invoice\"\n");
+        sb.append("            - simple: \"${body} contains 'contract'\"\n");
+        sb.append("              steps:\n");
+        sb.append("                - to: \"direct:handle-contract\"\n");
+        sb.append("          otherwise:\n");
+        sb.append("            steps:\n");
+        sb.append("              - to: \"direct:handle-other\"\n");
+        return sb.toString();
+    }
+
+    // ---- Source endpoint helpers ----
+
+    private void appendSourceEndpoint(StringBuilder sb, String source) {
+        switch (source) {
+            case "file" -> {
+                sb.append("      uri: \"file:{{document.input.dir}}\"\n");
+                sb.append("      parameters:\n");
+                sb.append("        noop: true\n");
+                sb.append("        include: 
\".*\\\\.(pdf|docx|png|jpg|tiff)\"\n");
+            }
+            case "s3" -> {
+                sb.append("      uri: \"aws2-s3:{{document.s3.bucket}}\"\n");
+                sb.append("      parameters:\n");
+                sb.append("        region: \"{{aws.region}}\"\n");
+                sb.append("        deleteAfterRead: false\n");
+            }
+            case "url" -> {
+                sb.append("      uri: \"direct:process-url\"\n");
+                sb.append("      # Send document URLs to this endpoint via: 
template.sendBody(\"direct:process-url\", url)\n");
+            }
+            default -> sb.append("      uri: \"direct:start\"\n");
+        }
+    }
+
+    // ---- Document processing helpers ----
+
+    private void appendDocumentProcessing(StringBuilder sb, String processor) {
+        switch (processor) {
+            case "docling" -> appendDoclingStep(sb);
+            case "textract" -> appendTextractStep(sb);
+            case "combined" -> {
+                appendDoclingStep(sb);
+                sb.append("      # Also extract tables/forms via Textract for 
structured data\n");
+                appendTextractStep(sb);
+                sb.append("      - process:\n");
+                sb.append("          ref: \"#mergeDoclingAndTextract\"\n");
+            }
+            default -> appendDoclingStep(sb);
+        }
+    }
+
+    private void appendDoclingStep(StringBuilder sb) {
+        sb.append("      - to:\n");
+        sb.append("          uri: \"docling:convert\"\n");
+        sb.append("          parameters:\n");
+        sb.append("            operation: CONVERT_TO_MARKDOWN\n");
+        sb.append("            useDoclingServe: true\n");
+        sb.append("            doclingServeUrl: \"{{docling.server.url}}\"\n");
+    }
+
+    private void appendTextractStep(StringBuilder sb) {
+        sb.append("      - to:\n");
+        sb.append("          uri: \"aws2-textract:detect\"\n");
+        sb.append("          parameters:\n");
+        sb.append("            operation: detectDocumentText\n");
+        sb.append("            region: \"{{aws.region}}\"\n");
+    }
+
+    private void appendBedrockConverse(StringBuilder sb, String model, String 
region) {
+        sb.append("      - to:\n");
+        sb.append("          uri: \"aws-bedrock:label\"\n");
+        sb.append("          parameters:\n");
+        sb.append("            operation: converse\n");
+        sb.append("            modelId: \"").append(model).append("\"\n");
+        sb.append("            region: \"").append(region).append("\"\n");
+    }
+
+    // ---- Properties generation ----
+
+    private String generateProperties(String processor, String source, String 
model, String region) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("# === AI Pipeline Configuration ===\n\n");
+
+        // AWS common
+        sb.append("# AWS Configuration\n");
+        sb.append("aws.region=").append(region).append("\n");
+        sb.append("# aws.accessKey={{aws-access-key}}\n");
+        sb.append("# aws.secretKey={{aws-secret-key}}\n\n");
+
+        // Document source
+        switch (source) {
+            case "file" -> {
+                sb.append("# Document Input\n");
+                sb.append("document.input.dir=/path/to/documents\n\n");
+            }
+            case "s3" -> {
+                sb.append("# S3 Document Source\n");
+                sb.append("document.s3.bucket=my-document-bucket\n\n");
+            }
+            default -> {
+            }
+        }
+
+        // Document processor
+        if ("docling".equals(processor) || "combined".equals(processor)) {
+            sb.append("# Docling Server (start with: docker run -p 5001:5001 
quay.io/docling-project/docling-serve)\n");
+            sb.append("docling.server.url=http://localhost:5001\n\n";);
+        }
+
+        // Bedrock
+        sb.append("# Bedrock LLM\n");
+        sb.append("# Model: ").append(model).append("\n");
+        sb.append("# Ensure IAM credentials have bedrock:InvokeModel 
permission\n\n");
+
+        return sb.toString();
+    }
+
+    // ---- Description generation ----
+
+    private String generateDescription(String type, String processor, String 
source) {
+        String processorDesc = switch (processor) {
+            case "docling" -> "Docling (open-source document converter)";
+            case "textract" -> "AWS Textract (managed OCR/table extraction)";
+            case "combined" -> "Docling (text) + Textract (tables/forms)";
+            default -> processor;
+        };
+        String sourceDesc = switch (source) {
+            case "file" -> "local filesystem";
+            case "s3" -> "AWS S3 bucket";
+            case "url" -> "HTTP/HTTPS URLs";
+            default -> source;
+        };
+        return String.format(
+                "%s pipeline using %s for document processing. Documents 
sourced from %s, processed by AWS Bedrock.",
+                capitalize(type), processorDesc, sourceDesc);
+    }
+
+    private static String capitalize(String s) {
+        if (s == null || s.isEmpty()) {
+            return s;
+        }
+        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+    }
+
+    // ---- Result record ----
+
+    public record ScaffoldResult(
+            String yamlRoute,
+            String applicationProperties,
+            String description) {
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/AiPipelineScaffoldToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/AiPipelineScaffoldToolsTest.java
new file mode 100644
index 000000000000..ef736764e4bf
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/AiPipelineScaffoldToolsTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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 io.quarkiverse.mcp.server.ToolCallException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class AiPipelineScaffoldToolsTest {
+
+    private AiPipelineScaffoldTools tools;
+
+    @BeforeEach
+    void setUp() {
+        tools = new AiPipelineScaffoldTools();
+    }
+
+    @Test
+    void shouldRequirePipelineType() {
+        assertThatThrownBy(() -> tools.camel_ai_pipeline_scaffold(null, null, 
null, null, null))
+                .isInstanceOf(ToolCallException.class)
+                .hasMessageContaining("pipelineType is required");
+    }
+
+    @Test
+    void shouldRejectUnknownPipelineType() {
+        assertThatThrownBy(() -> tools.camel_ai_pipeline_scaffold("unknown", 
null, null, null, null))
+                .isInstanceOf(ToolCallException.class)
+                .hasMessageContaining("Unknown pipeline type");
+    }
+
+    @Test
+    void shouldRejectUnknownDocumentProcessor() {
+        assertThatThrownBy(() -> 
tools.camel_ai_pipeline_scaffold("summarization", "invalid", null, null, null))
+                .isInstanceOf(ToolCallException.class)
+                .hasMessageContaining("Unknown document processor");
+    }
+
+    @Test
+    void shouldRejectUnknownDocumentSource() {
+        assertThatThrownBy(() -> 
tools.camel_ai_pipeline_scaffold("summarization", "docling", "ftp", null, null))
+                .isInstanceOf(ToolCallException.class)
+                .hasMessageContaining("Unknown document source");
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "summarization", "extraction", "rag", 
"classification" })
+    void shouldGenerateRouteForEachPipelineType(String type) {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold(type, "docling", "file", 
null, null);
+
+        assertThat(result.yamlRoute()).isNotBlank();
+        assertThat(result.yamlRoute()).contains("route:");
+        assertThat(result.yamlRoute()).contains("docling:convert");
+        assertThat(result.yamlRoute()).contains("aws-bedrock:");
+        assertThat(result.applicationProperties()).isNotBlank();
+        assertThat(result.description()).isNotBlank();
+    }
+
+    @Test
+    void shouldGenerateSummarizationWithDocling() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold("summarization", "docling", 
"file", null, null);
+
+        assertThat(result.yamlRoute())
+                .contains("ai-summarization")
+                .contains("docling:convert")
+                .contains("CONVERT_TO_MARKDOWN")
+                .contains("useDoclingServe: true")
+                .contains("doclingServeUrl:")
+                .contains("operation: converse")
+                .contains("modelId:")
+                .contains("buildSummarizationPrompt")
+                .doesNotContain("CamelAwsBedrockModelId")
+                .doesNotContain("CamelAwsBedrockInputType");
+
+        assertThat(result.applicationProperties())
+                .contains("docling.server.url")
+                .contains("document.input.dir");
+    }
+
+    @Test
+    void shouldGenerateExtractionWithTextract() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold("extraction", "textract", 
"s3", null, null);
+
+        assertThat(result.yamlRoute())
+                .contains("ai-extraction")
+                .contains("aws2-textract:")
+                .contains("detectDocumentText")
+                .contains("modelId:")
+                .contains("buildExtractionPrompt")
+                .doesNotContain("CamelAwsBedrockModelId");
+
+        assertThat(result.applicationProperties())
+                .contains("document.s3.bucket")
+                .doesNotContain("docling.server.url");
+    }
+
+    @Test
+    void shouldGenerateRagPipelineWithTwoRoutes() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold("rag", "docling", "file", 
null, null);
+
+        assertThat(result.yamlRoute())
+                .contains("rag-ingestion")
+                .contains("rag-query")
+                .contains("langchain4j-embeddings:")
+                .contains("split:");
+    }
+
+    @Test
+    void shouldGenerateClassificationWithChoiceRouter() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold("classification", 
"docling", "file", null, null);
+
+        assertThat(result.yamlRoute())
+                .contains("ai-classification")
+                .contains("choice:")
+                .contains("handle-invoice")
+                .contains("handle-contract");
+    }
+
+    @Test
+    void shouldSupportCombinedProcessor() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold("summarization", 
"combined", "file", null, null);
+
+        assertThat(result.yamlRoute())
+                .contains("docling:convert")
+                .contains("aws2-textract:");
+
+        assertThat(result.applicationProperties())
+                .contains("docling.server.url");
+    }
+
+    @Test
+    void shouldUseCustomModelAndRegion() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold(
+                        "summarization", "docling", "file", 
"amazon.nova-pro-v1:0", "eu-west-1");
+
+        assertThat(result.yamlRoute())
+                .contains("amazon.nova-pro-v1:0")
+                .contains("eu-west-1");
+
+        assertThat(result.applicationProperties())
+                .contains("eu-west-1");
+    }
+
+    @Test
+    void shouldGenerateS3SourceEndpoint() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold("summarization", "docling", 
"s3", null, null);
+
+        assertThat(result.yamlRoute())
+                .contains("aws2-s3:")
+                .contains("document.s3.bucket");
+    }
+
+    @Test
+    void shouldGenerateUrlSourceEndpoint() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold("summarization", "docling", 
"url", null, null);
+
+        assertThat(result.yamlRoute())
+                .contains("direct:process-url");
+    }
+
+    @Test
+    void shouldUseDefaultsWhenOptionalParamsAreNull() {
+        AiPipelineScaffoldTools.ScaffoldResult result
+                = tools.camel_ai_pipeline_scaffold("summarization", null, 
null, null, null);
+
+        assertThat(result.yamlRoute())
+                .contains("docling:convert")
+                .contains("us-east-1");
+
+        assertThat(result.description())
+                .contains("Docling");
+    }
+}

Reply via email to