Repository: deepjavalibrary/djl
Pull request #3502 (2024-10-15 21:12:12)

Bump com.google.protobuf:protobuf-java from 3.25.4 to 3.25.5
Bumps [com.google.protobuf:protobuf-java](https://github.com/protocolbuffers/protobuf) from 3.25.4 to 3.25.5.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/9d0ec0f92b5b5fdeeda11f9dcecc1872ff378014"><code>9d0ec0f</code></a> Updating version.json and repo version numbers to: 25.5</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/4a197e78ad2430e22e992c5a7727b61ae220f727"><code>4a197e7</code></a> Merge pull request <a href="https://redirect.github.com/protocolbuffers/protobuf/issues/18387">#18387</a> from protocolbuffers/cp-lp-25</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/b5a7cf7cf4b7e39f6b02205e45afe2104a7faf81"><code>b5a7cf7</code></a> Remove RecursiveGroup test case which doesn't exist in 25.x pre-Editions</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/f000b7e18fd6921ca02ea4b87608e8cadcb7b64f"><code>f000b7e</code></a> Fix merge conflict by adding optional label to proto2 unittest_lite.proto</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/4728531c162f2f9e8c2ca1add713cfee2db6be3b"><code>4728531</code></a> Add recursion check when parsing unknown fields in Java.</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/850fcce9176e2c9070614dab53537760498c926b"><code>850fcce</code></a> Internal change</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/b7044987de77f1dc368fee558636d0b56d7e75e1"><code>b704498</code></a> Internal change</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/e67347986eaf7d777a6ee34367fa99f4912423ab"><code>e673479</code></a> Fix cord handling in DynamicMessage and oneofs. (<a href="https://redirect.github.com/protocolbuffers/protobuf/issues/18375">#18375</a>)</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/8a60b6527a976cfd0028153da3ad8e4ed280e0de"><code>8a60b65</code></a> Merge pull request <a href="https://redirect.github.com/protocolbuffers/protobuf/issues/17704">#17704</a> from protocolbuffers/cp-segv</li>
<li><a href="https://github.com/protocolbuffers/protobuf/commit/94a26630e362a4771b5ec80eac49f494988ca408"><code>94a2663</code></a> Fixed a SEGV when deep copying a non-reified sub-message.</li>
<li>Additional commits viewable in <a href="https://github.com/protocolbuffers/protobuf/compare/v3.25.4...v3.25.5">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.google.protobuf:protobuf-java&package-manager=gradle&previous-version=3.25.4&new-version=3.25.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/deepjavalibrary/djl/network/alerts).

</details>

