Repository: deepjavalibrary/djl
Pull request #2877 (2023-11-26 19:11:00)

[api] Update offline property name to avoid conflict with other app.
## 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/.github/workflows/native_s3_huggingface.yml b/.github/workflows/native_s3_huggingface.yml
index e5a3d57fe..0492ce67e 100644
--- a/.github/workflows/native_s3_huggingface.yml
+++ b/.github/workflows/native_s3_huggingface.yml
@@ -155,8 +155,8 @@ jobs:
       - name: Copy files to S3 with the AWS CLI
         run: |
           TOKENIZERS_VERSION="$(cat gradle.properties | awk -F '=' '/tokenizers_version/ {print $2}')"
-          aws s3 sync extensions/tokenizers/jnilib s3://djl-ai/publish/tokenizers/${TOKENIZERS_VERSION}/jnilib/
-          aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/tokenizers/${TOKENIZERS_VERSION}/jnilib/*"
+          /opt/homebrew/bin/aws s3 sync extensions/tokenizers/jnilib s3://djl-ai/publish/tokenizers/${TOKENIZERS_VERSION}/jnilib/
+          /opt/homebrew/bin/aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/tokenizers/${TOKENIZERS_VERSION}/jnilib/*"
 
   create-aarch64-runner:
     if: github.repository == 'deepjavalibrary/djl'
@@ -179,15 +179,14 @@ jobs:
     runs-on: [ self-hosted, aarch64 ]
     timeout-minutes: 30
     needs: create-aarch64-runner
-    container: centos:centos7
+    container: amazonlinux:2
     steps:
       - name: Install Environment
         run: |
           yum -y update
-          yum -y install centos-release-scl-rh epel-release perl-core
-          yum -y install devtoolset-7 git patch cmake3 libstdc++-static
+          yum -y groupinstall "Development Tools"
+          yum -y install patch perl-IPC-Cmd cmake3
           ln -s /usr/bin/cmake3 /usr/bin/cmake
-          curl https://sh.rustup.rs -sSf | sh -s -- -y
           pip3 install awscli --upgrade
       - uses: actions-rs/toolchain@v1
         with:
@@ -207,8 +206,6 @@ jobs:
             ${{ runner.os }}-gradle-
       - name: Release JNI prep
         run: |
-          source "$HOME/.cargo/env"
-          export PATH=$PATH:/opt/rh/devtoolset-7/root/usr/bin
           ./gradlew :extensions:tokenizers:compileJNI
           PYTORCH_PRECXX11=true ./gradlew -Pjni :extensions:tokenizers:test
       - name: Configure AWS Credentials
diff --git a/api/src/main/java/ai/djl/repository/AbstractRepository.java b/api/src/main/java/ai/djl/repository/AbstractRepository.java
index 3b83c359a..2ca087fdf 100644
--- a/api/src/main/java/ai/djl/repository/AbstractRepository.java
+++ b/api/src/main/java/ai/djl/repository/AbstractRepository.java
@@ -243,7 +243,7 @@ public abstract class AbstractRepository implements Repository {
         bis = new CloseShieldFilterInputStream(bis);
         try (TarArchiveInputStream tis = new TarArchiveInputStream(bis)) {
             TarArchiveEntry entry;
-            while ((entry = tis.getNextTarEntry()) != null) {
+            while ((entry = tis.getNextEntry()) != null) {
                 String entryName = entry.getName();
                 if (entryName.contains("..")) {
                     throw new IOException("Malicious zip entry: " + entryName);
diff --git a/api/src/main/java/ai/djl/repository/RemoteRepository.java b/api/src/main/java/ai/djl/repository/RemoteRepository.java
index 6b01ce14e..52f87afbf 100644
--- a/api/src/main/java/ai/djl/repository/RemoteRepository.java
+++ b/api/src/main/java/ai/djl/repository/RemoteRepository.java
@@ -75,7 +75,7 @@ public class RemoteRepository extends AbstractRepository {
                 Metadata metadata = JsonUtils.GSON_PRETTY.fromJson(reader, Metadata.class);
                 metadata.init(arguments);
                 Date lastUpdated = metadata.getLastUpdated();
-                if (Boolean.getBoolean("offline")
+                if (Utils.isOfflineMode()
                         || System.currentTimeMillis() - lastUpdated.getTime() < ONE_DAY) {
                     metadata.setRepositoryUri(mrlUri);
                     return metadata;
diff --git a/api/src/main/java/ai/djl/util/Ec2Utils.java b/api/src/main/java/ai/djl/util/Ec2Utils.java
index 178c3d7ef..540818296 100644
--- a/api/src/main/java/ai/djl/util/Ec2Utils.java
+++ b/api/src/main/java/ai/djl/util/Ec2Utils.java
@@ -97,7 +97,7 @@ public final class Ec2Utils {
      * @param engine the default engine name
      */
     public static void callHome(String engine) {
-        if (Boolean.getBoolean("offline")
+        if (Utils.isOfflineMode()
                 || Boolean.parseBoolean(Utils.getEnvOrSystemProperty("OPT_OUT_TRACKING"))
                 || System.currentTimeMillis() - lastCheckIn < ONE_DAY) {
             return;
diff --git a/api/src/main/java/ai/djl/util/Utils.java b/api/src/main/java/ai/djl/util/Utils.java
index c8e1bd514..270958d5b 100644
--- a/api/src/main/java/ai/djl/util/Utils.java
+++ b/api/src/main/java/ai/djl/util/Utils.java
@@ -357,6 +357,20 @@ public final class Utils {
         return Paths.get(cacheDir);
     }
 
+    /**
+     * Returns if offline mode is enabled.
+     *
+     * @return true if offline mode is enabled
+     */
+    public static boolean isOfflineMode() {
+        String mode = getenv("DJL_OFFLINE", System.getProperty("ai.djl.offline"));
+        if (mode != null) {
+            return Boolean.parseBoolean(mode);
+        }
+        // backward compatible
+        return Boolean.getBoolean("offline");
+    }
+
     /**
      * Returns nested model directory if the directory contains only one subdirectory.
      *
@@ -481,7 +495,7 @@ public final class Utils {
      */
     public static InputStream openUrl(URL url) throws IOException {
         String protocol = url.getProtocol();
-        if (Boolean.getBoolean("offline")
+        if (isOfflineMode()
                 && ("http".equalsIgnoreCase(protocol) || "https".equalsIgnoreCase(protocol))) {
             throw new IOException("Offline model is enabled.");
         }
diff --git a/bom/build.gradle b/bom/build.gradle
index 736e8e4a5..aa17702f9 100644
--- a/bom/build.gradle
+++ b/bom/build.gradle
@@ -109,15 +109,15 @@ publishing {
                     addDependency(dependencies, "ai.djl.mxnet", "mxnet-native-mkl", "linux-x86_64", "${mxnet_version}")
                     addDependency(dependencies, "ai.djl.mxnet", "mxnet-native-mkl", "win-x86_64", "${mxnet_version}")
                     addDependency(dependencies, "ai.djl.mxnet", "mxnet-native-cu112mkl", "linux-x86_64", "${mxnet_version}")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu", "osx-x86_64", "${pytorch_version}-SNAPSHOT")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu", "osx-aarch64", "${pytorch_version}-SNAPSHOT")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu", "linux-x86_64", "${pytorch_version}-SNAPSHOT")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu", "win-x86_64", "${pytorch_version}-SNAPSHOT")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu-precxx11", "linux-x86_64", "${pytorch_version}-SNAPSHOT")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu-precxx11", "linux-aarch64", "${pytorch_version}-SNAPSHOT")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu121", "linux-x86_64", "${pytorch_version}-SNAPSHOT")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu121", "win-x86_64", "${pytorch_version}-SNAPSHOT")
-                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu121-precxx11", "linux-x86_64", "${pytorch_version}-SNAPSHOT")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu", "osx-x86_64", "${pytorch_version}")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu", "osx-aarch64", "${pytorch_version}")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu", "linux-x86_64", "${pytorch_version}")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu", "win-x86_64", "${pytorch_version}")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu-precxx11", "linux-x86_64", "${pytorch_version}")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cpu-precxx11", "linux-aarch64", "${pytorch_version}")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu121", "linux-x86_64", "${pytorch_version}")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu121", "win-x86_64", "${pytorch_version}")
+                    addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu121-precxx11", "linux-x86_64", "${pytorch_version}")
                     addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu117", "linux-x86_64", "1.13.1")
                     addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu117", "win-x86_64", "1.13.1")
                     addDependency(dependencies, "ai.djl.pytorch", "pytorch-native-cu117-precxx11", "linux-x86_64", "1.13.1")
