This is an automated email from the ASF dual-hosted git repository.
davsclaus 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 2d084caa00de CAMEL-24961: camel-ai - the body types of openai,
langchain4j-chat, a2a, docling, milvus, qdrant, ai-tool and ibm-watson-language
(#28250)
2d084caa00de is described below
commit 2d084caa00de1526290c3d0aafd032cbb3c53cde
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Oct 24 08:59:07 2026 +0200
CAMEL-24961: camel-ai - the body types of openai, langchain4j-chat, a2a,
docling, milvus, qdrant, ai-tool and ibm-watson-language (#28250)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
.../org/apache/camel/catalog/components/a2a.json | 6 ++
.../apache/camel/catalog/components/ai-tool.json | 4 ++
.../apache/camel/catalog/components/docling.json | 5 ++
.../catalog/components/ibm-watson-language.json | 4 ++
.../camel/catalog/components/langchain4j-chat.json | 4 ++
.../org/apache/camel/catalog/components/llm.json | 6 ++
.../apache/camel/catalog/components/milvus.json | 4 ++
.../apache/camel/catalog/components/openai.json | 6 ++
.../apache/camel/catalog/components/qdrant.json | 4 ++
.../org/apache/camel/component/a2a/a2a.json | 6 ++
.../apache/camel/component/a2a/A2AEndpoint.java | 72 +++++++++++++++++++
.../apache/camel/component/ai/tool/ai-tool.json | 4 ++
.../camel/component/ai/tool/AiToolEndpoint.java | 13 ++++
.../apache/camel/component/docling/docling.json | 5 ++
.../camel/component/docling/DoclingEndpoint.java | 66 +++++++++++++++++
.../langchain4j/chat/langchain4j-chat.json | 4 ++
.../langchain4j/chat/LangChain4jChatEndpoint.java | 16 +++++
.../org/apache/camel/component/milvus/milvus.json | 4 ++
.../camel/component/milvus/MilvusEndpoint.java | 14 ++++
.../org/apache/camel/component/openai/llm.json | 6 ++
.../org/apache/camel/component/openai/openai.json | 6 ++
.../camel/component/openai/OpenAIEndpoint.java | 83 ++++++++++++++++++++++
.../org/apache/camel/component/qdrant/qdrant.json | 4 ++
.../camel/component/qdrant/QdrantEndpoint.java | 18 +++++
.../ibm/watson/language/ibm-watson-language.json | 4 ++
.../watson/language/WatsonLanguageEndpoint.java | 14 ++++
.../modules/ROOT/pages/camel-catalog.adoc | 3 +-
.../modules/ROOT/pages/camel-jbang-validation.adoc | 5 +-
.../camel/dsl/yaml/validator/CatalogBodyTypes.java | 35 +++++++++
.../camel/dsl/yaml/validator/TextBodyFlowTest.java | 44 ++++++++++++
.../camel/maven/packaging/BodyTypesHelper.java | 43 +++++++----
.../packaging/EndpointSchemaGeneratorMojoTest.java | 16 ++++-
.../endpoint/SomeEndpointWithBadBodyTypes.java | 6 ++
33 files changed, 515 insertions(+), 19 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/a2a.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/a2a.json
index 4fb7bd58f981..2ac1c899e37a 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/a2a.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/a2a.json
@@ -49,6 +49,12 @@
"CamelA2AListStatus": { "index": 17, "kind": "header", "displayName": "",
"group": "common", "label": "common", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Comma-separated status filter for task listing",
"constantName": "org.apache.camel.component.a2a.A2AConstants#LIST_STATUS" },
"CamelA2AExtensions": { "index": 18, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"java.util.List", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Negotiated A2A extension URIs requested
by the client", "constantName":
"org.apache.camel.component.a2a.A2AConstants#EXTENSIONS" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"dataFormat", "value": "POJO", "carriers": [ "pojo" ], "javaType":
"org.apache.camel.component.a2a.model.Message" } ], "description": "The message
of the caller, for SendMessage and SendStreamingMessage (the component answers
the task and push config requests itself): the text of its parts, one per line,
a data part as JSON and a file part as its URL. With dataFormat POJO the
Message, with RAW the Message as JSON" },
+ "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "pojo"
], "description": "The reply of the agent: text, sent as one text part (bytes,
a stream or a file as their text), or a Message sent as it is. Another body is
sent as text: a Task as the text of its last message, a Map or a POJO as its
toString(): marshal it first. The reply goes in a Task, completed unless the
CamelA2ATaskState header says another state, or as a Message when the
CamelA2AResponseType header is [...]
+ "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ],
"byOption": [ { "option": "operation", "value": "TASK_GET", "carriers": [ "any"
] }, { "option": "operation", "value": "TASK_LIST", "carriers": [ "any" ] }, {
"option": "operation", "value": "TASK_CANCEL", "carriers": [ "any" ] }, {
"option": "operation", "value": "TASK_SUBSCRIBE", "carriers": [ "any" ] }, {
"option": "operation", "value": "PUSH_CONFIG_CREATE", "carriers": [ "pojo" ],
"javaType": "org.apache.c [...]
+ "producerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"operation", "value": "MESSAGE_STREAM", "carriers": [ "iterator" ], "javaType":
"org.apache.camel.component.a2a.streaming.SseEventIterator" }, { "option":
"operation", "value": "TASK_GET", "carriers": [ "text" ] }, { "option":
"operation", "value": "TASK_LIST", "carriers": [ "list", "null" ] }, {
"option": "operation", "value": "TASK_CANCEL", "carriers": [ "text" ] }, {
"option": "operation", "value": "TASK_SUBSCRIBE" [...]
+ },
"properties": {
"agentCardSource": { "index": 0, "kind": "path", "displayName": "Agent
Card Source", "group": "common", "label": "", "required": true, "type":
"string", "javaType": "java.lang.String", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
agent card source (classpath:, file:, http:\/\/, https:\/\/, or plain name)" },
"agentCard": { "index": 1, "kind": "parameter", "displayName": "Agent
Card", "group": "common", "label": "", "required": false, "type": "object",
"javaType": "org.apache.camel.component.a2a.model.AgentCard", "deprecated":
false, "deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.a2a.A2AConfiguration",
"configurationField": "configuration", "description": "Agent card bean
reference or inline configuration" },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ai-tool.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ai-tool.json
index 0ce16172cd96..deed749b292a 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ai-tool.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ai-tool.json
@@ -46,6 +46,10 @@
"CamelAiToolCallId": { "index": 0, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The tool invocation identifier supplied by the
calling framework. Absent when the framework does not supply one.",
"constantName":
"org.apache.camel.component.ai.tool.AiToolConstants#TOOL_CALL_ID" },
"CamelAiToolConversationId": { "index": 1, "kind": "header",
"displayName": "", "group": "consumer", "label": "consumer", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The conversation identifier supplied by
the calling framework. Absent when the framework does not supply one.",
"constantName":
"org.apache.camel.component.ai.tool.AiToolConstants#CONVERSATION_ID" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "null" ], "description": "No body: each
argument of the tool call is a header named as its parameter, a Map or a List
for a nested JSON value. Undeclared arguments and names starting with camel are
dropped" },
+ "consumerReply": { "carriers": [ "any" ], "dependsOn": [ "outputSchema",
"outputParameters" ], "description": "The tool result, as text: the body
converted to a String, so a Map, a List or a POJO is sent as its toString():
marshal it to JSON first. No body gives No result. With outputSchema or
outputParameter.NAME options the body must be JSON: a JSON String, a Map, a
List, a number or a boolean, and bytes, a stream or another object fail the
call" }
+ },
"properties": {
"toolName": { "index": 0, "kind": "path", "displayName": "Tool Name",
"group": "consumer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The tool name. This is the
name the LLM sees and uses to invoke the tool." },
"argSchema": { "index": 1, "kind": "parameter", "displayName": "Arg
Schema", "group": "consumer", "label": "consumer", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"supportFileReference": true, "configurationClass":
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField":
"configuration", "description": "Raw JSON Schema for tool input parameters.
Supports in [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/docling.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/docling.json
index 2929f7f68134..426611f97d2f 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/docling.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/docling.json
@@ -117,6 +117,11 @@
"CamelDoclingChunkingMaxTokens": { "index": 30, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Maximum tokens per chunk for hybrid
chunking", "constantName":
"org.apache.camel.component.docling.DoclingHeaders#CHUNKING_MAX_TOKENS" },
"CamelDoclingChunkingMergePeers": { "index": 31, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Whether to merge peer chunks in hybrid
chunking", "constantName":
"org.apache.camel.component.docling.DoclingHeaders#CHUNKING_MERGE_PEERS" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "text" ], "description": "The document of
an async conversion that completed (SUBMIT_ASYNC_CONVERSION on the same
component), as text in the outputFormat it was submitted with: markdown by
default, JSON text for json. The task id is in the CamelDoclingTaskId header,
and a failed conversion goes to the error handler" },
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "BATCH_CONVERT_TO_MARKDOWN", "carriers": [ "list" ] }, {
"option": "operation", "value": "BATCH_CONVERT_TO_HTML", "carriers": [ "list" ]
}, { "option": "operation", "value": "BATCH_CONVERT_TO_JSON", "carriers": [
"list" ] }, { "option": "operation", "value": "BATCH_EXTRACT_TEXT", "carriers":
[ "list" ] }, { "option": "operation", "value":
"BATCH_EXTRACT_STRUCTURED_DATA", "carriers": [ "list" ] } ], [...]
+ "producerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"operation", "value": "CONVERT_TO_JSON", "carriers": [ "pojo" ], "javaType":
"ai.docling.core.DoclingDocument" }, { "option": "operation", "value":
"EXTRACT_STRUCTURED_DATA", "carriers": [ "pojo" ], "javaType":
"ai.docling.core.DoclingDocument" }, { "option": "operation", "value":
"CHECK_CONVERSION_STATUS", "carriers": [ "pojo" ], "javaType":
"org.apache.camel.component.docling.ConversionStatus" }, { "option": "operat
[...]
+ },
"properties": {
"operationId": { "index": 0, "kind": "path", "displayName": "Operation
Id", "group": "common", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The operation identifier"
},
"contentInBody": { "index": 1, "kind": "parameter", "displayName":
"Content In Body", "group": "common", "label": "", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": false,
"configurationClass":
"org.apache.camel.component.docling.DoclingConfiguration",
"configurationField": "configuration", "description": "Include the content of
the output file in the exchange body and delet [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-watson-language.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-watson-language.json
index ce47ac5e383e..d0b502549cf5 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-watson-language.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ibm-watson-language.json
@@ -53,6 +53,10 @@
"CamelIBMWatsonLanguageSentimentScore": { "index": 10, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The sentiment score", "constantName":
"org.apache.camel.component.ibm.watson.language.WatsonLanguageConstants#SENTIMENT_SCORE"
},
"CamelIBMWatsonLanguageSentimentLabel": { "index": 11, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The sentiment label (positive,
negative, neutral)", "constantName":
"org.apache.camel.component.ibm.watson.language.WatsonLanguageConstants#SENTIMENT_LABEL"
}
},
+ "bodyTypes": {
+ "producerIn": { "carriers": [ "any" ], "description": "The text to analyze
for analyzeText, or the URL of the page for analyzeUrl, read as text, unless
the CamelIBMWatsonLanguageText or CamelIBMWatsonLanguageUrl header gives it.
Without the header, no body or a blank one fails, and a Map or a POJO is
analyzed as its toString(): marshal it first" },
+ "producerOut": { "carriers": [ "pojo" ], "javaType":
"com.ibm.watson.natural_language_understanding.v1.model.AnalysisResults",
"description": "The results of the features enabled (sentiment, entities,
keywords and so on), with the sentiment score and label and the language in
headers too. Converted to text, it is the JSON of the results" }
+ },
"properties": {
"label": { "index": 0, "kind": "path", "displayName": "Label", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Logical name" },
"serviceUrl": { "index": 1, "kind": "parameter", "displayName": "Service
Url", "group": "common", "label": "common", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.ibm.watson.language.WatsonLanguageConfiguration",
"configurationField": "configuration", "description": "The service endpoint
URL. If not specified, the default URL will be used." },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/langchain4j-chat.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/langchain4j-chat.json
index 951f48a17cf5..f1aedc286daf 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/langchain4j-chat.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/langchain4j-chat.json
@@ -48,6 +48,10 @@
"CamelLangChain4jChatRequestModel": { "index": 6, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The request model name.",
"constantName":
"org.apache.camel.component.langchain4j.chat.LangChain4jChatHeaders#REQUEST_MODEL"
},
"CamelLangChain4jChatResponseModel": { "index": 7, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The response model name.",
"constantName":
"org.apache.camel.component.langchain4j.chat.LangChain4jChatHeaders#RESPONSE_MODEL"
}
},
+ "bodyTypes": {
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"chatOperation", "value": "CHAT_SINGLE_MESSAGE_WITH_PROMPT", "carriers": [
"map" ] }, { "option": "chatOperation", "value": "CHAT_MULTIPLE_MESSAGES",
"carriers": [ "list" ] } ], "description": "The prompt as text, or a
ChatMessage; another body is sent as its toString(), and no body fails.
CHAT_SINGLE_MESSAGE_WITH_PROMPT takes the variables of the template in the
CamelLangChain4jChatPromptTemplate header as a Map, and C [...]
+ "producerOut": { "carriers": [ "text" ], "description": "The answer of the
model as text, also when it is asked for JSON: unmarshal it to read its fields"
}
+ },
"properties": {
"chatId": { "index": 0, "kind": "path", "displayName": "Chat Id", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The id" },
"chatOperation": { "index": 1, "kind": "parameter", "displayName": "Chat
Operation", "group": "producer", "label": "", "required": true, "type": "enum",
"javaType":
"org.apache.camel.component.langchain4j.chat.LangChain4jChatOperations",
"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. [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/llm.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/llm.json
index a2451cbd1b69..6870d839b3a6 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/llm.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/llm.json
@@ -146,6 +146,12 @@
"CamelOpenAIImageOutputTokens": { "index": 107, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The number of output tokens billed for
the image request. Only reported by GPT image models", "constantName":
"org.apache.camel.component.openai.OpenAIConstants#IMAGE_OUTPUT_TOKENS" },
"CamelOpenAIImageTotalTokens": { "index": 108, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The total number of tokens billed for
the image request. Only reported by GPT image models", "constantName":
"org.apache.camel.component.openai.OpenAIConstants#IMAGE_TOTAL_TOKENS" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "pojo" ], "javaType":
"com.openai.models.webhooks.UnwrapWebhookEvent", "description": "The event of
the webhook operation, verified with webhookSecret, such as a batch or a
background response that completed. The event type and the id of the object it
is about are in headers" },
+ "consumerReply": { "carriers": [ "any" ], "description": "The body at the
end of the route is not sent: OpenAI gets 200 without a body, or 500 when the
route fails, and then sends the event again" },
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "decisions", "carriers": [ "pojo" ], "javaType":
"com.openai.models.decisions.DecisionCreateParams" }, { "option": "operation",
"value": "batch", "carriers": [ "map", "list", "text", "bytes", "stream",
"file" ] }, { "option": "operation", "value": "audio-transcription",
"carriers": [ "file", "bytes", "stream" ] }, { "option": "operation", "value":
"audio-translation", "carriers": [ "file", "bytes", [...]
+ "producerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"streaming", "value": "true", "carriers": [ "iterator" ] }, { "option":
"autoToolExecution", "value": "false", "carriers": [ "text", "pojo", "list" ]
}, { "option": "operation", "value": "decisions", "carriers": [ "pojo" ],
"javaType": "com.openai.models.decisions.Decision" }, { "option": "operation",
"value": "responses", "carriers": [ "text" ] }, { "option": "operation",
"value": "responses-retrieve", "carriers": [ [...]
+ },
"properties": {
"operation": { "index": 0, "kind": "path", "displayName": "Operation",
"group": "common", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.openai.OpenAIOperations", "enum": [
"chat-completion", "decisions", "responses", "responses-retrieve",
"responses-cancel", "batch", "batch-retrieve", "batch-cancel", "batch-results",
"embeddings", "tool-execution", "audio-transcription", "audio-translation",
"audio-speech", "moderation", "image-generation", "i [...]
"additionalBodyProperty": { "index": 1, "kind": "parameter",
"displayName": "Additional Body Property", "group": "common", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalBodyProperty.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configurat [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/milvus.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/milvus.json
index 401fd632d4cc..c2c82cb38cd3 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/milvus.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/milvus.json
@@ -42,6 +42,10 @@
"CamelMilvusKeyName": { "index": 6, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Key Name for Insert\/Upsert operation", "constantName":
"org.apache.camel.component.milvus.MilvusHeaders#KEY_NAME" },
"CamelMilvusKeyValue": { "index": 7, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Key Value for Insert\/Upsert operation", "constantName":
"org.apache.camel.component.milvus.MilvusHeaders#KEY_VALUE" }
},
+ "bodyTypes": {
+ "producerIn": { "carriers": [ "pojo" ], "description": "The request of the
Milvus SDK for the action in the CamelMilvusAction header: a
CreateCollectionParam, CreateIndexParam, InsertParam, UpsertParam,
SearchSimpleParam, QueryParam or DeleteParam. Another body fails, a vector as a
List too: the milvus:embeddings data type transformer builds the InsertParam or
UpsertParam from the output of langchain4j-embeddings" },
+ "producerOut": { "carriers": [ "pojo" ], "description": "The result of the
Milvus SDK for the action: an RpcStatus for CREATE_COLLECTION and CREATE_INDEX,
a MutationResult for INSERT, UPSERT and DELETE, a SearchResponse for SEARCH and
a QueryResults for QUERY" }
+ },
"properties": {
"collection": { "index": 0, "kind": "path", "displayName": "Collection",
"group": "producer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The collection Name" },
"host": { "index": 1, "kind": "parameter", "displayName": "Host", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": "localhost", "configurationClass":
"org.apache.camel.component.milvus.MilvusConfiguration", "configurationField":
"configuration", "description": "The host to connect to." },
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
index d212b19e520c..4d569fa9d943 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openai.json
@@ -146,6 +146,12 @@
"CamelOpenAIImageOutputTokens": { "index": 107, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The number of output tokens billed for
the image request. Only reported by GPT image models", "constantName":
"org.apache.camel.component.openai.OpenAIConstants#IMAGE_OUTPUT_TOKENS" },
"CamelOpenAIImageTotalTokens": { "index": 108, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The total number of tokens billed for
the image request. Only reported by GPT image models", "constantName":
"org.apache.camel.component.openai.OpenAIConstants#IMAGE_TOTAL_TOKENS" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "pojo" ], "javaType":
"com.openai.models.webhooks.UnwrapWebhookEvent", "description": "The event of
the webhook operation, verified with webhookSecret, such as a batch or a
background response that completed. The event type and the id of the object it
is about are in headers" },
+ "consumerReply": { "carriers": [ "any" ], "description": "The body at the
end of the route is not sent: OpenAI gets 200 without a body, or 500 when the
route fails, and then sends the event again" },
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "decisions", "carriers": [ "pojo" ], "javaType":
"com.openai.models.decisions.DecisionCreateParams" }, { "option": "operation",
"value": "batch", "carriers": [ "map", "list", "text", "bytes", "stream",
"file" ] }, { "option": "operation", "value": "audio-transcription",
"carriers": [ "file", "bytes", "stream" ] }, { "option": "operation", "value":
"audio-translation", "carriers": [ "file", "bytes", [...]
+ "producerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"streaming", "value": "true", "carriers": [ "iterator" ] }, { "option":
"autoToolExecution", "value": "false", "carriers": [ "text", "pojo", "list" ]
}, { "option": "operation", "value": "decisions", "carriers": [ "pojo" ],
"javaType": "com.openai.models.decisions.Decision" }, { "option": "operation",
"value": "responses", "carriers": [ "text" ] }, { "option": "operation",
"value": "responses-retrieve", "carriers": [ [...]
+ },
"properties": {
"operation": { "index": 0, "kind": "path", "displayName": "Operation",
"group": "common", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.openai.OpenAIOperations", "enum": [
"chat-completion", "decisions", "responses", "responses-retrieve",
"responses-cancel", "batch", "batch-retrieve", "batch-cancel", "batch-results",
"embeddings", "tool-execution", "audio-transcription", "audio-translation",
"audio-speech", "moderation", "image-generation", "i [...]
"additionalBodyProperty": { "index": 1, "kind": "parameter",
"displayName": "Additional Body Property", "group": "common", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalBodyProperty.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configurat [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/qdrant.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/qdrant.json
index 283a45b213cb..4370a6d81573 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/qdrant.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/qdrant.json
@@ -48,6 +48,10 @@
"CamelQdrantPointId": { "index": 9, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "int",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The point id to use for operation.", "constantName":
"org.apache.camel.component.qdrant.QdrantHeaders#POINT_ID" },
"CamelQdrantMaxResults": { "index": 10, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "int",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The maximum number of results to return from a
similarity search.", "constantName":
"org.apache.camel.component.qdrant.QdrantHeaders#MAX_RESULTS" }
},
+ "bodyTypes": {
+ "producerIn": { "carriers": [ "pojo", "list", "null" ], "description": "By
the action in the CamelQdrantAction header: a VectorParams for
CREATE_COLLECTION, a PointStruct or a List of them for UPSERT, a PointId or a
List of them for RETRIEVE, a PointsSelector, PointId, Filter or Condition for
DELETE, and the query vector as a List of Float, a float array or a PointStruct
for SIMILARITY_SEARCH. DELETE_COLLECTION and COLLECTION_INFO do not read the
body. Another body fails, such as a n [...]
+ "producerOut": { "carriers": [ "list", "pojo", "unchanged" ],
"description": "By the action: a List of RetrievedPoint for RETRIEVE, a List of
ScoredPoint for SIMILARITY_SEARCH and a CollectionInfo for COLLECTION_INFO. The
other actions keep the body, UPSERT and DELETE with the operation id and status
in headers" }
+ },
"properties": {
"collection": { "index": 0, "kind": "path", "displayName": "Collection",
"group": "producer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The collection Name" },
"host": { "index": 1, "kind": "parameter", "displayName": "Host", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": "localhost", "configurationClass":
"org.apache.camel.component.qdrant.QdrantConfiguration", "configurationField":
"configuration", "description": "The host to connect to." },
diff --git
a/components/camel-ai/camel-a2a/src/generated/resources/META-INF/org/apache/camel/component/a2a/a2a.json
b/components/camel-ai/camel-a2a/src/generated/resources/META-INF/org/apache/camel/component/a2a/a2a.json
index 4fb7bd58f981..2ac1c899e37a 100644
---
a/components/camel-ai/camel-a2a/src/generated/resources/META-INF/org/apache/camel/component/a2a/a2a.json
+++
b/components/camel-ai/camel-a2a/src/generated/resources/META-INF/org/apache/camel/component/a2a/a2a.json
@@ -49,6 +49,12 @@
"CamelA2AListStatus": { "index": 17, "kind": "header", "displayName": "",
"group": "common", "label": "common", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Comma-separated status filter for task listing",
"constantName": "org.apache.camel.component.a2a.A2AConstants#LIST_STATUS" },
"CamelA2AExtensions": { "index": 18, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"java.util.List", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Negotiated A2A extension URIs requested
by the client", "constantName":
"org.apache.camel.component.a2a.A2AConstants#EXTENSIONS" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"dataFormat", "value": "POJO", "carriers": [ "pojo" ], "javaType":
"org.apache.camel.component.a2a.model.Message" } ], "description": "The message
of the caller, for SendMessage and SendStreamingMessage (the component answers
the task and push config requests itself): the text of its parts, one per line,
a data part as JSON and a file part as its URL. With dataFormat POJO the
Message, with RAW the Message as JSON" },
+ "consumerReply": { "carriers": [ "text", "bytes", "stream", "file", "pojo"
], "description": "The reply of the agent: text, sent as one text part (bytes,
a stream or a file as their text), or a Message sent as it is. Another body is
sent as text: a Task as the text of its last message, a Map or a POJO as its
toString(): marshal it first. The reply goes in a Task, completed unless the
CamelA2ATaskState header says another state, or as a Message when the
CamelA2AResponseType header is [...]
+ "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ],
"byOption": [ { "option": "operation", "value": "TASK_GET", "carriers": [ "any"
] }, { "option": "operation", "value": "TASK_LIST", "carriers": [ "any" ] }, {
"option": "operation", "value": "TASK_CANCEL", "carriers": [ "any" ] }, {
"option": "operation", "value": "TASK_SUBSCRIBE", "carriers": [ "any" ] }, {
"option": "operation", "value": "PUSH_CONFIG_CREATE", "carriers": [ "pojo" ],
"javaType": "org.apache.c [...]
+ "producerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"operation", "value": "MESSAGE_STREAM", "carriers": [ "iterator" ], "javaType":
"org.apache.camel.component.a2a.streaming.SseEventIterator" }, { "option":
"operation", "value": "TASK_GET", "carriers": [ "text" ] }, { "option":
"operation", "value": "TASK_LIST", "carriers": [ "list", "null" ] }, {
"option": "operation", "value": "TASK_CANCEL", "carriers": [ "text" ] }, {
"option": "operation", "value": "TASK_SUBSCRIBE" [...]
+ },
"properties": {
"agentCardSource": { "index": 0, "kind": "path", "displayName": "Agent
Card Source", "group": "common", "label": "", "required": true, "type":
"string", "javaType": "java.lang.String", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
agent card source (classpath:, file:, http:\/\/, https:\/\/, or plain name)" },
"agentCard": { "index": 1, "kind": "parameter", "displayName": "Agent
Card", "group": "common", "label": "", "required": false, "type": "object",
"javaType": "org.apache.camel.component.a2a.model.AgentCard", "deprecated":
false, "deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.a2a.A2AConfiguration",
"configurationField": "configuration", "description": "Agent card bean
reference or inline configuration" },
diff --git
a/components/camel-ai/camel-a2a/src/main/java/org/apache/camel/component/a2a/A2AEndpoint.java
b/components/camel-ai/camel-a2a/src/main/java/org/apache/camel/component/a2a/A2AEndpoint.java
index c9868f484217..0f3a5649244d 100644
---
a/components/camel-ai/camel-a2a/src/main/java/org/apache/camel/component/a2a/A2AEndpoint.java
+++
b/components/camel-ai/camel-a2a/src/main/java/org/apache/camel/component/a2a/A2AEndpoint.java
@@ -47,6 +47,9 @@ import
org.apache.camel.component.a2a.push.PushNotificationSubscriber;
import org.apache.camel.component.a2a.state.A2ATaskStore;
import org.apache.camel.component.a2a.state.GuardedTaskStore;
import org.apache.camel.component.a2a.state.InMemoryTaskStore;
+import org.apache.camel.spi.BodyType;
+import org.apache.camel.spi.BodyType.Carrier;
+import org.apache.camel.spi.BodyTypes;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
@@ -70,6 +73,75 @@ import org.slf4j.LoggerFactory;
syntax = "a2a:agentCardSource",
category = { Category.AI },
headersClass = A2AConstants.class)
+@BodyTypes(consumerOut = @BodyType(value = Carrier.TEXT,
+ byOption = @BodyType.When(option =
"dataFormat", value = "POJO", carriers = Carrier.POJO,
+ javaType =
"org.apache.camel.component.a2a.model.Message"),
+ description = "The message of the caller,
for SendMessage and SendStreamingMessage (the"
+ + " component answers the
task and push config requests itself): the text"
+ + " of its parts, one per
line, a data part as JSON and a file part as its"
+ + " URL. With dataFormat POJO
the Message, with RAW the Message as JSON"),
+ consumerReply = @BodyType(value = { Carrier.TEXT, Carrier.BYTES,
Carrier.STREAM, Carrier.FILE, Carrier.POJO },
+ description = "The reply of the agent:
text, sent as one text part (bytes, a stream or a"
+ + " file as their text), or
a Message sent as it is. Another body is sent"
+ + " as text: a Task as the
text of its last message, a Map or a POJO as its"
+ + " toString(): marshal it
first. The reply goes in a Task, completed"
+ + " unless the
CamelA2ATaskState header says another state, or as a Message"
+ + " when the
CamelA2AResponseType header is message"),
+ producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES,
Carrier.STREAM, Carrier.FILE, Carrier.POJO },
+ byOption = {
+ @BodyType.When(option = "operation",
value = "TASK_GET", carriers = Carrier.ANY),
+ @BodyType.When(option = "operation",
value = "TASK_LIST", carriers = Carrier.ANY),
+ @BodyType.When(option = "operation",
value = "TASK_CANCEL", carriers = Carrier.ANY),
+ @BodyType.When(option = "operation",
value = "TASK_SUBSCRIBE",
+ carriers =
Carrier.ANY),
+ @BodyType.When(option = "operation",
value = "PUSH_CONFIG_CREATE",
+ carriers =
Carrier.POJO,
+ javaType =
"org.apache.camel.component.a2a.model.TaskPushNotificationConfig"),
+ @BodyType.When(option = "operation",
value = "PUSH_CONFIG_GET",
+ carriers =
Carrier.ANY),
+ @BodyType.When(option = "operation",
value = "PUSH_CONFIG_LIST",
+ carriers =
Carrier.ANY),
+ @BodyType.When(option = "operation",
value = "PUSH_CONFIG_DELETE",
+ carriers =
Carrier.ANY) },
+ description = "The message to send: text,
sent as one text part (bytes, a stream or a"
+ + " file as their text), or a
Message sent as it is; MESSAGE_STREAM also"
+ + " takes a
SendMessageRequest. Another body is sent as its toString(), so a"
+ + " Map or a POJO needs
marshalling first, and JSON text is sent as text."
+ + " PUSH_CONFIG_CREATE needs a
TaskPushNotificationConfig, and the other"
+ + " operations read headers,
not the body"),
+ producerOut = @BodyType(value = Carrier.TEXT,
+ byOption = {
+ @BodyType.When(option =
"operation", value = "MESSAGE_STREAM",
+ carriers =
Carrier.ITERATOR,
+ javaType =
"org.apache.camel.component.a2a.streaming.SseEventIterator"),
+ @BodyType.When(option =
"operation", value = "TASK_GET", carriers = Carrier.TEXT),
+ @BodyType.When(option =
"operation", value = "TASK_LIST",
+ carriers = {
Carrier.LIST, Carrier.NULL }),
+ @BodyType.When(option =
"operation", value = "TASK_CANCEL", carriers = Carrier.TEXT),
+ @BodyType.When(option =
"operation", value = "TASK_SUBSCRIBE",
+ carriers =
Carrier.ITERATOR,
+ javaType =
"org.apache.camel.component.a2a.streaming.SseEventIterator"),
+ @BodyType.When(option =
"operation", value = "PUSH_CONFIG_CREATE",
+ carriers =
Carrier.POJO,
+ javaType =
"org.apache.camel.component.a2a.model.TaskPushNotificationConfig"),
+ @BodyType.When(option =
"operation", value = "PUSH_CONFIG_GET",
+ carriers =
Carrier.POJO,
+ javaType =
"org.apache.camel.component.a2a.model.TaskPushNotificationConfig"),
+ @BodyType.When(option =
"operation", value = "PUSH_CONFIG_LIST",
+ carriers = {
Carrier.LIST, Carrier.NULL }),
+ @BodyType.When(option =
"operation", value = "PUSH_CONFIG_DELETE",
+ carriers =
Carrier.MAP),
+ @BodyType.When(option =
"dataFormat", value = "POJO", carriers = Carrier.POJO),
+ @BodyType.When(option =
"dataFormat", value = "RAW", carriers = Carrier.TEXT) },
+ description = "The reply of the agent as
text: the parts of the Message, or of the last"
+ + " message in the history of
the Task, one per line. TASK_GET and"
+ + " TASK_CANCEL give the text
of the Task, TASK_LIST a List of Task,"
+ + " MESSAGE_STREAM and
TASK_SUBSCRIBE an Iterator of the events as they"
+ + " arrive (split it), and
the push config operations a"
+ + "
TaskPushNotificationConfig, a List of them, or an empty Map for delete."
+ + " With dataFormat POJO the
Task or the Message, with RAW the JSON of the"
+ + " response as text (a
stream of the events for MESSAGE_STREAM and"
+ + " TASK_SUBSCRIBE)"))
public class A2AEndpoint extends DefaultEndpoint {
private static final Logger LOG =
LoggerFactory.getLogger(A2AEndpoint.class);
diff --git
a/components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json
b/components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json
index 0ce16172cd96..deed749b292a 100644
---
a/components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json
+++
b/components/camel-ai/camel-ai-tool/src/generated/resources/META-INF/org/apache/camel/component/ai/tool/ai-tool.json
@@ -46,6 +46,10 @@
"CamelAiToolCallId": { "index": 0, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The tool invocation identifier supplied by the
calling framework. Absent when the framework does not supply one.",
"constantName":
"org.apache.camel.component.ai.tool.AiToolConstants#TOOL_CALL_ID" },
"CamelAiToolConversationId": { "index": 1, "kind": "header",
"displayName": "", "group": "consumer", "label": "consumer", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The conversation identifier supplied by
the calling framework. Absent when the framework does not supply one.",
"constantName":
"org.apache.camel.component.ai.tool.AiToolConstants#CONVERSATION_ID" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "null" ], "description": "No body: each
argument of the tool call is a header named as its parameter, a Map or a List
for a nested JSON value. Undeclared arguments and names starting with camel are
dropped" },
+ "consumerReply": { "carriers": [ "any" ], "dependsOn": [ "outputSchema",
"outputParameters" ], "description": "The tool result, as text: the body
converted to a String, so a Map, a List or a POJO is sent as its toString():
marshal it to JSON first. No body gives No result. With outputSchema or
outputParameter.NAME options the body must be JSON: a JSON String, a Map, a
List, a number or a boolean, and bytes, a stream or another object fail the
call" }
+ },
"properties": {
"toolName": { "index": 0, "kind": "path", "displayName": "Tool Name",
"group": "consumer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The tool name. This is the
name the LLM sees and uses to invoke the tool." },
"argSchema": { "index": 1, "kind": "parameter", "displayName": "Arg
Schema", "group": "consumer", "label": "consumer", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"supportFileReference": true, "configurationClass":
"org.apache.camel.component.ai.tool.AiToolConfiguration", "configurationField":
"configuration", "description": "Raw JSON Schema for tool input parameters.
Supports in [...]
diff --git
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolEndpoint.java
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolEndpoint.java
index c6edab87148f..f85439260633 100644
---
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolEndpoint.java
+++
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolEndpoint.java
@@ -20,6 +20,9 @@ import org.apache.camel.Category;
import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
+import org.apache.camel.spi.BodyType;
+import org.apache.camel.spi.BodyType.Carrier;
+import org.apache.camel.spi.BodyTypes;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
@@ -43,6 +46,16 @@ import static
org.apache.camel.component.ai.tool.AiTool.SCHEME;
remote = false,
headersClass = AiToolConstants.class,
category = { Category.AI })
+@BodyTypes(consumerOut = @BodyType(value = Carrier.NULL,
+ description = "No body: each argument of
the tool call is a header named as its"
+ + " parameter, a Map or a
List for a nested JSON value. Undeclared"
+ + " arguments and names
starting with camel are dropped"),
+ consumerReply = @BodyType(value = Carrier.ANY, dependsOn = {
"outputSchema", "outputParameters" },
+ description = "The tool result, as text:
the body converted to a String, so a Map, a"
+ + " List or a POJO is sent
as its toString(): marshal it to JSON first. No"
+ + " body gives No result.
With outputSchema or outputParameter.NAME"
+ + " options the body must
be JSON: a JSON String, a Map, a List, a number"
+ + " or a boolean, and
bytes, a stream or another object fail the call"))
public class AiToolEndpoint extends DefaultEndpoint {
@Metadata(required = true)
diff --git
a/components/camel-ai/camel-docling/src/generated/resources/META-INF/org/apache/camel/component/docling/docling.json
b/components/camel-ai/camel-docling/src/generated/resources/META-INF/org/apache/camel/component/docling/docling.json
index 2929f7f68134..426611f97d2f 100644
---
a/components/camel-ai/camel-docling/src/generated/resources/META-INF/org/apache/camel/component/docling/docling.json
+++
b/components/camel-ai/camel-docling/src/generated/resources/META-INF/org/apache/camel/component/docling/docling.json
@@ -117,6 +117,11 @@
"CamelDoclingChunkingMaxTokens": { "index": 30, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Maximum tokens per chunk for hybrid
chunking", "constantName":
"org.apache.camel.component.docling.DoclingHeaders#CHUNKING_MAX_TOKENS" },
"CamelDoclingChunkingMergePeers": { "index": 31, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Whether to merge peer chunks in hybrid
chunking", "constantName":
"org.apache.camel.component.docling.DoclingHeaders#CHUNKING_MERGE_PEERS" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "text" ], "description": "The document of
an async conversion that completed (SUBMIT_ASYNC_CONVERSION on the same
component), as text in the outputFormat it was submitted with: markdown by
default, JSON text for json. The task id is in the CamelDoclingTaskId header,
and a failed conversion goes to the error handler" },
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "BATCH_CONVERT_TO_MARKDOWN", "carriers": [ "list" ] }, {
"option": "operation", "value": "BATCH_CONVERT_TO_HTML", "carriers": [ "list" ]
}, { "option": "operation", "value": "BATCH_CONVERT_TO_JSON", "carriers": [
"list" ] }, { "option": "operation", "value": "BATCH_EXTRACT_TEXT", "carriers":
[ "list" ] }, { "option": "operation", "value":
"BATCH_EXTRACT_STRUCTURED_DATA", "carriers": [ "list" ] } ], [...]
+ "producerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"operation", "value": "CONVERT_TO_JSON", "carriers": [ "pojo" ], "javaType":
"ai.docling.core.DoclingDocument" }, { "option": "operation", "value":
"EXTRACT_STRUCTURED_DATA", "carriers": [ "pojo" ], "javaType":
"ai.docling.core.DoclingDocument" }, { "option": "operation", "value":
"CHECK_CONVERSION_STATUS", "carriers": [ "pojo" ], "javaType":
"org.apache.camel.component.docling.ConversionStatus" }, { "option": "operat
[...]
+ },
"properties": {
"operationId": { "index": 0, "kind": "path", "displayName": "Operation
Id", "group": "common", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The operation identifier"
},
"contentInBody": { "index": 1, "kind": "parameter", "displayName":
"Content In Body", "group": "common", "label": "", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "defaultValue": false,
"configurationClass":
"org.apache.camel.component.docling.DoclingConfiguration",
"configurationField": "configuration", "description": "Include the content of
the output file in the exchange body and delet [...]
diff --git
a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingEndpoint.java
b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingEndpoint.java
index d7e152bfa55f..35fbad76dff3 100644
---
a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingEndpoint.java
+++
b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingEndpoint.java
@@ -20,6 +20,9 @@ import org.apache.camel.Category;
import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
+import org.apache.camel.spi.BodyType;
+import org.apache.camel.spi.BodyType.Carrier;
+import org.apache.camel.spi.BodyTypes;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
@@ -33,6 +36,69 @@ import org.apache.camel.support.ScheduledPollEndpoint;
title = "Docling",
syntax = "docling:operationId",
category = { Category.TRANSFORMATION, Category.AI }, headersClass
= DoclingHeaders.class)
+@BodyTypes(consumerOut = @BodyType(value = Carrier.TEXT,
+ description = "The document of an async
conversion that completed"
+ + " (SUBMIT_ASYNC_CONVERSION
on the same component), as text in the"
+ + " outputFormat it was
submitted with: markdown by default, JSON text for"
+ + " json. The task id is in
the CamelDoclingTaskId header, and a failed"
+ + " conversion goes to the
error handler"),
+ producerIn = @BodyType(value = Carrier.ANY,
+ byOption = {
+ @BodyType.When(option = "operation",
value = "BATCH_CONVERT_TO_MARKDOWN",
+ carriers =
Carrier.LIST),
+ @BodyType.When(option = "operation",
value = "BATCH_CONVERT_TO_HTML",
+ carriers =
Carrier.LIST),
+ @BodyType.When(option = "operation",
value = "BATCH_CONVERT_TO_JSON",
+ carriers =
Carrier.LIST),
+ @BodyType.When(option = "operation",
value = "BATCH_EXTRACT_TEXT",
+ carriers =
Carrier.LIST),
+ @BodyType.When(option = "operation",
value = "BATCH_EXTRACT_STRUCTURED_DATA",
+ carriers =
Carrier.LIST) },
+ description = "The document: its content as
text or bytes, or a File (the GenericFile of"
+ + " the file consumers too). A
stream or a Path fails: convert it to bytes"
+ + " first. Text that looks
like a URL or a path (it starts with http://,"
+ + " https:// or /, or has a
backslash) is read as one only with"
+ + " allowUrlSource or
allowFilePathSource, and fails otherwise. With the"
+ + " CamelDoclingInputFilePath
header the body is not read. The batch"
+ + " operations take a List or
an array of paths or files (a directory path"
+ + " with allowFilePathSource),
and CHECK_CONVERSION_STATUS the task id, unless"
+ + " the CamelDoclingTaskId
header gives it"),
+ producerOut = @BodyType(value = Carrier.TEXT,
+ byOption = {
+ @BodyType.When(option =
"operation", value = "CONVERT_TO_JSON",
+ carriers =
Carrier.POJO,
+ javaType =
"ai.docling.core.DoclingDocument"),
+ @BodyType.When(option =
"operation", value = "EXTRACT_STRUCTURED_DATA",
+ carriers =
Carrier.POJO,
+ javaType =
"ai.docling.core.DoclingDocument"),
+ @BodyType.When(option =
"operation", value = "CHECK_CONVERSION_STATUS",
+ carriers =
Carrier.POJO,
+ javaType =
"org.apache.camel.component.docling.ConversionStatus"),
+ @BodyType.When(option =
"operation", value = "EXTRACT_METADATA",
+ carriers =
Carrier.POJO,
+ javaType =
"org.apache.camel.component.docling.DocumentMetadata"),
+ @BodyType.When(option =
"operation", value = "BATCH_CONVERT_TO_MARKDOWN",
+ carriers = {
Carrier.POJO, Carrier.LIST }),
+ @BodyType.When(option =
"operation", value = "BATCH_CONVERT_TO_HTML",
+ carriers = {
Carrier.POJO, Carrier.LIST }),
+ @BodyType.When(option =
"operation", value = "BATCH_CONVERT_TO_JSON",
+ carriers = {
Carrier.POJO, Carrier.LIST }),
+ @BodyType.When(option =
"operation", value = "BATCH_EXTRACT_TEXT",
+ carriers = {
Carrier.POJO, Carrier.LIST }),
+ @BodyType.When(option =
"operation", value = "BATCH_EXTRACT_STRUCTURED_DATA",
+ carriers = {
Carrier.POJO, Carrier.LIST }),
+ @BodyType.When(option =
"operation", value = "CHUNK_HYBRID",
+ carriers = {
Carrier.POJO, Carrier.LIST }),
+ @BodyType.When(option =
"operation", value = "CHUNK_HIERARCHICAL",
+ carriers = {
Carrier.POJO, Carrier.LIST }) },
+ description = "By the operation: text for
the conversions to markdown, HTML and text,"
+ + " which with the docling
CLI is the path of the output file unless"
+ + " contentInBody
(useDoclingServe gives the content); the task id for"
+ + " SUBMIT_ASYNC_CONVERSION;
a DoclingDocument for CONVERT_TO_JSON and"
+ + " EXTRACT_STRUCTURED_DATA;
a BatchProcessingResults for the batch"
+ + " operations, a List of
BatchConversionResult with splitBatchResults; and a"
+ + " ChunkDocumentResponse for
the chunking, a List of Chunk with"
+ + " contentInBody"))
public class DoclingEndpoint extends ScheduledPollEndpoint {
@Metadata(required = true)
diff --git
a/components/camel-ai/camel-langchain4j-chat/src/generated/resources/META-INF/org/apache/camel/component/langchain4j/chat/langchain4j-chat.json
b/components/camel-ai/camel-langchain4j-chat/src/generated/resources/META-INF/org/apache/camel/component/langchain4j/chat/langchain4j-chat.json
index 951f48a17cf5..f1aedc286daf 100644
---
a/components/camel-ai/camel-langchain4j-chat/src/generated/resources/META-INF/org/apache/camel/component/langchain4j/chat/langchain4j-chat.json
+++
b/components/camel-ai/camel-langchain4j-chat/src/generated/resources/META-INF/org/apache/camel/component/langchain4j/chat/langchain4j-chat.json
@@ -48,6 +48,10 @@
"CamelLangChain4jChatRequestModel": { "index": 6, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The request model name.",
"constantName":
"org.apache.camel.component.langchain4j.chat.LangChain4jChatHeaders#REQUEST_MODEL"
},
"CamelLangChain4jChatResponseModel": { "index": 7, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The response model name.",
"constantName":
"org.apache.camel.component.langchain4j.chat.LangChain4jChatHeaders#RESPONSE_MODEL"
}
},
+ "bodyTypes": {
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"chatOperation", "value": "CHAT_SINGLE_MESSAGE_WITH_PROMPT", "carriers": [
"map" ] }, { "option": "chatOperation", "value": "CHAT_MULTIPLE_MESSAGES",
"carriers": [ "list" ] } ], "description": "The prompt as text, or a
ChatMessage; another body is sent as its toString(), and no body fails.
CHAT_SINGLE_MESSAGE_WITH_PROMPT takes the variables of the template in the
CamelLangChain4jChatPromptTemplate header as a Map, and C [...]
+ "producerOut": { "carriers": [ "text" ], "description": "The answer of the
model as text, also when it is asked for JSON: unmarshal it to read its fields"
}
+ },
"properties": {
"chatId": { "index": 0, "kind": "path", "displayName": "Chat Id", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The id" },
"chatOperation": { "index": 1, "kind": "parameter", "displayName": "Chat
Operation", "group": "producer", "label": "", "required": true, "type": "enum",
"javaType":
"org.apache.camel.component.langchain4j.chat.LangChain4jChatOperations",
"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. [...]
diff --git
a/components/camel-ai/camel-langchain4j-chat/src/main/java/org/apache/camel/component/langchain4j/chat/LangChain4jChatEndpoint.java
b/components/camel-ai/camel-langchain4j-chat/src/main/java/org/apache/camel/component/langchain4j/chat/LangChain4jChatEndpoint.java
index d333487bb13e..e70f27fa5e79 100644
---
a/components/camel-ai/camel-langchain4j-chat/src/main/java/org/apache/camel/component/langchain4j/chat/LangChain4jChatEndpoint.java
+++
b/components/camel-ai/camel-langchain4j-chat/src/main/java/org/apache/camel/component/langchain4j/chat/LangChain4jChatEndpoint.java
@@ -20,6 +20,9 @@ import org.apache.camel.Category;
import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
+import org.apache.camel.spi.BodyType;
+import org.apache.camel.spi.BodyType.Carrier;
+import org.apache.camel.spi.BodyTypes;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
@@ -33,6 +36,19 @@ import static
org.apache.camel.component.langchain4j.chat.LangChain4jChat.SCHEME
syntax = "langchain4j-chat:chatId",
producerOnly = true,
category = { Category.AI }, headersClass =
LangChain4jChatHeaders.class)
+@BodyTypes(producerIn = @BodyType(value = Carrier.ANY,
+ byOption = {
+ @BodyType.When(option =
"chatOperation", value = "CHAT_SINGLE_MESSAGE_WITH_PROMPT",
+ carriers =
Carrier.MAP),
+ @BodyType.When(option =
"chatOperation", value = "CHAT_MULTIPLE_MESSAGES",
+ carriers =
Carrier.LIST) },
+ description = "The prompt as text, or a
ChatMessage; another body is sent as its"
+ + " toString(), and no body
fails. CHAT_SINGLE_MESSAGE_WITH_PROMPT takes the"
+ + " variables of the template
in the CamelLangChain4jChatPromptTemplate"
+ + " header as a Map, and
CHAT_MULTIPLE_MESSAGES a List of ChatMessage"),
+ producerOut = @BodyType(value = Carrier.TEXT,
+ description = "The answer of the model as
text, also when it is asked for JSON: unmarshal"
+ + " it to read its fields"))
public class LangChain4jChatEndpoint extends DefaultEndpoint {
@Metadata(required = true)
diff --git
a/components/camel-ai/camel-milvus/src/generated/resources/META-INF/org/apache/camel/component/milvus/milvus.json
b/components/camel-ai/camel-milvus/src/generated/resources/META-INF/org/apache/camel/component/milvus/milvus.json
index 401fd632d4cc..c2c82cb38cd3 100644
---
a/components/camel-ai/camel-milvus/src/generated/resources/META-INF/org/apache/camel/component/milvus/milvus.json
+++
b/components/camel-ai/camel-milvus/src/generated/resources/META-INF/org/apache/camel/component/milvus/milvus.json
@@ -42,6 +42,10 @@
"CamelMilvusKeyName": { "index": 6, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Key Name for Insert\/Upsert operation", "constantName":
"org.apache.camel.component.milvus.MilvusHeaders#KEY_NAME" },
"CamelMilvusKeyValue": { "index": 7, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "Key Value for Insert\/Upsert operation", "constantName":
"org.apache.camel.component.milvus.MilvusHeaders#KEY_VALUE" }
},
+ "bodyTypes": {
+ "producerIn": { "carriers": [ "pojo" ], "description": "The request of the
Milvus SDK for the action in the CamelMilvusAction header: a
CreateCollectionParam, CreateIndexParam, InsertParam, UpsertParam,
SearchSimpleParam, QueryParam or DeleteParam. Another body fails, a vector as a
List too: the milvus:embeddings data type transformer builds the InsertParam or
UpsertParam from the output of langchain4j-embeddings" },
+ "producerOut": { "carriers": [ "pojo" ], "description": "The result of the
Milvus SDK for the action: an RpcStatus for CREATE_COLLECTION and CREATE_INDEX,
a MutationResult for INSERT, UPSERT and DELETE, a SearchResponse for SEARCH and
a QueryResults for QUERY" }
+ },
"properties": {
"collection": { "index": 0, "kind": "path", "displayName": "Collection",
"group": "producer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The collection Name" },
"host": { "index": 1, "kind": "parameter", "displayName": "Host", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": "localhost", "configurationClass":
"org.apache.camel.component.milvus.MilvusConfiguration", "configurationField":
"configuration", "description": "The host to connect to." },
diff --git
a/components/camel-ai/camel-milvus/src/main/java/org/apache/camel/component/milvus/MilvusEndpoint.java
b/components/camel-ai/camel-milvus/src/main/java/org/apache/camel/component/milvus/MilvusEndpoint.java
index c803a3b4bfdc..bc647b2b11ea 100644
---
a/components/camel-ai/camel-milvus/src/main/java/org/apache/camel/component/milvus/MilvusEndpoint.java
+++
b/components/camel-ai/camel-milvus/src/main/java/org/apache/camel/component/milvus/MilvusEndpoint.java
@@ -26,6 +26,9 @@ 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.BodyType;
+import org.apache.camel.spi.BodyType.Carrier;
+import org.apache.camel.spi.BodyTypes;
import org.apache.camel.spi.EndpointServiceLocation;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
@@ -47,6 +50,17 @@ import org.apache.camel.support.DefaultEndpoint;
Category.AI
},
headersClass = MilvusHeaders.class)
+@BodyTypes(producerIn = @BodyType(value = Carrier.POJO,
+ description = "The request of the Milvus SDK
for the action in the CamelMilvusAction"
+ + " header: a
CreateCollectionParam, CreateIndexParam, InsertParam,"
+ + " UpsertParam,
SearchSimpleParam, QueryParam or DeleteParam. Another body"
+ + " fails, a vector as a List
too: the milvus:embeddings data type"
+ + " transformer builds the
InsertParam or UpsertParam from the output of"
+ + " langchain4j-embeddings"),
+ producerOut = @BodyType(value = Carrier.POJO,
+ description = "The result of the Milvus SDK
for the action: an RpcStatus for"
+ + " CREATE_COLLECTION and
CREATE_INDEX, a MutationResult for INSERT, UPSERT"
+ + " and DELETE, a
SearchResponse for SEARCH and a QueryResults for QUERY"))
public class MilvusEndpoint extends DefaultEndpoint implements
EndpointServiceLocation {
@Metadata(required = true)
diff --git
a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/llm.json
b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/llm.json
index a2451cbd1b69..6870d839b3a6 100644
---
a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/llm.json
+++
b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/llm.json
@@ -146,6 +146,12 @@
"CamelOpenAIImageOutputTokens": { "index": 107, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The number of output tokens billed for
the image request. Only reported by GPT image models", "constantName":
"org.apache.camel.component.openai.OpenAIConstants#IMAGE_OUTPUT_TOKENS" },
"CamelOpenAIImageTotalTokens": { "index": 108, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The total number of tokens billed for
the image request. Only reported by GPT image models", "constantName":
"org.apache.camel.component.openai.OpenAIConstants#IMAGE_TOTAL_TOKENS" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "pojo" ], "javaType":
"com.openai.models.webhooks.UnwrapWebhookEvent", "description": "The event of
the webhook operation, verified with webhookSecret, such as a batch or a
background response that completed. The event type and the id of the object it
is about are in headers" },
+ "consumerReply": { "carriers": [ "any" ], "description": "The body at the
end of the route is not sent: OpenAI gets 200 without a body, or 500 when the
route fails, and then sends the event again" },
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "decisions", "carriers": [ "pojo" ], "javaType":
"com.openai.models.decisions.DecisionCreateParams" }, { "option": "operation",
"value": "batch", "carriers": [ "map", "list", "text", "bytes", "stream",
"file" ] }, { "option": "operation", "value": "audio-transcription",
"carriers": [ "file", "bytes", "stream" ] }, { "option": "operation", "value":
"audio-translation", "carriers": [ "file", "bytes", [...]
+ "producerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"streaming", "value": "true", "carriers": [ "iterator" ] }, { "option":
"autoToolExecution", "value": "false", "carriers": [ "text", "pojo", "list" ]
}, { "option": "operation", "value": "decisions", "carriers": [ "pojo" ],
"javaType": "com.openai.models.decisions.Decision" }, { "option": "operation",
"value": "responses", "carriers": [ "text" ] }, { "option": "operation",
"value": "responses-retrieve", "carriers": [ [...]
+ },
"properties": {
"operation": { "index": 0, "kind": "path", "displayName": "Operation",
"group": "common", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.openai.OpenAIOperations", "enum": [
"chat-completion", "decisions", "responses", "responses-retrieve",
"responses-cancel", "batch", "batch-retrieve", "batch-cancel", "batch-results",
"embeddings", "tool-execution", "audio-transcription", "audio-translation",
"audio-speech", "moderation", "image-generation", "i [...]
"additionalBodyProperty": { "index": 1, "kind": "parameter",
"displayName": "Additional Body Property", "group": "common", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalBodyProperty.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configurat [...]
diff --git
a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
index d212b19e520c..4d569fa9d943 100644
---
a/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
+++
b/components/camel-ai/camel-openai/src/generated/resources/META-INF/org/apache/camel/component/openai/openai.json
@@ -146,6 +146,12 @@
"CamelOpenAIImageOutputTokens": { "index": 107, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The number of output tokens billed for
the image request. Only reported by GPT image models", "constantName":
"org.apache.camel.component.openai.OpenAIConstants#IMAGE_OUTPUT_TOKENS" },
"CamelOpenAIImageTotalTokens": { "index": 108, "kind": "header",
"displayName": "", "group": "common", "label": "", "required": false,
"javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The total number of tokens billed for
the image request. Only reported by GPT image models", "constantName":
"org.apache.camel.component.openai.OpenAIConstants#IMAGE_TOTAL_TOKENS" }
},
+ "bodyTypes": {
+ "consumerOut": { "carriers": [ "pojo" ], "javaType":
"com.openai.models.webhooks.UnwrapWebhookEvent", "description": "The event of
the webhook operation, verified with webhookSecret, such as a batch or a
background response that completed. The event type and the id of the object it
is about are in headers" },
+ "consumerReply": { "carriers": [ "any" ], "description": "The body at the
end of the route is not sent: OpenAI gets 200 without a body, or 500 when the
route fails, and then sends the event again" },
+ "producerIn": { "carriers": [ "any" ], "byOption": [ { "option":
"operation", "value": "decisions", "carriers": [ "pojo" ], "javaType":
"com.openai.models.decisions.DecisionCreateParams" }, { "option": "operation",
"value": "batch", "carriers": [ "map", "list", "text", "bytes", "stream",
"file" ] }, { "option": "operation", "value": "audio-transcription",
"carriers": [ "file", "bytes", "stream" ] }, { "option": "operation", "value":
"audio-translation", "carriers": [ "file", "bytes", [...]
+ "producerOut": { "carriers": [ "text" ], "byOption": [ { "option":
"streaming", "value": "true", "carriers": [ "iterator" ] }, { "option":
"autoToolExecution", "value": "false", "carriers": [ "text", "pojo", "list" ]
}, { "option": "operation", "value": "decisions", "carriers": [ "pojo" ],
"javaType": "com.openai.models.decisions.Decision" }, { "option": "operation",
"value": "responses", "carriers": [ "text" ] }, { "option": "operation",
"value": "responses-retrieve", "carriers": [ [...]
+ },
"properties": {
"operation": { "index": 0, "kind": "path", "displayName": "Operation",
"group": "common", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.openai.OpenAIOperations", "enum": [
"chat-completion", "decisions", "responses", "responses-retrieve",
"responses-cancel", "batch", "batch-retrieve", "batch-cancel", "batch-results",
"embeddings", "tool-execution", "audio-transcription", "audio-translation",
"audio-speech", "moderation", "image-generation", "i [...]
"additionalBodyProperty": { "index": 1, "kind": "parameter",
"displayName": "Additional Body Property", "group": "common", "label": "",
"required": false, "type": "object", "javaType":
"java.util.Map<java.lang.String, java.lang.Object>", "prefix":
"additionalBodyProperty.", "multiValue": true, "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.openai.OpenAIConfiguration",
"configurationField": "configurat [...]
diff --git
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIEndpoint.java
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIEndpoint.java
index 1b65e239cb5b..4fbad0d31b78 100644
---
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIEndpoint.java
+++
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIEndpoint.java
@@ -63,6 +63,9 @@ import
org.apache.camel.component.ai.tool.AiToolParameterHelper;
import org.apache.camel.component.ai.tool.AiToolRegistry;
import org.apache.camel.component.ai.tool.AiToolRegistryListener;
import org.apache.camel.component.ai.tool.AiToolSpec;
+import org.apache.camel.spi.BodyType;
+import org.apache.camel.spi.BodyType.Carrier;
+import org.apache.camel.spi.BodyTypes;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
@@ -85,6 +88,86 @@ import org.slf4j.LoggerFactory;
syntax = "openai:operation",
category = { Category.AI },
headersClass = OpenAIConstants.class)
+@BodyTypes(consumerOut = @BodyType(value = Carrier.POJO, javaType =
"com.openai.models.webhooks.UnwrapWebhookEvent",
+ description = "The event of the webhook
operation, verified with webhookSecret, such as"
+ + " a batch or a background
response that completed. The event type and"
+ + " the id of the object it
is about are in headers"),
+ consumerReply = @BodyType(value = Carrier.ANY,
+ description = "The body at the end of the
route is not sent: OpenAI gets 200 without a"
+ + " body, or 500 when the
route fails, and then sends the event again"),
+ producerIn = @BodyType(value = Carrier.ANY,
+ byOption = {
+ @BodyType.When(option = "operation",
value = "decisions", carriers = Carrier.POJO,
+ javaType =
"com.openai.models.decisions.DecisionCreateParams"),
+ @BodyType.When(option = "operation",
value = "batch",
+ carriers = {
+ Carrier.MAP,
Carrier.LIST, Carrier.TEXT, Carrier.BYTES,
+
Carrier.STREAM, Carrier.FILE }),
+ @BodyType.When(option = "operation",
value = "audio-transcription",
+ carriers = {
Carrier.FILE, Carrier.BYTES, Carrier.STREAM }),
+ @BodyType.When(option = "operation",
value = "audio-translation",
+ carriers = {
Carrier.FILE, Carrier.BYTES, Carrier.STREAM }),
+ @BodyType.When(option = "operation",
value = "image-edit",
+ carriers = {
+ Carrier.FILE,
Carrier.BYTES, Carrier.STREAM,
+ Carrier.LIST
}) },
+ description = "The prompt of chat-completion
and responses: text, or a file, bytes or a"
+ + " stream with an image or a
PDF (chat-completion also audio), which"
+ + " userMessage then
describes. Another body is sent as its toString(),"
+ + " and userMessage or the
CamelOpenAIUserMessage header replaces a text"
+ + " body. embeddings and
moderation take text or a List of texts"
+ + " (moderation also an
image), audio-speech the text to speak, and"
+ + " image-generation the
prompt unless imagePrompt is set. batch takes the"
+ + " requests as a Map keyed by
custom_id, a List, or JSONL; audio takes the"
+ + " audio and image-edit the
image, or a List of images. The operations"
+ + " that read a stored
response, a batch or the tool calls of the last"
+ + " answer do not use the
body"),
+ producerOut = @BodyType(value = Carrier.TEXT,
+ byOption = {
+ @BodyType.When(option =
"streaming", value = "true", carriers = Carrier.ITERATOR),
+ @BodyType.When(option =
"autoToolExecution", value = "false",
+ carriers = {
Carrier.TEXT, Carrier.POJO, Carrier.LIST }),
+ @BodyType.When(option =
"operation", value = "decisions", carriers = Carrier.POJO,
+ javaType =
"com.openai.models.decisions.Decision"),
+ @BodyType.When(option =
"operation", value = "responses", carriers = Carrier.TEXT),
+ @BodyType.When(option =
"operation", value = "responses-retrieve",
+ carriers =
Carrier.TEXT),
+ @BodyType.When(option =
"operation", value = "responses-cancel",
+ carriers =
Carrier.TEXT),
+ @BodyType.When(option =
"operation", value = "batch", carriers = Carrier.UNCHANGED),
+ @BodyType.When(option =
"operation", value = "batch-retrieve",
+ carriers =
Carrier.UNCHANGED),
+ @BodyType.When(option =
"operation", value = "batch-cancel",
+ carriers =
Carrier.UNCHANGED),
+ @BodyType.When(option =
"operation", value = "batch-results",
+ carriers =
Carrier.ITERATOR),
+ @BodyType.When(option =
"operation", value = "embeddings", carriers = Carrier.LIST),
+ @BodyType.When(option =
"operation", value = "tool-execution",
+ carriers = {
Carrier.NULL, Carrier.UNCHANGED }),
+ @BodyType.When(option =
"operation", value = "audio-transcription",
+ carriers =
Carrier.TEXT),
+ @BodyType.When(option =
"operation", value = "audio-translation",
+ carriers =
Carrier.TEXT),
+ @BodyType.When(option =
"operation", value = "audio-speech",
+ carriers =
Carrier.BYTES,
+ javaType = "byte[]"),
+ @BodyType.When(option =
"operation", value = "moderation",
+ carriers =
Carrier.UNCHANGED),
+ @BodyType.When(option =
"operation", value = "image-generation",
+ carriers = {
Carrier.BYTES, Carrier.TEXT, Carrier.LIST }),
+ @BodyType.When(option =
"operation", value = "image-edit",
+ carriers = {
Carrier.BYTES, Carrier.TEXT, Carrier.LIST }) },
+ description = "The answer of
chat-completion as text, also with outputClass or"
+ + " jsonSchema, where it is
JSON text: unmarshal it to read its fields."
+ + " With streaming, an
Iterator of the chunks (text when MCP tools run in"
+ + " a loop). With
autoToolExecution=false, the tool calls the model asks"
+ + " for: an Optional of the
List of tool calls, or a List of the function"
+ + " calls for responses.
embeddings gives a vector (a List of Float), or a"
+ + " List of vectors for a
List of texts; image-generation and image-edit"
+ + " the image as bytes or its
URL as text, a List for several; batch-results"
+ + " an Iterator of the result
lines as Maps. batch and moderation keep the"
+ + " body and set headers, and
tool-execution clears it when it runs tool"
+ + " calls"))
public class OpenAIEndpoint extends DefaultEndpoint {
private static final Logger LOG =
LoggerFactory.getLogger(OpenAIEndpoint.class);
diff --git
a/components/camel-ai/camel-qdrant/src/generated/resources/META-INF/org/apache/camel/component/qdrant/qdrant.json
b/components/camel-ai/camel-qdrant/src/generated/resources/META-INF/org/apache/camel/component/qdrant/qdrant.json
index 283a45b213cb..4370a6d81573 100644
---
a/components/camel-ai/camel-qdrant/src/generated/resources/META-INF/org/apache/camel/component/qdrant/qdrant.json
+++
b/components/camel-ai/camel-qdrant/src/generated/resources/META-INF/org/apache/camel/component/qdrant/qdrant.json
@@ -48,6 +48,10 @@
"CamelQdrantPointId": { "index": 9, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "int",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The point id to use for operation.", "constantName":
"org.apache.camel.component.qdrant.QdrantHeaders#POINT_ID" },
"CamelQdrantMaxResults": { "index": 10, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "int",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The maximum number of results to return from a
similarity search.", "constantName":
"org.apache.camel.component.qdrant.QdrantHeaders#MAX_RESULTS" }
},
+ "bodyTypes": {
+ "producerIn": { "carriers": [ "pojo", "list", "null" ], "description": "By
the action in the CamelQdrantAction header: a VectorParams for
CREATE_COLLECTION, a PointStruct or a List of them for UPSERT, a PointId or a
List of them for RETRIEVE, a PointsSelector, PointId, Filter or Condition for
DELETE, and the query vector as a List of Float, a float array or a PointStruct
for SIMILARITY_SEARCH. DELETE_COLLECTION and COLLECTION_INFO do not read the
body. Another body fails, such as a n [...]
+ "producerOut": { "carriers": [ "list", "pojo", "unchanged" ],
"description": "By the action: a List of RetrievedPoint for RETRIEVE, a List of
ScoredPoint for SIMILARITY_SEARCH and a CollectionInfo for COLLECTION_INFO. The
other actions keep the body, UPSERT and DELETE with the operation id and status
in headers" }
+ },
"properties": {
"collection": { "index": 0, "kind": "path", "displayName": "Collection",
"group": "producer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The collection Name" },
"host": { "index": 1, "kind": "parameter", "displayName": "Host", "group":
"producer", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "defaultValue": "localhost", "configurationClass":
"org.apache.camel.component.qdrant.QdrantConfiguration", "configurationField":
"configuration", "description": "The host to connect to." },
diff --git
a/components/camel-ai/camel-qdrant/src/main/java/org/apache/camel/component/qdrant/QdrantEndpoint.java
b/components/camel-ai/camel-qdrant/src/main/java/org/apache/camel/component/qdrant/QdrantEndpoint.java
index 3b7a1b845540..8dbb2ba6b487 100644
---
a/components/camel-ai/camel-qdrant/src/main/java/org/apache/camel/component/qdrant/QdrantEndpoint.java
+++
b/components/camel-ai/camel-qdrant/src/main/java/org/apache/camel/component/qdrant/QdrantEndpoint.java
@@ -25,6 +25,9 @@ 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.BodyType;
+import org.apache.camel.spi.BodyType.Carrier;
+import org.apache.camel.spi.BodyTypes;
import org.apache.camel.spi.EndpointServiceLocation;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
@@ -46,6 +49,21 @@ import org.apache.camel.support.DefaultEndpoint;
Category.AI
},
headersClass = QdrantHeaders.class)
+@BodyTypes(producerIn = @BodyType(value = { Carrier.POJO, Carrier.LIST,
Carrier.NULL },
+ description = "By the action in the
CamelQdrantAction header: a VectorParams for"
+ + " CREATE_COLLECTION, a
PointStruct or a List of them for UPSERT, a PointId"
+ + " or a List of them for
RETRIEVE, a PointsSelector, PointId, Filter or"
+ + " Condition for DELETE, and
the query vector as a List of Float, a float"
+ + " array or a PointStruct for
SIMILARITY_SEARCH. DELETE_COLLECTION and"
+ + " COLLECTION_INFO do not
read the body. Another body fails, such as a"
+ + " number as the id (use
PointIdFactory.id) or a List of Double. The"
+ + " qdrant:embeddings data
type transformer builds the PointStruct from the"
+ + " output of
langchain4j-embeddings"),
+ producerOut = @BodyType(value = { Carrier.LIST, Carrier.POJO,
Carrier.UNCHANGED },
+ description = "By the action: a List of
RetrievedPoint for RETRIEVE, a List of"
+ + " ScoredPoint for
SIMILARITY_SEARCH and a CollectionInfo for"
+ + " COLLECTION_INFO. The
other actions keep the body, UPSERT and DELETE with"
+ + " the operation id and
status in headers"))
public class QdrantEndpoint extends DefaultEndpoint implements
EndpointServiceLocation {
@Metadata(required = true)
diff --git
a/components/camel-ibm/camel-ibm-watson-language/src/generated/resources/META-INF/org/apache/camel/component/ibm/watson/language/ibm-watson-language.json
b/components/camel-ibm/camel-ibm-watson-language/src/generated/resources/META-INF/org/apache/camel/component/ibm/watson/language/ibm-watson-language.json
index ce47ac5e383e..d0b502549cf5 100644
---
a/components/camel-ibm/camel-ibm-watson-language/src/generated/resources/META-INF/org/apache/camel/component/ibm/watson/language/ibm-watson-language.json
+++
b/components/camel-ibm/camel-ibm-watson-language/src/generated/resources/META-INF/org/apache/camel/component/ibm/watson/language/ibm-watson-language.json
@@ -53,6 +53,10 @@
"CamelIBMWatsonLanguageSentimentScore": { "index": 10, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The sentiment score", "constantName":
"org.apache.camel.component.ibm.watson.language.WatsonLanguageConstants#SENTIMENT_SCORE"
},
"CamelIBMWatsonLanguageSentimentLabel": { "index": 11, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The sentiment label (positive,
negative, neutral)", "constantName":
"org.apache.camel.component.ibm.watson.language.WatsonLanguageConstants#SENTIMENT_LABEL"
}
},
+ "bodyTypes": {
+ "producerIn": { "carriers": [ "any" ], "description": "The text to analyze
for analyzeText, or the URL of the page for analyzeUrl, read as text, unless
the CamelIBMWatsonLanguageText or CamelIBMWatsonLanguageUrl header gives it.
Without the header, no body or a blank one fails, and a Map or a POJO is
analyzed as its toString(): marshal it first" },
+ "producerOut": { "carriers": [ "pojo" ], "javaType":
"com.ibm.watson.natural_language_understanding.v1.model.AnalysisResults",
"description": "The results of the features enabled (sentiment, entities,
keywords and so on), with the sentiment score and label and the language in
headers too. Converted to text, it is the JSON of the results" }
+ },
"properties": {
"label": { "index": 0, "kind": "path", "displayName": "Label", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Logical name" },
"serviceUrl": { "index": 1, "kind": "parameter", "displayName": "Service
Url", "group": "common", "label": "common", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.ibm.watson.language.WatsonLanguageConfiguration",
"configurationField": "configuration", "description": "The service endpoint
URL. If not specified, the default URL will be used." },
diff --git
a/components/camel-ibm/camel-ibm-watson-language/src/main/java/org/apache/camel/component/ibm/watson/language/WatsonLanguageEndpoint.java
b/components/camel-ibm/camel-ibm-watson-language/src/main/java/org/apache/camel/component/ibm/watson/language/WatsonLanguageEndpoint.java
index 801c07756df8..f8ff4572ffdb 100644
---
a/components/camel-ibm/camel-ibm-watson-language/src/main/java/org/apache/camel/component/ibm/watson/language/WatsonLanguageEndpoint.java
+++
b/components/camel-ibm/camel-ibm-watson-language/src/main/java/org/apache/camel/component/ibm/watson/language/WatsonLanguageEndpoint.java
@@ -22,6 +22,9 @@ import org.apache.camel.Category;
import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
+import org.apache.camel.spi.BodyType;
+import org.apache.camel.spi.BodyType.Carrier;
+import org.apache.camel.spi.BodyTypes;
import org.apache.camel.spi.EndpointServiceLocation;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
@@ -35,6 +38,17 @@ import org.apache.camel.support.DefaultEndpoint;
@UriEndpoint(firstVersion = "4.16.0", scheme = "ibm-watson-language", title =
"IBM Watson Language",
syntax = "ibm-watson-language:label", producerOnly = true,
category = { Category.AI, Category.CLOUD },
headersClass = WatsonLanguageConstants.class)
+@BodyTypes(producerIn = @BodyType(value = Carrier.ANY,
+ description = "The text to analyze for
analyzeText, or the URL of the page for"
+ + " analyzeUrl, read as text,
unless the CamelIBMWatsonLanguageText or"
+ + " CamelIBMWatsonLanguageUrl
header gives it. Without the header, no body or"
+ + " a blank one fails, and a
Map or a POJO is analyzed as its toString():"
+ + " marshal it first"),
+ producerOut = @BodyType(value = Carrier.POJO,
+ javaType =
"com.ibm.watson.natural_language_understanding.v1.model.AnalysisResults",
+ description = "The results of the features
enabled (sentiment, entities, keywords and so"
+ + " on), with the sentiment
score and label and the language in headers too."
+ + " Converted to text, it is
the JSON of the results"))
public class WatsonLanguageEndpoint extends DefaultEndpoint implements
EndpointServiceLocation {
@UriPath(description = "Logical name")
diff --git a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
index 573ada4c6125..454c8930fb64 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
@@ -188,7 +188,8 @@ public class FileEndpoint extends GenericFileEndpoint<File>
{
----
The build checks that each side belongs to the kind (a consumer-only component
has no producer sides), that the
-options a side names exist, that `unchanged` is only on the body after a step
and `any` only on what a step needs.
+options a side names exist, that a value of an option is one the option can
have (such as `chat-completion` for the
+`operation` of openai), and that `unchanged` is only on the body after a step
and `any` only on what a step needs.
The values are read from the code that sets the body, and checked against the
body types the message history records
when the routes run.
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-validation.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-validation.adoc
index 26c18f3af01c..54c39089b805 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-validation.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-validation.adoc
@@ -77,8 +77,9 @@ The YAML validator follows the message body through the steps
of each route, and
it: a field read such as `+${body.qty}+` on text that was never unmarshalled,
or a Map that a `to: file` cannot write.
What a component or a data format does with the body comes from the
xref:camel-catalog.adoc#_body_types_in_the_catalog[body types in the catalog]:
the file consumer gives the file it
-read, the `http` producer leaves the response as bytes or a stream, and
`unmarshal: json` makes a Map or a List.
-Where they do not tell what the body is, the validator says nothing: after a
`platform-http` consumer the body
+read, the `http` producer leaves the response as bytes or a stream, and
`unmarshal: json` makes a Map or a List. The
+answer of `openai:chat-completion` is text, also when it is asked for JSON
with `jsonSchema`, so a field read on it
+needs `unmarshal: json` first. Where they do not tell what the body is, the
validator says nothing: after a `platform-http` consumer the body
depends on the request, and the deserializer of a `kafka` consumer can be set
on the component, which the route file
does not show.
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/CatalogBodyTypes.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/CatalogBodyTypes.java
index 3259582da25f..897532a0a271 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/CatalogBodyTypes.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/CatalogBodyTypes.java
@@ -191,9 +191,44 @@ final class CatalogBodyTypes {
String text = node != null && node.isTextual() ? node.asText()
: node != null && node.isObject() && node.has("uri") ?
node.get("uri").asText() : null;
set.putAll(query(text));
+ readPath(uri, model, set, configurable);
return answer(model.getBodyType(side), set, configurable);
}
+ /**
+ * Adds the options in the path of an endpoint uri, as the syntax of its
component names them: the operation of
+ * openai:embeddings. A path the catalog cannot read says nothing about
them, as an option the component may set.
+ */
+ private void readPath(String uri, ComponentModel model, Map<String,
String> set, Set<String> configurable) {
+ if (uri == null || model.getEndpointPathOptions().isEmpty()) {
+ return;
+ }
+ // the query is read on its own, and may hold what the parser of the
catalog does not take, such as a space
+ int q = uri.indexOf('?');
+ String path = q < 0 ? uri : uri.substring(0, q);
+ Optional<Map<String, String>> properties = endpointProperties(catalog,
path);
+ if (properties.isEmpty() && fallback != null) {
+ properties = endpointProperties(fallback, path);
+ }
+ for (BaseOptionModel option : model.getEndpointPathOptions()) {
+ String name = option.getName();
+ if (properties.isEmpty()) {
+ configurable.add(name);
+ } else if (properties.get().containsKey(name)) {
+ set.putIfAbsent(name, properties.get().get(name));
+ }
+ }
+ }
+
+ private static Optional<Map<String, String>>
endpointProperties(CamelCatalog catalog, String uri) {
+ try {
+ return Optional.ofNullable(catalog.endpointProperties(uri));
+ } catch (Exception e) {
+ // not an endpoint the catalog can read, such as one of a
component it does not have
+ return Optional.empty();
+ }
+ }
+
/**
* What a side says for the options a step sets: the body for an option
value it names, or its body when the options
* it depends on keep their defaults; null when it is not known for the
step.
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java
index 5c0085afe5a8..0c2af877367d 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java
@@ -1037,6 +1037,50 @@ public class TextBodyFlowTest {
assertThat(lines(yaml)).isEmpty();
}
+ @Test
+ public void testTheOptionsInThePathOfTheEndpoint() {
+ // the operation of openai is in the path: the answer of
chat-completion is text, also when it is asked for as
+ // JSON, embeddings gives a List, and an operation only known when the
route runs says nothing
+ String yaml = """
+ - route:
+ id: city
+ from:
+ uri: timer:tick
+ steps:
+ - setBody:
+ constant: Name a city in Denmark
+ - to:
openai:chat-completion?jsonSchema=resource:classpath:city.json
+ - setHeader:
+ name: city
+ simple: ${body[name]}
+ - route:
+ id: vector
+ from:
+ uri: timer:tick
+ steps:
+ - setBody:
+ constant: Apache Camel
+ - to: llm:embeddings
+ - setHeader:
+ name: first
+ simple: ${body[0]}
+ - route:
+ id: dynamic
+ from:
+ uri: timer:tick
+ steps:
+ - setBody:
+ constant: Apache Camel
+ - toD: openai:${header.operation}
+ - setHeader:
+ name: first
+ simple: ${body[0]}
+ """;
+ assertThat(lines(yaml)).singleElement().satisfies(m -> assertThat(m)
+ .contains("route city: simple reads a field of the body
(${body[name]})")
+ .contains("to: openai:chat-completion returns it as text:
unmarshal it first"));
+ }
+
@Test
public void testOneReportForAStepThatFails() {
// the first write fails at runtime, so the second does not run with
this body
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java
index fcb3789aa78a..8ce467e34661 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java
+++
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/BodyTypesHelper.java
@@ -18,7 +18,7 @@ package org.apache.camel.maven.packaging;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -28,6 +28,7 @@ import org.apache.camel.spi.BodyType;
import org.apache.camel.spi.BodyType.Carrier;
import org.apache.camel.spi.BodyTypes;
import org.apache.camel.tooling.model.BaseModel;
+import org.apache.camel.tooling.model.BaseOptionModel;
import org.apache.camel.tooling.model.BodyTypeModel;
import org.apache.camel.tooling.model.ComponentModel;
@@ -50,8 +51,8 @@ final class BodyTypesHelper {
* @param bodyTypes the annotation, null when the class
has none
* @param owner the class with the annotation, for the
error message
* @throws IllegalArgumentException when a side does not fit the model: a
data format side on a component, a
- * producer side on a consumer-only
component, unchanged on what a step needs or
- * any on what it leaves
+ * producer side on a consumer-only
component, unchanged on what a step needs, any
+ * on what it leaves, or a value an
option cannot have
*/
static void addBodyTypes(BaseModel<?> model, BodyTypes bodyTypes, Class<?>
owner) {
if (bodyTypes == null) {
@@ -70,7 +71,7 @@ final class BodyTypesHelper {
sides.put(BodyTypeModel.EXPRESSION_OUT, bodyTypes.expressionOut());
Set<String> allowed = allowedSides(model);
- Set<String> options = optionNames(model);
+ Map<String, BaseOptionModel> options = options(model);
for (Map.Entry<String, BodyType> entry : sides.entrySet()) {
String side = entry.getKey();
BodyType bodyType = entry.getValue();
@@ -93,6 +94,7 @@ final class BodyTypesHelper {
}
for (BodyType.When when : bodyType.byOption()) {
checkOption(owner, side, when.option(), options);
+ checkValue(owner, side, when, options.get(when.option()));
check(owner, side + " " + when.option() + "=" + when.value(),
when.carriers());
}
model.getBodyTypes().put(side, asModel(bodyType));
@@ -118,24 +120,39 @@ final class BodyTypesHelper {
};
}
- /** The options of the model, which the body types can depend on. */
- private static Set<String> optionNames(BaseModel<?> model) {
- Set<String> names = new HashSet<>();
- model.getOptions().forEach(o -> names.add(o.getName()));
+ /** The options of the model by name, which the body types can depend on.
*/
+ private static Map<String, BaseOptionModel> options(BaseModel<?> model) {
+ Map<String, BaseOptionModel> answer = new HashMap<>();
+ model.getOptions().forEach(o -> answer.put(o.getName(), o));
if (model instanceof ComponentModel component) {
- component.getComponentOptions().forEach(o ->
names.add(o.getName()));
- component.getEndpointOptions().forEach(o ->
names.add(o.getName()));
+ component.getComponentOptions().forEach(o ->
answer.put(o.getName(), o));
+ component.getEndpointOptions().forEach(o ->
answer.put(o.getName(), o));
}
- return names;
+ return answer;
}
- private static void checkOption(Class<?> owner, String side, String
option, Set<String> options) {
- if (!options.contains(option)) {
+ private static void checkOption(Class<?> owner, String side, String
option, Map<String, BaseOptionModel> options) {
+ if (!options.containsKey(option)) {
throw new IllegalArgumentException(
owner.getName() + ": the @BodyTypes " + side + " depends
on " + option + ", which is not an option");
}
}
+ /**
+ * A body for a value the option cannot have would never hold: the value
is one of an enum, or true or false for a
+ * boolean, in any case, as tooling compares it with what the endpoint
sets.
+ */
+ private static void checkValue(Class<?> owner, String side, BodyType.When
when, BaseOptionModel option) {
+ List<String> values = option.getEnums() != null &&
!option.getEnums().isEmpty()
+ ? option.getEnums()
+ : "boolean".equals(option.getType()) ? List.of("true",
"false") : List.of();
+ if (!values.isEmpty() && values.stream().noneMatch(v ->
v.equalsIgnoreCase(when.value()))) {
+ throw new IllegalArgumentException(
+ owner.getName() + ": the @BodyTypes " + side + " has " +
when.option() + "=" + when.value()
+ + ", which is not a value of
the option: " + String.join(", ", values));
+ }
+ }
+
/**
* A body has a carrier: an option value without one would say nothing.
UNCHANGED is for the body after a step, such
* as a sql query that leaves it unchanged unless it is a select. ANY is
for the bodies a step needs. ANY and
diff --git
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
index 5fa2fe71426d..24d5b3d86b84 100644
---
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
+++
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/EndpointSchemaGeneratorMojoTest.java
@@ -233,7 +233,8 @@ class EndpointSchemaGeneratorMojoTest {
@Test
void testEndpointWithBodyTypes() throws Exception {
// CAMEL-24961: the body a step needs and the body it leaves, in the
order of the annotation
- addEndpointOptions("resultHandlerFactory", "output");
+ addEndpointOptions("resultHandlerFactory");
+ addEnumOption("output", "string", "bytes");
mojo.addBodyTypes(model, SomeEndpointWithBodyTypes.class);
assertEquals(List.of(BodyTypeModel.CONSUMER_OUT,
BodyTypeModel.PRODUCER_IN, BodyTypeModel.PRODUCER_OUT),
List.copyOf(model.getBodyTypes().keySet()));
@@ -278,9 +279,10 @@ class EndpointSchemaGeneratorMojoTest {
SomeEndpointWithBadBodyTypes.DataFormatSide.class,
SomeEndpointWithBadBodyTypes.ProducerSideOfConsumer.class,
SomeEndpointWithBadBodyTypes.UnchangedInput.class,
SomeEndpointWithBadBodyTypes.AnyOutput.class,
SomeEndpointWithBadBodyTypes.UnknownWithOthers.class,
SomeEndpointWithBadBodyTypes.NoCarriers.class,
- SomeEndpointWithBadBodyTypes.UnknownOption.class,
SomeEndpointWithBadBodyTypes.OptionValueWithoutCarriers.class })
+ SomeEndpointWithBadBodyTypes.UnknownOption.class,
SomeEndpointWithBadBodyTypes.OptionValueWithoutCarriers.class,
+ SomeEndpointWithBadBodyTypes.ValueTheOptionDoesNotHave.class })
void testEndpointWithBadBodyTypes(Class<?> clazz) {
- addEndpointOptions("output");
+ addEnumOption("output", "string", "bytes");
model.setConsumerOnly(clazz.getAnnotation(UriEndpoint.class).consumerOnly());
MojoExecutionException e = assertThrows(MojoExecutionException.class,
() -> mojo.addBodyTypes(model, clazz));
assertTrue(e.getMessage().startsWith(clazz.getName() + ": the
@BodyTypes "), e.getMessage());
@@ -294,4 +296,12 @@ class EndpointSchemaGeneratorMojoTest {
model.addEndpointOption(option);
}
}
+
+ private void addEnumOption(String name, String... values) {
+ ComponentModel.EndpointOptionModel option = new
ComponentModel.EndpointOptionModel();
+ option.setName(name);
+ option.setType("enum");
+ option.setEnums(List.of(values));
+ model.addEndpointOption(option);
+ }
}
diff --git
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java
index cccd18faa715..8c0f53728d6a 100644
---
a/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java
+++
b/tooling/maven/camel-package-maven-plugin/src/test/java/org/apache/camel/maven/packaging/endpoint/SomeEndpointWithBadBodyTypes.java
@@ -69,4 +69,10 @@ public final class SomeEndpointWithBadBodyTypes {
byOption = @BodyType.When(option =
"output", value = "bytes", carriers = {})))
public static class OptionValueWithoutCarriers {
}
+
+ @UriEndpoint(scheme = "some", syntax = "some", title = "some")
+ @BodyTypes(producerOut = @BodyType(value = Carrier.TEXT,
+ byOption = @BodyType.When(option =
"output", value = "DOM", carriers = Carrier.POJO)))
+ public static class ValueTheOptionDoesNotHave {
+ }
}