Repository: deepjavalibrary/djl
Pull request #3403 (2024-08-11 01:53:17)

[api] Adds Criteria.isDownload() api
Fixes: #3397

## Description ##

Brief description of what this PR is about

- If this change is a backward incompatible change, why must this change be made?
- Interesting edge cases to note here



Patch:
diff --git a/README.md b/README.md
index 3ed6c465f..4fcef8132 100644
--- a/README.md
+++ b/README.md
@@ -151,7 +151,7 @@ Follow our [<img src='https://cdn2.iconfinder.com/data/icons/social-media-2285/5
 
 * [DJL Website](https://djl.ai/)
 * [Documentation](https://docs.djl.ai/)
-* [DJL Demos](https://docs.djl.ai/docs/demos/index.html)
+* [DJL Demos](https://docs.djl.ai/master/docs/demos/index.html)
 * [Dive into Deep Learning Book Java version](https://d2l.djl.ai/)
 
 ## License
diff --git a/api/build.gradle.kts b/api/build.gradle.kts
index cbac880e6..025f546c9 100644
--- a/api/build.gradle.kts
+++ b/api/build.gradle.kts
@@ -40,7 +40,7 @@ tasks {
                 "Notice" to "DJL will collect telemetry to help us better understand our users'" +
                         " needs, diagnose issues, and deliver additional features. If you would" +
                         " like to learn more or opt-out please go to: " +
-                        "https://docs.djl.ai/docs/telemetry.html for more information."
+                        "https://docs.djl.ai/master/docs/telemetry.html for more information."
             )
         }
     }
diff --git a/api/src/main/java/ai/djl/engine/Engine.java b/api/src/main/java/ai/djl/engine/Engine.java
index 8a1fc8871..bf10ef49f 100644
--- a/api/src/main/java/ai/djl/engine/Engine.java
+++ b/api/src/main/java/ai/djl/engine/Engine.java
@@ -48,10 +48,10 @@ import java.util.regex.Pattern;
  * contain methods to detect information about the usable machine hardware and to create a new
  * {@link NDManager} and {@link Model}.
  *
- * @see <a href="http://docs.djl.ai/docs/engine.html">Engine Guide</a>
+ * @see <a href="https://docs.djl.ai/master/docs/engine.html">Engine Guide</a>
  * @see EngineProvider
- * @see <a href="http://docs.djl.ai/docs/development/cache_management.html">The guide on resource
- *     and engine caching</a>
+ * @see <a href="https://docs.djl.ai/master/docs/development/cache_management.html">The guide on
+ *     resource and engine caching</a>
  */
 public abstract class Engine {
 
diff --git a/api/src/main/java/ai/djl/inference/Predictor.java b/api/src/main/java/ai/djl/inference/Predictor.java
index b8f87a4dd..5f6edb424 100644
--- a/api/src/main/java/ai/djl/inference/Predictor.java
+++ b/api/src/main/java/ai/djl/inference/Predictor.java
@@ -61,25 +61,26 @@ import java.util.concurrent.ConcurrentHashMap;
  *
  * <ul>
  *   <li><a
- *       href="http://docs.djl.ai/docs/demos/jupyter/tutorial/03_image_classification_with_your_model.html">Inference
+ *       href="https://docs.djl.ai/master/docs/demos/jupyter/tutorial/03_image_classification_with_your_model.html">Inference
  *       with a custom trained model</a>
  *   <li><a
- *       href="http://docs.djl.ai/docs/demos/jupyter/object_detection_with_model_zoo.html">Inference
+ *       href="https://docs.djl.ai/master/docs/demos/jupyter/object_detection_with_model_zoo.html">Inference
  *       with a model zoo model</a>
- *   <li><a href="http://docs.djl.ai/docs/demos/jupyter/load_mxnet_model.html">Inference with an
- *       MXNet model</a>
+ *   <li><a href="https://docs.djl.ai/master/docs/demos/jupyter/load_mxnet_model.html">Inference
+ *       with an MXNet model</a>
  * </ul>
  *
  * @param <I> the input type
  * @param <O> the output type
  * @see Model
  * @see Translator
- * @see <a href="http://docs.djl.ai/docs/development/memory_management.html">The guide on memory
- *     management</a>
+ * @see <a href="https://docs.djl.ai/master/docs/development/memory_management.html">The guide on
+ *     memory management</a>
  * @see <a
  *     href="https://github.com/deepjavalibrary/djl/blob/master/examples/docs/multithread_inference.md">The
  *     guide on running multi-threaded inference</a>
- * @see <a href="http://docs.djl.ai/docs/development/inference_performance_optimization.html">The
+ * @see <a
+ *     href="https://docs.djl.ai/master/docs/development/inference_performance_optimization.html">The
  *     guide on inference performance optimization</a>
  */
 public class Predictor<I, O> implements AutoCloseable {
diff --git a/api/src/main/java/ai/djl/nn/Block.java b/api/src/main/java/ai/djl/nn/Block.java
index 7ace6880c..56c92a23e 100644
--- a/api/src/main/java/ai/djl/nn/Block.java
+++ b/api/src/main/java/ai/djl/nn/Block.java
@@ -105,7 +105,7 @@ import java.util.function.Predicate;
  * further refine these elements, use {@link Block#freezeParameters(boolean)} to unfreeze them.
  *
  * @see <a
- *     href="http://docs.djl.ai/docs/demos/jupyter/tutorial/01_create_your_first_network.html">this
+ *     href="https://docs.djl.ai/master/docs/demos/jupyter/tutorial/01_create_your_first_network.html">this
  *     tutorial on creating your first network</a>
  * @see <a href="https://d2l.djl.ai/chapter_deep-learning-computation/model-construction.html">The
  *     D2L chapter on blocks</a> and <a
diff --git a/api/src/main/java/ai/djl/repository/MRL.java b/api/src/main/java/ai/djl/repository/MRL.java
index ad5ac005c..2afec188b 100644
--- a/api/src/main/java/ai/djl/repository/MRL.java
+++ b/api/src/main/java/ai/djl/repository/MRL.java
@@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.net.URI;
+import java.nio.file.Files;
 import java.util.List;
 import java.util.Map;
 
@@ -245,6 +246,17 @@ public final class MRL {
         return getMetadata().getArtifacts();
     }
 
+    /**
+     * Returns {@code true} if the artifact is ready for use.
+     *
+     * @param artifact the artifact to prepare
+     * @return {@code true} if the artifact is ready for use
+     * @throws IOException if it failed to prepare
+     */
+    public boolean isPrepared(Artifact artifact) throws IOException {
+        return Files.exists(repository.getResourceDirectory(artifact));
+    }
+
     /**
      * Prepares the artifact for use.
      *
diff --git a/api/src/main/java/ai/djl/repository/zoo/BaseModelLoader.java b/api/src/main/java/ai/djl/repository/zoo/BaseModelLoader.java
index f2bdf29a7..8411a5412 100644
--- a/api/src/main/java/ai/djl/repository/zoo/BaseModelLoader.java
+++ b/api/src/main/java/ai/djl/repository/zoo/BaseModelLoader.java
@@ -61,6 +61,12 @@ public class BaseModelLoader implements ModelLoader {
         defaultFactory = new DefaultTranslatorFactory();
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public String getGroupId() {
+        return mrl.getGroupId();
+    }
+
     /** {@inheritDoc} */
     @Override
     public String getArtifactId() {
@@ -175,6 +181,28 @@ public class BaseModelLoader implements ModelLoader {
         }
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public <I, O> boolean isDownloaded(Criteria<I, O> criteria)
+            throws IOException, ModelNotFoundException {
+        Artifact artifact = mrl.match(criteria.getFilters());
+        if (artifact == null) {
+            throw new ModelNotFoundException("No matching filter found");
+        }
+        return mrl.isPrepared(artifact);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public <I, O> void downloadModel(Criteria<I, O> criteria, Progress progress)
+            throws IOException, ModelNotFoundException {
+        Artifact artifact = mrl.match(criteria.getFilters());
+        if (artifact == null) {
+            throw new ModelNotFoundException("No matching filter found");
+        }
+        mrl.prepare(artifact, progress);
+    }
+
     /** {@inheritDoc} */
     @Override
     public List<Artifact> listModels() throws IOException {
diff --git a/api/src/main/java/ai/djl/repository/zoo/Criteria.java b/api/src/main/java/ai/djl/repository/zoo/Criteria.java
index e97a92fff..5b6ba47ba 100644
--- a/api/src/main/java/ai/djl/repository/zoo/Criteria.java
+++ b/api/src/main/java/ai/djl/repository/zoo/Criteria.java
@@ -53,8 +53,8 @@ import java.util.Set;
  *         .build();
  * </pre>
  *
- * <p>See <a href="http://docs.djl.ai/docs/load_model.html#criteria-class">Model loading</a> for
- * more detail.
+ * <p>See <a href="https://docs.djl.ai/master/docs/load_model.html#criteria-class">Model loading</a>
+ * for more detail.
  *
  * @param <I> the model input type
  * @param <O> the model output type
@@ -76,6 +76,7 @@ public class Criteria<I, O> {
     private Block block;
     private String modelName;
     private Progress progress;
+    private List<ModelLoader> resolvedLoaders;
 
     Criteria(Builder<I, O> builder) {
         this.application = builder.application;
@@ -96,7 +97,41 @@ public class Criteria<I, O> {
     }
 
     /**
-     * Load the {@link ZooModel} that matches this criteria.
+     * Returns {@code true} of the model artifacts has been downloaded.
+     *
+     * @return {@code true} of the model artifacts has been downloaded
+     * @throws IOException for various exceptions loading data from the repository
+     * @throws ModelNotFoundException if no model with the specified criteria is found
+     */
+    public boolean isDownloaded() throws IOException, ModelNotFoundException {
+        if (resolvedLoaders == null) {
+            resolvedLoaders = resolveModelLoaders();
+        }
+
+        for (ModelLoader loader : resolvedLoaders) {
+            if (!loader.isDownloaded(this)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Downloads the model artifacts that matches this criteria.
+     *
+     * @throws IOException for various exceptions loading data from the repository
+     * @throws ModelNotFoundException if no model with the specified criteria is found
+     */
+    public void downloadModel() throws ModelNotFoundException, IOException {
+        if (!isDownloaded()) {
+            for (ModelLoader loader : resolvedLoaders) {
+                loader.downloadModel(this, progress);
+            }
+        }
+    }
+
+    /**
+     * Loads the {@link ZooModel} that matches this criteria.
      *
      * @return the model that matches the criteria
      * @throws IOException for various exceptions loading data from the repository
@@ -105,82 +140,23 @@ public class Criteria<I, O> {
      */
     public ZooModel<I, O> loadModel()
             throws IOException, ModelNotFoundException, MalformedModelException {
-        if (inputClass == null || outputClass == null) {
-            throw new IllegalArgumentException("inputClass and outputClass are required.");
+        if (resolvedLoaders == null) {
+            resolvedLoaders = resolveModelLoaders();
         }
 
         Logger logger = LoggerFactory.getLogger(ModelZoo.class);
-        logger.debug("Loading model with {}", this);
-
-        List<ModelZoo> list = new ArrayList<>();
-        if (modelZoo != null) {
-            logger.debug("Searching model in specified model zoo: {}", modelZoo.getGroupId());
-            if (groupId != null && !modelZoo.getGroupId().equals(groupId)) {
-                throw new ModelNotFoundException(
-                        "groupId conflict with ModelZoo criteria."
-                                + modelZoo.getGroupId()
-                                + " v.s. "
-                                + groupId);
-            }
-            Set<String> supportedEngine = modelZoo.getSupportedEngines();
-            if (engine != null && !supportedEngine.contains(engine)) {
-                throw new ModelNotFoundException(
-                        "ModelZoo doesn't support specified engine: " + engine);
-            }
-            list.add(modelZoo);
-        } else {
-            for (ModelZoo zoo : ModelZoo.listModelZoo()) {
-                if (groupId != null && !zoo.getGroupId().equals(groupId)) {
-                    // filter out ModelZoo by groupId
-                    logger.debug("Ignore ModelZoo {} by groupId: {}", zoo.getGroupId(), groupId);
-                    continue;
-                }
-                Set<String> supportedEngine = zoo.getSupportedEngines();
-                if (engine != null && !supportedEngine.contains(engine)) {
-                    logger.debug("Ignore ModelZoo {} by engine: {}", zoo.getGroupId(), engine);
-                    continue;
-                }
-                list.add(zoo);
-            }
-        }
-
         Exception lastException = null;
-        for (ModelZoo zoo : list) {
-            String loaderGroupId = zoo.getGroupId();
-            for (ModelLoader loader : zoo.getModelLoaders()) {
-                Application app = loader.getApplication();
-                String loaderArtifactId = loader.getArtifactId();
-                logger.debug("Checking ModelLoader: {}", loader);
-                if (artifactId != null && !artifactId.equals(loaderArtifactId)) {
-                    // filter out by model loader artifactId
-                    logger.debug(
-                            "artifactId mismatch for ModelLoader: {}:{}",
-                            loaderGroupId,
-                            loaderArtifactId);
-                    continue;
-                }
-                if (application != Application.UNDEFINED
-                        && app != Application.UNDEFINED
-                        && !app.matches(application)) {
-                    // filter out ModelLoader by application
-                    logger.debug(
-                            "application mismatch for ModelLoader: {}:{}",
-                            loaderGroupId,
-                            loaderArtifactId);
-                    continue;
-                }
-
-                try {
-                    return loader.loadModel(this);
-                } catch (ModelNotFoundException e) {
-                    lastException = e;
-                    logger.trace("", e);
-                    logger.debug(
-                            "{} for ModelLoader: {}:{}",
-                            e.getMessage(),
-                            loaderGroupId,
-                            loaderArtifactId);
-                }
+        for (ModelLoader loader : resolvedLoaders) {
+            try {
+                return loader.loadModel(this);
+            } catch (ModelNotFoundException e) {
+                lastException = e;
+                logger.trace("", e);
+                logger.debug(
+                        "{} for ModelLoader: {}:{}",
+                        e.getMessage(),
+                        loader.getGroupId(),
+                        loader.getArtifactId());
             }
         }
         throw new ModelNotFoundException(
@@ -396,6 +372,82 @@ public class Criteria<I, O> {
         return new Builder<>();
     }
 
+    private List<ModelLoader> resolveModelLoaders() throws ModelNotFoundException {
+        if (inputClass == null || outputClass == null) {
+            throw new IllegalArgumentException("inputClass and outputClass are required.");
+        }
+
+        Logger logger = LoggerFactory.getLogger(ModelZoo.class);
+        logger.debug("Loading model with {}", this);
+
+        List<ModelZoo> list = new ArrayList<>();
+        if (modelZoo != null) {
+            logger.debug("Searching model in specified model zoo: {}", modelZoo.getGroupId());
+            if (groupId != null && !modelZoo.getGroupId().equals(groupId)) {
+                throw new ModelNotFoundException(
+                        "groupId conflict with ModelZoo criteria."
+                                + modelZoo.getGroupId()
+                                + " v.s. "
+                                + groupId);
+            }
+            Set<String> supportedEngine = modelZoo.getSupportedEngines();
+            if (engine != null && !supportedEngine.contains(engine)) {
+                throw new ModelNotFoundException(
+                        "ModelZoo doesn't support specified engine: " + engine);
+            }
+            list.add(modelZoo);
+        } else {
+            for (ModelZoo zoo : ModelZoo.listModelZoo()) {
+                if (groupId != null && !zoo.getGroupId().equals(groupId)) {
+                    // filter out ModelZoo by groupId
+                    logger.debug("Ignore ModelZoo {} by groupId: {}", zoo.getGroupId(), groupId);
+                    continue;
+                }
+                Set<String> supportedEngine = zoo.getSupportedEngines();
+                if (engine != null && !supportedEngine.contains(engine)) {
+                    logger.debug("Ignore ModelZoo {} by engine: {}", zoo.getGroupId(), engine);
+                    continue;
+                }
+                list.add(zoo);
+            }
+        }
+
+        List<ModelLoader> loaders = new ArrayList<>();
+        for (ModelZoo zoo : list) {
+            String loaderGroupId = zoo.getGroupId();
+            for (ModelLoader loader : zoo.getModelLoaders()) {
+                Application app = loader.getApplication();
+                String loaderArtifactId = loader.getArtifactId();
+                logger.debug("Checking ModelLoader: {}", loader);
+                if (artifactId != null && !artifactId.equals(loaderArtifactId)) {
+                    // filter out by model loader artifactId
+                    logger.debug(
+                            "artifactId mismatch for ModelLoader: {}:{}",
+                            loaderGroupId,
+                            loaderArtifactId);
+                    continue;
+                }
+                if (application != Application.UNDEFINED
+                        && app != Application.UNDEFINED
+                        && !app.matches(application)) {
+                    // filter out ModelLoader by application
+                    logger.debug(
+                            "application mismatch for ModelLoader: {}:{}",
+                            loaderGroupId,
+                            loaderArtifactId);
+                    continue;
+                }
+
+                loaders.add(loader);
+            }
+        }
+
+        if (loaders.isEmpty()) {
+            throw new ModelNotFoundException("No model matching the criteria is found.");
+        }
+        return loaders;
+    }
+
     /** A Builder to construct a {@code Criteria}. */
     public static final class Builder<I, O> {
 
diff --git a/api/src/main/java/ai/djl/repository/zoo/ModelLoader.java b/api/src/main/java/ai/djl/repository/zoo/ModelLoader.java
index 7625d46d7..1a59f59b4 100644
--- a/api/src/main/java/ai/djl/repository/zoo/ModelLoader.java
+++ b/api/src/main/java/ai/djl/repository/zoo/ModelLoader.java
@@ -15,6 +15,7 @@ package ai.djl.repository.zoo;
 import ai.djl.Application;
 import ai.djl.MalformedModelException;
 import ai.djl.repository.Artifact;
+import ai.djl.util.Progress;
 
 import java.io.IOException;
 import java.util.List;
@@ -22,6 +23,13 @@ import java.util.List;
 /** A ModelLoader loads a particular {@link ZooModel} from a Repository for a model zoo. */
 public interface ModelLoader {
 
+    /**
+     * Returns the group ID of the {@code ModelLoader}.
+     *
+     * @return the group ID of the {@code ModelLoader}
+     */
+    String getGroupId();
+
     /**
      * Returns the artifact ID of the {@code ModelLoader}.
      *
@@ -50,6 +58,31 @@ public interface ModelLoader {
     <I, O> ZooModel<I, O> loadModel(Criteria<I, O> criteria)
             throws IOException, ModelNotFoundException, MalformedModelException;
 
+    /**
+     * Returns {@code true} if the model is downloaded in local directory.
+     *
+     * @param <I> the input data type
+     * @param <O> the output data type
+     * @param criteria the criteria to match against the loaded model
+     * @return {@code true} if the model is downloaded in local directory
+     * @throws IOException for various exceptions loading data from the repository
+     * @throws ModelNotFoundException if no model with the specified criteria is found
+     */
+    <I, O> boolean isDownloaded(Criteria<I, O> criteria) throws IOException, ModelNotFoundException;
+
+    /**
+     * Downloads the model artifacts to local directory.
+     *
+     * @param <I> the input data type
+     * @param <O> the output data type
+     * @param criteria the criteria to match against the loaded model
+     * @param progress the progress tracker
+     * @throws IOException for various exceptions loading data from the repository
+     * @throws ModelNotFoundException if no model with the specified criteria is found
+     */
+    <I, O> void downloadModel(Criteria<I, O> criteria, Progress progress)
+            throws IOException, ModelNotFoundException;
+
     /**
      * Returns a list of the available artifacts that can be loaded.
      *
diff --git a/api/src/main/java/ai/djl/training/Trainer.java b/api/src/main/java/ai/djl/training/Trainer.java
index 6d79dde3e..8122511e4 100644
--- a/api/src/main/java/ai/djl/training/Trainer.java
+++ b/api/src/main/java/ai/djl/training/Trainer.java
@@ -52,16 +52,17 @@ import java.util.function.Consumer;
  *
  * <ul>
  *   <li><a
- *       href="http://docs.djl.ai/docs/demos/jupyter/tutorial/02_train_your_first_model.html">Training
+ *       href="https://docs.djl.ai/master/docs/demos/jupyter/tutorial/02_train_your_first_model.html">Training
  *       your first model</a>
- *   <li><a href="http://docs.djl.ai/docs/demos/jupyter/transfer_learning_on_cifar10.html">Training
+ *   <li><a
+ *       href="https://docs.djl.ai/master/docs/demos/jupyter/transfer_learning_on_cifar10.html">Training
  *       using transfer learning</a>
- *   <li><a href="http://docs.djl.ai/docs/demos/jupyter/load_mxnet_model.html">Inference with an
- *       MXNet model</a>
+ *   <li><a href="https://docs.djl.ai/master/docs/demos/jupyter/load_mxnet_model.html">Inference
+ *       with an MXNet model</a>
  * </ul>
  *
- * @see <a href="http://docs.djl.ai/docs/development/memory_management.html">The guide on memory
- *     management</a>
+ * @see <a href="https://docs.djl.ai/master/docs/development/memory_management.html">The guide on
+ *     memory management</a>
  */
 public class Trainer implements AutoCloseable {
 
diff --git a/api/src/main/java/ai/djl/training/dataset/Dataset.java b/api/src/main/java/ai/djl/training/dataset/Dataset.java
index 1d903d587..451d4dff6 100644
--- a/api/src/main/java/ai/djl/training/dataset/Dataset.java
+++ b/api/src/main/java/ai/djl/training/dataset/Dataset.java
@@ -23,8 +23,8 @@ import java.util.concurrent.ExecutorService;
 /**
  * An interface to represent a set of sample data/label pairs to train a model.
  *
- * @see <a href="http://docs.djl.ai/docs/dataset.html">The guide to datasets</a>
- * @see <a href="http://docs.djl.ai/docs/development/how_to_use_dataset.html">The guide to
+ * @see <a href="https://docs.djl.ai/master/docs/dataset.html">The guide to datasets</a>
+ * @see <a href="https://docs.djl.ai/master/docs/development/how_to_use_dataset.html">The guide to
  *     implementing a custom dataset</a>
  */
 public interface Dataset {
diff --git a/api/src/main/java/ai/djl/training/dataset/RandomAccessDataset.java b/api/src/main/java/ai/djl/training/dataset/RandomAccessDataset.java
index 3698e9609..41e4b1a1c 100644
--- a/api/src/main/java/ai/djl/training/dataset/RandomAccessDataset.java
+++ b/api/src/main/java/ai/djl/training/dataset/RandomAccessDataset.java
@@ -42,7 +42,7 @@ import java.util.stream.IntStream;
  *
  * <p>Almost all datasets in DJL extend, either directly or indirectly, {@link RandomAccessDataset}.
  *
- * @see <a href="http://docs.djl.ai/docs/development/how_to_use_dataset.html">The guide to
+ * @see <a href="https://docs.djl.ai/master/docs/development/how_to_use_dataset.html">The guide to
  *     implementing a custom dataset</a>
  */
 public abstract class RandomAccessDataset implements Dataset {
diff --git a/api/src/main/java/ai/djl/training/listener/LoggingTrainingListener.java b/api/src/main/java/ai/djl/training/listener/LoggingTrainingListener.java
index 01b54ef9b..d14f1ba39 100644
--- a/api/src/main/java/ai/djl/training/listener/LoggingTrainingListener.java
+++ b/api/src/main/java/ai/djl/training/listener/LoggingTrainingListener.java
@@ -29,8 +29,8 @@ import java.util.List;
 /**
  * {@link TrainingListener} that outputs the progress of training each batch and epoch into logs.
  *
- * @see <a href="http://docs.djl.ai/docs/development/configure_logging.html">The guide on DJL
- *     logging</a>
+ * @see <a href="https://docs.djl.ai/master/docs/development/configure_logging.html">The guide on
+ *     DJL logging</a>
  */
 public class LoggingTrainingListener implements TrainingListener {
 
diff --git a/api/src/main/java/ai/djl/util/Ec2Utils.java b/api/src/main/java/ai/djl/util/Ec2Utils.java
index 8a22cfadb..b91f8077a 100644
--- a/api/src/main/java/ai/djl/util/Ec2Utils.java
+++ b/api/src/main/java/ai/djl/util/Ec2Utils.java
@@ -140,7 +140,8 @@ public final class Ec2Utils {
                         "DJL will collect telemetry to help us better understand our users' needs,"
                             + " diagnose issues, and deliver additional features. If you would like"
                             + " to learn more or opt-out please go to:"
-                            + " https://docs.djl.ai/docs/telemetry.html for more information.");
+                            + " https://docs.djl.ai/master/docs/telemetry.html for more"
+                            + " information.");
             }
         } catch (IOException e) {
             logger.debug("Failed call home.");
diff --git a/basicdataset/README.md b/basicdataset/README.md
index f312659f1..a577c81d0 100644
--- a/basicdataset/README.md
+++ b/basicdataset/README.md
@@ -4,7 +4,7 @@
 
 This module contains a number of basic and standard datasets in the Deep Java Library's (DJL). These datasets are used to train deep learning models.
 
-You can find the datasets provided by this module on our [docs](http://docs.djl.ai/docs/dataset.html).
+You can find the datasets provided by this module on our [docs](https://docs.djl.ai/master/docs/dataset.html).
 
 ## Documentation
 
diff --git a/djl-zero/src/main/java/ai/djl/zero/RequireZoo.java b/djl-zero/src/main/java/ai/djl/zero/RequireZoo.java
index 2b47d9be3..6c211fb58 100644
--- a/djl-zero/src/main/java/ai/djl/zero/RequireZoo.java
+++ b/djl-zero/src/main/java/ai/djl/zero/RequireZoo.java
@@ -29,7 +29,7 @@ public final class RequireZoo {
         if (!ModelZoo.hasModelZoo("ai.djl.zoo")) {
             throw new IllegalStateException(
                     "The basic model zoo is required, but not found.Please install it by following"
-                            + " http://docs.djl.ai/model-zoo/index.html#installation");
+                            + " https://docs.djl.ai/model-zoo/index.html#installation");
         }
     }
 
@@ -38,12 +38,12 @@ public final class RequireZoo {
         if (!ModelZoo.hasModelZoo("ai.djl.mxnet")) {
             throw new IllegalStateException(
                     "The MXNet model zoo is required, but not found.Please install it by following"
-                        + " http://docs.djl.ai/engines/mxnet/mxnet-model-zoo/index.html#installation");
+                        + " https://docs.djl.ai/master/engines/mxnet/mxnet-model-zoo/index.html#installation");
         }
         if (!Engine.hasEngine("MXNet")) {
             throw new IllegalStateException(
                     "The MXNet engine is required, but not found.Please install it by following"
-                        + " http://docs.djl.ai/engines/mxnet/mxnet-engine/index.html#installation");
+                        + " https://docs.djl.ai/master/engines/mxnet/mxnet-engine/index.html#installation");
         }
     }
 
@@ -53,12 +53,12 @@ public final class RequireZoo {
             throw new IllegalStateException(
                     "The PyTorch model zoo is required, but not found.Please install it by"
                         + " following"
-                        + " http://docs.djl.ai/pytorch/pytorch-model-zoo/index.html#installation");
+                        + " https://docs.djl.ai/master/pytorch/pytorch-model-zoo/index.html#installation");
         }
         if (!Engine.hasEngine("PyTorch")) {
             throw new IllegalStateException(
                     "The PyTorch engine is required, but not found.Please install it by following"
-                            + " http://docs.djl.ai/pytorch/pytorch-engine/index.html#installation");
+                        + " https://docs.djl.ai/master/pytorch/pytorch-engine/index.html#installation");
         }
     }
 
@@ -68,13 +68,13 @@ public final class RequireZoo {
             throw new IllegalStateException(
                     "The TensorFlow model zoo is required, but not found.Please install it by"
                         + " following"
-                        + " http://docs.djl.ai/engines/tensorflow/tensorflow-model-zoo/index.html#installation");
+                        + " https://docs.djl.ai/master/engines/tensorflow/tensorflow-model-zoo/index.html#installation");
         }
         if (!Engine.hasEngine("TensorFlow")) {
             throw new IllegalStateException(
                     "The TensorFlow engine is required, but not found.Please install it by"
                         + " following"
-                        + " http://docs.djl.ai/engines/tensorflow/tensorflow-engine/index.html#installation");
+                        + " https://docs.djl.ai/master/engines/tensorflow/tensorflow-engine/index.html#installation");
         }
     }
 }
diff --git a/djl-zero/src/main/java/ai/djl/zero/package-info.java b/djl-zero/src/main/java/ai/djl/zero/package-info.java
index 8c64d054e..60bb58dd8 100644
--- a/djl-zero/src/main/java/ai/djl/zero/package-info.java
+++ b/djl-zero/src/main/java/ai/djl/zero/package-info.java
@@ -14,6 +14,6 @@
 /**
  * Contains a zero deep learning knowledge required wrapper over DJL.
  *
- * <p><a href="https://docs.djl.ai/zero/index.html">See more details</a>.
+ * <p><a href="https://docs.djl.ai/master/zero/index.html">See more details</a>.
  */
 package ai.djl.zero;
diff --git a/docs/README.md b/docs/README.md
index 7749d39eb..82e78d6dc 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -20,14 +20,14 @@ Note: when searching in JavaDoc, if your access is denied, please try removing t
 - [Troubleshooting](development/troubleshooting.md)
 - [Inference Optimization](development/inference_performance_optimization.md)
 
-## [Jupyter notebook tutorials](http://docs.djl.ai/docs/demos/jupyter/index.html)
-
-- **[Beginner Jupyter Tutorial](http://docs.djl.ai/docs/demos/jupyter/tutorial/index.html)**
-- [Run object detection with model zoo](http://docs.djl.ai/docs/demos/jupyter/object_detection_with_model_zoo.html)
-- [Load pre-trained PyTorch model](http://docs.djl.ai/docs/demos/jupyter/load_pytorch_model.html)
-- [Load pre-trained Apache MXNet model](http://docs.djl.ai/docs/demos/jupyter/load_mxnet_model.html)
-- [Transfer learning example](http://docs.djl.ai/docs/demos/jupyter/transfer_learning_on_cifar10.html)
-- [Question answering example](http://docs.djl.ai/docs/demos/jupyter/BERTQA.html)
+## [Jupyter notebook tutorials](https://docs.djl.ai/master/docs/demos/jupyter/index.html)
+
+- **[Beginner Jupyter Tutorial](https://docs.djl.ai/master/docs/demos/jupyter/tutorial/index.html)**
+- [Run object detection with model zoo](https://docs.djl.ai/master/docs/demos/jupyter/object_detection_with_model_zoo.html)
+- [Load pre-trained PyTorch model](https://docs.djl.ai/master/docs/demos/jupyter/load_pytorch_model.html)
+- [Load pre-trained Apache MXNet model](https://docs.djl.ai/master/docs/demos/jupyter/load_mxnet_model.html)
+- [Transfer learning example](https://docs.djl.ai/master/docs/demos/jupyter/transfer_learning_on_cifar10.html)
+- [Question answering example](https://docs.djl.ai/master/docs/demos/jupyter/BERTQA.html)
 
 ## [API Examples](../examples/README.md)
 
diff --git a/docs/cv_utils.md b/docs/cv_utils.md
index 95d309620..a334ba833 100644
--- a/docs/cv_utils.md
+++ b/docs/cv_utils.md
@@ -105,6 +105,6 @@ img.save(out2, "png");
 ```
 
 ## Useful Information
-If you want to learn more about loading models, click [here](http://docs.djl.ai/docs/load_model.html).
+If you want to learn more about loading models, click [here](https://docs.djl.ai/master/docs/load_model.html).
 
-If you want to learn more about the model zoo, click [here](http://docs.djl.ai/docs/model-zoo.html).
+If you want to learn more about the model zoo, click [here](https://docs.djl.ai/master/docs/model-zoo.html).
diff --git a/docs/development/inference_performance_optimization.md b/docs/development/inference_performance_optimization.md
index 0fdc67d99..0e0cabc7a 100644
--- a/docs/development/inference_performance_optimization.md
+++ b/docs/development/inference_performance_optimization.md
@@ -15,13 +15,13 @@ For engines that are thread-safe, we do nothing and for ones that are not, we wi
 
 Therefore, we recommend creating a new [Predictor](https://javadoc.io/doc/ai.djl/api/latest/ai/djl/inference/Predictor.html) for each thread.
 That Predictor should be reused if the thread does multiple predictions.
-Alternatively, you can also use a pool of Predictors or you can leverage the [DJL Serving WorkLoadManager](http://docs.djl.ai/docs/serving/wlm/index.html).
+Alternatively, you can also use a pool of Predictors or you can leverage the [DJL Serving WorkLoadManager](https://docs.djl.ai/master/docs/serving/wlm/index.html).
 
 For a reference implementation, see [Multi-threaded Benchmark](https://github.com/deepjavalibrary/djl-serving/blob/master/benchmark/src/main/java/ai/djl/benchmark/MultithreadedBenchmark.java).
 
 In addition, you may need to set engine-specific configurations as well.
 Engine-specific details are given below.
-You can also reference the [list of all DJL system configurations](http://docs.djl.ai/docs/serving/serving/docs/configurations.html).
+You can also reference the [list of all DJL system configurations](https://docs.djl.ai/master/docs/serving/serving/docs/configurations.html).
 
 ### Apache MXNet
 
diff --git a/docs/development/troubleshooting.md b/docs/development/troubleshooting.md
index f46dab0ea..73cc4764f 100644
--- a/docs/development/troubleshooting.md
+++ b/docs/development/troubleshooting.md
@@ -204,7 +204,7 @@ ensorflow.so: /usr/lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.11'
 nsorflow/cache/2.3.1-cu101-linux-x86_64/libtensorflow.so.2)
 ```
 
-Please upgrade gcc version according to the steps on [TensorFlow install page](https://docs.djl.ai/engines/tensorflow/index.html#note)
+Please upgrade gcc version according to the steps on [TensorFlow install page](https://docs.djl.ai/master/engines/tensorflow/index.html#note)
 
 ## 9. X11 error when running object detection notebooks on EC2 instances.
 
diff --git a/docs/interactive_tool.md b/docs/interactive_tool.md
index 568c248c2..6f9ff91db 100644
--- a/docs/interactive_tool.md
+++ b/docs/interactive_tool.md
@@ -63,7 +63,7 @@ After that, click `run` and you should see the following result:
 
 Finally, you can get the running project setup by clicking `Get Template`. This will bring you a gradle project that can be used in your local machine.
 
-## [Java Jupyter Notebook](http://docs.djl.ai/docs/demos/jupyter/index.html)
+## [Java Jupyter Notebook](https://docs.djl.ai/master/docs/demos/jupyter/index.html)
 
 Wait a second, are we talking about hosting Jupyter Notebook in python? 
 No, it’s Java 11, only.
@@ -71,6 +71,6 @@ No, it’s Java 11, only.
 ![jupyter](https://djl-ai.s3.amazonaws.com/web-data/images/jupyter.gif)
 
 Inspired by Spencer Park’s [IJava project](https://github.com/SpencerPark/IJava), we integrated DJL with Jupyter Notebooks. 
-For more information on the simple setup, follow the instructions in [DJL Jupyter notebooks](http://docs.djl.ai/docs/demos/jupyter/index.html#setup).
+For more information on the simple setup, follow the instructions in [DJL Jupyter notebooks](https://docs.djl.ai/master/docs/demos/jupyter/index.html#setup).
 After that, use the Jupyter Notebook freely in your hosted server. You can do all kinds of work, like block building and plotting a graph.
-There are [tutorials and instructions](http://docs.djl.ai/docs/demos/jupyter/index.html#djl---jupyter-notebooks) to guide you how you can run training and/or inference with Java.
\ No newline at end of file
+There are [tutorials and instructions](https://docs.djl.ai/master/docs/demos/jupyter/index.html#djl---jupyter-notebooks) to guide you how you can run training and/or inference with Java.
\ No newline at end of file
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index b862f6752..64ccf132e 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -52,7 +52,7 @@ plugins:
   - mike
   - exclude:
       regex:
-        - '.*\.(symbol|params|pt|jar|gz|zip|txt|json|sh|bat|cmd|puml|g4|java|py|cc|h|pro|properties|xml|html|scala|sbt|kt|npy|npz|onnx|rs|rst|safetensors|tar|toml|trt|vue|scss|yml|yaml|Dockerfile|cmake|RepositoryFactory|EngineProvider|ZooProvider)$|LICENSE|NOTICE|.*gradle.*|.*/mvnw|.*/Dockerfile.*|.*(hdpi|\.git|\.mvn|docker|buildSrc|gradle|integration|testing|jacoco|tools|javadoc|META-INF|/webapp|apache-spark/notebook|visualization-vue|malicious-url-detector/docs|frontend|interactive-console)/.*'
+        - '.*\.(symbol|params|pt|jar|gz|zip|txt|json|sh|bat|cmd|puml|g4|java|py|cc|h|pro|properties|xml|html|scala|sbt|kt|npy|npz|onnx|rs|rst|safetensors|tar|toml|trt|vue|scss|yml|yaml|Dockerfile|cmake|RepositoryFactory|EngineProvider|ZooProvider)$|LICENSE|NOTICE|.*gradle.*|.*/mvnw|.*/Dockerfile.*|.*(hdpi|\.git|\.mvn|docker|buildSrc|gradle|integration|testing|jacoco|tools|javadoc|META-INF|/webapp|apache-spark/notebook|frontend)/.*'
 
 
 nav:
@@ -170,7 +170,6 @@ nav:
       - User Guides:
           - 'docs/serving/serving/docs/lmi/user_guides/README.md'
           - 'docs/serving/serving/docs/lmi/user_guides/starting-guide.md'
-          - 'docs/serving/serving/docs/lmi/user_guides/deepspeed_user_guide.md'
           - 'docs/serving/serving/docs/lmi/user_guides/lmi-dist_user_guide.md'
           - 'docs/serving/serving/docs/lmi/user_guides/vllm_user_guide.md'
           - 'docs/serving/serving/docs/lmi/user_guides/tnx_user_guide.md'
@@ -218,7 +217,6 @@ nav:
           - Doodledraw (PyTorch): 'docs/demos/android/pytorch_android/quickdraw_recognition/README.md'
           - Style Transfer (PyTorch): 'docs/demos/android/pytorch_android/style_transfer_cyclegan/README.md'
           - Face Detection (PyTorch): 'docs/demos/android/pytorch_android/face_detection/README.md'
-          - MXNet Android Template: 'docs/demos/android/mxnet-android/README.md'
       - EcoSystem:
           - Java Integrations:
               - DJL Component in Apache Camel: 'docs/demos/camel-djl/README.md'
diff --git a/docs/pytorch/pytorch-djl-ndarray-cheatsheet.md b/docs/pytorch/pytorch-djl-ndarray-cheatsheet.md
index 37d24276d..eec9b9af9 100644
--- a/docs/pytorch/pytorch-djl-ndarray-cheatsheet.md
+++ b/docs/pytorch/pytorch-djl-ndarray-cheatsheet.md
@@ -3,7 +3,7 @@
 In the following examples, we assume
 
 - `manager` is an instance of `ai.djl.ndarray.NDManager`
-    - it is recommended to look through https://docs.djl.ai/docs/development/memory_management.html
+    - it is recommended to look through https://docs.djl.ai/master/docs/development/memory_management.html
       in advance so that you can get better insight of `NDManager`
 - `$tensor` is a placeholder for an instance of `torch.tensor`
 - `$ndarray` is a placeholder for an instance of `ai.djl.ndarray.NDArray`
diff --git a/docs/quick_start.md b/docs/quick_start.md
index cbbb52d2c..c562a0886 100644
--- a/docs/quick_start.md
+++ b/docs/quick_start.md
@@ -1,7 +1,7 @@
 # Quick start
 
 Deep Java Library (DJL) is designed to be easy to get started with and simple to use.
-The easiest way to learn DJL is to read the [beginner tutorial](http://docs.djl.ai/docs/demos/jupyter/tutorial/README.md) or
+The easiest way to learn DJL is to read the [beginner tutorial](https://docs.djl.ai/master/docs/demos/jupyter/tutorial/README.md) or
 our [examples](../examples/README.md).
 
 You can also view our 1.5 hour long (in 8 x ~10 minute segments) DJL 101 tutorial video series:
@@ -20,7 +20,7 @@ You can start from here to run inference online and download starter template wi
 
 ## Beginner tutorial
 
-To get started, we recommend that you follow our short [beginner tutorial](http://docs.djl.ai/docs/demos/jupyter/tutorial/index.html). It takes you through some of the basics of deep learning to create a model, train your model, and run inference using your trained model.
+To get started, we recommend that you follow our short [beginner tutorial](https://docs.djl.ai/master/docs/demos/jupyter/tutorial/index.html). It takes you through some of the basics of deep learning to create a model, train your model, and run inference using your trained model.
 
 ## Run examples
 
@@ -31,7 +31,7 @@ All of our examples are executed by a simple command. For detailed command line
 - [Train your first model](../examples/docs/train_mnist_mlp.md)
 - [Single-shot Object Detection inference example](../examples/docs/object_detection.md)
 - [More examples](https://github.com/deepjavalibrary/djl/tree/master/examples)
-- [Jupyter examples](http://docs.djl.ai/docs/demos/jupyter/index.html)
+- [Jupyter examples](https://docs.djl.ai/master/docs/demos/jupyter/index.html)
 
 ## Other resources
 
diff --git a/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.md b/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.md
index c57fd055c..13139431b 100644
--- a/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.md
+++ b/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.md
@@ -116,7 +116,7 @@ For more information on saving, loading and exporting checkpoints, please refer
 
 ## How to load DJL TensorFlow model zoo models
 
-The steps are the same as loading any other DJL model zoo models, you can use the `Criteria` API as documented [here](https://docs.djl.ai/docs/load_model.html#criteria-class).
+The steps are the same as loading any other DJL model zoo models, you can use the `Criteria` API as documented [here](https://docs.djl.ai/master/docs/load_model.html#criteria-class).
 
 Note for TensorFlow image classification models, you need to manually specify the translator instead of using the built-in one because
 TensorFlow requires channels last ("NHWC") image formats while DJL use channels first ("NCHW") image formats. By default, DJL will add
diff --git a/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtEngine.java b/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtEngine.java
index f909a5788..a0864462b 100644
--- a/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtEngine.java
+++ b/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtEngine.java
@@ -67,7 +67,7 @@ public final class PtEngine extends Engine {
                 logger.info(
                         "PyTorch graph executor optimizer is enabled, this may impact your"
                             + " inference latency and throughput. See:"
-                            + " https://docs.djl.ai/docs/development/inference_performance_optimization.html#graph-executor-optimization");
+                            + " https://docs.djl.ai/master/docs/development/inference_performance_optimization.html#graph-executor-optimization");
             }
             logger.info("Number of inter-op threads is {}", JniUtils.getNumInteropThreads());
             logger.info("Number of intra-op threads is {}", JniUtils.getNumThreads());
diff --git a/engines/tensorflow/README.md b/engines/tensorflow/README.md
index 513583881..41375d00d 100644
--- a/engines/tensorflow/README.md
+++ b/engines/tensorflow/README.md
@@ -5,7 +5,7 @@ This directory contains the Deep Java Library (DJL) EngineProvider for TensorFlo
 It is based off the [TensorFlow Deep Learning Framework](https://www.tensorflow.org/).
 
 The DJL TensorFlow Engine allows you to run prediction with TensorFlow or Keras models using Java.
-Refer to [How to import TensorFlow models](https://docs.djl.ai/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.html) for loading TF models in DJL.
+Refer to [How to import TensorFlow models](https://docs.djl.ai/master/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.html) for loading TF models in DJL.
 
 ## Modules
 
diff --git a/engines/tensorflow/tensorflow-engine/src/main/java/ai/djl/tensorflow/engine/TfEngine.java b/engines/tensorflow/tensorflow-engine/src/main/java/ai/djl/tensorflow/engine/TfEngine.java
index 631194bc0..c2e949972 100644
--- a/engines/tensorflow/tensorflow-engine/src/main/java/ai/djl/tensorflow/engine/TfEngine.java
+++ b/engines/tensorflow/tensorflow-engine/src/main/java/ai/djl/tensorflow/engine/TfEngine.java
@@ -68,7 +68,7 @@ public final class TfEngine extends Engine implements AutoCloseable {
                                 + " native library.\n"
                                 + "It can be installed or upgraded through gcc by following the"
                                 + " instructions on the TensorFlow install page:"
-                                + " https://docs.djl.ai/engines/tensorflow/index.html#note";
+                                + " https://docs.djl.ai/master/engines/tensorflow/index.html#note";
                 throw new EngineException(msg, t);
             }
 
diff --git a/examples/docs/image_classification.md b/examples/docs/image_classification.md
index 7c3e00570..e0a2175b1 100644
--- a/examples/docs/image_classification.md
+++ b/examples/docs/image_classification.md
@@ -6,7 +6,7 @@ In this example, you learn how to implement inference code with Deep Java Librar
 
 The image classification example code can be found at [ImageClassification.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/cv/ImageClassification.java).
 
-You can also use the [Jupyter notebook tutorial](http://docs.djl.ai/docs/demos/jupyter/tutorial/03_image_classification_with_your_model.html).
+You can also use the [Jupyter notebook tutorial](https://docs.djl.ai/master/docs/demos/jupyter/tutorial/03_image_classification_with_your_model.html).
 The Jupyter notebook explains the key concepts in detail.
 
 ## Setup Guide
diff --git a/examples/docs/mask_detection.md b/examples/docs/mask_detection.md
index 55bd56b4a..d1ea0465d 100644
--- a/examples/docs/mask_detection.md
+++ b/examples/docs/mask_detection.md
@@ -5,7 +5,7 @@ YOLOv5 is a powerful model for object detection tasks. With the transfer learnin
 In this example, we apply it on the [Face Mask Detection dataset](https://www.kaggle.com/datasets/andrewmvd/face-mask-detection?select=images). We first train the YOLOv5s model in Python, with the help of [ATLearn](https://github.com/awslabs/atlearn/blob/main/examples/docs/face_mask_detection.md), a python transfer learning toolkit.
 Then, the model is saved as an ONNX model, which is then imported into DJL for inference. We apply it on the mask wearing detection task. 
 
-The source code can be found at [MaskDetectionOnnx.java](../src/main/java/ai/djl/examples/inference/cv/MaskDetection.java)
+The source code can be found at [MaskDetectionOnnx.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/cv/MaskDetection.java)
 
 ## The training part in ATLearn
 
@@ -13,7 +13,7 @@ We initially attempted to import a pretrained YOLOv5 into DJL, and fine-tune it
 
 The retraining of YOLOv5 can be found in an example in ATLearn: `examples/docs/face_mask_detection.md`. In this example, the YOLOv5 layers near the input are frozen while those near the output are fine-tuned with the customized data. This follows the transfer learning idea.
 
-In this example, the trained model is first exported to ONNX file, eg. `mask.onnx` and then imported in  [MaskDetectionOnnx.java](../src/main/java/ai/djl/examples/inference/cv/MaskDetection.java).
+In this example, the trained model is first exported to ONNX file, eg. `mask.onnx` and then imported in  [MaskDetectionOnnx.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/cv/MaskDetection.java).
 This ONNX model file can also be used for inference in python, which will serve as a benchmark. See the [tutorial doc](https://github.com/awslabs/atlearn/blob/main/examples/docs/face_mask_detection.md). 
 
 ## Setup guide
diff --git a/examples/docs/object_detection.md b/examples/docs/object_detection.md
index 220883f62..b60705c27 100644
--- a/examples/docs/object_detection.md
+++ b/examples/docs/object_detection.md
@@ -7,7 +7,7 @@ In this example, you learn how to implement inference code with a [ModelZoo mode
 
 The source code can be found at [ObjectDetection.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/cv/ObjectDetection.java).
 
-You can also use the [Jupyter notebook tutorial](http://docs.djl.ai/docs/demos/jupyter/object_detection_with_model_zoo.html).
+You can also use the [Jupyter notebook tutorial](https://docs.djl.ai/master/docs/demos/jupyter/object_detection_with_model_zoo.html).
 The Jupyter notebook explains the key concepts in detail.
 
 ## Setup guide
diff --git a/examples/docs/train_cifar10_resnet.md b/examples/docs/train_cifar10_resnet.md
index 1cdfcb495..c11ac3481 100644
--- a/examples/docs/train_cifar10_resnet.md
+++ b/examples/docs/train_cifar10_resnet.md
@@ -5,7 +5,7 @@ In this example, you learn how to train the [CIFAR-10](https://www.cs.toronto.ed
 
 You can find the example source code in: [TrainResnetWithCifar10.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/training/transferlearning/TrainResnetWithCifar10.java).
 
-You can also find the Jupyter notebook tutorial [here](http://docs.djl.ai/docs/demos/jupyter/transfer_learning_on_cifar10.html).
+You can also find the Jupyter notebook tutorial [here](https://docs.djl.ai/master/docs/demos/jupyter/transfer_learning_on_cifar10.html).
 The Jupyter notebook explains the key concepts in detail.
 
 ## Setup guide
diff --git a/examples/docs/train_mnist_mlp.md b/examples/docs/train_mnist_mlp.md
index 40a32ca36..95308d172 100644
--- a/examples/docs/train_mnist_mlp.md
+++ b/examples/docs/train_mnist_mlp.md
@@ -6,7 +6,7 @@ In this example, you learn how to train the MNIST dataset with Deep Java Library
 
 The source code for this example can be found at [TrainMnist.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/training/TrainMnist.java).
 
-You can also use the [Jupyter notebook tutorial](http://docs.djl.ai/docs/demos/jupyter/tutorial/02_train_your_first_model.html).
+You can also use the [Jupyter notebook tutorial](https://docs.djl.ai/master/docs/demos/jupyter/tutorial/02_train_your_first_model.html).
 The Jupyter notebook explains the key concepts in detail.
 
 ## Setup guide
diff --git a/examples/docs/train_transfer_fresh_fruit.md b/examples/docs/train_transfer_fresh_fruit.md
index 2f1feb0cf..2a8dc9b12 100644
--- a/examples/docs/train_transfer_fresh_fruit.md
+++ b/examples/docs/train_transfer_fresh_fruit.md
@@ -26,7 +26,7 @@ The example is structured as follows:
 3. The demonstration of transfer learning in Java
 4. Experiment on the reduction of the training data size
 
-The full source code is available [here](../src/main/java/ai/djl/examples/training/transferlearning/TransferFreshFruit.java).
+The full source code is available [here](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/training/transferlearning/TransferFreshFruit.java).
 
 ## The data set and the problem formulation
 In this example, we demonstrate with the [fruit fresh and rotten dataset](https://www.kaggle.com/datasets/sriramr/fruits-fresh-and-rotten-for-classification),
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 6f75ec728..d02da5437 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
@@ -33,8 +33,8 @@ import java.io.IOException;
  * <p>See:
  *
  * <ul>
- *   <li>the <a href="http://docs.djl.ai/docs/demos/jupyter/BERTQA.html">jupyter demo</a> with more
- *       information about BERT.
+ *   <li>the <a href="https://docs.djl.ai/master/docs/demos/jupyter/BERTQA.html">jupyter demo</a>
+ *       with more information about BERT.
  *   <li>the <a
  *       href="https://github.com/deepjavalibrary/djl/blob/master/examples/docs/BERT_question_and_answer.md">docs</a>
  *       for information about running this example.
diff --git a/extensions/timeseries/README.md b/extensions/timeseries/README.md
index 0cd38b8b4..e5798263e 100644
--- a/extensions/timeseries/README.md
+++ b/extensions/timeseries/README.md
@@ -116,7 +116,7 @@ The following pseudocode demonstrates how to create a `DeepARTranslator` with `a
 If you want to customize your own time series model translator, you can easily use the transform
 package for your data preprocess.
 
-See [examples](src/test/java/ai/djl/timeseries/translator/DeepARTranslatorTest.java) for more details.
+See [examples](https://github.com/deepjavalibrary/djl/blob/master/extensions/timeseries/src/test/java/ai/djl/timeseries/translator/DeepARTranslatorTest.java) for more details.
 
 ## Simple Example
 
@@ -223,7 +223,7 @@ Since djl doesn't support drawing yet, you can find our script for visualization
 [here](https://gist.github.com/Carkham/a5162c9298bc51fec648a458a3437008).
 
 The **full source code** for this example is available
-[here](../../examples/src/main/java/ai/djl/examples/inference/timeseries/AirPassengersDeepAR.java)
+[here](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/timeseries/AirPassengersDeepAR.java)
 
 ## Documentation
 
diff --git a/extensions/tokenizers/rust/src/models/bert.rs b/extensions/tokenizers/rust/src/models/bert.rs
index 00bcd27ad..0e7b1722f 100644
--- a/extensions/tokenizers/rust/src/models/bert.rs
+++ b/extensions/tokenizers/rust/src/models/bert.rs
@@ -487,8 +487,8 @@ impl BertClassificationHead {
             vb.pp("bert.pooler.dense"),
             config.hidden_size,
             config.hidden_size,
-            None)
-        {
+            None,
+        ) {
             Ok(layer) => Some(layer),
             Err(_) => None,
         };
@@ -555,7 +555,6 @@ impl BertModel {
 }
 
 impl Model for BertModel {
-
     fn get_input_names(&self) -> Vec<String> {
         return vec![
             "input_ids".to_string(),
@@ -590,9 +589,8 @@ pub struct BertForSequenceClassification {
 impl BertForSequenceClassification {
     pub fn load(vb: VarBuilder, config: &BertConfig) -> Result<Self> {
         let bert = Box::new(BertModel::load(vb.clone(), &config)?);
-        let classifier: Box<dyn ClassificationHead + Send> = Box::new(
-            BertClassificationHead::load(vb.pp("classifier"), config)?,
-        );
+        let classifier: Box<dyn ClassificationHead + Send> =
+            Box::new(BertClassificationHead::load(vb.pp("classifier"), config)?);
         Ok(Self {
             bert,
             classifier,
@@ -603,7 +601,6 @@ impl BertForSequenceClassification {
 }
 
 impl Model for BertForSequenceClassification {
-
     fn get_input_names(&self) -> Vec<String> {
         return vec![
             "input_ids".to_string(),
@@ -626,4 +623,4 @@ impl Model for BertForSequenceClassification {
         let logits = self.classifier.forward(&sequence_output)?;
         Ok(logits)
     }
-}
\ No newline at end of file
+}
diff --git a/extensions/tokenizers/rust/src/models/camembert.rs b/extensions/tokenizers/rust/src/models/camembert.rs
index 0aa119914..da42a4f29 100644
--- a/extensions/tokenizers/rust/src/models/camembert.rs
+++ b/extensions/tokenizers/rust/src/models/camembert.rs
@@ -166,7 +166,8 @@ impl BertEmbeddings {
         let mut embeddings = (&input_embeddings + token_type_embeddings)?;
         if let Some(position_embeddings) = &self.position_embeddings {
             let position_offset = self.config.pad_token_id as u32 + 1;
-            let position_ids = (position_offset..(seq_len as u32 + position_offset)).collect::<Vec<_>>();
+            let position_ids =
+                (position_offset..(seq_len as u32 + position_offset)).collect::<Vec<_>>();
             let position_ids = Tensor::new(&position_ids[..], input_ids.device())?;
             embeddings = embeddings.broadcast_add(&position_embeddings.forward(&position_ids)?)?
         }
@@ -504,7 +505,6 @@ impl CamembertModel {
 }
 
 impl Model for CamembertModel {
-
     fn get_input_names(&self) -> Vec<String> {
         return vec![
             "input_ids".to_string(),
@@ -526,4 +526,4 @@ impl Model for CamembertModel {
         let sequence_output = self.encoder.forward(&embedding_output)?;
         Ok(sequence_output)
     }
-}
\ No newline at end of file
+}
diff --git a/extensions/tokenizers/rust/src/models/distilbert.rs b/extensions/tokenizers/rust/src/models/distilbert.rs
index fabf11c01..345be40d1 100644
--- a/extensions/tokenizers/rust/src/models/distilbert.rs
+++ b/extensions/tokenizers/rust/src/models/distilbert.rs
@@ -366,7 +366,6 @@ impl DistilBertModel {
 }
 
 impl Model for DistilBertModel {
-
     fn get_input_names(&self) -> Vec<String> {
         return vec!["input_ids".to_string(), "attention_mask".to_string()];
     }
diff --git a/extensions/tokenizers/rust/src/models/mod.rs b/extensions/tokenizers/rust/src/models/mod.rs
index 707cfc58f..c11ec585a 100644
--- a/extensions/tokenizers/rust/src/models/mod.rs
+++ b/extensions/tokenizers/rust/src/models/mod.rs
@@ -6,18 +6,18 @@ mod xlm_roberta;
 
 use crate::ndarray::as_data_type;
 use crate::{cast_handle, drop_handle, to_handle, to_string_array};
-use candle::{DType, Device, Result, Tensor, Error};
-use candle_nn::VarBuilder;
-use bert::{BertConfig, BertModel, BertForSequenceClassification};
+use bert::{BertConfig, BertForSequenceClassification, BertModel};
 use camembert::{CamembertConfig, CamembertModel};
+use candle::{DType, Device, Error, Result, Tensor};
+use candle_nn::VarBuilder;
 use distilbert::{DistilBertConfig, DistilBertModel};
-use roberta::{RobertaConfig, RobertaModel, RobertaForSequenceClassification};
-use xlm_roberta::{XLMRobertaConfig, XLMRobertaModel, XLMRobertaForSequenceClassification};
 use jni::objects::{JLongArray, JObject, JString, ReleaseMode};
 use jni::sys::{jint, jlong, jobjectArray};
 use jni::JNIEnv;
+use roberta::{RobertaConfig, RobertaForSequenceClassification, RobertaModel};
 use serde::Deserialize;
 use std::path::PathBuf;
+use xlm_roberta::{XLMRobertaConfig, XLMRobertaForSequenceClassification, XLMRobertaModel};
 
 #[derive(Debug, PartialEq, Clone)]
 #[allow(dead_code, unused)]
@@ -40,7 +40,6 @@ enum Config {
 }
 
 pub(crate) trait Model {
-
     fn get_input_names(&self) -> Vec<String>;
 
     fn forward(
@@ -109,7 +108,9 @@ fn load_model<'local>(
             config.use_flash_attn = Some(use_flash_attn);
             match config.architectures.first() {
                 Some(arch) => match arch.as_str() {
-                    "BertForSequenceClassification" => Ok(Box::new(BertForSequenceClassification::load(vb, &config)?)),
+                    "BertForSequenceClassification" => {
+                        Ok(Box::new(BertForSequenceClassification::load(vb, &config)?))
+                    }
                     _ => Ok(Box::new(BertModel::load(vb, &config)?)),
                 },
                 None => Ok(Box::new(BertModel::load(vb, &config)?)),
@@ -125,7 +126,9 @@ fn load_model<'local>(
             config.use_flash_attn = Some(use_flash_attn);
             match config.architectures.first() {
                 Some(arch) => match arch.as_str() {
-                    "RobertaForSequenceClassification" => Ok(Box::new(RobertaForSequenceClassification::load(vb, &config)?)),
+                    "RobertaForSequenceClassification" => Ok(Box::new(
+                        RobertaForSequenceClassification::load(vb, &config)?,
+                    )),
                     _ => Ok(Box::new(RobertaModel::load(vb, &config)?)),
                 },
                 None => Ok(Box::new(RobertaModel::load(vb, &config)?)),
@@ -136,7 +139,9 @@ fn load_model<'local>(
             config.use_flash_attn = Some(use_flash_attn);
             match config.architectures.first() {
                 Some(arch) => match arch.as_str() {
-                    "XLMRobertaForSequenceClassification" => Ok(Box::new(XLMRobertaForSequenceClassification::load(vb, &config)?)),
+                    "XLMRobertaForSequenceClassification" => Ok(Box::new(
+                        XLMRobertaForSequenceClassification::load(vb, &config)?,
+                    )),
                     _ => Ok(Box::new(XLMRobertaModel::load(vb, &config)?)),
                 },
                 None => Ok(Box::new(XLMRobertaModel::load(vb, &config)?)),
diff --git a/extensions/tokenizers/rust/src/models/roberta.rs b/extensions/tokenizers/rust/src/models/roberta.rs
index 4198927f4..27fd65995 100644
--- a/extensions/tokenizers/rust/src/models/roberta.rs
+++ b/extensions/tokenizers/rust/src/models/roberta.rs
@@ -166,7 +166,8 @@ impl BertEmbeddings {
         let mut embeddings = (&input_embeddings + token_type_embeddings)?;
         if let Some(position_embeddings) = &self.position_embeddings {
             let position_offset = self.config.pad_token_id as u32 + 1;
-            let position_ids = (position_offset..(seq_len as u32 + position_offset)).collect::<Vec<_>>();
+            let position_ids =
+                (position_offset..(seq_len as u32 + position_offset)).collect::<Vec<_>>();
             let position_ids = Tensor::new(&position_ids[..], input_ids.device())?;
             embeddings = embeddings.broadcast_add(&position_embeddings.forward(&position_ids)?)?
         }
@@ -484,7 +485,8 @@ impl RobertaClassificationHead {
             None => candle::bail!("`id2label` must be set for classifier models"),
             Some(id2label) => id2label.len(),
         };
-        let intermediate = Linear::load(vb.pp("dense"), config.hidden_size, config.hidden_size, None)?;
+        let intermediate =
+            Linear::load(vb.pp("dense"), config.hidden_size, config.hidden_size, None)?;
         let output = Linear::load(vb.pp("out_proj"), config.hidden_size, n_classes, None)?;
         Ok(Self {
             intermediate,
@@ -544,7 +546,6 @@ impl RobertaModel {
 }
 
 impl Model for RobertaModel {
-
     fn get_input_names(&self) -> Vec<String> {
         return vec![
             "input_ids".to_string(),
@@ -592,7 +593,6 @@ impl RobertaForSequenceClassification {
 }
 
 impl Model for RobertaForSequenceClassification {
-
     fn get_input_names(&self) -> Vec<String> {
         return vec![
             "input_ids".to_string(),
@@ -615,4 +615,4 @@ impl Model for RobertaForSequenceClassification {
         let logits = self.classifier.forward(&sequence_output)?;
         Ok(logits)
     }
-}
\ No newline at end of file
+}
diff --git a/extensions/tokenizers/rust/src/models/xlm_roberta.rs b/extensions/tokenizers/rust/src/models/xlm_roberta.rs
index f3b6ebc9d..4d7eef4ee 100644
--- a/extensions/tokenizers/rust/src/models/xlm_roberta.rs
+++ b/extensions/tokenizers/rust/src/models/xlm_roberta.rs
@@ -166,7 +166,8 @@ impl BertEmbeddings {
         let mut embeddings = (&input_embeddings + token_type_embeddings)?;
         if let Some(position_embeddings) = &self.position_embeddings {
             let position_offset = self.config.pad_token_id as u32 + 1;
-            let position_ids = (position_offset..(seq_len as u32 + position_offset)).collect::<Vec<_>>();
+            let position_ids =
+                (position_offset..(seq_len as u32 + position_offset)).collect::<Vec<_>>();
             let position_ids = Tensor::new(&position_ids[..], input_ids.device())?;
             embeddings = embeddings.broadcast_add(&position_embeddings.forward(&position_ids)?)?
         }
@@ -484,7 +485,8 @@ impl XLMRobertaClassificationHead {
             None => candle::bail!("`id2label` must be set for classifier models"),
             Some(id2label) => id2label.len(),
         };
-        let intermediate = Linear::load(vb.pp("dense"), config.hidden_size, config.hidden_size, None)?;
+        let intermediate =
+            Linear::load(vb.pp("dense"), config.hidden_size, config.hidden_size, None)?;
         let output = Linear::load(vb.pp("out_proj"), config.hidden_size, n_classes, None)?;
         Ok(Self {
             intermediate,
@@ -549,7 +551,6 @@ impl XLMRobertaModel {
 }
 
 impl Model for XLMRobertaModel {
-
     fn get_input_names(&self) -> Vec<String> {
         return vec![
             "input_ids".to_string(),
@@ -597,7 +598,6 @@ impl XLMRobertaForSequenceClassification {
 }
 
 impl Model for XLMRobertaForSequenceClassification {
-
     fn get_input_names(&self) -> Vec<String> {
         return vec![
             "input_ids".to_string(),
@@ -620,4 +620,4 @@ impl Model for XLMRobertaForSequenceClassification {
         let logits = self.classifier.forward(&sequence_output)?;
         Ok(logits)
     }
-}
\ No newline at end of file
+}
diff --git a/extensions/tokenizers/rust/src/ndarray/mod.rs b/extensions/tokenizers/rust/src/ndarray/mod.rs
index f2bd7f56c..781c459ec 100644
--- a/extensions/tokenizers/rust/src/ndarray/mod.rs
+++ b/extensions/tokenizers/rust/src/ndarray/mod.rs
@@ -9,6 +9,7 @@ use crate::{cast_handle, drop_handle, to_handle};
 mod binary;
 mod cmp;
 mod creation;
+mod nn;
 mod other;
 mod reduce;
 mod unary;
diff --git a/extensions/tokenizers/rust/src/ndarray/nn.rs b/extensions/tokenizers/rust/src/ndarray/nn.rs
new file mode 100644
index 000000000..f577310a3
--- /dev/null
+++ b/extensions/tokenizers/rust/src/ndarray/nn.rs
@@ -0,0 +1,53 @@
+use crate::cast_handle;
+use crate::ndarray::return_handle;
+use candle::Tensor;
+use jni::objects::JObject;
+use jni::sys::{jfloat, jint, jlong};
+use jni::JNIEnv;
+
+#[no_mangle]
+pub extern "system" fn Java_ai_djl_engine_rust_RustLibrary_sigmoid<'local>(
+    mut env: JNIEnv,
+    _: JObject,
+    handle: jlong,
+) -> jlong {
+    let tensor = cast_handle::<Tensor>(handle);
+    let ret = candle_nn::ops::sigmoid(&tensor);
+    return_handle(&mut env, ret)
+}
+
+#[no_mangle]
+pub extern "system" fn Java_ai_djl_engine_rust_RustLibrary_leakyRelu<'local>(
+    mut env: JNIEnv,
+    _: JObject,
+    handle: jlong,
+    alpha: jfloat,
+) -> jlong {
+    let tensor = cast_handle::<Tensor>(handle);
+    let ret = candle_nn::ops::leaky_relu(&tensor, alpha as f64);
+    return_handle(&mut env, ret)
+}
+
+#[no_mangle]
+pub extern "system" fn Java_ai_djl_engine_rust_RustLibrary_softmax<'local>(
+    mut env: JNIEnv,
+    _: JObject,
+    handle: jlong,
+    axis: jint,
+) -> jlong {
+    let tensor = cast_handle::<Tensor>(handle);
+    let ret = candle_nn::ops::softmax(&tensor, axis as usize);
+    return_handle(&mut env, ret)
+}
+
+#[no_mangle]
+pub extern "system" fn Java_ai_djl_engine_rust_RustLibrary_logSoftmax<'local>(
+    mut env: JNIEnv,
+    _: JObject,
+    handle: jlong,
+    axis: jint,
+) -> jlong {
+    let tensor = cast_handle::<Tensor>(handle);
+    let ret = candle_nn::ops::log_softmax(&tensor, axis as usize);
+    return_handle(&mut env, ret)
+}
diff --git a/extensions/tokenizers/src/main/java/ai/djl/engine/rust/RsNDArray.java b/extensions/tokenizers/src/main/java/ai/djl/engine/rust/RsNDArray.java
index ba526b098..97e1148bf 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/engine/rust/RsNDArray.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/engine/rust/RsNDArray.java
@@ -1229,6 +1229,9 @@ public class RsNDArray extends NativeResource<Long> implements NDArray {
     /** {@inheritDoc} */
     @Override
     public RsNDArray softmax(int axis) {
+        if (getShape().isScalar() || shape.size() == 0) {
+            return (RsNDArray) duplicate();
+        }
         return toArray(RustLibrary.softmax(getHandle(), axis));
     }
 
diff --git a/extensions/tokenizers/src/main/java/ai/djl/engine/rust/RustLibrary.java b/extensions/tokenizers/src/main/java/ai/djl/engine/rust/RustLibrary.java
index 131e1f7ca..852c392f1 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/engine/rust/RustLibrary.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/engine/rust/RustLibrary.java
@@ -322,13 +322,9 @@ public final class RustLibrary {
         throw new UnsupportedOperationException("Not implemented");
     }
 
-    public static long softmax(long handle, int axis) {
-        throw new UnsupportedOperationException("Not implemented");
-    }
+    public static native long softmax(long handle, int axis);
 
-    public static long logSoftmax(long handle, int axis) {
-        throw new UnsupportedOperationException("Not implemented");
-    }
+    public static native long logSoftmax(long handle, int axis);
 
     public static native long cumSum(long handle, int axis);
 
@@ -400,9 +396,7 @@ public final class RustLibrary {
         throw new UnsupportedOperationException("Not implemented");
     }
 
-    public static long sigmoid(long handle) {
-        throw new UnsupportedOperationException("Not implemented");
-    }
+    public static native long sigmoid(long handle);
 
     public static long softPlus(long handle) {
         throw new UnsupportedOperationException("Not implemented");
@@ -412,9 +406,7 @@ public final class RustLibrary {
         throw new UnsupportedOperationException("Not implemented");
     }
 
-    public static long leakyRelu(long handle, float alpha) {
-        throw new UnsupportedOperationException("Not implemented");
-    }
+    public static native long leakyRelu(long handle, float alpha);
 
     public static long elu(long handle, float alpha) {
         throw new UnsupportedOperationException("Not implemented");
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 6776f8d1f..c616d9948 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py
@@ -127,7 +127,7 @@ class HuggingfaceConverter:
             os.makedirs(temp_dir)
 
         tokenizer = AutoTokenizer.from_pretrained(model_id)
-        include_types = "token_type_ids" in tokenizer.model_input_names
+        include_types = config.model_type != "distilbert"
         hf_pipeline = PipelineHolder(tokenizer, ModelHolder(config))
         try:
             # Save tokenizer.json to temp dir
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 2d0f99827..a9f7b6d90 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/model_converter.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/model_converter.py
@@ -43,7 +43,7 @@ def main():
 
     if os.listdir(output_dir):
         logging.error(f"output directory: {output_dir} is not empty.")
-        return False
+        return 1
 
     if os.path.exists(args.model_id):
         logging.info(f"converting local model: {args.model_id}")
@@ -66,7 +66,7 @@ def main():
     if not task:
         logging.error(
             f"Unsupported model architecture: {arch} for {args.model_id}.")
-        return False
+        return 1
 
     converter = SUPPORTED_TASKS[task]
 
@@ -75,16 +75,15 @@ def main():
                                                  output_dir, False)
         if result:
             logging.info(f"Convert model {model_info.modelId} finished.")
-            return True
+            return 0
         else:
             logging.error(f"{model_info.modelId}: {reason}")
     except Exception as e:
         logging.warning(f"Failed to convert model: {model_info.modelId}.")
         logging.warning(e, exc_info=True)
 
-    return False
+    return 1
 
 
 if __name__ == "__main__":
-    if not main():
-        sys.exit(1)
+    sys.exit(main())
diff --git a/index1.0.html b/index1.0.html
index f31c18c8d..16ec42427 100644
--- a/index1.0.html
+++ b/index1.0.html
@@ -59,7 +59,7 @@
                     <li><a href="website/javadoc.html">JavaDoc</a></li>
                     <li><a href="https://github.com/deepjavalibrary/djl-demo?tab=readme-ov-file#deep-java-library-examples">Demos</a></li>
                     <li><a href="website/blog.html">Blogs</a></li>
-                    <li><a href="https://docs.djl.ai/docs/demos/jupyter/tutorial/index.html">Tutorial</a></li>
+                    <li><a href="https://docs.djl.ai/master/docs/demos/jupyter/tutorial/index.html">Tutorial</a></li>
                     <li><a href="https://github.com/deepjavalibrary/djl/tree/master/examples">Examples</a></li>
                     <li><a
                             href="https://deepjavalibrary.slack.com/join/shared_invite/zt-ar91gjkz-qbXhr1l~LFGEIEeGBibT7w#/">Slack</a>
@@ -73,7 +73,7 @@
                     <li><a href="website/javadoc.html">JavaDoc</a></li>
                     <li><a href="https://github.com/deepjavalibrary/djl-demo?tab=readme-ov-file#deep-java-library-examples">Demos</a></li>
                     <li><a href="website/blog.html">Blogs</a></li>
-                    <li><a href="https://docs.djl.ai/docs/demos/jupyter/tutorial/index.html">Tutorial</a></li>
+                    <li><a href="https://docs.djl.ai/master/docs/demos/jupyter/tutorial/index.html">Tutorial</a></li>
                     <li><a href="https://github.com/deepjavalibrary/djl/tree/master/examples">Examples</a></li>
                     <li><a
                             href="https://deepjavalibrary.slack.com/join/shared_invite/zt-ar91gjkz-qbXhr1l~LFGEIEeGBibT7w#/">Slack</a>
@@ -95,7 +95,7 @@
                     <h5 class="header col s12 light">Open source library to build and deploy deep learning in Java</h5>
                 </div>
                 <div class="row center">
-                    <a href="https://docs.djl.ai/docs/quick_start.html" id="download-button"
+                    <a href="https://docs.djl.ai/master/docs/quick_start.html" id="download-button"
                         class="btn-large waves-effect waves-light light-blue accent-2">Get Started</a>
                     <a href="https://github.com/deepjavalibrary/djl"
                         class="btn-large waves-effect waves-light light-blue accent-2">GitHub</a>
@@ -226,7 +226,7 @@
                 <div class="col l6 s12">
                     <h5 class="white-text">About Us</h5>
                     <p class="grey-text text-lighten-4">Built with <span style="color: red;">&hearts;</span> for the
-                        Java community by <a href="https://docs.djl.ai/leaders.html">DJL Developers</a></p>
+                        Java community by <a href="https://docs.djl.ai/master/leaders.html">DJL Developers</a></p>
                 </div>
             </div>
         </div>
diff --git a/jupyter/README.md b/jupyter/README.md
index 1b9a25842..175daf694 100644
--- a/jupyter/README.md
+++ b/jupyter/README.md
@@ -1,3 +1,3 @@
 # DJL - Jupyter notebooks
 
-The jupyter notebook documentation and examples have been moved to the [DJL Demos repo](http://docs.djl.ai/docs/demos/jupyter/index.html).
\ No newline at end of file
+The jupyter notebook documentation and examples have been moved to the [DJL Demos repo](https://docs.djl.ai/master/docs/demos/jupyter/index.html).
\ No newline at end of file
diff --git a/website/blog.html b/website/blog.html
index 1030f1151..e1f8a88e6 100644
--- a/website/blog.html
+++ b/website/blog.html
@@ -185,7 +185,7 @@
         <div class="row">
             <div class="col l6 s12">
                 <h5 class="white-text">About Us</h5>
-                <p class="grey-text text-lighten-4">Built with <span style="color: red;">&hearts;</span> for the Java community by <a href="https://docs.djl.ai/leaders.html">DJL Developers</a></p>
+                <p class="grey-text text-lighten-4">Built with <span style="color: red;">&hearts;</span> for the Java community by <a href="https://docs.djl.ai/master/leaders.html">DJL Developers</a></p>
             </div>
         </div>
     </div>
diff --git a/website/javadoc.html b/website/javadoc.html
index ead1f5613..83a274af3 100644
--- a/website/javadoc.html
+++ b/website/javadoc.html
@@ -114,7 +114,7 @@
         <div class="row">
             <div class="col l6 s12">
                 <h5 class="white-text">About Us</h5>
-                <p class="grey-text text-lighten-4">Built with <span style="color: red;">&hearts;</span> for the Java community by <a href="https://docs.djl.ai/leaders.html">DJL Developers</a></p>
+                <p class="grey-text text-lighten-4">Built with <span style="color: red;">&hearts;</span> for the Java community by <a href="https://docs.djl.ai/master/leaders.html">DJL Developers</a></p>
             </div>
         </div>
     </div>
diff --git a/website/js/index.js b/website/js/index.js
index 2944304de..367505827 100644
--- a/website/js/index.js
+++ b/website/js/index.js
@@ -27,7 +27,7 @@ let app = new Vue({
             },
             {
                 name: 'Tutorial',
-                url: 'https://docs.djl.ai/docs/demos/jupyter/tutorial/index.html'
+                url: 'https://docs.djl.ai/master/docs/demos/jupyter/tutorial/index.html'
             },
             {
                 name: 'Examples',
@@ -49,7 +49,7 @@ let app = new Vue({
         page1: {
             title: 'Deep Java Library',
             abstract: 'Open source library to build and deploy deep learning in Java',
-            getStartUtl: 'https://docs.djl.ai/docs/quick_start.html',
+            getStartUtl: 'https://docs.djl.ai/master/docs/quick_start.html',
             gitHubUrl: 'https://github.com/deepjavalibrary/djl',
         },
         page2: {
@@ -92,7 +92,7 @@ let app = new Vue({
         page5: {
             title: 'About Us',
             abstract: 'Built with ❤️ for the Java community by',
-            contactUrl: 'https://docs.djl.ai/leaders.html',
+            contactUrl: 'https://docs.djl.ai/master/leaders.html',
             leadrs: [
                 {
                     name: 'anthony',


Test patch:
diff --git a/api/src/test/java/ai/djl/repository/ZooTest.java b/api/src/test/java/ai/djl/repository/ZooTest.java
index 29fc10391..be965f3b2 100644
--- a/api/src/test/java/ai/djl/repository/ZooTest.java
+++ b/api/src/test/java/ai/djl/repository/ZooTest.java
@@ -15,6 +15,9 @@ package ai.djl.repository;
 import ai.djl.MalformedModelException;
 import ai.djl.modality.Input;
 import ai.djl.modality.Output;
+import ai.djl.ndarray.NDList;
+import ai.djl.nn.Block;
+import ai.djl.nn.Blocks;
 import ai.djl.repository.zoo.Criteria;
 import ai.djl.repository.zoo.ModelNotFoundException;
 import ai.djl.repository.zoo.ModelZoo;
@@ -23,9 +26,38 @@ 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;
 
 public class ZooTest {
 
+    @Test
+    public void testCriteria() throws ModelNotFoundException, IOException {
+        Block block = Blocks.identityBlock();
+        Path modelDir = Paths.get("build/model");
+        Files.createDirectories(modelDir);
+
+        Criteria<NDList, NDList> criteria =
+                Criteria.builder()
+                        .setTypes(NDList.class, NDList.class)
+                        .optModelPath(modelDir)
+                        .optBlock(block)
+                        .optOption("hasParameter", "false")
+                        .optEngine("PyTorch")
+                        .build();
+
+        criteria.downloadModel();
+        Assert.assertTrue(criteria.isDownloaded());
+
+        criteria =
+                Criteria.builder()
+                        .setTypes(NDList.class, NDList.class)
+                        .optEngine("Nonexist")
+                        .build();
+        Assert.assertThrows(criteria::isDownloaded);
+    }
+
     @Test
     public void testCriteriaToBuilder() {
         Criteria<Input, Output> criteria1 =
diff --git a/engines/mxnet/mxnet-model-zoo/src/test/java/ai/djl/mxnet/integration/MxSymbolBlockTest.java b/engines/mxnet/mxnet-model-zoo/src/test/java/ai/djl/mxnet/integration/MxSymbolBlockTest.java
index 8620d16ec..8ddec04fd 100644
--- a/engines/mxnet/mxnet-model-zoo/src/test/java/ai/djl/mxnet/integration/MxSymbolBlockTest.java
+++ b/engines/mxnet/mxnet-model-zoo/src/test/java/ai/djl/mxnet/integration/MxSymbolBlockTest.java
@@ -104,7 +104,7 @@ public class MxSymbolBlockTest {
         if ("MXNet".equals(Engine.getDefaultEngineName())) {
             // TODO: WARN The gradMeans (but not predictions or loss) changed during the upgrade
             // to MXNet 1.8. The issue affect only CPU, but GPU has not changed.
-            TestRequirements.gpu();
+            TestRequirements.gpu("MXNet");
         }
 
         TrainingConfig config =
diff --git a/extensions/tokenizers/src/test/java/ai/djl/engine/rust/NDArrayTests.java b/extensions/tokenizers/src/test/java/ai/djl/engine/rust/NDArrayTests.java
index 8f9523c7f..5ef89ee95 100644
--- a/extensions/tokenizers/src/test/java/ai/djl/engine/rust/NDArrayTests.java
+++ b/extensions/tokenizers/src/test/java/ai/djl/engine/rust/NDArrayTests.java
@@ -18,6 +18,7 @@ import ai.djl.ndarray.NDList;
 import ai.djl.ndarray.NDManager;
 import ai.djl.ndarray.types.DataType;
 import ai.djl.ndarray.types.Shape;
+import ai.djl.nn.Activation;
 import ai.djl.testing.Assertions;
 import ai.djl.testing.TestRequirements;
 
@@ -118,7 +119,7 @@ public class NDArrayTests {
 
     @Test
     public void testI64toF16() {
-        TestRequirements.notGpu();
+        TestRequirements.notGpu("Rust");
         try (NDManager manager = NDManager.newBaseManager("Rust")) {
             NDArray array = manager.create(2);
             Assert.assertEquals(array.getDataType(), DataType.INT32);
@@ -215,6 +216,70 @@ public class NDArrayTests {
         }
     }
 
+    @Test
+    public void testSoftmax() {
+        try (NDManager manager = NDManager.newBaseManager("Rust")) {
+            NDArray array = manager.ones(new Shape(10));
+            NDArray expected = manager.zeros(new Shape(10)).add(0.1f);
+            Assertions.assertAlmostEquals(array.softmax(0), expected);
+            // test multi-dim
+            array = manager.ones(new Shape(2, 3, 1, 3));
+            expected = manager.zeros(new Shape(2, 3, 1, 3)).add(0.5f);
+            Assertions.assertAlmostEquals(array.softmax(0), expected);
+            expected = manager.zeros(new Shape(2, 3, 1, 3)).add(0.33333334f);
+            Assertions.assertAlmostEquals(array.softmax(1), expected);
+            expected = manager.ones(new Shape(2, 3, 1, 3));
+            Assertions.assertAlmostEquals(array.softmax(2), expected);
+            expected = manager.zeros(new Shape(2, 3, 1, 3)).add(0.33333334f);
+            Assertions.assertAlmostEquals(array.softmax(3), expected);
+            // test scalar
+            array = manager.create(1f);
+            Assertions.assertAlmostEquals(array.softmax(0), array);
+            // test zero
+            array = manager.create(new Shape(2, 0, 1));
+            Assertions.assertAlmostEquals(array.softmax(0), array);
+        }
+    }
+
+    @Test
+    public void testLogSoftmax() {
+        try (NDManager manager = NDManager.newBaseManager("Rust")) {
+            NDArray array = manager.ones(new Shape(10));
+            NDArray expected = manager.zeros(new Shape(10)).add(-2.3025851f);
+            Assertions.assertAlmostEquals(array.logSoftmax(0), expected);
+            // test multi-dim
+            array = manager.ones(new Shape(2, 3, 1, 3));
+            expected = manager.zeros(new Shape(2, 3, 1, 3)).add(-0.6931472f);
+            Assertions.assertAlmostEquals(array.logSoftmax(0), expected);
+            expected = manager.zeros(new Shape(2, 3, 1, 3)).add(-1.0986123f);
+            Assertions.assertAlmostEquals(array.logSoftmax(1), expected);
+            // test scalar
+            array = manager.create(1f);
+            Assertions.assertAlmostEquals(array.softmax(0), array);
+            // test zero
+            array = manager.create(new Shape(2, 0, 1));
+            Assertions.assertAlmostEquals(array.softmax(0), array);
+        }
+    }
+
+    @Test
+    public void testSigmoid() {
+        try (NDManager manager = NDManager.newBaseManager("Rust")) {
+            NDArray data = manager.create(new float[] {0});
+            NDArray expected = manager.create(new float[] {0.5f});
+            Assertions.assertAlmostEquals(Activation.sigmoid(data), expected);
+        }
+    }
+
+    @Test
+    public void testLeakyRelu() {
+        try (NDManager manager = NDManager.newBaseManager("Rust")) {
+            NDArray data = manager.create(new float[] {-1, 0, 2});
+            NDArray expected = manager.create(new float[] {-1, 0, 2});
+            Assert.assertEquals(Activation.leakyRelu(data, 1.0f), expected);
+        }
+    }
+
     @Test
     public void testExpandDim() {
         try (NDManager manager = NDManager.newBaseManager("Rust")) {
diff --git a/integration/src/main/java/ai/djl/integration/tests/inference/PredictorTest.java b/integration/src/main/java/ai/djl/integration/tests/inference/PredictorTest.java
index 896407e49..d2c81338b 100644
--- a/integration/src/main/java/ai/djl/integration/tests/inference/PredictorTest.java
+++ b/integration/src/main/java/ai/djl/integration/tests/inference/PredictorTest.java
@@ -36,7 +36,7 @@ public class PredictorTest {
 
     @Test
     public void testPredictorWithDeviceGpu() throws TranslateException {
-        TestRequirements.gpu();
+        TestRequirements.gpu(TestUtils.getEngine());
 
         // CPU model, GPU predictor
         predictWithDeviceHelper(Device.cpu(), Device.gpu());
@@ -44,7 +44,7 @@ public class PredictorTest {
 
     @Test
     public void testPredictorWithDeviceCpu() throws TranslateException {
-        TestRequirements.gpu();
+        TestRequirements.gpu(TestUtils.getEngine());
 
         // GPU model, CPU predictor
         predictWithDeviceHelper(Device.gpu(), Device.cpu());
diff --git a/integration/src/main/java/ai/djl/integration/tests/model_zoo/classification/VGGTest.java b/integration/src/main/java/ai/djl/integration/tests/model_zoo/classification/VGGTest.java
index a9a43257b..3ddf5877b 100644
--- a/integration/src/main/java/ai/djl/integration/tests/model_zoo/classification/VGGTest.java
+++ b/integration/src/main/java/ai/djl/integration/tests/model_zoo/classification/VGGTest.java
@@ -46,7 +46,7 @@ public class VGGTest {
     public void testTrainWithDefaultChannels() {
         // Vgg is too large to be fit into the github action cpu main memory
         // only run the test when we have GPU
-        TestRequirements.gpu();
+        TestRequirements.gpu(TestUtils.getEngine());
 
         TrainingConfig config =
                 new DefaultTrainingConfig(Loss.softmaxCrossEntropyLoss())
diff --git a/testing/src/main/java/ai/djl/testing/TestRequirements.java b/testing/src/main/java/ai/djl/testing/TestRequirements.java
index 09063a8f0..d39d7be9d 100644
--- a/testing/src/main/java/ai/djl/testing/TestRequirements.java
+++ b/testing/src/main/java/ai/djl/testing/TestRequirements.java
@@ -51,16 +51,24 @@ public final class TestRequirements {
         }
     }
 
-    /** Requires a test have at least one gpu. */
-    public static void gpu() {
-        if (Engine.getInstance().getGpuCount() == 0) {
+    /**
+     * Requires a test have at least one gpu.
+     *
+     * @param engineName the engine name
+     */
+    public static void gpu(String engineName) {
+        if (Engine.getEngine(engineName).getGpuCount() == 0) {
             throw new SkipException("This test requires a GPU to run");
         }
     }
 
-    /** Requires the test runs on non-GPU. */
-    public static void notGpu() {
-        if (Engine.getInstance().getGpuCount() > 0) {
+    /**
+     * Requires the test runs on non-GPU.
+     *
+     * @param engineName the engine name
+     */
+    public static void notGpu(String engineName) {
+        if (Engine.getEngine(engineName).getGpuCount() > 0) {
             throw new SkipException("This test requires non-GPU to run");
         }
     }