diff --git a/build.gradle b/build.gradle
index f98b86c4e..4b8b20cd5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -88,7 +88,7 @@ configure(javaProjects()) {
             systemProperty "disableProgressBar", "true"
             systemProperty "nightly", System.getProperty("nightly", "false")
             if (gradle.startParameter.offline) {
-                systemProperty "offline", "true"
+                systemProperty "ai.djl.offline", "true"
             }
             // This is used to avoid overriding on default engine for modules:
             // mxnet-engine, mxnet-model-zoo, api (MockEngine), basicdataset, fasttext, etc
diff --git a/docs/telemetry.md b/docs/telemetry.md
index d6ff9b20b..256adf00a 100644
--- a/docs/telemetry.md
+++ b/docs/telemetry.md
@@ -20,5 +20,5 @@ System.setProperty("OPT_OUT_TRACKING", "true")
 Usage tracking is also disable in `offline` mode:
 
 ```java
-System.setProperty("offline", "true")
+System.setProperty("ai.djl.offline", "true")
 ```
diff --git a/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtEngineProvider.java b/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtEngineProvider.java
index 1b9cdd0ab..42ca3c5b8 100644
--- a/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtEngineProvider.java
+++ b/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtEngineProvider.java
@@ -18,6 +18,8 @@ import ai.djl.engine.EngineProvider;
 /** {@code PtEngineProvider} is the PyTorch implementation of {@link EngineProvider}. */
 public class PtEngineProvider implements EngineProvider {
 
+    private static volatile Engine engine; // NOPMD
+
     /** {@inheritDoc} */
     @Override
     public String getEngineName() {
@@ -33,10 +35,13 @@ public class PtEngineProvider implements EngineProvider {
     /** {@inheritDoc} */
     @Override
     public Engine getEngine() {
-        return InstanceHolder.INSTANCE;
-    }
-
-    private static class InstanceHolder {
-        static final Engine INSTANCE = PtEngine.newInstance();
+        if (engine == null) {
+            synchronized (PtEngineProvider.class) {
+                if (engine == null) {
+                    engine = PtEngine.newInstance();
+                }
+            }
+        }
+        return engine;
     }
 }
diff --git a/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/jni/LibUtils.java b/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/jni/LibUtils.java
index 9d4224639..860c1bc48 100644
--- a/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/jni/LibUtils.java
+++ b/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/jni/LibUtils.java
@@ -106,10 +106,6 @@ public final class LibUtils {
 
     private static void loadLibTorch(LibTorch libTorch) {
         Path libDir = libTorch.dir.toAbsolutePath();
-        if ("1.8.1".equals(getVersion()) && System.getProperty("os.name").startsWith("Mac")) {
-            // PyTorch 1.8.1 libtorch_cpu.dylib cannot be loaded individually
-            return;
-        }
         boolean isCuda = libTorch.flavor.contains("cu");
         List<String> deferred =
                 Arrays.asList(
@@ -120,6 +116,7 @@ public final class LibUtils {
                         System.mapLibraryName("torch_cuda_cpp"),
                         System.mapLibraryName("torch_cuda_cu"),
                         System.mapLibraryName("torch_cuda"),
+                        System.mapLibraryName("nvfuser_codegen"),
                         System.mapLibraryName("torch"));
 
         Set<String> loadLater = new HashSet<>(deferred);
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/zoo/HfModelZoo.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/zoo/HfModelZoo.java
index 9ee8fc19c..a0099073c 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/zoo/HfModelZoo.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/zoo/HfModelZoo.java
@@ -131,8 +131,7 @@ public class HfModelZoo extends ModelZoo {
             Path file = dir.resolve("models.json");
             if (Files.exists(file)) {
                 long lastModified = Files.getLastModifiedTime(file).toMillis();
-                if (Boolean.getBoolean("offline")
-                        || System.currentTimeMillis() - lastModified < ONE_DAY) {
+                if (Utils.isOfflineMode() || System.currentTimeMillis() - lastModified < ONE_DAY) {
                     try (Reader reader = Files.newBufferedReader(file)) {
                         return JsonUtils.GSON.fromJson(reader, type);
                     }
diff --git a/extensions/tokenizers/src/main/python/huggingface_converter.py b/extensions/tokenizers/src/main/python/huggingface_converter.py
index f3b85c241..1efabb61c 100644
--- a/extensions/tokenizers/src/main/python/huggingface_converter.py
+++ b/extensions/tokenizers/src/main/python/huggingface_converter.py
@@ -41,9 +41,20 @@ class HuggingfaceConverter:
         if not os.path.exists(temp_dir):
             os.makedirs(temp_dir)
 
-        hf_pipeline = self.load_model(model_id)
-        # Save tokenizer.json to temp dir
-        self.save_tokenizer(hf_pipeline, temp_dir)
+        try:
+            hf_pipeline = self.load_model(model_id)
+        except Exception as e:
+            logging.warning(f"Failed to load model: {model_id}.")
+            logging.warning(e, exc_info=True)
+            return False, "Failed to load model", -1
+
+        try:
+            # Save tokenizer.json to temp dir
+            self.save_tokenizer(hf_pipeline, temp_dir)
+        except Exception as e:
+            logging.warning(f"Failed to save tokenizer: {model_id}.")
+            logging.warning(e, exc_info=True)
+            return False, "Failed to save tokenizer", -1
 
         # Save config.json just for reference
         config = hf_hub_download(repo_id=model_id, filename="config.json")
@@ -112,7 +123,7 @@ class HuggingfaceConverter:
             logging.info(f"Saving torchscript model: {model_name}.pt ...")
             model_file = os.path.join(temp_dir, f"{model_name}.pt")
             script_module.save(model_file)
-        except (RuntimeError, ValueError) as e:
+        except Exception as e:
             logging.warning(f"Failed to trace model: {model_id}.")
             logging.warning(e, exc_info=True)
             return None
diff --git a/extensions/tokenizers/src/main/python/huggingface_models.py b/extensions/tokenizers/src/main/python/huggingface_models.py
index 549db3788..12db9cf2c 100644
--- a/extensions/tokenizers/src/main/python/huggingface_models.py
+++ b/extensions/tokenizers/src/main/python/huggingface_models.py
@@ -56,6 +56,8 @@ class HuggingfaceModels:
         self.temp_dir = f"{self.output_dir}/tmp"
 
     def list_models(self, args: Namespace) -> List[dict]:
+        import_all = os.environ.get("HF_IMPORT_ALL")
+
         api = HfApi()
         if args.model_name:
             models = api.list_models(filter="pytorch",
@@ -63,16 +65,20 @@ class HuggingfaceModels:
                                      sort="downloads",
                                      direction=-1,
                                      limit=args.limit)
-            if not models:
-                logging.warning(f"no model found: {args.model_name}.")
+            import_all = True
         else:
             models = api.list_models(filter=f"{args.category},pytorch",
                                      sort="downloads",
                                      direction=-1,
                                      limit=args.limit)
-            if not models:
+        if not models:
+            if args.model_name:
+                logging.warning(f"no model found: {args.model_name}.")
+            else:
                 logging.warning(f"no model matches category: {args.category}.")
 
+            return []
+
         ret = []
         for model_info in models:
             model_id = model_info.modelId
@@ -83,7 +89,7 @@ class HuggingfaceModels:
                 continue
 
             languages = get_lang_tags(model_info)
-            if "en" not in languages and not os.environ["HF_IMPORT_ALL"]:
+            if "en" not in languages and not import_all:
                 logging.warning(f"Skip non-English model: {model_id}.")
                 continue
 
@@ -94,6 +100,12 @@ class HuggingfaceModels:
                     logging.info(f"Skip converted model: {model_id}.")
                     continue
 
+            if model_info.downloads < 50 and not import_all:
+                logging.info(
+                    f"Skip model {model_info.modelId}, downloads {model_info.downloads} < 50"
+                )
+                continue
+
             try:
                 config = hf_hub_download(repo_id=model_id,
                                          filename="config.json")
diff --git a/extensions/tokenizers/src/main/python/model_zoo_importer.py b/extensions/tokenizers/src/main/python/model_zoo_importer.py
index 9ed32ec58..0ed67bd10 100644
--- a/extensions/tokenizers/src/main/python/model_zoo_importer.py
+++ b/extensions/tokenizers/src/main/python/model_zoo_importer.py
@@ -49,9 +49,17 @@ def main():
         model_info = model["model_info"]
         converter = SUPPORTED_TASK[task]
 
-        result, reason, size = converter.save_model(model_info, args, temp_dir)
-        if not result:
-            logging.error(f"{model_info.modelId}: {reason}")
+        try:
+            result, reason, size = converter.save_model(
+                model_info, args, temp_dir)
+            if not result:
+                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)
+            result = False
+            reason = "Failed to convert model"
+            size = -1
 
         huggingface_models.update_progress(model_info, converter.application,
                                            result, reason, size, args.cpu_only)
diff --git a/gradle.properties b/gradle.properties
index 20426a4d0..9c8822596 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,20 +20,20 @@ trt_version=8.4.1
 onnxruntime_version=1.16.0
 paddlepaddle_version=2.3.2
 sentencepiece_version=0.1.97
-tokenizers_version=0.14.1
+tokenizers_version=0.15.0
 fasttext_version=0.9.2
 xgboost_version=2.0.1
 lightgbm_version=3.2.110
 rapis_version=22.12.0
 
-commons_cli_version=1.5.0
-commons_compress_version=1.23.0
+commons_cli_version=1.6.0
+commons_compress_version=1.25.0
 commons_csv_version=1.10.0
 commons_logging_version=1.2
 gson_version=2.10.1
 jna_version=5.13.0
 slf4j_version=1.7.36
-log4j_slf4j_version=2.21.0
+log4j_slf4j_version=2.22.0
 awssdk_version=2.20.121
 hadoop_version=3.3.5
 javacpp_version=1.5.9


Test patch:
diff --git a/examples/src/test/java/ai/djl/testing/TestRequirements.java b/examples/src/test/java/ai/djl/testing/TestRequirements.java
index e8c9bd4bd..01eef7562 100644
--- a/examples/src/test/java/ai/djl/testing/TestRequirements.java
+++ b/examples/src/test/java/ai/djl/testing/TestRequirements.java
@@ -14,6 +14,7 @@ package ai.djl.testing;
 
 import ai.djl.engine.Engine;
 import ai.djl.engine.EngineException;
+import ai.djl.util.Utils;
 
 import org.testng.SkipException;
 
@@ -45,7 +46,7 @@ public final class TestRequirements {
 
     /** Requires a test not be run in offline mode. */
     public static void notOffline() {
-        if (Boolean.getBoolean("offline")) {
+        if (Utils.isOfflineMode()) {
             throw new SkipException("This test can not run while offline");
         }
     }
diff --git a/extensions/tokenizers/src/test/java/ai/djl/huggingface/zoo/ModelZooTest.java b/extensions/tokenizers/src/test/java/ai/djl/huggingface/zoo/ModelZooTest.java
index e585a219f..5c3ed6c3e 100644
--- a/extensions/tokenizers/src/test/java/ai/djl/huggingface/zoo/ModelZooTest.java
+++ b/extensions/tokenizers/src/test/java/ai/djl/huggingface/zoo/ModelZooTest.java
@@ -103,21 +103,21 @@ public class ModelZooTest {
     @Test
     public void testOffLine() throws IOException {
         System.setProperty("DJL_CACHE_DIR", "build/cache");
-        System.setProperty("offline", "true");
+        System.setProperty("ai.djl.offline", "true");
         try {
             Utils.deleteQuietly(Paths.get("build/cache"));
             // static variables cannot not be initialized properly if directly use new HfModelZoo()
             ModelZoo.getModelZoo("ai.djl.huggingface.pytorch");
 
             ModelZoo zoo = new HfModelZoo();
-            Assert.assertTrue(zoo.getModelLoaders().size() > 0);
+            Assert.assertFalse(zoo.getModelLoaders().isEmpty());
 
             Set<String> engines = zoo.getSupportedEngines();
             Assert.assertEquals(engines.size(), 1);
             Assert.assertEquals(engines.iterator().next(), "PyTorch");
         } finally {
             System.clearProperty("DJL_CACHE_DIR");
-            System.clearProperty("offline");
+            System.clearProperty("ai.djl.offline");
         }
     }
 }
diff --git a/testing/src/main/java/ai/djl/testing/TestRequirements.java b/testing/src/main/java/ai/djl/testing/TestRequirements.java
index bf57d64bd..32f242589 100644
--- a/testing/src/main/java/ai/djl/testing/TestRequirements.java
+++ b/testing/src/main/java/ai/djl/testing/TestRequirements.java
@@ -13,6 +13,7 @@
 package ai.djl.testing;
 
 import ai.djl.engine.Engine;
+import ai.djl.util.Utils;
 
 import org.testng.SkipException;
 
@@ -45,7 +46,7 @@ public final class TestRequirements {
 
     /** Requires a test not be run in offline mode. */
     public static void notOffline() {
-        if (Boolean.getBoolean("offline")) {
+        if (Utils.isOfflineMode()) {
             throw new SkipException("This test can not run while offline");
         }
     }