Repository: deepjavalibrary/djl
Pull request #3558 (2024-12-10 03:43:57)

[xgb] Fix xgb intern to close replaced array
## Description ##

close replaced array for xgb engine


Patch:
diff --git a/README.md b/README.md
index cda6533df..47d0c20be 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 
 ![Continuous](https://github.com/deepjavalibrary/djl/workflows/Continuous/badge.svg)
 [![Docs](https://github.com/deepjavalibrary/djl/workflows/Docs/badge.svg)](https://docs.djl.ai/master/index.html)
-![Nightly Publish](https://github.com/deepjavalibrary/djl/workflows/Nightly%20Publish/badge.svg)
+[![Nightly Publish](https://github.com/deepjavalibrary/djl/workflows/Nightly%20Publish/badge.svg)](https://github.com/deepjavalibrary/djl/actions/workflows/nightly_publish.yml)
 
 # Deep Java Library (DJL)
 
diff --git a/android/gradle.properties b/android/gradle.properties
index 78b3fa76c..56211c900 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -17,6 +17,6 @@ org.gradle.jvmargs=-Xmx1536m
 android.useAndroidX=true
 # Automatically convert third-party libraries to use AndroidX
 android.enableJetifier=true
-djl_version=0.31.0
+djl_version=0.32.0
 pytorch_version=2.5.1
-tokenizers_version=0.20.3
+tokenizers_version=0.21.0
diff --git a/api/src/main/java/ai/djl/Application.java b/api/src/main/java/ai/djl/Application.java
index 0f3d49d5d..0cc7ef409 100644
--- a/api/src/main/java/ai/djl/Application.java
+++ b/api/src/main/java/ai/djl/Application.java
@@ -97,6 +97,9 @@ public class Application {
             case "nlp/token_classification":
             case "token_classification":
                 return NLP.TOKEN_CLASSIFICATION;
+            case "nlp/zero_shot_classification":
+            case "zero_shot_classification":
+                return NLP.ZERO_SHOT_CLASSIFICATION;
             case "nlp/word_embedding":
             case "word_embedding":
                 return NLP.WORD_EMBEDDING;
@@ -280,6 +283,12 @@ public class Application {
          */
         Application SENTIMENT_ANALYSIS = new Application("nlp/sentiment_analysis");
 
+        /**
+         * An application that classifies text into arbitrary label, a specific case of {@link
+         * #TEXT_CLASSIFICATION}.
+         */
+        Application ZERO_SHOT_CLASSIFICATION = new Application("nlp/zero_shot_classification");
+
         /**
          * A natural language understanding application that assigns a label to some tokens in a
          * text.
diff --git a/api/src/main/java/ai/djl/inference/Predictor.java b/api/src/main/java/ai/djl/inference/Predictor.java
index 5f6edb424..c5e850895 100644
--- a/api/src/main/java/ai/djl/inference/Predictor.java
+++ b/api/src/main/java/ai/djl/inference/Predictor.java
@@ -144,6 +144,9 @@ public class Predictor<I, O> implements AutoCloseable {
     protected NDList predictInternal(TranslatorContext ctx, NDList ndList)
             throws TranslateException {
         logger.trace("Predictor input data: {}", ndList);
+        if (ndList.isEmpty()) {
+            return new NDList();
+        }
         return block.forward(parameterStore, ndList, false);
     }
 
diff --git a/api/src/main/java/ai/djl/modality/cv/transform/Resize.java b/api/src/main/java/ai/djl/modality/cv/transform/Resize.java
index f2bddc8f5..1947b6973 100644
--- a/api/src/main/java/ai/djl/modality/cv/transform/Resize.java
+++ b/api/src/main/java/ai/djl/modality/cv/transform/Resize.java
@@ -57,6 +57,24 @@ public class Resize implements Transform {
         this.interpolation = interpolation;
     }
 
+    /**
+     * Returns the width.
+     *
+     * @return the width
+     */
+    public int getWidth() {
+        return width;
+    }
+
+    /**
+     * Returns the height.
+     *
+     * @return the height
+     */
+    public int getHeight() {
+        return height;
+    }
+
     /** {@inheritDoc} */
     @Override
     public NDArray transform(NDArray array) {
diff --git a/api/src/main/java/ai/djl/modality/cv/translator/BaseImageTranslator.java b/api/src/main/java/ai/djl/modality/cv/translator/BaseImageTranslator.java
index 3a66df732..675c4fb2f 100644
--- a/api/src/main/java/ai/djl/modality/cv/translator/BaseImageTranslator.java
+++ b/api/src/main/java/ai/djl/modality/cv/translator/BaseImageTranslator.java
@@ -116,6 +116,26 @@ public abstract class BaseImageTranslator<T> implements Translator<Image, T> {
          */
         public T setPipeline(Pipeline pipeline) {
             this.pipeline = pipeline;
+            for (Transform transform : pipeline.getTransforms()) {
+                if (transform instanceof Resize) {
+                    Resize resize = (Resize) transform;
+                    width = resize.getWidth();
+                    height = resize.getHeight();
+                }
+            }
+            return self();
+        }
+
+        /**
+         * Sets the image size.
+         *
+         * @param width the image width
+         * @param height the image height
+         * @return this builder
+         */
+        public T setImageSize(int width, int height) {
+            this.width = width;
+            this.height = height;
             return self();
         }
 
@@ -129,6 +149,12 @@ public abstract class BaseImageTranslator<T> implements Translator<Image, T> {
             if (pipeline == null) {
                 pipeline = new Pipeline();
             }
+            if (transform instanceof Resize) {
+                Resize resize = (Resize) transform;
+                width = resize.getWidth();
+                height = resize.getHeight();
+            }
+
             pipeline.add(transform);
             return self();
         }
@@ -167,13 +193,14 @@ public abstract class BaseImageTranslator<T> implements Translator<Image, T> {
             } else if (!"false".equals(resize)) {
                 String[] tokens = resize.split("\\s*,\\s*");
                 if (tokens.length > 1) {
-                    addTransform(
-                            new Resize(
-                                    (int) Double.parseDouble(tokens[0]),
-                                    (int) Double.parseDouble(tokens[1])));
+                    setImageSize(
+                            (int) Double.parseDouble(tokens[0]),
+                            (int) Double.parseDouble(tokens[1]));
                 } else {
-                    addTransform(new Resize((int) Double.parseDouble(tokens[0])));
+                    int size = (int) Double.parseDouble(tokens[0]);
+                    setImageSize(size, size);
                 }
+                addTransform(new Resize(width, height));
             }
             if (ArgumentsUtil.booleanValue(arguments, "centerCrop", false)) {
                 addTransform(new CenterCrop(width, height));
diff --git a/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationInput.java b/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationInput.java
new file mode 100644
index 000000000..c2345fc13
--- /dev/null
+++ b/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationInput.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 ai.djl.modality.nlp.translator;
+
+import ai.djl.modality.Input;
+import ai.djl.translate.TranslateException;
+import ai.djl.util.JsonUtils;
+
+import com.google.gson.JsonParseException;
+import com.google.gson.annotations.SerializedName;
+
+/** A class that represents a {@code ZeroShotClassificationInput} object. */
+public class ZeroShotClassificationInput {
+
+    private String text;
+
+    @SerializedName("candidate_labels")
+    private String[] candidates;
+
+    @SerializedName("multi_label")
+    private boolean multiLabel;
+
+    @SerializedName("hypothesis_template")
+    private String hypothesisTemplate;
+
+    /**
+     * Constructs a new {@code ZeroShotClassificationInput} instance.
+     *
+     * @param text the text to classify
+     * @param candidates the candidate labels
+     */
+    public ZeroShotClassificationInput(String text, String[] candidates) {
+        this(text, candidates, false);
+    }
+
+    /**
+     * Constructs a new {@code ZeroShotClassificationInput} instance.
+     *
+     * @param text the text to classify
+     * @param candidates the candidate labels
+     * @param multiLabel true to classify multiple labels
+     */
+    public ZeroShotClassificationInput(String text, String[] candidates, boolean multiLabel) {
+        this(text, candidates, multiLabel, null);
+    }
+
+    /**
+     * Constructs a new {@code ZeroShotClassificationInput} instance.
+     *
+     * @param text the text to classify
+     * @param candidates the candidate labels
+     * @param multiLabel true to classify multiple labels
+     * @param hypothesisTemplate the custom template
+     */
+    public ZeroShotClassificationInput(
+            String text, String[] candidates, boolean multiLabel, String hypothesisTemplate) {
+        this.text = text;
+        this.candidates = candidates;
+        this.multiLabel = multiLabel;
+        this.hypothesisTemplate = hypothesisTemplate;
+    }
+
+    /**
+     * Returns the {@code ZeroShotClassificationInput} from the {@link Input}.
+     *
+     * @param input the input object
+     * @return the {@code ZeroShotClassificationInput} from the {@link Input}
+     * @throws TranslateException if the input is invalid
+     */
+    public static ZeroShotClassificationInput parseInput(Input input) throws TranslateException {
+        String text = input.getData().getAsString();
+        try {
+            return JsonUtils.GSON.fromJson(text, ZeroShotClassificationInput.class);
+        } catch (JsonParseException e) {
+            throw new TranslateException("Input is not a valid json.", e);
+        }
+    }
+
+    /**
+     * Returns the text.
+     *
+     * @return the text to be classified
+     */
+    public String getText() {
+        return text;
+    }
+
+    /**
+     * Returns the candidate labels.
+     *
+     * @return the candidate labels
+     */
+    public String[] getCandidates() {
+        return candidates;
+    }
+
+    /**
+     * Returns {@code true} if to classify multiple labels.
+     *
+     * @return {@code true} if to classify multiple labels
+     */
+    public boolean isMultiLabel() {
+        return multiLabel;
+    }
+
+    /**
+     * Returns the custom template.
+     *
+     * @return the custom template
+     */
+    public String getHypothesisTemplate() {
+        return hypothesisTemplate == null ? "This example is {}." : hypothesisTemplate;
+    }
+}
diff --git a/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationOutput.java b/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationOutput.java
new file mode 100644
index 000000000..975f9e103
--- /dev/null
+++ b/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationOutput.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 ai.djl.modality.nlp.translator;
+
+/** A class that represents a {@code ZeroShotClassificationOutput} object. */
+public class ZeroShotClassificationOutput {
+
+    private String sequence;
+    private String[] labels;
+    private double[] scores;
+
+    /**
+     * Constructs a new {@code ZeroShotClassificationOutput} instance.
+     *
+     * @param sequence the input text
+     * @param labels the labels
+     * @param scores the scores of the labels
+     */
+    public ZeroShotClassificationOutput(String sequence, String[] labels, double[] scores) {
+        this.sequence = sequence;
+        this.labels = labels;
+        this.scores = scores;
+    }
+
+    /**
+     * Returns the input text.
+     *
+     * @return the input text
+     */
+    public String getSequence() {
+        return sequence;
+    }
+
+    /**
+     * Returns the labels in sorted order.
+     *
+     * @return the labels in sorted order
+     */
+    public String[] getLabels() {
+        return labels;
+    }
+
+    /**
+     * Returns the scores of the labels.
+     *
+     * @return the scores of the labels
+     */
+    public double[] getScores() {
+        return scores;
+    }
+}
diff --git a/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationServingTranslator.java b/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationServingTranslator.java
new file mode 100644
index 000000000..9b9cdb9ad
--- /dev/null
+++ b/api/src/main/java/ai/djl/modality/nlp/translator/ZeroShotClassificationServingTranslator.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 ai.djl.modality.nlp.translator;
+
+import ai.djl.modality.Input;
+import ai.djl.modality.Output;
+import ai.djl.ndarray.BytesSupplier;
+import ai.djl.ndarray.NDList;
+import ai.djl.translate.Batchifier;
+import ai.djl.translate.NoBatchifyTranslator;
+import ai.djl.translate.TranslateException;
+import ai.djl.translate.Translator;
+import ai.djl.translate.TranslatorContext;
+
+/**
+ * A {@link Translator} that can handle generic zero-shot-classification {@link Input} and {@link
+ * Output}.
+ */
+public class ZeroShotClassificationServingTranslator
+        implements NoBatchifyTranslator<Input, Output> {
+
+    private Translator<ZeroShotClassificationInput, ZeroShotClassificationOutput> translator;
+
+    /**
+     * Constructs a {@code ZeroShotClassificationServingTranslator} instance.
+     *
+     * @param translator a {@code Translator} processes zero-shot-classification input
+     */
+    public ZeroShotClassificationServingTranslator(
+            Translator<ZeroShotClassificationInput, ZeroShotClassificationOutput> translator) {
+        this.translator = translator;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void prepare(TranslatorContext ctx) throws Exception {
+        translator.prepare(ctx);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NDList processInput(TranslatorContext ctx, Input input) throws Exception {
+        if (input.getContent().isEmpty()) {
+            throw new TranslateException("Input data is empty.");
+        }
+
+        ZeroShotClassificationInput prompt = ZeroShotClassificationInput.parseInput(input);
+        NDList ret = translator.processInput(ctx, prompt);
+        Batchifier batchifier = translator.getBatchifier();
+        if (batchifier != null) {
+            NDList[] batch = {ret};
+            return batchifier.batchify(batch);
+        }
+        return ret;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Output processOutput(TranslatorContext ctx, NDList list) throws Exception {
+        Output output = new Output();
+        output.addProperty("Content-Type", "application/json");
+        Batchifier batchifier = translator.getBatchifier();
+        if (batchifier != null) {
+            list = batchifier.unbatchify(list)[0];
+        }
+        output.add(BytesSupplier.wrapAsJson(translator.processOutput(ctx, list)));
+        return output;
+    }
+}
diff --git a/api/src/main/java/ai/djl/ndarray/NDArray.java b/api/src/main/java/ai/djl/ndarray/NDArray.java
index 3d93c9878..aaefd2f88 100644
--- a/api/src/main/java/ai/djl/ndarray/NDArray.java
+++ b/api/src/main/java/ai/djl/ndarray/NDArray.java
@@ -1016,15 +1016,13 @@ public interface NDArray extends NDResource, BytesSupplier {
         return getManager().create(getShape());
     }
 
-    ////////////////////////////////////////
-    ////////////////////////////////////////
+    /*
     // Operations
-    ////////////////////////////////////////
-    ////////////////////////////////////////
+    */
 
-    ////////////////////////////////////////
+    /*
     // Operations: Element Comparison
-    ////////////////////////////////////////
+    */
 
     /**
      * Returns {@code true} if all elements in this {@code NDArray} are equal to the {@link Number}.
@@ -1375,9 +1373,9 @@ public interface NDArray extends NDResource, BytesSupplier {
      */
     NDArray lte(NDArray other);
 
-    ////////////////////////////////////////
+    /*
     // Operations: Element Arithmetic
-    ////////////////////////////////////////
+    */
 
     /**
      * Adds a number to this {@code NDArray} element-wise.
@@ -2006,9 +2004,9 @@ public interface NDArray extends NDResource, BytesSupplier {
      */
     NDArray minimum(NDArray other);
 
-    ////////////////////////////////////////
+    /*
     // Operations: Basic Numeric
-    ////////////////////////////////////////
+    */
 
     /**
      * Returns the numerical negative {@code NDArray} element-wise.
@@ -2516,9 +2514,9 @@ public interface NDArray extends NDResource, BytesSupplier {
      */
     NDArray toRadians();
 
-    ////////////////////////////////////////
+    /*
     // Operations: Reduction
-    ////////////////////////////////////////
+    */
 
     /**
      * Returns the maximum of this {@code NDArray}.
@@ -3161,9 +3159,9 @@ public interface NDArray extends NDResource, BytesSupplier {
      */
     NDArray trace(int offset, int axis1, int axis2);
 
-    ////////////////////////////////////////
+    /*
     // Operations: Shapes and Arrays Manipulation
-    ////////////////////////////////////////
+    */
 
     /**
      * Splits this {@code NDArray} into multiple sub{@code NDArray}s given sections along first
@@ -3885,9 +3883,9 @@ public interface NDArray extends NDResource, BytesSupplier {
         return getNDArrayInternal().concat(new NDList(array), axis);
     }
 
-    ////////////////////////////////////////
+    /*
     // Operations: Logical Op
-    ////////////////////////////////////////
+    */
 
     /**
      * Returns the truth value of this {@code NDArray} AND the other {@code NDArray} element-wise.
@@ -4010,9 +4008,9 @@ public interface NDArray extends NDResource, BytesSupplier {
      */
     NDArray logicalNot();
 
-    ////////////////////////////////////////
+    /*
     // Operations: Other
-    ////////////////////////////////////////
+    */
 
     /**
      * Returns the indices that would sort this {@code NDArray}.
diff --git a/api/src/main/java/ai/djl/ndarray/NDArrayAdapter.java b/api/src/main/java/ai/djl/ndarray/NDArrayAdapter.java
index cceb5efd4..b27cd0ac1 100644
--- a/api/src/main/java/ai/djl/ndarray/NDArrayAdapter.java
+++ b/api/src/main/java/ai/djl/ndarray/NDArrayAdapter.java
@@ -40,7 +40,7 @@ public abstract class NDArrayAdapter implements NDArray {
 
     protected NDManager manager;
     protected NDManager alternativeManager;
-    private NDArray alternativeArray;
+    protected NDArray alternativeArray;
 
     protected Shape shape;
     protected DataType dataType;
diff --git a/api/src/main/java/ai/djl/ndarray/NDArrays.java b/api/src/main/java/ai/djl/ndarray/NDArrays.java
index 0e1c0922a..6391b4076 100644
--- a/api/src/main/java/ai/djl/ndarray/NDArrays.java
+++ b/api/src/main/java/ai/djl/ndarray/NDArrays.java
@@ -32,9 +32,9 @@ public final class NDArrays {
         }
     }
 
-    ////////////////////////////////////////
+    /*
     // Operations: Element Comparison
-    ////////////////////////////////////////
+    */
 
     /**
      * Returns {@code true} if all elements in {@link NDArray} a are equal to {@link NDArray} b.
@@ -788,9 +788,9 @@ public final class NDArrays {
         return data.sequenceMask(sequenceLength);
     }
 
-    ////////////////////////////////////////
+    /*
     // Operations: Element Arithmetic
-    ////////////////////////////////////////
+    */
 
     /**
      * Adds a number to the {@link NDArray} element-wise.
diff --git a/api/src/main/java/ai/djl/ndarray/internal/NDArrayEx.java b/api/src/main/java/ai/djl/ndarray/internal/NDArrayEx.java
index b25852ad5..c4b874220 100644
--- a/api/src/main/java/ai/djl/ndarray/internal/NDArrayEx.java
+++ b/api/src/main/java/ai/djl/ndarray/internal/NDArrayEx.java
@@ -28,9 +28,9 @@ import java.util.List;
 @SuppressWarnings("MissingJavadocMethod")
 public interface NDArrayEx {
 
-    ////////////////////////////////////////
+    /*
     // NDArrays
-    ////////////////////////////////////////
+    */
 
     /**
      * Applies reverse division with a scalar - i.e., (n / thisArrayValues).
@@ -144,9 +144,9 @@ public interface NDArrayEx {
      */
     NDArray rpowi(Number n);
 
-    ////////////////////////////////////////
+    /*
     // Activations
-    ////////////////////////////////////////
+    */
 
     /**
      * Computes rectified linear activation.
@@ -179,9 +179,9 @@ public interface NDArrayEx {
         return getArray().exp().add(1).log2().tanh().mul(getArray());
     }
 
-    ////////////////////////////////////////
+    /*
     // Pooling Operations
-    ////////////////////////////////////////
+    */
 
     NDArray maxPool(Shape kernelShape, Shape stride, Shape padding, boolean ceilMode);
 
@@ -201,9 +201,9 @@ public interface NDArrayEx {
 
     NDArray globalLpPool(float normType);
 
-    ////////////////////////////////////////
+    /*
     // Optimizer
-    ////////////////////////////////////////
+    */
 
     void adadeltaUpdate(
             NDList inputs,
@@ -268,9 +268,9 @@ public interface NDArrayEx {
             float momentum,
             boolean lazyUpdate);
 
-    ////////////////////////////////////////
+    /*
     // Neural network
-    ////////////////////////////////////////
+    */
 
     NDList convolution(
             NDArray input,
@@ -398,9 +398,9 @@ public interface NDArrayEx {
             boolean bidirectional,
             boolean batchFirst);
 
-    ////////////////////////////////////////
+    /*
     // Image and CV
-    ////////////////////////////////////////
+    */
 
     /**
      * Normalizes a NDArray of shape CHW or NCHW with mean and standard deviation.
@@ -489,9 +489,9 @@ public interface NDArrayEx {
     // TODO: add TorchVision support
     NDArray randomColorJitter(float brightness, float contrast, float saturation, float hue);
 
-    ////////////////////////////////////////
+    /*
     // Miscellaneous
-    ////////////////////////////////////////
+    */
 
     /**
      * Returns an {@link NDArrayIndexer}.
diff --git a/api/src/main/java/ai/djl/translate/Batchifier.java b/api/src/main/java/ai/djl/translate/Batchifier.java
index 32fa23f94..9ffcb781d 100644
--- a/api/src/main/java/ai/djl/translate/Batchifier.java
+++ b/api/src/main/java/ai/djl/translate/Batchifier.java
@@ -13,6 +13,7 @@
 package ai.djl.translate;
 
 import ai.djl.ndarray.NDList;
+import ai.djl.util.ClassLoaderUtils;
 
 import java.io.Serializable;
 import java.util.Arrays;
@@ -47,7 +48,12 @@ public interface Batchifier extends Serializable {
             case "none":
                 return null;
             default:
-                throw new IllegalArgumentException("Invalid batchifier name");
+                ClassLoader cl = ClassLoaderUtils.getContextClassLoader();
+                Batchifier b = ClassLoaderUtils.initClass(cl, Batchifier.class, name);
+                if (b == null) {
+                    throw new IllegalArgumentException("Invalid batchifier name: " + name);
+                }
+                return b;
         }
     }
 
diff --git a/api/src/main/java/ai/djl/translate/Pipeline.java b/api/src/main/java/ai/djl/translate/Pipeline.java
index d31fcf315..3de467fdd 100644
--- a/api/src/main/java/ai/djl/translate/Pipeline.java
+++ b/api/src/main/java/ai/djl/translate/Pipeline.java
@@ -17,6 +17,7 @@ import ai.djl.ndarray.NDList;
 import ai.djl.util.Pair;
 import ai.djl.util.PairList;
 
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -176,6 +177,15 @@ public class Pipeline {
         return new NDList(arrays);
     }
 
+    /**
+     * Returns the list of transforms.
+     *
+     * @return the list of transforms
+     */
+    public List<Transform> getTransforms() {
+        return transforms.values();
+    }
+
     private static final class IndexKey {
         private String key;
         private int index;
diff --git a/engines/ml/xgboost/src/main/java/ai/djl/ml/xgboost/XgbNDArray.java b/engines/ml/xgboost/src/main/java/ai/djl/ml/xgboost/XgbNDArray.java
index 69eb6914b..990fad712 100644
--- a/engines/ml/xgboost/src/main/java/ai/djl/ml/xgboost/XgbNDArray.java
+++ b/engines/ml/xgboost/src/main/java/ai/djl/ml/xgboost/XgbNDArray.java
@@ -88,14 +88,33 @@ public class XgbNDArray extends NDArrayAdapter {
     /** {@inheritDoc} */
     @Override
     public void intern(NDArray replaced) {
+        if (!(replaced instanceof XgbNDArray)) {
+            throw new IllegalArgumentException(
+                    "The replaced NDArray must be an instance of XgbNDArray.");
+        }
+        XgbNDArray array = (XgbNDArray) replaced;
+        if (isReleased()) {
+            throw new IllegalArgumentException("This array is already closed");
+        }
+        if (replaced.isReleased()) {
+            throw new IllegalArgumentException("This target array is already closed");
+        }
+
         if (handle != null && handle.get() != 0L) {
             long pointer = handle.getAndSet(0L);
             JniUtils.deleteDMatrix(pointer);
         }
-        XgbNDArray array = (XgbNDArray) replaced;
+        if (alternativeArray != null) {
+            alternativeArray.close();
+        }
+
         data = array.data;
         handle = array.handle;
         format = array.format;
+        alternativeArray = array.alternativeArray;
+        array.handle = null;
+        array.alternativeArray = null;
+        array.close();
     }
 
     /** {@inheritDoc} */
diff --git a/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/engine/MxNDArrayEx.java b/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/engine/MxNDArrayEx.java
index 00a7a0f25..4ea8d07fa 100644
--- a/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/engine/MxNDArrayEx.java
+++ b/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/engine/MxNDArrayEx.java
@@ -31,6 +31,7 @@ import java.util.Arrays;
 import java.util.List;
 
 /** {@code MxNDArrayEx} is the MXNet implementation of the {@link NDArrayEx}. */
+@SuppressWarnings("dangling-doc-comments")
 class MxNDArrayEx implements NDArrayEx {
 
     private MxNDArray array;
diff --git a/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/jna/JnaUtils.java b/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/jna/JnaUtils.java
index 3ec9aab02..cb31f3f35 100644
--- a/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/jna/JnaUtils.java
+++ b/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/jna/JnaUtils.java
@@ -51,7 +51,7 @@ import java.util.concurrent.ConcurrentHashMap;
 /**
  * A class containing utilities to interact with the MXNet Engine's Java Native Access (JNA) layer.
  */
-@SuppressWarnings("MissingJavadocMethod")
+@SuppressWarnings({"MissingJavadocMethod", "dangling-doc-comments"})
 public final class JnaUtils {
 
     public static final ObjectPool<PointerByReference> REFS =
diff --git a/engines/pytorch/pytorch-engine/README.md b/engines/pytorch/pytorch-engine/README.md
index 4623fd032..973e0bff9 100644
--- a/engines/pytorch/pytorch-engine/README.md
+++ b/engines/pytorch/pytorch-engine/README.md
@@ -129,7 +129,7 @@ export PYTORCH_FLAVOR=cpu
 **Note:**
 
 - x86_64 macOS is no longer supported since 0.30.0
-- PyTorch 1.13+ doesn't support mac 11 any more, you must use DJL 0.19.0 ane lower version.
+- PyTorch 1.13+ doesn't support mac 11 any more, you must use DJL 0.19.0 or a lower version.
 
 ### macOS M1
 
diff --git a/examples/src/main/java/ai/djl/examples/inference/nlp/BertQaInference.java b/examples/src/main/java/ai/djl/examples/inference/nlp/BertQaInference.java
index d02da5437..507bf2589 100644
--- a/examples/src/main/java/ai/djl/examples/inference/nlp/BertQaInference.java
+++ b/examples/src/main/java/ai/djl/examples/inference/nlp/BertQaInference.java
@@ -48,7 +48,7 @@ public final class BertQaInference {
 
     public static void main(String[] args) throws IOException, TranslateException, ModelException {
         String answer = BertQaInference.predict();
-        logger.info("Answer: {}", answer);
+        logger.info("Output: {}", answer);
     }
 
     public static String predict() throws IOException, TranslateException, ModelException {
@@ -69,6 +69,7 @@ public final class BertQaInference {
                                 "djl://ai.djl.huggingface.pytorch/deepset/minilm-uncased-squad2")
                         .optEngine("PyTorch")
                         .optTranslatorFactory(new QuestionAnsweringTranslatorFactory())
+                        .optArgument("detail", true)
                         .optProgress(new ProgressBar())
                         .build();
 
diff --git a/examples/src/main/java/ai/djl/examples/inference/nlp/ZeroShotClassification.java b/examples/src/main/java/ai/djl/examples/inference/nlp/ZeroShotClassification.java
new file mode 100644
index 000000000..0fc85a3fb
--- /dev/null
+++ b/examples/src/main/java/ai/djl/examples/inference/nlp/ZeroShotClassification.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 ai.djl.examples.inference.nlp;
+
+import ai.djl.ModelException;
+import ai.djl.huggingface.translator.ZeroShotClassificationTranslatorFactory;
+import ai.djl.inference.Predictor;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationInput;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationOutput;
+import ai.djl.repository.zoo.Criteria;
+import ai.djl.repository.zoo.ZooModel;
+import ai.djl.translate.TranslateException;
+import ai.djl.util.JsonUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public final class ZeroShotClassification {
+
+    private static final Logger logger = LoggerFactory.getLogger(ZeroShotClassification.class);
+
+    private ZeroShotClassification() {}
+
+    public static void main(String[] args) throws ModelException, IOException, TranslateException {
+        ZeroShotClassificationOutput ret = predict(false);
+        logger.info("{}", JsonUtils.GSON_PRETTY.toJson(ret));
+
+        ret = predict(true);
+        logger.info("{}", JsonUtils.GSON_PRETTY.toJson(ret));
+    }
+
+    public static ZeroShotClassificationOutput predict(boolean multiLabels)
+            throws ModelException, IOException, TranslateException {
+        Criteria<ZeroShotClassificationInput, ZeroShotClassificationOutput> criteria =
+                Criteria.builder()
+                        .setTypes(
+                                ZeroShotClassificationInput.class,
+                                ZeroShotClassificationOutput.class)
+                        .optModelUrls("djl://ai.djl.huggingface.pytorch/facebook/bart-large-mnli")
+                        .optEngine("PyTorch")
+                        .optTranslatorFactory(new ZeroShotClassificationTranslatorFactory())
+                        .build();
+        String prompt = "one day I will see the world";
+        String[] candidates = {"travel", "cooking", "dancing", "exploration"};
+
+        try (ZooModel<ZeroShotClassificationInput, ZeroShotClassificationOutput> model =
+                        criteria.loadModel();
+                Predictor<ZeroShotClassificationInput, ZeroShotClassificationOutput> predictor =
+                        model.newPredictor()) {
+            ZeroShotClassificationInput input =
+                    new ZeroShotClassificationInput(prompt, candidates, multiLabels);
+            return predictor.predict(input);
+        }
+    }
+}
diff --git a/extensions/tokenizers/rust/Cargo.toml b/extensions/tokenizers/rust/Cargo.toml
index ebfa6a93f..f33a58bdc 100644
--- a/extensions/tokenizers/rust/Cargo.toml
+++ b/extensions/tokenizers/rust/Cargo.toml
@@ -13,7 +13,7 @@ candle-flash-attn = { version = "*", optional = true }
 candle-cublaslt = { git = "https://github.com/huggingface/candle-cublaslt", rev = "cf789b7dd6d4abb19b03b9556442f94f0588b4a0", optional = true }
 candle-layer-norm = { git = "https://github.com/xyang16/candle-layer-norm", rev = "e574de6a7f88bafbede8edf9ee43170c6a8ce51a", optional = true }
 candle-rotary = { git = "https://github.com/huggingface/candle-rotary", rev = "0a718a0856569a92f3112e64f10d07e4447822e8", optional = true }
-tokenizers = { version = "0.20.3" }
+tokenizers = { version = "0.21.0" }
 half = "2.4.0"
 tracing = "0.1.40"
 safetensors = "0.4.3"
@@ -34,7 +34,7 @@ openssl-src = { git = "https://github.com/alexcrichton/openssl-src-rs", rev = "c
 openssl = { version = "0.10", features = ["vendored"] }
 
 [target.'cfg(not(target_os = "android"))'.dependencies]
-tokenizers = { version = "0.20.0", features = ["http"] }
+tokenizers = { version = "0.21.0", features = ["http"] }
 
 [lib]
 crate-type = ["cdylib"]
diff --git a/extensions/tokenizers/rust/src/lib.rs b/extensions/tokenizers/rust/src/lib.rs
index 3d4cd1264..a7d1072c9 100644
--- a/extensions/tokenizers/rust/src/lib.rs
+++ b/extensions/tokenizers/rust/src/lib.rs
@@ -39,8 +39,8 @@ use tk::models::bpe::BPE;
 use tk::tokenizer::{EncodeInput, Encoding};
 use tk::utils::padding::{PaddingParams, PaddingStrategy};
 use tk::utils::truncation::{TruncationParams, TruncationStrategy};
-use tk::Tokenizer;
 use tk::Offsets;
+use tk::Tokenizer;
 
 #[cfg(not(target_os = "android"))]
 use tk::FromPretrainedParameters;
@@ -407,6 +407,31 @@ pub extern "system" fn Java_ai_djl_huggingface_tokenizers_jni_TokenizersLibrary_
     array
 }
 
+#[no_mangle]
+pub extern "system" fn Java_ai_djl_huggingface_tokenizers_jni_TokenizersLibrary_getSequenceIds<
+    'local,
+>(
+    env: JNIEnv<'local>,
+    _: JObject,
+    handle: jlong,
+) -> JLongArray<'local> {
+    let encoding = cast_handle::<Encoding>(handle);
+    let sequence_ids = encoding.get_sequence_ids();
+    let len = sequence_ids.len() as jsize;
+    let mut long_ids: Vec<jlong> = Vec::new();
+    for i in sequence_ids {
+        if let Some(sequence_id) = i {
+            long_ids.push(sequence_id as jlong)
+        } else {
+            long_ids.push(-1)
+        }
+    }
+
+    let array = env.new_long_array(len).unwrap();
+    env.set_long_array_region(&array, 0, &long_ids).unwrap();
+    array
+}
+
 #[no_mangle]
 pub extern "system" fn Java_ai_djl_huggingface_tokenizers_jni_TokenizersLibrary_getTokens<
     'local,
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/Encoding.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/Encoding.java
index 301490a2b..8670a0163 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/Encoding.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/Encoding.java
@@ -13,6 +13,7 @@
 package ai.djl.huggingface.tokenizers;
 
 import ai.djl.huggingface.tokenizers.jni.CharSpan;
+import ai.djl.ndarray.NDArray;
 import ai.djl.ndarray.NDList;
 import ai.djl.ndarray.NDManager;
 
@@ -25,6 +26,7 @@ public class Encoding {
     private long[] typeIds;
     private String[] tokens;
     private long[] wordIds;
+    private long[] sequenceIds;
     private long[] attentionMask;
     private long[] specialTokenMask;
     private CharSpan[] charTokenSpans;
@@ -36,6 +38,7 @@ public class Encoding {
             long[] typeIds,
             String[] tokens,
             long[] wordIds,
+            long[] sequenceIds,
             long[] attentionMask,
             long[] specialTokenMask,
             CharSpan[] charTokenSpans,
@@ -45,6 +48,7 @@ public class Encoding {
         this.typeIds = typeIds;
         this.tokens = tokens;
         this.wordIds = wordIds;
+        this.sequenceIds = sequenceIds;
         this.attentionMask = attentionMask;
         this.specialTokenMask = specialTokenMask;
         this.charTokenSpans = charTokenSpans;
@@ -52,23 +56,85 @@ public class Encoding {
         this.overflowing = overflowing;
     }
 
+    /**
+     * Returns the {@link NDList} representation of the encodings.
+     *
+     * @param encodings the {@code Encoding} batch
+     * @param manager the {@link NDManager} to create the NDList
+     * @param withTokenType true to include the token type id
+     * @param int32 true to use int32 datatype
+     * @return the {@link NDList}
+     */
+    public static NDList toNDList(
+            Encoding[] encodings, NDManager manager, boolean withTokenType, boolean int32) {
+        NDList list = new NDList();
+        if (!int32) {
+            long[][] ids = new long[encodings.length][];
+            long[][] attentionMask = new long[encodings.length][];
+            long[][] typeIds = new long[encodings.length][];
+            for (int i = 0; i < encodings.length; i++) {
+                ids[i] = encodings[i].getIds();
+                attentionMask[i] = encodings[i].getAttentionMask();
+                if (withTokenType) {
+                    typeIds[i] = encodings[i].getTypeIds();
+                }
+            }
+            list.add(manager.create(ids));
+            NDArray inputAttentionMask = manager.create(attentionMask);
+            list.add(inputAttentionMask);
+            if (withTokenType) {
+                list.add(manager.create(typeIds));
+            }
+            return list;
+        }
+
+        int[][] ids = new int[encodings.length][];
+        int[][] attentionMask = new int[encodings.length][];
+        int[][] typeIds = new int[encodings.length][];
+        for (int i = 0; i < encodings.length; i++) {
+            ids[i] = Arrays.stream(encodings[i].getIds()).mapToInt(l -> (int) l).toArray();
+            attentionMask[i] =
+                    Arrays.stream(encodings[i].getAttentionMask()).mapToInt(l -> (int) l).toArray();
+            if (withTokenType) {
+                typeIds[i] =
+                        Arrays.stream(encodings[i].getTypeIds()).mapToInt(l -> (int) l).toArray();
+            }
+        }
+        list.add(manager.create(ids));
+        NDArray inputAttentionMask = manager.create(attentionMask);
+        list.add(inputAttentionMask);
+        if (withTokenType) {
+            list.add(manager.create(typeIds));
+        }
+        return list;
+    }
+
     /**
      * Returns the {@link NDList} representation of the encoding.
      *
      * @param manager the {@link NDManager} to create the NDList
      * @param withTokenType true to include the token type id
+     * @param int32 true to use int32 datatype
      * @return the {@link NDList}
      */
-    public NDList toNDList(NDManager manager, boolean withTokenType) {
+    public NDList toNDList(NDManager manager, boolean withTokenType, boolean int32) {
         // Converting encoding to int32 NDList because candle can't convert int64 to fp16 in cuda
         NDList list = new NDList(withTokenType ? 3 : 2);
-        int[] intIds = Arrays.stream(ids).mapToInt(i -> (int) i).toArray();
-        int[] intAttentionMask = Arrays.stream(attentionMask).mapToInt(i -> (int) i).toArray();
-        list.add(manager.create(intIds));
-        list.add(manager.create(intAttentionMask));
-        if (withTokenType) {
-            int[] intTypeIds = Arrays.stream(typeIds).mapToInt(i -> (int) i).toArray();
-            list.add(manager.create(intTypeIds));
+        if (int32) {
+            int[] intIds = Arrays.stream(ids).mapToInt(i -> (int) i).toArray();
+            int[] intAttentionMask = Arrays.stream(attentionMask).mapToInt(i -> (int) i).toArray();
+            list.add(manager.create(intIds));
+            list.add(manager.create(intAttentionMask));
+            if (withTokenType) {
+                int[] intTypeIds = Arrays.stream(typeIds).mapToInt(i -> (int) i).toArray();
+                list.add(manager.create(intTypeIds));
+            }
+        } else {
+            list.add(manager.create(ids));
+            list.add(manager.create(attentionMask));
+            if (withTokenType) {
+                list.add(manager.create(typeIds));
+            }
         }
         return list;
     }
@@ -109,6 +175,15 @@ public class Encoding {
         return wordIds;
     }
 
+    /**
+     * Returns the sequence ids.
+     *
+     * @return the sequence ids
+     */
+    public long[] getSequenceIds() {
+        return sequenceIds;
+    }
+
     /**
      * Returns the attention masks.
      *
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizer.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizer.java
index cd6eaf289..c4c04c4df 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizer.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizer.java
@@ -623,6 +623,7 @@ public final class HuggingFaceTokenizer extends NativeResource<Long> implements
         long[] typeIds = TokenizersLibrary.LIB.getTypeIds(encoding);
         String[] tokens = TokenizersLibrary.LIB.getTokens(encoding);
         long[] wordIds = TokenizersLibrary.LIB.getWordIds(encoding);
+        long[] sequenceIds = TokenizersLibrary.LIB.getSequenceIds(encoding);
         long[] attentionMask = TokenizersLibrary.LIB.getAttentionMask(encoding);
         long[] specialTokenMask = TokenizersLibrary.LIB.getSpecialTokenMask(encoding);
         CharSpan[] charSpans = TokenizersLibrary.LIB.getTokenCharSpans(encoding);
@@ -646,6 +647,7 @@ public final class HuggingFaceTokenizer extends NativeResource<Long> implements
                 typeIds,
                 tokens,
                 wordIds,
+                sequenceIds,
                 attentionMask,
                 specialTokenMask,
                 charSpans,
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/jni/CharSpan.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/jni/CharSpan.java
index 7478d0e8b..9a15980b4 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/jni/CharSpan.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/jni/CharSpan.java
@@ -10,8 +10,6 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-
-/** A class holds character span information. */
 package ai.djl.huggingface.tokenizers.jni;
 
 /** A class holds character span information. */
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/jni/TokenizersLibrary.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/jni/TokenizersLibrary.java
index 7ef895091..994694fe2 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/jni/TokenizersLibrary.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/jni/TokenizersLibrary.java
@@ -50,6 +50,8 @@ public final class TokenizersLibrary {
 
     public native long[] getWordIds(long encoding);
 
+    public native long[] getSequenceIds(long encoding);
+
     public native String[] getTokens(long encoding);
 
     public native long[] getAttentionMask(long encoding);
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/CrossEncoderTranslator.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/CrossEncoderTranslator.java
index ac5e4ed55..843bb5716 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/CrossEncoderTranslator.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/CrossEncoderTranslator.java
@@ -35,16 +35,19 @@ public class CrossEncoderTranslator implements Translator<StringPair, float[]> {
 
     private HuggingFaceTokenizer tokenizer;
     private boolean includeTokenTypes;
+    private boolean int32;
     private boolean sigmoid;
     private Batchifier batchifier;
 
     CrossEncoderTranslator(
             HuggingFaceTokenizer tokenizer,
             boolean includeTokenTypes,
+            boolean int32,
             boolean sigmoid,
             Batchifier batchifier) {
         this.tokenizer = tokenizer;
         this.includeTokenTypes = includeTokenTypes;
+        this.int32 = int32;
         this.sigmoid = sigmoid;
         this.batchifier = batchifier;
     }
@@ -60,7 +63,7 @@ public class CrossEncoderTranslator implements Translator<StringPair, float[]> {
     public NDList processInput(TranslatorContext ctx, StringPair input) {
         Encoding encoding = tokenizer.encode(input.getKey(), input.getValue());
         ctx.setAttachment("encoding", encoding);
-        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes);
+        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes, int32);
     }
 
     /** {@inheritDoc} */
@@ -71,7 +74,7 @@ public class CrossEncoderTranslator implements Translator<StringPair, float[]> {
         Encoding[] encodings = tokenizer.batchEncode(list);
         NDList[] batch = new NDList[encodings.length];
         for (int i = 0; i < encodings.length; ++i) {
-            batch[i] = encodings[i].toNDList(manager, includeTokenTypes);
+            batch[i] = encodings[i].toNDList(manager, includeTokenTypes, int32);
         }
         return batchifier.batchify(batch);
     }
@@ -145,6 +148,7 @@ public class CrossEncoderTranslator implements Translator<StringPair, float[]> {
 
         private HuggingFaceTokenizer tokenizer;
         private boolean includeTokenTypes;
+        private boolean int32;
         private boolean sigmoid = true;
         private Batchifier batchifier = Batchifier.STACK;
 
@@ -163,6 +167,17 @@ public class CrossEncoderTranslator implements Translator<StringPair, float[]> {
             return this;
         }
 
+        /**
+         * Sets if use int32 datatype for the {@link Translator}.
+         *
+         * @param int32 true to include token types
+         * @return this builder
+         */
+        public Builder optInt32(boolean int32) {
+            this.int32 = int32;
+            return this;
+        }
+
         /**
          * Sets if apply sigmoid for the {@link Translator}.
          *
@@ -192,6 +207,7 @@ public class CrossEncoderTranslator implements Translator<StringPair, float[]> {
          */
         public void configure(Map<String, ?> arguments) {
             optIncludeTokenTypes(ArgumentsUtil.booleanValue(arguments, "includeTokenTypes"));
+            optInt32(ArgumentsUtil.booleanValue(arguments, "int32"));
             optSigmoid(ArgumentsUtil.booleanValue(arguments, "sigmoid", true));
             String batchifierStr = ArgumentsUtil.stringValue(arguments, "batchifier", "stack");
             optBatchifier(Batchifier.fromString(batchifierStr));
@@ -204,7 +220,8 @@ public class CrossEncoderTranslator implements Translator<StringPair, float[]> {
          * @throws IOException if I/O error occurs
          */
         public CrossEncoderTranslator build() throws IOException {
-            return new CrossEncoderTranslator(tokenizer, includeTokenTypes, sigmoid, batchifier);
+            return new CrossEncoderTranslator(
+                    tokenizer, includeTokenTypes, int32, sigmoid, batchifier);
         }
     }
 }
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/FillMaskTranslator.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/FillMaskTranslator.java
index 6eaf75597..7fe20e0fe 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/FillMaskTranslator.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/FillMaskTranslator.java
@@ -38,6 +38,7 @@ public class FillMaskTranslator implements Translator<String, Classifications> {
     private long maskTokenId;
     private int topK;
     private boolean includeTokenTypes;
+    private boolean int32;
     private Batchifier batchifier;
 
     FillMaskTranslator(
@@ -45,11 +46,13 @@ public class FillMaskTranslator implements Translator<String, Classifications> {
             String maskToken,
             int topK,
             boolean includeTokenTypes,
+            boolean int32,
             Batchifier batchifier) {
         this.tokenizer = tokenizer;
         this.maskToken = maskToken;
         this.topK = topK;
         this.includeTokenTypes = includeTokenTypes;
+        this.int32 = int32;
         this.batchifier = batchifier;
         Encoding encoding = tokenizer.encode(maskToken, false, false);
         maskTokenId = encoding.getIds()[0];
@@ -68,7 +71,7 @@ public class FillMaskTranslator implements Translator<String, Classifications> {
         long[] indices = encoding.getIds();
         int maskIndex = getMaskIndex(indices);
         ctx.setAttachment("maskIndex", maskIndex);
-        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes);
+        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes, int32);
     }
 
     /** {@inheritDoc} */
@@ -83,7 +86,7 @@ public class FillMaskTranslator implements Translator<String, Classifications> {
         for (int i = 0; i < batch.length; ++i) {
             long[] indices = encodings[i].getIds();
             maskIndices[i] = getMaskIndex(indices);
-            batch[i] = encodings[i].toNDList(manager, includeTokenTypes);
+            batch[i] = encodings[i].toNDList(manager, includeTokenTypes, int32);
         }
         return batchifier.batchify(batch);
     }
@@ -167,6 +170,7 @@ public class FillMaskTranslator implements Translator<String, Classifications> {
         private String maskedToken = "[MASK]";
         private int topK = 5;
         private boolean includeTokenTypes;
+        private boolean int32;
         private Batchifier batchifier = Batchifier.STACK;
 
         Builder(HuggingFaceTokenizer tokenizer) {
@@ -206,6 +210,17 @@ public class FillMaskTranslator implements Translator<String, Classifications> {
             return this;
         }
 
+        /**
+         * Sets if use int32 datatype for the {@link Translator}.
+         *
+         * @param int32 true to include token types
+         * @return this builder
+         */
+        public Builder optInt32(boolean int32) {
+            this.int32 = int32;
+            return this;
+        }
+
         /**
          * Sets the {@link Batchifier} for the {@link Translator}.
          *
@@ -224,6 +239,7 @@ public class FillMaskTranslator implements Translator<String, Classifications> {
          */
         public void configure(Map<String, ?> arguments) {
             optMaskToken(ArgumentsUtil.stringValue(arguments, "maskToken", "[MASK]"));
+            optInt32(ArgumentsUtil.booleanValue(arguments, "int32"));
             optTopK(ArgumentsUtil.intValue(arguments, "topK", 5));
             optIncludeTokenTypes(ArgumentsUtil.booleanValue(arguments, "includeTokenTypes"));
             String batchifierStr = ArgumentsUtil.stringValue(arguments, "batchifier", "stack");
@@ -238,7 +254,7 @@ public class FillMaskTranslator implements Translator<String, Classifications> {
          */
         public FillMaskTranslator build() throws IOException {
             return new FillMaskTranslator(
-                    tokenizer, maskedToken, topK, includeTokenTypes, batchifier);
+                    tokenizer, maskedToken, topK, includeTokenTypes, int32, batchifier);
         }
     }
 }
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/QuestionAnsweringTranslator.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/QuestionAnsweringTranslator.java
index f0b0fac02..92cba3726 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/QuestionAnsweringTranslator.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/QuestionAnsweringTranslator.java
@@ -23,25 +23,35 @@ import ai.djl.translate.ArgumentsUtil;
 import ai.djl.translate.Batchifier;
 import ai.djl.translate.Translator;
 import ai.djl.translate.TranslatorContext;
+import ai.djl.util.JsonUtils;
 import ai.djl.util.PairList;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /** The translator for Huggingface question answering model. */
 public class QuestionAnsweringTranslator implements Translator<QAInput, String> {
 
     private HuggingFaceTokenizer tokenizer;
     private boolean includeTokenTypes;
+    private boolean int32;
     private Batchifier batchifier;
+    private boolean detail;
 
     QuestionAnsweringTranslator(
-            HuggingFaceTokenizer tokenizer, boolean includeTokenTypes, Batchifier batchifier) {
+            HuggingFaceTokenizer tokenizer,
+            boolean includeTokenTypes,
+            boolean int32,
+            Batchifier batchifier,
+            boolean detail) {
         this.tokenizer = tokenizer;
         this.includeTokenTypes = includeTokenTypes;
+        this.int32 = int32;
         this.batchifier = batchifier;
+        this.detail = detail;
     }
 
     /** {@inheritDoc} */
@@ -55,7 +65,7 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
     public NDList processInput(TranslatorContext ctx, QAInput input) {
         Encoding encoding = tokenizer.encode(input.getQuestion(), input.getParagraph());
         ctx.setAttachment("encoding", encoding);
-        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes);
+        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes, int32);
     }
 
     /** {@inheritDoc} */
@@ -70,7 +80,7 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
         ctx.setAttachment("encodings", encodings);
         NDList[] batch = new NDList[encodings.length];
         for (int i = 0; i < encodings.length; ++i) {
-            batch[i] = encodings[i].toNDList(manager, includeTokenTypes);
+            batch[i] = encodings[i].toNDList(manager, includeTokenTypes, int32);
         }
         return batchifier.batchify(batch);
     }
@@ -102,6 +112,27 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
             startLogits = startLogits.duplicate();
             endLogits = endLogits.duplicate();
         }
+        if (detail) {
+            // exclude undesired sequences
+            long[] sequenceIds = encoding.getSequenceIds();
+            List<Integer> undesired = new ArrayList<>();
+            for (int i = 0; i < sequenceIds.length; ++i) {
+                if (sequenceIds[i] == 0) {
+                    undesired.add(i);
+                }
+            }
+            int[] idx = undesired.stream().mapToInt(Integer::intValue).toArray();
+            NDIndex ndIndex = new NDIndex("{}", list.getManager().create(idx));
+            startLogits.set(ndIndex, -100000f);
+            endLogits.set(ndIndex, -100000f);
+
+            // normalize
+            startLogits = startLogits.sub(startLogits.max()).exp();
+            startLogits = startLogits.div(startLogits.sum());
+            endLogits = endLogits.sub(endLogits.max()).exp();
+            endLogits = endLogits.div(endLogits.sum());
+        }
+
         // exclude <CLS>, TODO: exclude impossible ids properly and handle max answer length
         startLogits.set(new NDIndex(0), -100000);
         endLogits.set(new NDIndex(0), -100000);
@@ -111,12 +142,26 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
             int tmp = startIdx;
             startIdx = endIdx;
             endIdx = tmp;
+            NDArray tmpArray = startLogits;
+            startLogits = endLogits;
+            endLogits = tmpArray;
         }
         long[] indices = encoding.getIds();
         int len = endIdx - startIdx + 1;
         long[] ids = new long[len];
         System.arraycopy(indices, startIdx, ids, 0, len);
-        return tokenizer.decode(ids).trim();
+        String answer = tokenizer.decode(ids).trim();
+        if (detail) {
+            float score = startLogits.getFloat(startIdx) * endLogits.getFloat(endIdx);
+
+            Map<String, Object> dict = new ConcurrentHashMap<>();
+            dict.put("score", score);
+            dict.put("start", startIdx);
+            dict.put("end", endIdx);
+            dict.put("answer", answer);
+            return JsonUtils.toJson(dict);
+        }
+        return answer;
     }
 
     /**
@@ -148,7 +193,9 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
 
         private HuggingFaceTokenizer tokenizer;
         private boolean includeTokenTypes;
+        private boolean int32;
         private Batchifier batchifier = Batchifier.STACK;
+        private boolean detail;
 
         Builder(HuggingFaceTokenizer tokenizer) {
             this.tokenizer = tokenizer;
@@ -165,6 +212,17 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
             return this;
         }
 
+        /**
+         * Sets if use int32 datatype for the {@link Translator}.
+         *
+         * @param int32 true to include token types
+         * @return this builder
+         */
+        public Builder optInt32(boolean int32) {
+            this.int32 = int32;
+            return this;
+        }
+
         /**
          * Sets the {@link Batchifier} for the {@link Translator}.
          *
@@ -176,6 +234,17 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
             return this;
         }
 
+        /**
+         * Sets if output detail for the {@link Translator}.
+         *
+         * @param detail true to output detail
+         * @return this builder
+         */
+        public Builder optDetail(boolean detail) {
+            this.detail = detail;
+            return this;
+        }
+
         /**
          * Configures the builder with the model arguments.
          *
@@ -183,7 +252,9 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
          */
         public void configure(Map<String, ?> arguments) {
             optIncludeTokenTypes(ArgumentsUtil.booleanValue(arguments, "includeTokenTypes"));
+            optInt32(ArgumentsUtil.booleanValue(arguments, "int32"));
             String batchifierStr = ArgumentsUtil.stringValue(arguments, "batchifier", "stack");
+            optDetail(ArgumentsUtil.booleanValue(arguments, "detail"));
             optBatchifier(Batchifier.fromString(batchifierStr));
         }
 
@@ -194,7 +265,8 @@ public class QuestionAnsweringTranslator implements Translator<QAInput, String>
          * @throws IOException if I/O error occurs
          */
         public QuestionAnsweringTranslator build() throws IOException {
-            return new QuestionAnsweringTranslator(tokenizer, includeTokenTypes, batchifier);
+            return new QuestionAnsweringTranslator(
+                    tokenizer, includeTokenTypes, int32, batchifier, detail);
         }
     }
 }
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TextClassificationTranslator.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TextClassificationTranslator.java
index 25224d207..82c9112d3 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TextClassificationTranslator.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TextClassificationTranslator.java
@@ -37,13 +37,18 @@ public class TextClassificationTranslator implements Translator<String, Classifi
 
     private HuggingFaceTokenizer tokenizer;
     private boolean includeTokenTypes;
+    private boolean int32;
     private Batchifier batchifier;
     private PretrainedConfig config;
 
     TextClassificationTranslator(
-            HuggingFaceTokenizer tokenizer, boolean includeTokenTypes, Batchifier batchifier) {
+            HuggingFaceTokenizer tokenizer,
+            boolean includeTokenTypes,
+            boolean int32,
+            Batchifier batchifier) {
         this.tokenizer = tokenizer;
         this.includeTokenTypes = includeTokenTypes;
+        this.int32 = int32;
         this.batchifier = batchifier;
     }
 
@@ -67,7 +72,7 @@ public class TextClassificationTranslator implements Translator<String, Classifi
     @Override
     public NDList processInput(TranslatorContext ctx, String input) {
         Encoding encoding = tokenizer.encode(input);
-        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes);
+        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes, int32);
     }
 
     /** {@inheritDoc} */
@@ -77,7 +82,7 @@ public class TextClassificationTranslator implements Translator<String, Classifi
         Encoding[] encodings = tokenizer.batchEncode(inputs);
         NDList[] batch = new NDList[encodings.length];
         for (int i = 0; i < encodings.length; ++i) {
-            batch[i] = encodings[i].toNDList(manager, includeTokenTypes);
+            batch[i] = encodings[i].toNDList(manager, includeTokenTypes, int32);
         }
         return batchifier.batchify(batch);
     }
@@ -148,6 +153,7 @@ public class TextClassificationTranslator implements Translator<String, Classifi
 
         private HuggingFaceTokenizer tokenizer;
         private boolean includeTokenTypes;
+        private boolean int32;
         private Batchifier batchifier = Batchifier.STACK;
 
         Builder(HuggingFaceTokenizer tokenizer) {
@@ -165,6 +171,17 @@ public class TextClassificationTranslator implements Translator<String, Classifi
             return this;
         }
 
+        /**
+         * Sets if use int32 datatype for the {@link Translator}.
+         *
+         * @param int32 true to include token types
+         * @return this builder
+         */
+        public Builder optInt32(boolean int32) {
+            this.int32 = int32;
+            return this;
+        }
+
         /**
          * Sets the {@link Batchifier} for the {@link Translator}.
          *
@@ -183,6 +200,7 @@ public class TextClassificationTranslator implements Translator<String, Classifi
          */
         public void configure(Map<String, ?> arguments) {
             optIncludeTokenTypes(ArgumentsUtil.booleanValue(arguments, "includeTokenTypes"));
+            optInt32(ArgumentsUtil.booleanValue(arguments, "int32"));
             String batchifierStr = ArgumentsUtil.stringValue(arguments, "batchifier", "stack");
             optBatchifier(Batchifier.fromString(batchifierStr));
         }
@@ -194,7 +212,8 @@ public class TextClassificationTranslator implements Translator<String, Classifi
          * @throws IOException if I/O error occurs
          */
         public TextClassificationTranslator build() throws IOException {
-            return new TextClassificationTranslator(tokenizer, includeTokenTypes, batchifier);
+            return new TextClassificationTranslator(
+                    tokenizer, includeTokenTypes, int32, batchifier);
         }
     }
 }
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TextEmbeddingTranslator.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TextEmbeddingTranslator.java
index 0d91734d5..81e69b25e 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TextEmbeddingTranslator.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TextEmbeddingTranslator.java
@@ -44,6 +44,7 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
     private boolean normalize;
     private String pooling;
     private boolean includeTokenTypes;
+    private boolean int32;
     private String dense;
     private String denseActivation;
     private String layerNorm;
@@ -56,6 +57,7 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
             String pooling,
             boolean normalize,
             boolean includeTokenTypes,
+            boolean int32,
             String dense,
             String denseActivation,
             String layerNorm) {
@@ -64,6 +66,7 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
         this.pooling = pooling;
         this.normalize = normalize;
         this.includeTokenTypes = includeTokenTypes;
+        this.int32 = int32;
         this.dense = dense;
         this.denseActivation = denseActivation;
         this.layerNorm = layerNorm;
@@ -108,14 +111,8 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
     public NDList processInput(TranslatorContext ctx, String input) {
         NDManager manager = ctx.getNDManager();
         Encoding encoding = tokenizer.encode(input);
-        NDList list = new NDList();
-        list.add(manager.create(encoding.getIds()));
-        NDArray inputAttentionMask = manager.create(encoding.getAttentionMask());
-        list.add(inputAttentionMask);
-        ctx.setAttachment("attentionMask", inputAttentionMask);
-        if (includeTokenTypes) {
-            list.add(manager.create(encoding.getTypeIds()));
-        }
+        NDList list = encoding.toNDList(manager, includeTokenTypes, int32);
+        ctx.setAttachment("attentionMask", list.get(1));
         return list;
     }
 
@@ -124,24 +121,8 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
     public NDList batchProcessInput(TranslatorContext ctx, List<String> inputs) {
         NDManager manager = ctx.getNDManager();
         Encoding[] encodings = tokenizer.batchEncode(inputs);
-        long[][] ids = new long[encodings.length][];
-        long[][] attentionMask = new long[encodings.length][];
-        long[][] typeIds = new long[encodings.length][];
-        for (int i = 0; i < encodings.length; i++) {
-            ids[i] = encodings[i].getIds();
-            attentionMask[i] = encodings[i].getAttentionMask();
-            if (includeTokenTypes) {
-                typeIds[i] = encodings[i].getTypeIds();
-            }
-        }
-        NDList list = new NDList();
-        list.add(manager.create(ids));
-        NDArray inputAttentionMask = manager.create(attentionMask);
-        list.add(inputAttentionMask);
-        ctx.setAttachment("attentionMask", inputAttentionMask);
-        if (includeTokenTypes) {
-            list.add(manager.create(typeIds));
-        }
+        NDList list = Encoding.toNDList(encodings, manager, includeTokenTypes, int32);
+        ctx.setAttachment("attentionMask", list.get(1));
         return list;
     }
 
@@ -290,6 +271,7 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
         private boolean normalize = true;
         private String pooling = "mean";
         private boolean includeTokenTypes;
+        private boolean int32;
         private String dense;
         private String denseActivation;
         private String layerNorm;
@@ -351,6 +333,17 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
             return this;
         }
 
+        /**
+         * Sets if use int32 datatype for the {@link Translator}.
+         *
+         * @param int32 true to include token types
+         * @return this builder
+         */
+        public Builder optInt32(boolean int32) {
+            this.int32 = int32;
+            return this;
+        }
+
         /**
          * Sets the dense layer model file for the {@link Translator}.
          *
@@ -395,6 +388,7 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
             optNormalize(ArgumentsUtil.booleanValue(arguments, "normalize", true));
             optPoolingMode(ArgumentsUtil.stringValue(arguments, "pooling", "mean"));
             optIncludeTokenTypes(ArgumentsUtil.booleanValue(arguments, "includeTokenTypes"));
+            optInt32(ArgumentsUtil.booleanValue(arguments, "int32"));
             optDense(ArgumentsUtil.stringValue(arguments, "dense"));
             optDenseActivation(ArgumentsUtil.stringValue(arguments, "denseActivation"));
             optLayerNorm(ArgumentsUtil.stringValue(arguments, "layerNorm"));
@@ -413,6 +407,7 @@ public class TextEmbeddingTranslator implements Translator<String, float[]> {
                     pooling,
                     normalize,
                     includeTokenTypes,
+                    int32,
                     dense,
                     denseActivation,
                     layerNorm);
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TokenClassificationTranslator.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TokenClassificationTranslator.java
index c22b4011a..43aed0db2 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TokenClassificationTranslator.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/TokenClassificationTranslator.java
@@ -38,6 +38,7 @@ public class TokenClassificationTranslator implements Translator<String, NamedEn
 
     private HuggingFaceTokenizer tokenizer;
     private boolean includeTokenTypes;
+    private boolean int32;
     private boolean softmax;
     private Batchifier batchifier;
     private PretrainedConfig config;
@@ -45,10 +46,12 @@ public class TokenClassificationTranslator implements Translator<String, NamedEn
     TokenClassificationTranslator(
             HuggingFaceTokenizer tokenizer,
             boolean includeTokenTypes,
+            boolean int32,
             boolean softmax,
             Batchifier batchifier) {
         this.tokenizer = tokenizer;
         this.includeTokenTypes = includeTokenTypes;
+        this.int32 = int32;
         this.softmax = softmax;
         this.batchifier = batchifier;
     }
@@ -74,7 +77,7 @@ public class TokenClassificationTranslator implements Translator<String, NamedEn
     public NDList processInput(TranslatorContext ctx, String input) {
         Encoding encoding = tokenizer.encode(input);
         ctx.setAttachment("encoding", encoding);
-        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes);
+        return encoding.toNDList(ctx.getNDManager(), includeTokenTypes, int32);
     }
 
     /** {@inheritDoc} */
@@ -85,7 +88,7 @@ public class TokenClassificationTranslator implements Translator<String, NamedEn
         ctx.setAttachment("encodings", encodings);
         NDList[] batch = new NDList[encodings.length];
         for (int i = 0; i < encodings.length; ++i) {
-            batch[i] = encodings[i].toNDList(manager, includeTokenTypes);
+            batch[i] = encodings[i].toNDList(manager, includeTokenTypes, int32);
         }
         return batchifier.batchify(batch);
     }
@@ -170,6 +173,7 @@ public class TokenClassificationTranslator implements Translator<String, NamedEn
 
         private HuggingFaceTokenizer tokenizer;
         private boolean includeTokenTypes;
+        private boolean int32;
         private boolean softmax = true;
         private Batchifier batchifier = Batchifier.STACK;
 
@@ -188,6 +192,17 @@ public class TokenClassificationTranslator implements Translator<String, NamedEn
             return this;
         }
 
+        /**
+         * Sets if use int32 datatype for the {@link Translator}.
+         *
+         * @param int32 true to include token types
+         * @return this builder
+         */
+        public Builder optInt32(boolean int32) {
+            this.int32 = int32;
+            return this;
+        }
+
         /**
          * Sets if implement softmax operation for the {@link Translator}.
          *
@@ -217,6 +232,7 @@ public class TokenClassificationTranslator implements Translator<String, NamedEn
          */
         public void configure(Map<String, ?> arguments) {
             optIncludeTokenTypes(ArgumentsUtil.booleanValue(arguments, "includeTokenTypes"));
+            optInt32(ArgumentsUtil.booleanValue(arguments, "int32"));
             optSoftmax(ArgumentsUtil.booleanValue(arguments, "softmax", true));
             String batchifierStr = ArgumentsUtil.stringValue(arguments, "batchifier", "stack");
             optBatchifier(Batchifier.fromString(batchifierStr));
@@ -230,7 +246,7 @@ public class TokenClassificationTranslator implements Translator<String, NamedEn
          */
         public TokenClassificationTranslator build() throws IOException {
             return new TokenClassificationTranslator(
-                    tokenizer, includeTokenTypes, softmax, batchifier);
+                    tokenizer, includeTokenTypes, int32, softmax, batchifier);
         }
     }
 }
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/ZeroShotClassificationTranslator.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/ZeroShotClassificationTranslator.java
new file mode 100644
index 000000000..6159cfd17
--- /dev/null
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/ZeroShotClassificationTranslator.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 ai.djl.huggingface.translator;
+
+import ai.djl.Model;
+import ai.djl.ModelException;
+import ai.djl.huggingface.tokenizers.Encoding;
+import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer;
+import ai.djl.inference.Predictor;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationInput;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationOutput;
+import ai.djl.ndarray.NDArray;
+import ai.djl.ndarray.NDArrays;
+import ai.djl.ndarray.NDList;
+import ai.djl.ndarray.NDManager;
+import ai.djl.ndarray.index.NDIndex;
+import ai.djl.translate.ArgumentsUtil;
+import ai.djl.translate.Batchifier;
+import ai.djl.translate.NoBatchifyTranslator;
+import ai.djl.translate.NoopTranslator;
+import ai.djl.translate.TranslateException;
+import ai.djl.translate.Translator;
+import ai.djl.translate.TranslatorContext;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.UUID;
+
+/** The translator for Huggingface zero-shot-classification model. */
+public class ZeroShotClassificationTranslator
+        implements NoBatchifyTranslator<ZeroShotClassificationInput, ZeroShotClassificationOutput> {
+
+    private HuggingFaceTokenizer tokenizer;
+    private boolean int32;
+    private Predictor<NDList, NDList> predictor;
+
+    ZeroShotClassificationTranslator(HuggingFaceTokenizer tokenizer, boolean int32) {
+        this.tokenizer = tokenizer;
+        this.int32 = int32;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void prepare(TranslatorContext ctx) throws IOException, ModelException {
+        Model model = ctx.getModel();
+        predictor = model.newPredictor(new NoopTranslator(null));
+        ctx.getPredictorManager().attachInternal(UUID.randomUUID().toString(), predictor);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NDList processInput(TranslatorContext ctx, ZeroShotClassificationInput input) {
+        ctx.setAttachment("input", input);
+        return new NDList();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ZeroShotClassificationOutput processOutput(TranslatorContext ctx, NDList list)
+            throws TranslateException {
+        ZeroShotClassificationInput input =
+                (ZeroShotClassificationInput) ctx.getAttachment("input");
+
+        String template = input.getHypothesisTemplate();
+        String[] candidates = input.getCandidates();
+        if (candidates == null || candidates.length == 0) {
+            throw new TranslateException("Missing candidates in input");
+        }
+
+        NDManager manager = ctx.getNDManager();
+        NDList output = new NDList(candidates.length);
+        for (String candidate : candidates) {
+            String hypothesis = applyTemplate(template, candidate);
+            Encoding encoding = tokenizer.encode(input.getText(), hypothesis);
+            NDList in = encoding.toNDList(manager, false, int32);
+            NDList batch = Batchifier.STACK.batchify(new NDList[] {in});
+            output.add(predictor.predict(batch).get(0));
+        }
+
+        NDArray logits = NDArrays.concat(output);
+        if (input.isMultiLabel()) {
+            logits = logits.get(":, -1");
+            logits = logits.softmax(-1);
+        } else {
+            logits = logits.get(new NDIndex(":, {}", manager.create(new int[] {0, 2})));
+            logits = logits.softmax(1);
+            logits = logits.get(":, -1");
+        }
+        long[] indices = logits.argSort(-1, false).toLongArray();
+        float[] probabilities = logits.toFloatArray();
+
+        String[] labels = new String[candidates.length];
+        double[] scores = new double[candidates.length];
+        for (int i = 0; i < labels.length; ++i) {
+            int index = (int) indices[i];
+            labels[i] = candidates[index];
+            scores[i] = probabilities[index];
+        }
+
+        return new ZeroShotClassificationOutput(input.getText(), labels, scores);
+    }
+
+    private String applyTemplate(String template, String arg) {
+        int pos = template.indexOf("{}");
+        if (pos == -1) {
+            return template + arg;
+        }
+        int len = template.length();
+        return template.substring(0, pos) + arg + template.substring(pos + 2, len);
+    }
+
+    /**
+     * Creates a builder to build a {@code ZeroShotClassificationTranslator}.
+     *
+     * @param tokenizer the tokenizer
+     * @return a new builder
+     */
+    public static Builder builder(HuggingFaceTokenizer tokenizer) {
+        return new Builder(tokenizer);
+    }
+
+    /**
+     * Creates a builder to build a {@code ZeroShotClassificationTranslator}.
+     *
+     * @param tokenizer the tokenizer
+     * @param arguments the models' arguments
+     * @return a new builder
+     */
+    public static Builder builder(HuggingFaceTokenizer tokenizer, Map<String, ?> arguments) {
+        Builder builder = builder(tokenizer);
+        builder.configure(arguments);
+
+        return builder;
+    }
+
+    /** The builder for zero-shot classification translator. */
+    public static final class Builder {
+
+        private HuggingFaceTokenizer tokenizer;
+        private boolean int32;
+
+        Builder(HuggingFaceTokenizer tokenizer) {
+            this.tokenizer = tokenizer;
+        }
+
+        /**
+         * Sets if use int32 datatype for the {@link Translator}.
+         *
+         * @param int32 true to include token types
+         * @return this builder
+         */
+        public Builder optInt32(boolean int32) {
+            this.int32 = int32;
+            return this;
+        }
+
+        /**
+         * Configures the builder with the model arguments.
+         *
+         * @param arguments the model arguments
+         */
+        public void configure(Map<String, ?> arguments) {
+            optInt32(ArgumentsUtil.booleanValue(arguments, "int32"));
+        }
+
+        /**
+         * Builds the translator.
+         *
+         * @return the new translator
+         * @throws IOException if I/O error occurs
+         */
+        public ZeroShotClassificationTranslator build() throws IOException {
+            return new ZeroShotClassificationTranslator(tokenizer, int32);
+        }
+    }
+}
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/ZeroShotClassificationTranslatorFactory.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/ZeroShotClassificationTranslatorFactory.java
new file mode 100644
index 000000000..d314008ce
--- /dev/null
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/translator/ZeroShotClassificationTranslatorFactory.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 ai.djl.huggingface.translator;
+
+import ai.djl.Model;
+import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer;
+import ai.djl.modality.Input;
+import ai.djl.modality.Output;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationInput;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationOutput;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationServingTranslator;
+import ai.djl.translate.TranslateException;
+import ai.djl.translate.Translator;
+import ai.djl.translate.TranslatorFactory;
+import ai.djl.util.Pair;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Type;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/** A {@link TranslatorFactory} that creates a {@link ZeroShotClassificationTranslator} instance. */
+public class ZeroShotClassificationTranslatorFactory implements TranslatorFactory, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final Set<Pair<Type, Type>> SUPPORTED_TYPES = new HashSet<>();
+
+    static {
+        SUPPORTED_TYPES.add(
+                new Pair<>(ZeroShotClassificationInput.class, ZeroShotClassificationOutput.class));
+        SUPPORTED_TYPES.add(new Pair<>(Input.class, Output.class));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Set<Pair<Type, Type>> getSupportedTypes() {
+        return SUPPORTED_TYPES;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <I, O> Translator<I, O> newInstance(
+            Class<I> input, Class<O> output, Model model, Map<String, ?> arguments)
+            throws TranslateException {
+        Path modelPath = model.getModelPath();
+        try {
+            HuggingFaceTokenizer tokenizer =
+                    HuggingFaceTokenizer.builder(arguments)
+                            .optTokenizerPath(modelPath)
+                            .optManager(model.getNDManager())
+                            .build();
+            ZeroShotClassificationTranslator translator =
+                    ZeroShotClassificationTranslator.builder(tokenizer, arguments).build();
+            if (input == ZeroShotClassificationInput.class
+                    && output == ZeroShotClassificationOutput.class) {
+                return (Translator<I, O>) translator;
+            } else if (input == Input.class && output == Output.class) {
+                return (Translator<I, O>) new ZeroShotClassificationServingTranslator(translator);
+            }
+            throw new IllegalArgumentException("Unsupported input/output types.");
+        } catch (IOException e) {
+            throw new TranslateException("Failed to load tokenizer.", e);
+        }
+    }
+}
diff --git a/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py b/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py
index b0c4ed25f..2cc8418a0 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py
@@ -15,8 +15,12 @@ import os.path
 import shutil
 import sys
 from argparse import Namespace
+from collections import OrderedDict
 
 import onnx
+from torch import nn
+from transformers.modeling_outputs import TokenClassifierOutput, Seq2SeqSequenceClassifierOutput
+
 from djl_converter.safetensors_convert import convert_file
 import torch
 from huggingface_hub import hf_hub_download, HfApi
@@ -40,6 +44,35 @@ class ModelHolder(object):
         self.config = config
 
 
+class ModelWrapper(nn.Module):
+
+    def __init__(self, model, include_types: bool) -> None:
+        super().__init__()
+        self.model = model
+        self.include_types = include_types
+
+    def forward(self,
+                input_ids: torch.Tensor,
+                attention_mask: torch.Tensor,
+                token_type_ids: torch.Tensor = None):
+        if self.include_types:
+            output = self.model(input_ids, attention_mask)
+        else:
+            output = self.model(input_ids, attention_mask, token_type_ids)
+        if isinstance(output, TokenClassifierOutput) or isinstance(
+                output, Seq2SeqSequenceClassifierOutput):
+            # TokenClassifierOutput/Seq2SeqSequenceClassifierOutput
+            # may contains mix of Tensor and Tuple(Tensor)
+            # filter out non-Tensor items
+            ret = OrderedDict()
+            for k, v in output.items():
+                if isinstance(v, torch.Tensor):
+                    ret[k] = v
+            return ret
+
+        return output
+
+
 class HuggingfaceConverter:
 
     def __init__(self):
@@ -102,7 +135,7 @@ class HuggingfaceConverter:
         hf_pipeline = PipelineHolder(tokenizer, ModelHolder(config))
         arguments = self.save_serving_properties(model_info, "OnnxRuntime",
                                                  temp_dir, hf_pipeline,
-                                                 include_types)
+                                                 include_types, args.cpu_only)
         if model_zoo:
             model_size = self.get_dir_size(temp_dir)
             if model_size > self.max_model_size:
@@ -136,7 +169,8 @@ class HuggingfaceConverter:
         if not os.path.exists(temp_dir):
             os.makedirs(temp_dir)
 
-        tokenizer = AutoTokenizer.from_pretrained(model_id)
+        tokenizer = AutoTokenizer.from_pretrained(
+            model_id, trust_remote_code=args.trust_remote_code)
         include_types = config.model_type not in [
             "distilbert", "mistral", "qwen2", "gemma2"
         ]
@@ -177,6 +211,8 @@ class HuggingfaceConverter:
             if sf_files:
                 for f in sf_files:
                     file = hf_hub_download(repo_id=model_id, filename=f)
+                    dest_file = os.path.join(temp_dir, f)
+                    os.makedirs(os.path.dirname(dest_file), exist_ok=True)
                     shutil.copyfile(file, os.path.join(temp_dir, f))
             elif pt_files:
                 for f in pt_files:
@@ -189,7 +225,8 @@ class HuggingfaceConverter:
                 return False, f"No model file found for: {model_id}", -1
 
         arguments = self.save_serving_properties(model_info, "Rust", temp_dir,
-                                                 hf_pipeline, include_types)
+                                                 hf_pipeline, include_types,
+                                                 args.cpu_only)
         if model_zoo:
             model_size = self.get_dir_size(temp_dir)
             if model_size > self.max_model_size:
@@ -208,7 +245,7 @@ class HuggingfaceConverter:
             os.makedirs(temp_dir)
 
         try:
-            hf_pipeline = self.load_model(model_id)
+            hf_pipeline = self.load_model(model_id, args.trust_remote_code)
         except Exception as e:
             logging.warning(f"Failed to load model: {model_id}.")
             logging.warning(e, exc_info=True)
@@ -240,7 +277,7 @@ class HuggingfaceConverter:
 
         arguments = self.save_serving_properties(model_info, "PyTorch",
                                                  temp_dir, hf_pipeline,
-                                                 include_types)
+                                                 include_types, args.cpu_only)
         if model_zoo:
             model_size = self.get_dir_size(temp_dir)
             if model_size > self.max_model_size:
@@ -277,13 +314,13 @@ class HuggingfaceConverter:
 
         # noinspection PyBroadException
         try:
+            wrapper = ModelWrapper(hf_pipeline.model, include_types)
             if include_types:
                 script_module = torch.jit.trace(
-                    hf_pipeline.model,
-                    (input_ids, attention_mask, token_type_ids),
+                    wrapper, (input_ids, attention_mask, token_type_ids),
                     strict=False)
             else:
-                script_module = torch.jit.trace(hf_pipeline.model,
+                script_module = torch.jit.trace(wrapper,
                                                 (input_ids, attention_mask),
                                                 strict=False)
 
@@ -299,7 +336,8 @@ class HuggingfaceConverter:
         return model_file
 
     def save_serving_properties(self, model_info, engine: str, temp_dir: str,
-                                hf_pipeline, include_types: bool) -> dict:
+                                hf_pipeline, include_types: bool,
+                                cpu_only: bool) -> dict:
         model_id = model_info.modelId
         model_name = model_id.split("/")[-1]
 
@@ -307,6 +345,8 @@ class HuggingfaceConverter:
         arguments = self.get_extra_arguments(hf_pipeline, model_id, temp_dir)
         if include_types:
             arguments["includeTokenTypes"] = "true"
+        if cpu_only:
+            arguments["load_on_devices"] = "-1"
         arguments["translatorFactory"] = self.translator
 
         with open(serving_file, 'w') as f:
@@ -410,11 +450,12 @@ class HuggingfaceConverter:
 
         return True, None
 
-    def load_model(self, model_id: str):
+    def load_model(self, model_id: str, trust_remote_code: bool):
         logging.info(f"Loading model: {model_id} ...")
         kwargs = {
             "tokenizer": model_id,
-            "device": -1  # always use CPU to trace the model
+            "device": -1,  # always use CPU to trace the model
+            "trust_remote_code": trust_remote_code
         }
         return pipeline(task=self.task,
                         model=model_id,
diff --git a/extensions/tokenizers/src/main/python/djl_converter/huggingface_models.py b/extensions/tokenizers/src/main/python/djl_converter/huggingface_models.py
index 59d65a958..2467cab6f 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/huggingface_models.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/huggingface_models.py
@@ -25,6 +25,7 @@ from djl_converter.question_answering_converter import QuestionAnsweringConverte
 from djl_converter.sentence_similarity_converter import SentenceSimilarityConverter
 from djl_converter.text_classification_converter import TextClassificationConverter
 from djl_converter.token_classification_converter import TokenClassificationConverter
+from djl_converter.zero_shot_classification_converter import ZeroShotClassificationConverter
 
 ARCHITECTURES_2_TASK = {
     "ForQuestionAnswering": "question-answering",
@@ -42,6 +43,7 @@ SUPPORTED_TASKS = {
     "sentence-similarity": SentenceSimilarityConverter(),
     "text-classification": TextClassificationConverter(),
     "token-classification": TokenClassificationConverter(),
+    "zero-shot-classification": ZeroShotClassificationConverter(),
 }
 
 
@@ -127,6 +129,9 @@ class HuggingfaceModels:
             if not task:
                 if "sentence-similarity" in model_info.tags:
                     task = "sentence-similarity"
+            else:
+                if "zero-shot-classification" in model_info.tags:
+                    task = "zero-shot-classification"
 
             if not task:
                 logging.info(
diff --git a/extensions/tokenizers/src/main/python/djl_converter/model_converter.py b/extensions/tokenizers/src/main/python/djl_converter/model_converter.py
index a9f7b6d90..11498509f 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/model_converter.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/model_converter.py
@@ -63,6 +63,10 @@ def main():
     if not task:
         if "sentence-similarity" in model_info.tags:
             task = "sentence-similarity"
+    else:
+        if "zero-shot-classification" in model_info.tags:
+            task = "zero-shot-classification"
+
     if not task:
         logging.error(
             f"Unsupported model architecture: {arch} for {args.model_id}.")
diff --git a/extensions/tokenizers/src/main/python/djl_converter/sentence_similarity_converter.py b/extensions/tokenizers/src/main/python/djl_converter/sentence_similarity_converter.py
index b0ac589d6..f46925aea 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/sentence_similarity_converter.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/sentence_similarity_converter.py
@@ -32,10 +32,13 @@ class SentenceSimilarityConverter(HuggingfaceConverter):
         self.inputs = "This is an example sentence"
         self.outputs = 0
 
-    def load_model(self, model_id: str):
+    def load_model(self, model_id: str, trust_remote_code: bool):
         logging.info(f"Loading model: {model_id} ...")
-        tokenizer = AutoTokenizer.from_pretrained(model_id)
-        model = AutoModel.from_pretrained(model_id)
+        tokenizer = AutoTokenizer.from_pretrained(
+            model_id, trust_remote_code=trust_remote_code)
+
+        model = AutoModel.from_pretrained(model_id,
+                                          trust_remote_code=trust_remote_code)
 
         return PipelineHolder(tokenizer, model)
 
@@ -78,7 +81,8 @@ class SentenceSimilarityConverter(HuggingfaceConverter):
             if hasattr(hf_pipeline.model, "config"):
                 config = hf_pipeline.model.config
             else:
-                config = AutoConfig.from_pretrained(model_id)
+                config = AutoConfig.from_pretrained(
+                    model_id, trust_remote_code=trust_remote_code)
             tokenizer = hf_pipeline.tokenizer
             if hasattr(config, "max_position_embeddings") and hasattr(
                     tokenizer, "model_max_length"):
diff --git a/extensions/tokenizers/src/main/python/djl_converter/token_classification_converter.py b/extensions/tokenizers/src/main/python/djl_converter/token_classification_converter.py
index 33f910d3c..624ef665f 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/token_classification_converter.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/token_classification_converter.py
@@ -61,10 +61,14 @@ class TokenClassificationConverter(HuggingfaceConverter):
                         or self.outputs[2] in entity):
                     return True, None
 
+        pipeline_output = hf_pipeline(self.inputs)
+        if len(pipeline_output) == 0:
+            logging.warning(f"Warning: pipeline output is empty")
+            return True, None
+
         if len(entities) == 0:
             return False, "TokenClassification returns with empty result"
 
-        pipeline_output = hf_pipeline(self.inputs)
         for e in pipeline_output:
             if e["word"] == entities[0]["word"]:
                 if e["entity"] == entities[0]["entity"]:
diff --git a/extensions/tokenizers/src/main/python/djl_converter/zero_shot_classification_converter.py b/extensions/tokenizers/src/main/python/djl_converter/zero_shot_classification_converter.py
new file mode 100644
index 000000000..63b4b28c6
--- /dev/null
+++ b/extensions/tokenizers/src/main/python/djl_converter/zero_shot_classification_converter.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+#
+# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
+# except in compliance with the License. A copy of the License is located at
+#
+# http://aws.amazon.com/apache2.0/
+#
+# or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS"
+# BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for
+# the specific language governing permissions and limitations under the License.
+import math
+
+from djl_converter.huggingface_converter import HuggingfaceConverter
+
+
+class ZeroShotClassificationConverter(HuggingfaceConverter):
+
+    def __init__(self):
+        super().__init__()
+        self.task = "zero-shot-classification"
+        self.application = "nlp/zero_shot_classification"
+        self.translator = "ai.djl.huggingface.translator.ZeroShotClassificationTranslatorFactory"
+        self.inputs = "one day I will see the world"
+        self.labels = ['travel']
+
+    def encode_inputs(self, tokenizer):
+        return tokenizer(self.inputs,
+                         f"This example is {self.labels[0]}.",
+                         return_tensors='pt')
+
+    def verify_jit_output(self, hf_pipeline, encoding, out):
+        logits = out['logits']
+        entail_contradiction_logits = logits[:, [0, 2]]
+        probs = entail_contradiction_logits.softmax(dim=1)
+        score = probs[:, 1].item()
+
+        pipeline_output = hf_pipeline(self.inputs, self.labels)
+        expected = pipeline_output["scores"][0]
+
+        if math.isclose(expected, score, abs_tol=1e-3):
+            return True, None
+
+        return False, f"Unexpected inference result"
+
+    def get_extra_arguments(self, hf_pipeline, model_id: str,
+                            temp_dir: str) -> dict:
+        return {
+            "padding": "true",
+            "truncation": "only_first",
+        }
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 9a0221074..ddd35ca4e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -14,7 +14,7 @@ lightgbm = "3.2.110"
 tensorrt = "8.4.1"
 fasttext = "0.9.2"
 sentencepiece = "0.2.0"
-tokenizers = "0.20.3"
+tokenizers = "0.21.0"
 
 slf4j = "2.0.16"
 log4j_slf4j = "2.24.1"


Test patch:
diff --git a/api/src/test/java/ai/djl/translate/BatchifierTest.java b/api/src/test/java/ai/djl/translate/BatchifierTest.java
index c7ef460e9..92afd9ea4 100644
--- a/api/src/test/java/ai/djl/translate/BatchifierTest.java
+++ b/api/src/test/java/ai/djl/translate/BatchifierTest.java
@@ -22,6 +22,17 @@ public class BatchifierTest {
         Batchifier batchifier = Batchifier.fromString("stack");
         Assert.assertEquals(batchifier, Batchifier.STACK);
 
+        batchifier = Batchifier.fromString("none");
+        Assert.assertNull(batchifier);
+
+        batchifier = Batchifier.fromString("padding");
+        Assert.assertNotNull(batchifier);
+        Assert.assertEquals(batchifier.getClass(), SimplePaddingStackBatchifier.class);
+
+        batchifier = Batchifier.fromString("ai.djl.translate.SimplePaddingStackBatchifier");
+        Assert.assertNotNull(batchifier);
+        Assert.assertEquals(batchifier.getClass(), SimplePaddingStackBatchifier.class);
+
         Assert.assertThrows(() -> Batchifier.fromString("invalid"));
     }
 }
diff --git a/basicdataset/src/test/java/ai/djl/basicdataset/MnistTest.java b/basicdataset/src/test/java/ai/djl/basicdataset/MnistTest.java
index 8d0e609cc..792ff77b7 100644
--- a/basicdataset/src/test/java/ai/djl/basicdataset/MnistTest.java
+++ b/basicdataset/src/test/java/ai/djl/basicdataset/MnistTest.java
@@ -157,7 +157,7 @@ public class MnistTest {
                             .optUsage(Dataset.Usage.TEST)
                             .optRepository(repository)
                             .setSampling(32, false)
-                            .optLabelBatchifier(new StackBatchifier() {})
+                            .optLabelBatchifier(new StackBatchifier())
                             .build();
 
             try (Trainer trainer = model.newTrainer(config)) {
diff --git a/examples/src/test/java/ai/djl/examples/inference/nlp/BertQaTest.java b/examples/src/test/java/ai/djl/examples/inference/nlp/BertQaTest.java
index 21a70ff67..1a464d8f7 100644
--- a/examples/src/test/java/ai/djl/examples/inference/nlp/BertQaTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/nlp/BertQaTest.java
@@ -15,6 +15,9 @@ package ai.djl.examples.inference.nlp;
 import ai.djl.ModelException;
 import ai.djl.testing.TestRequirements;
 import ai.djl.translate.TranslateException;
+import ai.djl.util.JsonUtils;
+
+import com.google.gson.JsonObject;
 
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -28,6 +31,8 @@ public class BertQaTest {
         TestRequirements.linux();
 
         String result = BertQaInference.predict();
-        Assert.assertEquals(result, "december 2004");
+        JsonObject json = JsonUtils.GSON.fromJson(result, JsonObject.class);
+        String answer = json.get("answer").getAsString();
+        Assert.assertEquals(answer, "december 2004");
     }
 }
diff --git a/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizerTest.java b/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizerTest.java
index 4e11fee74..d44e426f7 100644
--- a/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizerTest.java
+++ b/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizerTest.java
@@ -39,7 +39,7 @@ public class HuggingFaceTokenizerTest {
     public void testVersion() {
         try (HuggingFaceTokenizer tokenizer = HuggingFaceTokenizer.newInstance("bert-base-cased")) {
             String djlVersion = Engine.getDjlVersion();
-            Assert.assertEquals(tokenizer.getVersion(), "0.20.3-" + djlVersion);
+            Assert.assertEquals(tokenizer.getVersion(), "0.21.0-" + djlVersion);
         }
     }
 
@@ -67,6 +67,7 @@ public class HuggingFaceTokenizerTest {
             long[] ids = {101, 8667, 117, 194, 112, 1155, 106, 1731, 1132, 1128, 100, 136, 102};
             long[] typeIds = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
             long[] wordIds = {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1};
+            long[] sequenceIds = {-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1};
             long[] attentionMask = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
             long[] specialTokenMask = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
 
@@ -74,6 +75,7 @@ public class HuggingFaceTokenizerTest {
             Assert.assertEquals(ids, encoding.getIds());
             Assert.assertEquals(typeIds, encoding.getTypeIds());
             Assert.assertEquals(wordIds, encoding.getWordIds());
+            Assert.assertEquals(sequenceIds, encoding.getSequenceIds());
             Assert.assertEquals(attentionMask, encoding.getAttentionMask());
             Assert.assertEquals(specialTokenMask, encoding.getSpecialTokenMask());
 
@@ -104,6 +106,10 @@ public class HuggingFaceTokenizerTest {
                 Assert.assertEquals(charSpansExpected[i].getEnd(), charSpansResult[i].getEnd());
             }
 
+            encoding = tokenizer.encode(inputs[0], inputs[1]);
+            sequenceIds = new long[] {-1, 0, 0, 0, 0, 0, 0, -1, 1, 1, 1, 1, 1, -1};
+            Assert.assertEquals(encoding.getSequenceIds(), sequenceIds);
+
             Assert.assertThrows(() -> tokenizer.encode((String) null));
             Assert.assertThrows(() -> tokenizer.encode(new String[] {null}));
             Assert.assertThrows(() -> tokenizer.encode(null, null));
diff --git a/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/ZeroShotClassificationTranslatorTest.java b/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/ZeroShotClassificationTranslatorTest.java
new file mode 100644
index 000000000..655733012
--- /dev/null
+++ b/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/ZeroShotClassificationTranslatorTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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 ai.djl.huggingface.tokenizers;
+
+import ai.djl.Model;
+import ai.djl.ModelException;
+import ai.djl.huggingface.translator.ZeroShotClassificationTranslatorFactory;
+import ai.djl.inference.Predictor;
+import ai.djl.modality.Input;
+import ai.djl.modality.Output;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationInput;
+import ai.djl.modality.nlp.translator.ZeroShotClassificationOutput;
+import ai.djl.ndarray.NDArray;
+import ai.djl.ndarray.NDList;
+import ai.djl.ndarray.NDManager;
+import ai.djl.nn.Block;
+import ai.djl.nn.LambdaBlock;
+import ai.djl.repository.zoo.Criteria;
+import ai.djl.repository.zoo.ZooModel;
+import ai.djl.testing.Assertions;
+import ai.djl.translate.TranslateException;
+import ai.djl.util.JsonUtils;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ZeroShotClassificationTranslatorTest {
+
+    private static final float[][][] ARRAYS = {
+        {{-2.6497f, 0.8422f, 2.0454f}},
+        {{2.9208f, 0.1431f, -3.3346f}},
+        {{1.6723f, 1.0156f, -3.1728f}},
+        {{-1.795f, 0.983f, 0.9644f}}
+    };
+
+    private static int index;
+
+    @Test
+    public void testZeroShotClassificationTranslator()
+            throws ModelException, IOException, TranslateException {
+        String text = "one day I will see the world";
+        String[] candidates = {"travel", "cooking", "dancing", "exploration"};
+
+        Block block =
+                new LambdaBlock(
+                        a -> {
+                            NDManager manager = a.getManager();
+                            NDArray arr = manager.create(ARRAYS[index++ % 4]);
+                            return new NDList(arr);
+                        },
+                        "model");
+
+        Path modelDir = Paths.get("build/model");
+        Files.createDirectories(modelDir);
+
+        Criteria<ZeroShotClassificationInput, ZeroShotClassificationOutput> criteria =
+                Criteria.builder()
+                        .setTypes(
+                                ZeroShotClassificationInput.class,
+                                ZeroShotClassificationOutput.class)
+                        .optModelPath(modelDir)
+                        .optBlock(block)
+                        .optEngine("PyTorch")
+                        .optArgument("tokenizer", "bert-base-uncased")
+                        .optOption("hasParameter", "false")
+                        .optTranslatorFactory(new ZeroShotClassificationTranslatorFactory())
+                        .build();
+
+        try (ZooModel<ZeroShotClassificationInput, ZeroShotClassificationOutput> model =
+                        criteria.loadModel();
+                Predictor<ZeroShotClassificationInput, ZeroShotClassificationOutput> predictor =
+                        model.newPredictor()) {
+            ZeroShotClassificationInput input = new ZeroShotClassificationInput(text, candidates);
+            ZeroShotClassificationOutput res = predictor.predict(input);
+            Assertions.assertAlmostEquals(res.getScores()[1], 0.940441);
+            Assert.assertEquals(res.getLabels()[1], "exploration");
+        }
+
+        Criteria<Input, Output> criteria2 =
+                Criteria.builder()
+                        .setTypes(Input.class, Output.class)
+                        .optModelPath(modelDir)
+                        .optBlock(block)
+                        .optEngine("PyTorch")
+                        .optArgument("tokenizer", "bert-base-uncased")
+                        .optOption("hasParameter", "false")
+                        .optTranslatorFactory(new ZeroShotClassificationTranslatorFactory())
+                        .build();
+
+        try (ZooModel<Input, Output> model = criteria2.loadModel();
+                Predictor<Input, Output> predictor = model.newPredictor()) {
+            Input input = new Input();
+            ZeroShotClassificationInput zsc =
+                    new ZeroShotClassificationInput(text, candidates, true);
+            input.add(JsonUtils.toJson(zsc));
+            Output out = predictor.predict(input);
+            ZeroShotClassificationOutput res =
+                    (ZeroShotClassificationOutput) out.getData().getAsObject();
+            Assertions.assertAlmostEquals(res.getScores()[1], 0.251434);
+            Assert.assertEquals(res.getLabels()[0], "travel");
+        }
+
+        try (Model model = Model.newInstance("test")) {
+            model.setBlock(block);
+            Map<String, String> options = new HashMap<>();
+            options.put("hasParameter", "false");
+            model.load(modelDir, "test", options);
+
+            ZeroShotClassificationTranslatorFactory factory =
+                    new ZeroShotClassificationTranslatorFactory();
+            Map<String, String> arguments = new HashMap<>();
+
+            Assert.assertThrows(
+                    TranslateException.class,
+                    () -> factory.newInstance(String.class, Integer.class, model, arguments));
+
+            arguments.put("tokenizer", "bert-base-uncased");
+
+            Assert.assertThrows(
+                    IllegalArgumentException.class,
+                    () -> factory.newInstance(String.class, Integer.class, model, arguments));
+        }
+    }
+}