This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.18.x by this push:
new d40cf55ee9aa [backport camel-4.18.x] CAMEL-24612:
camel-langchain4j-agent - fix content converter charset, content-type
normalization and stream close (#26104)
d40cf55ee9aa is described below
commit d40cf55ee9aa9c4baab09c983b9e1edbc4e6a510
Author: Guillaume Nodet <[email protected]>
AuthorDate: Thu Sep 3 21:53:47 2026 +0200
[backport camel-4.18.x] CAMEL-24612: camel-langchain4j-agent - fix content
converter charset, content-type normalization and stream close (#26104)
---
.../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();