diff --git a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/Agent.java b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/Agent.java index 532f73cd59f0c..7d52dfdaa4b9d 100644 --- a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/Agent.java +++ b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/Agent.java @@ -82,15 +82,19 @@ public interface Agent { * request body regardless of how the original message was formatted. *
* - * @param messagePayload the message payload from the exchange body; must be either an - * {@link AiAgentBody} or a {@link String} - * @param exchange the Camel exchange containing headers and context information - * @return an {@link AiAgentBody} instance ready for agent processing; returns the - * original payload if it's already an {@link AiAgentBody}, or creates a new - * one from a string payload and relevant headers - * @throws InvalidPayloadRuntimeException if the payload is neither an {@link AiAgentBody} nor a {@link String} - * @throws Exception if any other error occurs during payload processing + * @param messagePayload the message payload from the exchange body; must be either an + * {@link AiAgentBody} or a {@link String} + * @param exchange the Camel exchange containing headers and context information + * @return an {@link AiAgentBody} instance ready for agent processing; returns + * the original payload if it's already an {@link AiAgentBody}, or + * creates a new one from a string payload and relevant headers + * @throws InvalidPayloadRuntimeException if the payload is neither an {@link AiAgentBody} nor a {@link String} + * @throws Exception if any other error occurs during payload processing + * + * @deprecated This method is no longer used by {@code LangChain4jAgentProducer}. + * Body conversion is now handled via Camel TypeConverters. */ + @Deprecated(since = "4.19.0") default AiAgentBody> processBody(Object messagePayload, Exchange exchange) throws Exception { if (messagePayload instanceof AiAgentBody> payload) { return payload; diff --git a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverterLoader.java b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverterLoader.java index 1186963e3e9c3..240733da94ce3 100644 --- a/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverterLoader.java +++ b/components/camel-ai/camel-langchain4j-agent/src/generated/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverterLoader.java @@ -60,6 +60,14 @@ private void registerConverters(TypeConverterRegistry registry) { } return answer; }); + addTypeConverter(registry, org.apache.camel.component.langchain4j.agent.api.AiAgentBody.class, java.lang.String.class, false, + (type, exchange, value) -> { + Object answer = org.apache.camel.component.langchain4j.agent.LangChain4jAgentConverter.textToAiAgentBody((java.lang.String) value, exchange); + if (false && answer == null) { + answer = Void.class; + } + return answer; + }); addTypeConverter(registry, org.apache.camel.component.langchain4j.agent.api.AiAgentBody.class, org.apache.camel.WrappedFile.class, false, (type, exchange, value) -> { Object answer = org.apache.camel.component.langchain4j.agent.LangChain4jAgentConverter.toAiAgentBody((org.apache.camel.WrappedFile) value, exchange); diff --git a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverter.java b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverter.java index 07ec8ca41e809..5207757f98d76 100644 --- a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverter.java +++ b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverter.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URLConnection; import java.nio.file.Files; import java.util.Base64; @@ -34,6 +35,7 @@ import dev.langchain4j.data.video.Video; import org.apache.camel.Converter; import org.apache.camel.Exchange; +import org.apache.camel.Message; import org.apache.camel.WrappedFile; import org.apache.camel.component.langchain4j.agent.api.AiAgentBody; import org.slf4j.Logger; @@ -108,17 +110,7 @@ public static AiAgentBody> toAiAgentBody(WrappedFile> wrappedFile, Exchange byte[] fileData = readFileBytes(file); Content content = createContent(fileData, mimeType); - String userMessage = exchange.getIn().getHeader(USER_MESSAGE, String.class); - String systemMessage = exchange.getIn().getHeader(SYSTEM_MESSAGE, String.class); - Object memoryId = exchange.getIn().getHeader(MEMORY_ID); - - AiAgentBody+ * This converter is useful for the text components that return Text Body. + *
+ * + * @param text String as message + * @param exchange the Camel exchange containing headers + * @return an AiAgentBody with the appropriate Content type + */ + @Converter + public static AiAgentBody> textToAiAgentBody(String text, Exchange exchange) { + return buildAiAgentBody(exchange, null, text); + } + /** * Creates the appropriate LangChain4j Content object based on the MIME type. */ @@ -242,13 +239,13 @@ static Content createContent(byte[] data, String mimeType) { */ private static String detectMimeType(File file, Exchange exchange) { // Check agent-specific header first (highest priority) - String mediaType = exchange.getIn().getHeader(MEDIA_TYPE, String.class); + String mediaType = exchange.getMessage().getHeader(MEDIA_TYPE, String.class); if (mediaType != null) { return mediaType; } // Check file component's content type header - String fileContentType = exchange.getIn().getHeader(Exchange.FILE_CONTENT_TYPE, String.class); + String fileContentType = exchange.getMessage().getHeader(Exchange.FILE_CONTENT_TYPE, String.class); if (fileContentType != null) { return fileContentType; } @@ -273,11 +270,12 @@ private static String detectMimeType(File file, Exchange exchange) { */ private static String detectMimeTypeFromHeaders(Exchange exchange) { // Check agent-specific header first (highest priority) - String mediaType = exchange.getIn().getHeader(MEDIA_TYPE, String.class); + Message message = exchange.getMessage(); + + String mediaType = message.getHeader(MEDIA_TYPE, String.class); if (mediaType != null) { return normalizeContentType(mediaType); } - // Cloud storage component content type headers String[] cloudContentTypeHeaders = { "CamelAwsS3ContentType", // AWS S3 @@ -289,24 +287,30 @@ private static String detectMimeTypeFromHeaders(Exchange exchange) { }; for (String header : cloudContentTypeHeaders) { - String cloudContentType = exchange.getIn().getHeader(header, String.class); + String cloudContentType = message.getHeader(header, String.class); if (cloudContentType != null) { return normalizeContentType(cloudContentType); } } - // Check standard content type header - String contentType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class); + String contentType = message.getHeader(Exchange.CONTENT_TYPE, String.class); if (contentType != null) { return normalizeContentType(contentType); } - // Check file component's content type header - String fileContentType = exchange.getIn().getHeader(Exchange.FILE_CONTENT_TYPE, String.class); + String fileContentType = message.getHeader(Exchange.FILE_CONTENT_TYPE, String.class); if (fileContentType != null) { return normalizeContentType(fileContentType); } + String fileName = message.getHeader(Exchange.FILE_NAME, String.class); + if (fileName != null) { + String mime = URLConnection.guessContentTypeFromName(fileName); + if (mime != null) { + return normalizeContentType(mime); + } + } + throw new IllegalArgumentException( "MIME type is required for byte[] or InputStream input. " + "Please set the CamelLangChain4jAgentMediaType header."); @@ -405,4 +409,31 @@ private static byte[] readFileBytes(File file) { throw new IllegalArgumentException("Failed to read file: " + file.getAbsolutePath(), e); } } + + /** + * Utility method to build agent body. + * + * @param exchange the Camel exchange containing message headers + * @param content the LangChain4j content to attach to the agent body + * @param defaultUserMessage the fallback user message if the corresponding header is absent + * @return a fully populated {@link AiAgentBody} instance + * @param