This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch backport/26079-to-camel-4.18.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit ab3860c5294292abc281f998975e54e4a0dbf957 Author: Guillaume Nodet <[email protected]> AuthorDate: Thu Sep 3 19:06:45 2026 +0000 [backport camel-4.18.x] CAMEL-24612: camel-langchain4j-agent - fix content converter charset, content-type normalization and stream close --- .../agent/LangChain4jAgentConverter.java | 20 +++++++++--- .../agent/LangChain4jAgentConverterTest.java | 36 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) 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 eb5e0ddf3e92..51ab3daad6d8 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,8 @@ package org.apache.camel.component.langchain4j.agent; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Base64; @@ -214,8 +216,8 @@ public final class LangChain4jAgentConverter { */ @Converter public static AiAgentBody<?> inputStreamToAiAgentBody(InputStream inputStream, Exchange exchange) { - try { - byte[] data = inputStream.readAllBytes(); + try (InputStream in = inputStream) { + byte[] data = in.readAllBytes(); return byteArrayToAiAgentBody(data, exchange); } catch (IOException e) { throw new IllegalArgumentException("Failed to read input stream", e); @@ -252,7 +254,7 @@ public final class LangChain4jAgentConverter { .build(); return PdfFileContent.from(pdfFile); } else if (mimeType.startsWith("text/")) { - return TextContent.from(new String(data)); + return TextContent.from(new String(data, StandardCharsets.UTF_8)); } else { throw new IllegalArgumentException( "Unsupported MIME type: " + mimeType @@ -275,13 +277,13 @@ public final class LangChain4jAgentConverter { // Check agent-specific header first (highest priority) String mediaType = exchange.getIn().getHeader(MEDIA_TYPE, String.class); if (mediaType != null) { - return mediaType; + return normalizeContentType(mediaType); } // Check file component's content type header String fileContentType = exchange.getIn().getHeader(Exchange.FILE_CONTENT_TYPE, String.class); if (fileContentType != null) { - return fileContentType; + return normalizeContentType(fileContentType); } if (fileName == null) { @@ -344,6 +346,14 @@ public final class LangChain4jAgentConverter { return normalizeContentType(fileContentType); } + String fileName = exchange.getIn().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."); diff --git a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverterTest.java b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverterTest.java index 2c01e7091cae..27c7f28cf057 100644 --- a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverterTest.java +++ b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentConverterTest.java @@ -18,8 +18,11 @@ package org.apache.camel.component.langchain4j.agent; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicBoolean; import dev.langchain4j.data.message.ImageContent; import dev.langchain4j.data.message.TextContent; @@ -32,9 +35,11 @@ import org.apache.camel.test.junit5.CamelTestSupport; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class LangChain4jAgentConverterTest extends CamelTestSupport { @@ -58,6 +63,37 @@ public class LangChain4jAgentConverterTest extends CamelTestSupport { assertInstanceOf(TextContent.class, body.getContent()); } + @Test + void shouldDecodeTextBytesAsUtf8() { + Exchange exchange = context.getEndpoint("direct:test").createExchange(); + exchange.getMessage().setHeader(Headers.MEDIA_TYPE, "text/plain"); + + byte[] utf8 = "café ☕".getBytes(StandardCharsets.UTF_8); + AiAgentBody<?> body = context.getTypeConverter().convertTo(AiAgentBody.class, exchange, utf8); + + assertInstanceOf(TextContent.class, body.getContent()); + assertEquals("café ☕", ((TextContent) body.getContent()).text()); + } + + @Test + void shouldCloseInputStreamAfterConversion() { + Exchange exchange = context.getEndpoint("direct:test").createExchange(); + exchange.getMessage().setHeader(Headers.MEDIA_TYPE, "text/plain"); + + AtomicBoolean closed = new AtomicBoolean(false); + ByteArrayInputStream stream = new ByteArrayInputStream("Hello".getBytes(StandardCharsets.UTF_8)) { + @Override + public void close() throws IOException { + closed.set(true); + super.close(); + } + }; + + context.getTypeConverter().convertTo(AiAgentBody.class, exchange, stream); + + assertTrue(closed.get(), "the input stream must be closed after conversion"); + } + @Test void shouldConvertRemoteWrappedFileUsingFileNameHeader() { Exchange exchange = context.getEndpoint("direct:test").createExchange();