codecov-commenter: :warning: Please install the !['codecov app svg image'](https://github.com/codecov/engineering-team/assets/152432831/e90313f4-9d3a-4b63-8b54-cfe14e7ec20d) to ensure uploads and comments are reliably processed by Codecov.

## [Codecov](https://app.codecov.io/gh/deepjavalibrary/djl/pull/3502?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 58.91%. Comparing base [(`b6656ed`)](https://app.codecov.io/gh/deepjavalibrary/djl/commit/b6656ed60d823ee835bd69fb4e42a8244038b807?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) to head [(`baa4a3d`)](https://app.codecov.io/gh/deepjavalibrary/djl/commit/baa4a3d4aa98ebd6a64db53ee0a6829c359806f0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).
> Report is 10 commits behind head on master.

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@              Coverage Diff              @@
##             master    #3502       +/-   ##
=============================================
- Coverage     72.52%   58.91%   -13.62%     
+ Complexity     7394     5850     -1544     
=============================================
  Files           672      674        +2     
  Lines         32909    33021      +112     
  Branches       3501     3516       +15     
=============================================
- Hits          23868    19455     -4413     
- Misses         7400    12085     +4685     
+ Partials       1641     1481      -160     
```

| [Flag](https://app.codecov.io/gh/deepjavalibrary/djl/pull/3502/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) | Coverage Δ | |
|---|---|---|
| [](https://app.codecov.io/gh/deepjavalibrary/djl/pull/3502/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) | `?` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/deepjavalibrary/djl/pull/3502?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).


Patch:
diff --git a/api/src/main/java/ai/djl/modality/nlp/translator/QaServingTranslator.java b/api/src/main/java/ai/djl/modality/nlp/translator/QaServingTranslator.java
index 45e3f59af..7224a6d40 100644
--- a/api/src/main/java/ai/djl/modality/nlp/translator/QaServingTranslator.java
+++ b/api/src/main/java/ai/djl/modality/nlp/translator/QaServingTranslator.java
@@ -30,14 +30,17 @@ import com.google.gson.JsonParseException;
 import com.google.gson.reflect.TypeToken;
 
 import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * A {@link Translator} that can handle generic question answering {@link Input} and {@link Output}.
  */
 public class QaServingTranslator implements NoBatchifyTranslator<Input, Output> {
 
-    private static final Type LIST_TYPE = new TypeToken<List<String>>() {}.getType();
+    private static final Type LIST_TYPE = new TypeToken<List<QAInput>>() {}.getType();
 
     private Translator<QAInput, String> translator;
 
@@ -116,13 +119,20 @@ public class QaServingTranslator implements NoBatchifyTranslator<Input, Output>
         Output output = new Output();
         output.addProperty("Content-Type", "application/json");
         if (ctx.getAttachment("batch") != null) {
-            output.add(BytesSupplier.wrapAsJson(translator.batchProcessOutput(ctx, list)));
+            List<String> answers = translator.batchProcessOutput(ctx, list);
+            List<Map<String, String>> ret = new ArrayList<>();
+            for (String answer : answers) {
+                ret.add(Collections.singletonMap("answer", answer));
+            }
+            output.add(BytesSupplier.wrapAsJson(ret));
         } else {
             Batchifier batchifier = translator.getBatchifier();
             if (batchifier != null) {
                 list = batchifier.unbatchify(list)[0];
             }
-            output.add(translator.processOutput(ctx, list));
+            String answer = translator.processOutput(ctx, list);
+            Map<String, String> ret = Collections.singletonMap("answer", answer);
+            output.add(BytesSupplier.wrapAsJson(ret));
         }
         return output;
     }
diff --git a/api/src/main/java/ai/djl/modality/nlp/translator/TokenClassificationServingTranslator.java b/api/src/main/java/ai/djl/modality/nlp/translator/TokenClassificationServingTranslator.java
index a6ab6e8af..f6b1fd386 100644
--- a/api/src/main/java/ai/djl/modality/nlp/translator/TokenClassificationServingTranslator.java
+++ b/api/src/main/java/ai/djl/modality/nlp/translator/TokenClassificationServingTranslator.java
@@ -56,6 +56,7 @@ public class TokenClassificationServingTranslator implements NoBatchifyTranslato
         TextPrompt prompt = TextPrompt.parseInput(input);
         if (prompt.isBatch()) {
             ctx.setAttachment("batch", Boolean.TRUE);
+            return translator.batchProcessInput(ctx, prompt.getBatch());
         }
 
         NDList ret = translator.processInput(ctx, prompt.getText());
diff --git a/api/src/main/java/ai/djl/ndarray/BytesSupplierImpl.java b/api/src/main/java/ai/djl/ndarray/BytesSupplierImpl.java
index d3f7198c1..e6af90ebb 100644
--- a/api/src/main/java/ai/djl/ndarray/BytesSupplierImpl.java
+++ b/api/src/main/java/ai/djl/ndarray/BytesSupplierImpl.java
@@ -40,7 +40,7 @@ class BytesSupplierImpl implements BytesSupplier {
     public byte[] getAsBytes() {
         if (buf == null) {
             if (value == null) {
-                value = JsonUtils.toJson(obj) + '\n';
+                value = JsonUtils.toJson(obj);
             }
             buf = value.getBytes(StandardCharsets.UTF_8);
         }
@@ -52,7 +52,7 @@ class BytesSupplierImpl implements BytesSupplier {
     public String getAsString() {
         if (value == null) {
             if (obj != null) {
-                value = JsonUtils.toJson(obj) + '\n';
+                value = JsonUtils.toJson(obj);
             } else {
                 value = new String(buf, StandardCharsets.UTF_8);
             }
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizer.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizer.java
index c10e7c3e6..cd6eaf289 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizer.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizer.java
@@ -36,6 +36,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -232,6 +233,9 @@ public final class HuggingFaceTokenizer extends NativeResource<Long> implements
      * @return the {@code Encoding} of the input sentence
      */
     public Encoding encode(String text, boolean addSpecialTokens, boolean withOverflowingTokens) {
+        if (text == null) {
+            throw new NullPointerException("text cannot be null");
+        }
         if (doLowerCase != null) {
             text = text.toLowerCase(doLowerCase);
         }
@@ -261,6 +265,10 @@ public final class HuggingFaceTokenizer extends NativeResource<Long> implements
      */
     public Encoding encode(
             String text, String textPair, boolean addSpecialTokens, boolean withOverflowingTokens) {
+        if (text == null || textPair == null) {
+            throw new NullPointerException("text/text_pair cannot be null");
+        }
+
         if (doLowerCase != null) {
             text = text.toLowerCase(doLowerCase);
             textPair = textPair.toLowerCase(doLowerCase);
@@ -322,6 +330,8 @@ public final class HuggingFaceTokenizer extends NativeResource<Long> implements
             for (int i = 0; i < inputs.length; ++i) {
                 inputs[i] = inputs[i].toLowerCase(doLowerCase);
             }
+        } else if (Arrays.stream(inputs).anyMatch(Objects::isNull)) {
+            throw new NullPointerException("input text cannot be null");
         }
         long encoding = TokenizersLibrary.LIB.encodeList(getHandle(), inputs, addSpecialTokens);
         return toEncoding(encoding, withOverflowingTokens);
@@ -377,6 +387,8 @@ public final class HuggingFaceTokenizer extends NativeResource<Long> implements
             for (int i = 0; i < inputs.length; ++i) {
                 inputs[i] = inputs[i].toLowerCase(doLowerCase);
             }
+        } else if (Arrays.stream(inputs).anyMatch(Objects::isNull)) {
+            throw new NullPointerException("input text cannot be null");
         }
         long[] encodings = TokenizersLibrary.LIB.batchEncode(getHandle(), inputs, addSpecialTokens);
         Encoding[] ret = new Encoding[encodings.length];
@@ -418,6 +430,13 @@ public final class HuggingFaceTokenizer extends NativeResource<Long> implements
             for (int i = 0; i < textPair.length; ++i) {
                 textPair[i] = textPair[i].toLowerCase(doLowerCase);
             }
+        } else {
+            if (inputs.keys().stream().anyMatch(Objects::isNull)) {
+                throw new NullPointerException("text pair key cannot be null");
+            }
+            if (inputs.values().stream().anyMatch(Objects::isNull)) {
+                throw new NullPointerException("text pair value cannot be null");
+            }
         }
         long[] encodings =
                 TokenizersLibrary.LIB.batchEncodePair(
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index dc08c6467..990bad2da 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -34,7 +34,7 @@ openpnpOpencv = "4.9.0-0"
 spark = "3.5.2"
 tablesaw = "0.43.1"
 rapis = "24.08.0"
-protobuf = "3.25.4"
+protobuf = "3.25.5"
 jackson = "2.17.2"
 jtransforms = "3.1"
 


Test patch:
diff --git a/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizerTest.java b/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizerTest.java
index 5c1e63873..27fb1056b 100644
--- a/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizerTest.java
+++ b/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/HuggingFaceTokenizerTest.java
@@ -27,6 +27,7 @@ import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -102,6 +103,17 @@ public class HuggingFaceTokenizerTest {
                 Assert.assertEquals(charSpansExpected[i].getStart(), charSpansResult[i].getStart());
                 Assert.assertEquals(charSpansExpected[i].getEnd(), charSpansResult[i].getEnd());
             }
+
+            Assert.assertThrows(() -> tokenizer.encode((String) null));
+            Assert.assertThrows(() -> tokenizer.encode(new String[] {null}));
+            Assert.assertThrows(() -> tokenizer.encode(null, null));
+            Assert.assertThrows(() -> tokenizer.encode("null", null));
+            Assert.assertThrows(() -> tokenizer.batchEncode(new String[] {null}));
+            List<String> empty = Collections.singletonList(null);
+            List<String> some = Collections.singletonList("null");
+
+            Assert.assertThrows(() -> tokenizer.batchEncode(new PairList<>(empty, some)));
+            Assert.assertThrows(() -> tokenizer.batchEncode(new PairList<>(some, empty)));
         }
 
         Map<String, String> options = new ConcurrentHashMap<>();
diff --git a/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/QuestionAnsweringTranslatorTest.java b/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/QuestionAnsweringTranslatorTest.java
index acb283734..b67d2a7bc 100644
--- a/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/QuestionAnsweringTranslatorTest.java
+++ b/extensions/tokenizers/src/test/java/ai/djl/huggingface/tokenizers/QuestionAnsweringTranslatorTest.java
@@ -102,7 +102,7 @@ public class QuestionAnsweringTranslatorTest {
             input.add("question", question);
             input.add("paragraph", paragraph);
             Output res = predictor.predict(input);
-            Assert.assertEquals(res.getAsString(0), "December 2004");
+            Assert.assertEquals(res.getAsString(0), "{\"answer\":\"December 2004\"}");
 
             Assert.assertThrows(
                     "Input data is empty.",