This is an automated email from the ASF dual-hosted git repository.
Croway 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 a53ceb1417bc CAMEL-24528: camel-huggingface - surface Python inference
errors in four task predictors
a53ceb1417bc is described below
commit a53ceb1417bc49b154bb12da7ebfbc416226243e
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Aug 28 06:49:27 2026 +0200
CAMEL-24528: camel-huggingface - surface Python inference errors in four
task predictors
TextGenerationPredictor, SummarizationPredictor, QuestionAnsweringPredictor
and
TextToImagePredictor set the returned data as the successful result without
checking whether the Python script had returned an error payload. The six
other
task predictors guard the output with
`if (result.contains("\"error\"")) throw new RuntimeCamelException(...)`,
so a
failed inference in these four was silently delivered as a success. For
text-to-image the JSON error text was served as image/png bytes.
Add the same guard to the four predictors. Text-to-image decodes the
returned
bytes to a string to check for the error marker before setting the image
body.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../tasks/QuestionAnsweringPredictor.java | 4 +
.../huggingface/tasks/SummarizationPredictor.java | 4 +
.../huggingface/tasks/TextGenerationPredictor.java | 4 +
.../huggingface/tasks/TextToImagePredictor.java | 7 ++
.../tasks/PredictorErrorHandlingTest.java | 97 ++++++++++++++++++++++
5 files changed, 116 insertions(+)
diff --git
a/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/QuestionAnsweringPredictor.java
b/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/QuestionAnsweringPredictor.java
index ec07bfea7202..ec66b59a5f3d 100644
---
a/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/QuestionAnsweringPredictor.java
+++
b/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/QuestionAnsweringPredictor.java
@@ -25,6 +25,7 @@ import ai.djl.modality.Output;
import ai.djl.modality.nlp.qa.QAInput;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeCamelException;
import org.apache.camel.component.huggingface.HuggingFaceConstants;
import org.apache.camel.component.huggingface.HuggingFaceEndpoint;
@@ -116,6 +117,9 @@ public class QuestionAnsweringPredictor extends
AbstractTaskPredictor {
@Override
protected void processOutput(Exchange exchange, Output output) {
String result = output.getAsString("data");
+ if (result.contains("\"error\"")) {
+ throw new RuntimeCamelException("Python inference failed: " +
result);
+ }
exchange.getMessage().setBody(result);
exchange.getMessage().setHeader(HuggingFaceConstants.OUTPUT, result);
}
diff --git
a/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/SummarizationPredictor.java
b/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/SummarizationPredictor.java
index 09673a143721..1826a48740ce 100644
---
a/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/SummarizationPredictor.java
+++
b/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/SummarizationPredictor.java
@@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets;
import ai.djl.modality.Input;
import ai.djl.modality.Output;
import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeCamelException;
import org.apache.camel.component.huggingface.HuggingFaceConstants;
import org.apache.camel.component.huggingface.HuggingFaceEndpoint;
@@ -105,6 +106,9 @@ public class SummarizationPredictor extends
AbstractTaskPredictor {
@Override
protected void processOutput(Exchange exchange, Output output) {
String result = output.getAsString("data");
+ if (result.contains("\"error\"")) {
+ throw new RuntimeCamelException("Python inference failed: " +
result);
+ }
exchange.getMessage().setBody(result);
exchange.getMessage().setHeader(HuggingFaceConstants.OUTPUT, result);
}
diff --git
a/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/TextGenerationPredictor.java
b/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/TextGenerationPredictor.java
index a33f3ccda1d9..582b70594203 100644
---
a/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/TextGenerationPredictor.java
+++
b/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/TextGenerationPredictor.java
@@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets;
import ai.djl.modality.Input;
import ai.djl.modality.Output;
import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeCamelException;
import org.apache.camel.component.huggingface.HuggingFaceConstants;
import org.apache.camel.component.huggingface.HuggingFaceEndpoint;
@@ -103,6 +104,9 @@ public class TextGenerationPredictor extends
AbstractTaskPredictor {
@Override
protected void processOutput(Exchange exchange, Output output) {
String result = output.getAsString("data");
+ if (result.contains("\"error\"")) {
+ throw new RuntimeCamelException("Python inference failed: " +
result);
+ }
exchange.getMessage().setBody(result);
exchange.getMessage().setHeader(HuggingFaceConstants.OUTPUT, result);
}
diff --git
a/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/TextToImagePredictor.java
b/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/TextToImagePredictor.java
index 227c5b2e400b..a928854acd1a 100644
---
a/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/TextToImagePredictor.java
+++
b/components/camel-ai/camel-huggingface/src/main/java/org/apache/camel/component/huggingface/tasks/TextToImagePredictor.java
@@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets;
import ai.djl.modality.Input;
import ai.djl.modality.Output;
import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeCamelException;
import org.apache.camel.component.huggingface.HuggingFaceEndpoint;
/**
@@ -108,6 +109,12 @@ public class TextToImagePredictor extends
AbstractTaskPredictor {
@Override
protected void processOutput(Exchange exchange, Output output) throws
Exception {
byte[] imageBytes = output.getAsBytes("data");
+ // A failed Python inference returns a JSON error payload instead of
image bytes; surface it as
+ // an error rather than serving the error text as an image.
+ String resultJson = new String(imageBytes, StandardCharsets.UTF_8);
+ if (resultJson.contains("\"error\"")) {
+ throw new RuntimeCamelException("Python inference failed: " +
resultJson);
+ }
exchange.getMessage().setBody(imageBytes);
exchange.getMessage().setHeader("Content-Type", "image/png");
}
diff --git
a/components/camel-ai/camel-huggingface/src/test/java/org/apache/camel/component/huggingface/tasks/PredictorErrorHandlingTest.java
b/components/camel-ai/camel-huggingface/src/test/java/org/apache/camel/component/huggingface/tasks/PredictorErrorHandlingTest.java
new file mode 100644
index 000000000000..eacbce07f13f
--- /dev/null
+++
b/components/camel-ai/camel-huggingface/src/test/java/org/apache/camel/component/huggingface/tasks/PredictorErrorHandlingTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.huggingface.tasks;
+
+import ai.djl.modality.Output;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.component.huggingface.HuggingFaceConfiguration;
+import org.apache.camel.component.huggingface.HuggingFaceEndpoint;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Verifies that the task predictors whose Python script can return a JSON
error payload surface it as an error instead
+ * of treating it as a successful result.
+ */
+class PredictorErrorHandlingTest {
+
+ private static final String ERROR_PAYLOAD = "{\"error\": \"model failed to
load\"}";
+
+ private CamelContext context;
+ private HuggingFaceEndpoint endpoint;
+
+ @BeforeEach
+ void setUp() {
+ context = new DefaultCamelContext();
+ endpoint = new HuggingFaceEndpoint(null, null, new
HuggingFaceConfiguration());
+ }
+
+ @AfterEach
+ void tearDown() {
+ context.stop();
+ }
+
+ private Output outputWith(String data) {
+ Output output = new Output();
+ output.add("data", data);
+ return output;
+ }
+
+ @Test
+ void textGenerationSurfacesInferenceError() {
+ TextGenerationPredictor predictor = new
TextGenerationPredictor(endpoint);
+ Exchange exchange = new DefaultExchange(context);
+ assertThrows(RuntimeCamelException.class, () ->
predictor.processOutput(exchange, outputWith(ERROR_PAYLOAD)));
+ }
+
+ @Test
+ void textGenerationPassesThroughSuccessfulResult() throws Exception {
+ TextGenerationPredictor predictor = new
TextGenerationPredictor(endpoint);
+ Exchange exchange = new DefaultExchange(context);
+ predictor.processOutput(exchange, outputWith("a generated sentence"));
+ assertEquals("a generated sentence",
exchange.getMessage().getBody(String.class));
+ }
+
+ @Test
+ void summarizationSurfacesInferenceError() {
+ SummarizationPredictor predictor = new
SummarizationPredictor(endpoint);
+ Exchange exchange = new DefaultExchange(context);
+ assertThrows(RuntimeCamelException.class, () ->
predictor.processOutput(exchange, outputWith(ERROR_PAYLOAD)));
+ }
+
+ @Test
+ void questionAnsweringSurfacesInferenceError() {
+ QuestionAnsweringPredictor predictor = new
QuestionAnsweringPredictor(endpoint);
+ Exchange exchange = new DefaultExchange(context);
+ assertThrows(RuntimeCamelException.class, () ->
predictor.processOutput(exchange, outputWith(ERROR_PAYLOAD)));
+ }
+
+ @Test
+ void textToImageSurfacesInferenceError() {
+ TextToImagePredictor predictor = new TextToImagePredictor(endpoint);
+ Exchange exchange = new DefaultExchange(context);
+ assertThrows(RuntimeCamelException.class, () ->
predictor.processOutput(exchange, outputWith(ERROR_PAYLOAD)));
+ }
+}