Repository: deepjavalibrary/djl
Pull request #3244 (2024-06-06 19:42:16)

[api] Adds metric type to Metic class
## 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/serving_publish.yml b/.github/workflows/serving_publish.yml
index cc9c390b7..1d278e0a6 100644
--- a/.github/workflows/serving_publish.yml
+++ b/.github/workflows/serving_publish.yml
@@ -50,14 +50,14 @@ jobs:
         if: ${{ github.event.inputs.mode == '' || github.event.inputs.mode == 'snapshot' }}
         run: |
           ./gradlew :serving:createDeb -Psnapshot
-          DJL_VERSION=$(cat gradle/libs.versions.toml | awk -F '=' '/djl/ {gsub(/ ?"/, "", $2); print $2}')
+          DJL_VERSION=$(awk -F '=' '/djl / {gsub(/ ?"/, "", $2); print $2}' gradle/libs.versions.toml)
           aws s3 cp serving/build/distributions/*.deb s3://djl-ai/publish/djl-serving/
           aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/djl-serving/djl-serving_${DJL_VERSION}*"
       - name: Copy serving release artifacts to S3
         if: ${{ github.event.inputs.mode == 'staging' }}
         run: |
           ./gradlew :serving:dZ :serving:createDeb -Pstaging
-          DJL_VERSION=$(cat gradle/libs.versions.toml | awk -F '=' '/djl/ {gsub(/ ?"/, "", $2); print $2}')
+          DJL_VERSION=$(awk -F '=' '/djl / {gsub(/ ?"/, "", $2); print $2}' gradle/libs.versions.toml)
           aws s3 cp serving/build/distributions/*.deb s3://djl-ai/publish/djl-serving/
           aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/djl-serving/djl-serving_${DJL_VERSION}*"
           if [[ $(aws s3 ls s3://djl-ai/publish/djl-serving/serving-$DJL_VERSION.tar | wc -l) -eq 0 ]]; \
@@ -69,7 +69,7 @@ jobs:
         if: ${{ github.event.inputs.mode == 'staging' }}
         run: |
           ./gradlew :benchmark:dZ :benchmark:createDeb -Pstaging
-          DJL_VERSION=$(cat gradle/libs.versions.toml | awk -F '=' '/djl/ {gsub(/ ?"/, "", $2); print $2}')
+          DJL_VERSION=$(awk -F '=' '/djl / {gsub(/ ?"/, "", $2); print $2}' gradle/libs.versions.toml)
           aws s3 cp benchmark/build/distributions/*.tar s3://djl-ai/publish/djl-bench/${DJL_VERSION}/
           aws s3 cp benchmark/build/distributions/*.deb s3://djl-ai/publish/djl-bench/${DJL_VERSION}/
           aws s3 cp benchmark/build/distributions/*.zip s3://djl-ai/publish/djl-bench/${DJL_VERSION}/
@@ -84,7 +84,7 @@ jobs:
         if: ${{ github.event.inputs.mode == 'staging' }}
         run: |
           ./gradlew :awscurl:jar
-          DJL_VERSION=$(cat gradle/libs.versions.toml | awk -F '=' '/djl/ {gsub(/ ?"/, "", $2); print $2}')
+          DJL_VERSION=$(awk -F '=' '/djl / {gsub(/ ?"/, "", $2); print $2}' gradle/libs.versions.toml)
           aws s3 cp awscurl/build/awscurl s3://djl-ai/publish/awscurl/${DJL_VERSION}/
           aws cloudfront create-invalidation --distribution-id E371VB8JQ6NRVY --paths "/awscurl/${DJL_VERSION}/*"
       - name: Publish to snapshot repository
diff --git a/api/src/main/java/ai/djl/metric/Metric.java b/api/src/main/java/ai/djl/metric/Metric.java
index 4743421a1..b5ddd8ff6 100644
--- a/api/src/main/java/ai/djl/metric/Metric.java
+++ b/api/src/main/java/ai/djl/metric/Metric.java
@@ -28,13 +28,15 @@ public class Metric {
 
     private static final Pattern PATTERN =
             Pattern.compile(
-                    "\\s*([\\w\\s]+)\\.([\\w\\s]+):([0-9\\-,.e]+)(?>\\|#([^|]*))?(?>\\|(\\d+))?");
+                    "\\s*([\\w\\s]+)\\.([\\w\\s]+):([0-9\\-,.e]+)(?>\\|#([^|]*))?(?>\\|(\\d+))?(?>\\|([cgh]))?");
 
     private static final Dimension HOST = new Dimension("Host", getLocalHostName());
 
     @SerializedName("MetricName")
     private String metricName;
 
+    private transient MetricType metricType;
+
     @SerializedName("Value")
     private String value;
 
@@ -67,7 +69,7 @@ public class Metric {
      * @param unit the metric unit
      */
     public Metric(String metricName, Number value, Unit unit) {
-        this(metricName, value.toString(), unit.getValue(), null, HOST);
+        this(metricName, null, value.toString(), unit.getValue(), null, HOST);
     }
 
     /**
@@ -80,13 +82,33 @@ public class Metric {
      * @param dimensions the metric dimensions
      */
     public Metric(String metricName, Number value, Unit unit, Dimension... dimensions) {
-        this(metricName, value.toString(), unit.getValue(), null, dimensions);
+        this(metricName, null, value, unit, dimensions);
+    }
+
+    /**
+     * Constructs a {@code Metric} instance with the specified {@code metricName}, <code>value
+     * </code>, and {@code unit}.
+     *
+     * @param metricName the metric name
+     * @param metricType the {@link MetricType}
+     * @param value the metric value
+     * @param unit the metric unit
+     * @param dimensions the metric dimensions
+     */
+    public Metric(
+            String metricName,
+            MetricType metricType,
+            Number value,
+            Unit unit,
+            Dimension... dimensions) {
+        this(metricName, metricType, value.toString(), unit.getValue(), null, dimensions);
     }
 
     /**
      * Constructs a new {@code Metric} instance.
      *
      * @param metricName the metric name
+     * @param metricType the {@link MetricType}
      * @param value the metric value
      * @param unit the metric unit
      * @param timestamp the metric timestamp
@@ -94,11 +116,13 @@ public class Metric {
      */
     private Metric(
             String metricName,
+            MetricType metricType,
             String value,
             String unit,
             String timestamp,
             Dimension... dimensions) {
         this.metricName = metricName;
+        this.metricType = metricType;
         this.unit = unit;
         this.value = value;
         this.timestamp = timestamp;
@@ -112,7 +136,7 @@ public class Metric {
      * @return a copy of the metric
      */
     public Metric copyOf(String name) {
-        return new Metric(name, value, unit, timestamp, dimensions);
+        return new Metric(name, metricType, value, unit, timestamp, dimensions);
     }
 
     /**
@@ -124,6 +148,15 @@ public class Metric {
         return metricName;
     }
 
+    /**
+     * Returns the type of the {@code Metric}.
+     *
+     * @return the metric type
+     */
+    public MetricType getMetricType() {
+        return metricType;
+    }
+
     /**
      * Returns the int value of the {@code Metric}.
      *
@@ -167,7 +200,7 @@ public class Metric {
      * @return a {@code Metric} object
      */
     public static Metric parse(String line) {
-        // DiskAvailable.Gigabytes:311|#Host:localhost|1650953744320
+        // DiskAvailable.Gigabytes:311|#Host:localhost|1650953744320|c
         Matcher matcher = PATTERN.matcher(line);
         if (!matcher.matches()) {
             return null;
@@ -178,6 +211,8 @@ public class Metric {
         String value = matcher.group(3);
         String dimension = matcher.group(4);
         String timestamp = matcher.group(5);
+        String type = matcher.group(6);
+        MetricType metricType = type == null ? null : MetricType.of(type);
 
         Dimension[] dimensions = null;
         if (dimension != null) {
@@ -192,7 +227,7 @@ public class Metric {
             }
         }
 
-        return new Metric(metricName, value, unit, timestamp, dimensions);
+        return new Metric(metricName, metricType, value, unit, timestamp, dimensions);
     }
 
     /** {@inheritDoc} */
@@ -218,6 +253,9 @@ public class Metric {
         if (timestamp != null) {
             sb.append('|').append(timestamp);
         }
+        if (metricType != null) {
+            sb.append('|').append(metricType.getValue());
+        }
         return sb.toString();
     }
 
diff --git a/api/src/main/java/ai/djl/metric/MetricType.java b/api/src/main/java/ai/djl/metric/MetricType.java
new file mode 100644
index 000000000..30c5fece5
--- /dev/null
+++ b/api/src/main/java/ai/djl/metric/MetricType.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+package ai.djl.metric;
+
+/** An enum holds metric type constants. */
+public enum MetricType {
+    COUNTER("c"),
+    GAUGE("g"),
+    HISTOGRAM("h");
+
+    private final String value;
+
+    MetricType(String value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the string value of the {@code MetricType}.
+     *
+     * @return the string value of the {@code MetricType}
+     */
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Returns {@code Unit} instance from an string value.
+     *
+     * @param value the String value of Unit
+     * @return the {@code Unit}
+     */
+    public static MetricType of(String value) {
+        if ("c".equals(value)) {
+            return COUNTER;
+        } else if ("g".equals(value)) {
+            return GAUGE;
+        } else if ("h".equals(value)) {
+            return HISTOGRAM;
+        }
+        throw new IllegalArgumentException("Invalid MetricType value: " + value);
+    }
+}
diff --git a/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/LgbmModel.java b/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/LgbmModel.java
index c37553653..54d56d2ce 100644
--- a/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/LgbmModel.java
+++ b/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/LgbmModel.java
@@ -17,6 +17,7 @@ import ai.djl.Model;
 import ai.djl.ml.lightgbm.jni.JniUtils;
 import ai.djl.ndarray.NDManager;
 import ai.djl.ndarray.types.DataType;
+import ai.djl.translate.ArgumentsUtil;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -56,6 +57,12 @@ public class LgbmModel extends BaseModel {
             }
         }
         block = JniUtils.loadModel((LgbmNDManager) manager, modelFile.toAbsolutePath().toString());
+        if (options != null) {
+            String inferenceType = ArgumentsUtil.stringValue(options, "inference_type");
+            if (inferenceType != null) {
+                setInferenceType(inferenceType);
+            }
+        }
     }
 
     private Path findModelFile(String prefix) {
@@ -95,4 +102,26 @@ public class LgbmModel extends BaseModel {
         }
         super.close();
     }
+
+    /**
+     * Sets the inference type of the model based on the given string.
+     *
+     * <p>Supported inference types include NORMAL, RAW_SCORE, LEAF_INDEX, CONTRIB.
+     *
+     * @param inferenceType the string representing the inference type
+     * @throws IllegalArgumentException if the given inference type is not supported
+     */
+    public void setInferenceType(String inferenceType) {
+        ((LgbmSymbolBlock) block).setInferenceType(inferenceType);
+    }
+
+    /**
+     * Returns the string representation of the current model inference type.
+     *
+     * @return the string representation of the current inference type
+     * @throws IllegalStateException if the current inference type is unknown
+     */
+    public String getInferenceType() {
+        return ((LgbmSymbolBlock) block).getInferenceType();
+    }
 }
diff --git a/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/LgbmSymbolBlock.java b/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/LgbmSymbolBlock.java
index 826b1a0f9..0ae351941 100644
--- a/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/LgbmSymbolBlock.java
+++ b/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/LgbmSymbolBlock.java
@@ -24,6 +24,7 @@ import ai.djl.util.Pair;
 import ai.djl.util.PairList;
 
 import com.microsoft.ml.lightgbm.SWIGTYPE_p_p_void;
+import com.microsoft.ml.lightgbm.lightgbmlibConstants;
 
 import java.nio.ByteBuffer;
 import java.util.concurrent.atomic.AtomicReference;
@@ -35,6 +36,7 @@ public class LgbmSymbolBlock extends AbstractSymbolBlock implements AutoCloseabl
     private int iterations;
     private String uid;
     private LgbmNDManager manager;
+    private int inferenceType;
 
     /**
      * Constructs a {@code LgbmSymbolBlock}.
@@ -53,6 +55,7 @@ public class LgbmSymbolBlock extends AbstractSymbolBlock implements AutoCloseabl
         this.manager = manager;
         uid = String.valueOf(handle);
         manager.attachInternal(uid, this);
+        this.inferenceType = lightgbmlibConstants.C_API_PREDICT_NORMAL;
     }
 
     /** {@inheritDoc} */
@@ -66,7 +69,7 @@ public class LgbmSymbolBlock extends AbstractSymbolBlock implements AutoCloseabl
         try (LgbmNDManager sub = (LgbmNDManager) manager.newSubManager()) {
             LgbmNDArray lgbmNDArray = sub.from(array);
             Pair<Integer, ByteBuffer> result =
-                    JniUtils.inference(handle.get(), iterations, lgbmNDArray);
+                    JniUtils.inference(getHandle(), iterations, lgbmNDArray, inferenceType);
 
             NDArray ret =
                     manager.create(
@@ -107,4 +110,41 @@ public class LgbmSymbolBlock extends AbstractSymbolBlock implements AutoCloseabl
     public ParameterList getDirectParameters() {
         throw new UnsupportedOperationException("Not yet supported");
     }
+
+    void setInferenceType(String inferenceType) {
+        switch (inferenceType) {
+            case "NORMAL":
+                this.inferenceType = lightgbmlibConstants.C_API_PREDICT_NORMAL;
+                break;
+            case "RAW_SCORE":
+                this.inferenceType = lightgbmlibConstants.C_API_PREDICT_RAW_SCORE;
+                break;
+            case "LEAF_INDEX":
+                this.inferenceType = lightgbmlibConstants.C_API_PREDICT_LEAF_INDEX;
+                break;
+            case "CONTRIB":
+                this.inferenceType = lightgbmlibConstants.C_API_PREDICT_CONTRIB;
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Unsupported inference type: "
+                                + inferenceType
+                                + ". Supported types include: NORMAL, RAW_SCORE, LEAF_INDEX,"
+                                + " CONTRIB.");
+        }
+    }
+
+    String getInferenceType() {
+        if (inferenceType == lightgbmlibConstants.C_API_PREDICT_NORMAL) {
+            return "NORMAL";
+        } else if (inferenceType == lightgbmlibConstants.C_API_PREDICT_RAW_SCORE) {
+            return "RAW_SCORE";
+        } else if (inferenceType == lightgbmlibConstants.C_API_PREDICT_LEAF_INDEX) {
+            return "LEAF_INDEX";
+        } else if (inferenceType == lightgbmlibConstants.C_API_PREDICT_CONTRIB) {
+            return "CONTRIB";
+        } else {
+            throw new AssertionError("Unexpected inference type: " + inferenceType);
+        }
+    }
 }
diff --git a/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/jni/JniUtils.java b/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/jni/JniUtils.java
index 606676e24..2af6c6128 100644
--- a/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/jni/JniUtils.java
+++ b/engines/ml/lightgbm/src/main/java/ai/djl/ml/lightgbm/jni/JniUtils.java
@@ -26,7 +26,7 @@ import com.microsoft.ml.lightgbm.SWIGTYPE_p_int;
 import com.microsoft.ml.lightgbm.SWIGTYPE_p_long_long;
 import com.microsoft.ml.lightgbm.SWIGTYPE_p_p_void;
 import com.microsoft.ml.lightgbm.lightgbmlib;
-import com.microsoft.ml.lightgbm.lightgbmlibJNI;
+import com.microsoft.ml.lightgbm.lightgbmlibConstants;
 
 import java.nio.ByteBuffer;
 import java.nio.DoubleBuffer;
@@ -60,7 +60,7 @@ public final class JniUtils {
     }
 
     public static Pair<Integer, ByteBuffer> inference(
-            SWIGTYPE_p_p_void model, int iterations, NDArray a) {
+            SWIGTYPE_p_p_void model, int iterations, NDArray a, int inferenceType) {
         if (a instanceof LgbmDataset) {
             LgbmDataset dataset = (LgbmDataset) a;
             switch (dataset.getSrcType()) {
@@ -68,30 +68,25 @@ public final class JniUtils {
                     throw new IllegalArgumentException(
                             "LightGBM can only do inference with an Array LightGBMDataset");
                 case ARRAY:
-                    return inferenceMat(model, iterations, dataset.getSrcArrayConverted());
+                    return inferenceMat(
+                            model, iterations, dataset.getSrcArrayConverted(), inferenceType);
                 default:
                     throw new IllegalArgumentException("Unexpected LgbmDataset SrcType");
             }
         }
         if (a instanceof LgbmNDArray) {
-            return inferenceMat(model, iterations, (LgbmNDArray) a);
+            return inferenceMat(model, iterations, (LgbmNDArray) a, inferenceType);
         }
         throw new IllegalArgumentException("LightGBM inference must be called with a LgbmNDArray");
     }
 
     public static Pair<Integer, ByteBuffer> inferenceMat(
-            SWIGTYPE_p_p_void model, int iterations, LgbmNDArray a) {
+            SWIGTYPE_p_p_void model, int iterations, LgbmNDArray a, int inferenceType) {
         SWIGTYPE_p_long_long outLength = lightgbmlib.new_int64_tp();
         SWIGTYPE_p_double outBuffer = null;
-        SWIGTYPE_p_int numClasses = lightgbmlib.new_intp();
         try {
-            int outFlag =
-                    lightgbmlib.LGBM_BoosterGetNumClasses(
-                            lightgbmlib.voidpp_value(model), numClasses);
-            checkCall(outFlag);
-            int classes = lightgbmlib.intp_value(numClasses);
-
-            outBuffer = lightgbmlib.new_doubleArray((long) classes * a.getRows());
+            int bufferLength = calculateBufferLength(model, inferenceType, a.getRows(), iterations);
+            outBuffer = lightgbmlib.new_doubleArray(bufferLength);
             int result =
                     lightgbmlib.LGBM_BoosterPredictForMat(
                             lightgbmlib.voidpp_value(model),
@@ -100,7 +95,7 @@ public final class JniUtils {
                             a.getRows(),
                             a.getCols(),
                             1,
-                            lightgbmlibJNI.C_API_PREDICT_NORMAL_get(),
+                            inferenceType,
                             0,
                             iterations,
                             "",
@@ -137,7 +132,38 @@ public final class JniUtils {
             if (outBuffer != null) {
                 lightgbmlib.delete_doubleArray(outBuffer);
             }
+        }
+    }
+
+    private static int calculateBufferLength(
+            SWIGTYPE_p_p_void model, int inferenceType, int rows, int iterations) {
+        SWIGTYPE_p_int numClasses = lightgbmlib.new_intp();
+        SWIGTYPE_p_int numFeatures = lightgbmlib.new_intp();
+        try {
+            int outFlag =
+                    lightgbmlib.LGBM_BoosterGetNumClasses(
+                            lightgbmlib.voidpp_value(model), numClasses);
+            checkCall(outFlag);
+            int classes = lightgbmlib.intp_value(numClasses);
+
+            if (inferenceType == lightgbmlibConstants.C_API_PREDICT_NORMAL
+                    || inferenceType == lightgbmlibConstants.C_API_PREDICT_RAW_SCORE) {
+                return classes * rows;
+            } else if (inferenceType == lightgbmlibConstants.C_API_PREDICT_LEAF_INDEX) {
+                return classes * rows * iterations;
+            } else if (inferenceType == lightgbmlibConstants.C_API_PREDICT_CONTRIB) {
+                int outFlag2 =
+                        lightgbmlib.LGBM_BoosterGetNumFeature(
+                                lightgbmlib.voidpp_value(model), numFeatures);
+                checkCall(outFlag2);
+                int features = lightgbmlib.intp_value(numFeatures);
+                return classes * rows * (features + 1);
+            } else {
+                throw new IllegalArgumentException("Unsupported inference type: " + inferenceType);
+            }
+        } finally {
             lightgbmlib.delete_intp(numClasses);
+            lightgbmlib.delete_intp(numFeatures);
         }
     }
 
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 4255fb663..d9c2ecf3f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -85,6 +85,3 @@ antlrRuntime = { module = "org.antlr:antlr4-runtime", version.ref = "antlr" }
 testng = { module = "org.testng:testng", version.ref = "testng" }
 junit = { module = "junit:junit", version.ref = "junit" }
 mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" }
-
-[bundles]
-#fiji = ["fiji-fiji", "fiji-3dBlobSegmentation", "fiji-3dObjectsCounter", "fiji-3dViewer", "fiji-analyzeskeleton", "fiji-anisotropicDiffusion2d", "fiji-archipelagoPlugins", "fiji-arrow", "fiji-autoLocalThreshold", "fiji-autoThreshold", "fiji-balloonsegmentation", "fiji-bugSubmitter", "fiji-cpuMeter", "fiji-calculatorPlus", "fiji-cellCounter", "fiji-colocalisationAnalysis", "fiji-colorHistogram", "fiji-colorInspector3d", "fiji-colourDeconvolution", "fiji-correctbleach", "fiji-correct3dDrift", "fiji-descriptorBasedRegistration", "fiji-dichromacy", "fiji-directionality", "fiji-fsAlignTrakem2", "fiji-featureDetection", "fiji-archipelago", "fiji-developer", "fiji-packageMaker", "fiji-plugins", "fiji-filamentdetector", "fiji-flowj", "fiji-graphCut", "fiji-grayMorphology", "fiji-hdf5Vibez", "fiji-helmholtzAnalysis", "fiji-ijRobot", "fiji-io", "fiji-image5d", "fiji-imageExpressionParser", "fiji-interactive3dSurfacePlot", "fiji-isodataClassifier", "fiji-kappa", "fiji-kuwaharaFilter", "fiji-kymographbuilder", "fiji-lsmReader", "fiji-lsmToolbox", "fiji-lassoAndBlowTool", "fiji-linearKuwahara", "fiji-localthickness", "fiji-mtrack2", "fiji-mIP", "fiji-manualTracking", "fiji-multiKymograph", "fiji-omevisual", "fiji-pivAnalyser", "fiji-quickpalm", "fiji-rats", "fiji-reconstructReader", "fiji-spimOpener", "fiji-spimRegistration", "fiji-samples", "fiji-seriesLabeler", "fiji-sioxSegmentation", "fiji-skeletonize3d", "fiji-splinedeformationgenerator", "fiji-stackManipulation", "fiji-statisticalRegionMerging", "fiji-stitching", "fiji-syncWin", "fiji-threadKiller", "fiji-timeLapse", "fiji-timeStamper", "fiji-toast", "fiji-topoj", "fiji-trainableSegmentation", "fiji-trakem2Archipelago", "fiji-vibLib", "fiji-vib", "fiji-vaa3dReader", "fiji-vaa3dWriter", "fiji-videoEditing", "fiji-view5d", "fiji-volumeCalculator", "fiji-volumeViewer", "fiji-bunwarpj", "fiji-bij", "fiji-blockmatching", "fiji-lib", "fiji-legacyImglib1", "fiji-levelSets", "fiji-palOptimization", "fiji-panorama", "fiji-registerVirtualStackSlices", "fiji-registration3d", "fiji-weaveJy2java", "fiji-bigwarpFiji", "fiji-trakem2Tps", "fiji-zSpacing", "fiji-bigdataviewerCore", "fiji-bigdataviewerServer", "fiji-bigdataviewerVistools", "fiji-bigdataviewerFiji", "fiji-spimData", "fiji-trackmate", "fiji-trackmateCellpose", "fiji-trackmateCsvimporter", "fiji-trackmateExtrack", "fiji-trackmateIlastik", "fiji-trackmateMorpholibj", "fiji-trackmateOneat", "fiji-trackmateSkeleton", "fiji-trackmateStardist", "fiji-trackmateWeka", "fiji-t2Nit", "fiji-t2Treelinegraph", "fiji-trakem2", "fiji-vectorstring", "fiji-trakem2Transform", "fiji-mamut", "fiji-labkitPixelClassification", "fiji-labkitUi"]


Test patch:
diff --git a/api/src/test/java/ai/djl/metric/MetricsTest.java b/api/src/test/java/ai/djl/metric/MetricsTest.java
index 0726d711d..ac16e81e4 100644
--- a/api/src/test/java/ai/djl/metric/MetricsTest.java
+++ b/api/src/test/java/ai/djl/metric/MetricsTest.java
@@ -68,7 +68,7 @@ public class MetricsTest {
 
     @Test
     public void testParseMetrics() {
-        String line = "Disk.Gigabytes:311|#Host:localhost,Model:resnet|1650953744320";
+        String line = "Disk.Gigabytes:311|#Host:localhost,Model:resnet|1650953744320|g";
         Metric metric = Metric.parse(line);
         Assert.assertNotNull(metric);
         Assert.assertEquals(metric.getValue().intValue(), 311);
@@ -77,6 +77,7 @@ public class MetricsTest {
         Assert.assertEquals(metric.getDimensions()[0].getName(), "Host");
         Assert.assertEquals(metric.getDimensions()[0].getValue(), "localhost");
         Assert.assertEquals(line, metric.toString());
+        Assert.assertEquals(metric.getMetricType(), MetricType.GAUGE);
 
         Assert.assertNull(Metric.parse("DiskAvailable.Gigabytes:311#"));
         Metric invalid = Metric.parse("Disk.InvalidUnits:311");
@@ -105,4 +106,12 @@ public class MetricsTest {
         Metrics metrics = new Metrics();
         metrics.percentile("not_found", 1);
     }
+
+    @Test
+    public void testMetricType() {
+        Assert.assertEquals(MetricType.of("c"), MetricType.COUNTER);
+        Assert.assertEquals(MetricType.of("g"), MetricType.GAUGE);
+        Assert.assertEquals(MetricType.of("h"), MetricType.HISTOGRAM);
+        Assert.assertThrows(() -> MetricType.of("a"));
+    }
 }
diff --git a/engines/ml/lightgbm/src/test/java/ai/djl/ml/lightgbm/LgbmModelTest.java b/engines/ml/lightgbm/src/test/java/ai/djl/ml/lightgbm/LgbmModelTest.java
index da45a1c03..3e8079612 100644
--- a/engines/ml/lightgbm/src/test/java/ai/djl/ml/lightgbm/LgbmModelTest.java
+++ b/engines/ml/lightgbm/src/test/java/ai/djl/ml/lightgbm/LgbmModelTest.java
@@ -48,16 +48,30 @@ public class LgbmModelTest {
                         .setTypes(NDList.class, NDList.class)
                         .optModelPath(modelDir)
                         .optModelName("quadratic")
+                        .optOption("inference_type", "NORMAL")
                         .build();
 
         try (ZooModel<NDList, NDList> model = criteria.loadModel();
                 Predictor<NDList, NDList> predictor = model.newPredictor()) {
+            LgbmModel lgbm = (LgbmModel) model.getWrappedModel();
+            Assert.assertEquals(lgbm.getInferenceType(), "NORMAL");
             try (NDManager manager = NDManager.newBaseManager()) {
                 NDArray array = manager.ones(new Shape(10, 4));
                 NDList output = predictor.predict(new NDList(array));
                 Assert.assertEquals(output.singletonOrThrow().getDataType(), DataType.FLOAT32);
                 Assert.assertEquals(output.singletonOrThrow().getShape().size(), 10);
             }
+
+            lgbm.setInferenceType("RAW_SCORE");
+            Assert.assertEquals(lgbm.getInferenceType(), "RAW_SCORE");
+
+            lgbm.setInferenceType("LEAF_INDEX");
+            Assert.assertEquals(lgbm.getInferenceType(), "LEAF_INDEX");
+
+            lgbm.setInferenceType("CONTRIB");
+            Assert.assertEquals(lgbm.getInferenceType(), "CONTRIB");
+
+            Assert.assertThrows(() -> lgbm.setInferenceType("invalid"));
         }
     }
 }