Repository: opensearch-project/ml-commons
Pull request #3730: fix error message when input map and output map length not match
Author: login: mingshl
Labels: name: backport 2.17
name: backport 2.18
name: backport 2.19
Created: 2025-04-14T21:19:30Z | Merged: 2025-04-15T19:29:13Z

### Description
fix typo in error message when input map and output map length not match.

also fix when writing to ml_inference search extension, it's only supported when one_to_one is false, so when it's an array to write into ml_inference search extension, don't split up the array and store as a whole in the search extension. 

### Related Issues
Resolves #[Issue number to be closed when this PR is merged]
<!-- List any other related issues here -->

### Check List
- [ ] New functionality includes testing.
- [ ] New functionality has been documented.
- [ ] API changes companion pull request [created](https://github.com/opensearch-project/opensearch-api-specification/blob/main/DEVELOPER_GUIDE.md).
- [ ] Commits are signed per the DCO using `--signoff`.
- [ ] Public documentation issue/PR [created](https://github.com/opensearch-project/documentation-website/issues/new/choose).

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/ml-commons/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).



Diff:
diff --git a/plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchRequestProcessor.java b/plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchRequestProcessor.java
index e729236878..02b9c331c7 100644
--- a/plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchRequestProcessor.java
+++ b/plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchRequestProcessor.java
@@ -707,7 +707,7 @@ public MLInferenceSearchRequestProcessor create(
                     "when output_maps/optional_output_maps and input_maps/optional_input_maps are provided, their length needs to match. The input is in length of "
                         + combinedInputMaps.size()
                         + ", while output_maps is in the length of "
-                        + combinedInputMaps.size()
+                        + combinedOutputMaps.size()
                         + ". Please adjust mappings."
                 );
             }
diff --git a/plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessor.java b/plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessor.java
index 64ba5018d6..2ead511691 100644
--- a/plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessor.java
+++ b/plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessor.java
@@ -626,29 +626,37 @@ public void onResponse(Map<Integer, MLOutput> multipleMLOutputs) {
                                             ignoreMissing,
                                             fullResponsePath
                                         );
-                                        Object modelOutputValuePerDoc;
-                                        if (modelOutputValue instanceof List
-                                            && ((List) modelOutputValue).size() == hitCountInPredictions.get(mappingIndex)) {
-                                            Object valuePerDoc = ((List) modelOutputValue)
-                                                .get(MapUtils.getCounter(writeOutputMapDocCounter, mappingIndex, modelOutputFieldName));
-                                            modelOutputValuePerDoc = valuePerDoc;
-                                        } else {
-                                            modelOutputValuePerDoc = modelOutputValue;
-                                        }
                                         // writing to search response extension
                                         if (newDocumentFieldName.startsWith(EXTENSION_PREFIX)) {
                                             Map<String, Object> params = ((MLInferenceSearchResponse) response).getParams();
                                             String paramsName = newDocumentFieldName.replaceFirst(EXTENSION_PREFIX + ".", "");
 
                                             if (params != null) {
-                                                params.put(paramsName, modelOutputValuePerDoc);
+                                                params.put(paramsName, modelOutputValue);
                                                 ((MLInferenceSearchResponse) response).setParams(params);
                                             } else {
                                                 Map<String, Object> newParams = new HashMap<>();
-                                                newParams.put(paramsName, modelOutputValuePerDoc);
+                                                newParams.put(paramsName, modelOutputValue);
                                                 ((MLInferenceSearchResponse) response).setParams(newParams);
                                             }
                                         } else {
+                                            Object modelOutputValuePerDoc;
+                                            if (hitCountInPredictions.containsKey(mappingIndex)) {
+                                                if (modelOutputValue instanceof List
+                                                    && ((List) modelOutputValue).size() == hitCountInPredictions.get(mappingIndex)
+                                                    && !oneToOne) {
+                                                    Object valuePerDoc = ((List) modelOutputValue)
+                                                        .get(
+                                                            MapUtils
+                                                                .getCounter(writeOutputMapDocCounter, mappingIndex, modelOutputFieldName)
+                                                        );
+                                                    modelOutputValuePerDoc = valuePerDoc;
+                                                } else {
+                                                    modelOutputValuePerDoc = modelOutputValue;
+                                                }
+                                            } else {
+                                                modelOutputValuePerDoc = modelOutputValue;
+                                            }
                                             // writing to search response hits
                                             if (sourceAsMap.containsKey(newDocumentFieldName)) {
                                                 if (override) {
@@ -902,12 +910,15 @@ public MLInferenceSearchResponseProcessor create(
                         + ". Please reduce the size of input_map or optional_input_map or increase max_prediction_tasks."
                 );
             }
-            if (combinedOutputMaps != null && combinedInputMaps != null && combinedOutputMaps.size() != combinedInputMaps.size()) {
+
+            if (!CollectionUtils.isEmpty(combinedOutputMaps)
+                && !CollectionUtils.isEmpty(combinedInputMaps)
+                && combinedOutputMaps.size() != combinedInputMaps.size()) {
                 throw new IllegalArgumentException(
                     "when output_maps/optional_output_maps and input_maps/optional_input_maps are provided, their length needs to match. The input is in length of "
                         + combinedInputMaps.size()
                         + ", while output_maps is in the length of "
-                        + combinedInputMaps.size()
+                        + combinedOutputMaps.size()
                         + ". Please adjust mappings."
                 );
             }
diff --git a/plugin/src/test/java/org/opensearch/ml/processor/MLInferenceSearchRequestProcessorTests.java b/plugin/src/test/java/org/opensearch/ml/processor/MLInferenceSearchRequestProcessorTests.java
index a568073d54..e41b4b1a3d 100644
--- a/plugin/src/test/java/org/opensearch/ml/processor/MLInferenceSearchRequestProcessorTests.java
+++ b/plugin/src/test/java/org/opensearch/ml/processor/MLInferenceSearchRequestProcessorTests.java
@@ -2250,7 +2250,7 @@ public void testOutputMapsExceedInputMaps() throws Exception {
         } catch (IllegalArgumentException e) {
             assertEquals(
                 e.getMessage(),
-                ("when output_maps/optional_output_maps and input_maps/optional_input_maps are provided, their length needs to match. The input is in length of 2, while output_maps is in the length of 2. Please adjust mappings.")
+                ("when output_maps/optional_output_maps and input_maps/optional_input_maps are provided, their length needs to match. The input is in length of 2, while output_maps is in the length of 3. Please adjust mappings.")
             );
         }
     }
diff --git a/plugin/src/test/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessorTests.java b/plugin/src/test/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessorTests.java
index 0e33adf5ff..0f92815583 100644
--- a/plugin/src/test/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessorTests.java
+++ b/plugin/src/test/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessorTests.java
@@ -1036,6 +1036,126 @@ public void testProcessResponseSuccessWriteToExt() throws Exception {
             @Override
             public void onResponse(SearchResponse newSearchResponse) {
                 assertEquals(newSearchResponse.getHits().getHits().length, 5);
+                MLInferenceSearchResponse mLInferenceSearchResponse = (MLInferenceSearchResponse) newSearchResponse;
+                String resultsInResponse = (String) mLInferenceSearchResponse.getParams().get("llm_response");
+                assertEquals("there is 1 value", resultsInResponse);
+            }
+
+            @Override
+            public void onFailure(Exception e) {
+                throw new RuntimeException(e);
+            }
+
+        };
+        responseProcessor.processResponseAsync(request, response, responseContext, listener);
+        verify(client, times(1)).execute(any(), any(), any());
+    }
+
+    /**
+     * Tests the successful processing of a response with a single pair of input and output mappings.
+     * read the query text into model config with query extensions
+     * read the prediction outcome as array and store in search extension
+     * @throws Exception if an error occurs during the test
+     */
+    @Test
+    public void testProcessResponseSuccessArrayWriteToExt() throws Exception {
+        String documentField = "text";
+        String modelInputField = "context";
+        List<Map<String, String>> inputMap = new ArrayList<>();
+        Map<String, String> input = new HashMap<>();
+        input.put(modelInputField, documentField);
+        inputMap.add(input);
+
+        String newDocumentField = "ext.ml_inference.results";
+        String modelOutputField = "results[*].document.text";
+        List<Map<String, String>> outputMap = new ArrayList<>();
+        Map<String, String> output = new HashMap<>();
+        output.put(newDocumentField, modelOutputField);
+        outputMap.add(output);
+        Map<String, String> modelConfig = new HashMap<>();
+        modelConfig.put("query", "positive review");
+        MLInferenceSearchResponseProcessor responseProcessor = new MLInferenceSearchResponseProcessor(
+            "model1",
+            inputMap,
+            outputMap,
+            optionalInputMaps,
+            optionalOutputMaps,
+            modelConfig,
+            DEFAULT_MAX_PREDICTION_TASKS,
+            PROCESSOR_TAG,
+            DESCRIPTION,
+            false,
+            "remote",
+            false,
+            false,
+            false,
+            "{ \"parameters\": ${ml_inference.parameters} }",
+            client,
+            TEST_XCONTENT_REGISTRY_FOR_QUERY,
+            false
+        );
+
+        SearchRequest request = getSearchRequest();
+        String fieldName = "text";
+        SearchResponse response = getSearchResponse(5, true, fieldName);
+
+        Map<String, Object> inferenceResultMap = new HashMap<>();
+
+        Map<String, Object> doc1 = new HashMap<>();
+        Map<String, Object> doc1Text = new HashMap<>();
+        doc1Text.put("text", "value1");
+        doc1.put("document", doc1Text);
+        doc1.put("index", 0.0);
+        doc1.put("relevance_score", 2.6480842E-5);
+
+        Map<String, Object> doc2 = new HashMap<>();
+        Map<String, Object> doc2Text = new HashMap<>();
+        doc2Text.put("text", "value5");
+        doc2.put("document", doc2Text);
+        doc2.put("index", 4.0);
+        doc2.put("relevance_score", 2.5071593E-5);
+
+        Map<String, Object> doc3 = new HashMap<>();
+        Map<String, Object> doc3Text = new HashMap<>();
+        doc3Text.put("text", "value4");
+        doc3.put("document", doc3Text);
+        doc3.put("index", 3.0);
+        doc3.put("relevance_score", 2.373734E-5);
+
+        Map<String, Object> doc4 = new HashMap<>();
+        Map<String, Object> doc4Text = new HashMap<>();
+        doc4Text.put("text", "value2");
+        doc4.put("document", doc4Text);
+        doc4.put("index", 1.0);
+        doc4.put("relevance_score", 2.1112483E-5);
+
+        Map<String, Object> doc5 = new HashMap<>();
+        Map<String, Object> doc5Text = new HashMap<>();
+        doc5Text.put("text", "value3");
+        doc5.put("document", doc5Text);
+        doc5.put("index", 2.0);
+        doc5.put("relevance_score", 1.6187581E-5);
+
+        inferenceResultMap.put("results", Arrays.asList(doc1, doc2, doc3, doc4, doc5));
+
+        ModelTensor modelTensor = ModelTensor.builder().dataAsMap(inferenceResultMap).build();
+        ModelTensors modelTensors = ModelTensors.builder().mlModelTensors(Arrays.asList(modelTensor)).build();
+        ModelTensorOutput mlModelTensorOutput = ModelTensorOutput.builder().mlModelOutputs(Arrays.asList(modelTensors)).build();
+
+        doAnswer(invocation -> {
+            ActionListener<MLTaskResponse> actionListener = invocation.getArgument(2);
+            actionListener.onResponse(MLTaskResponse.builder().output(mlModelTensorOutput).build());
+            return null;
+        }).when(client).execute(any(), any(), any());
+
+        ActionListener<SearchResponse> listener = new ActionListener<>() {
+            @Override
+            public void onResponse(SearchResponse newSearchResponse) {
+                assertEquals(newSearchResponse.getHits().getHits().length, 5);
+                MLInferenceSearchResponse mLInferenceSearchResponse = (MLInferenceSearchResponse) newSearchResponse;
+                List<Map<String, Object>> results = (List<Map<String, Object>>) inferenceResultMap.get("results");
+                List<String> resultsInResponse = (List<String>) mLInferenceSearchResponse.getParams().get("results");
+                assertEquals(results.size(), resultsInResponse.size());
             }
 
             @Override
@@ -5166,17 +5286,151 @@ public void testOutputMapsExceedInputMaps() throws Exception {
         } catch (IllegalArgumentException e) {
             assertEquals(
                 e.getMessage(),
-                "when output_maps/optional_output_maps and input_maps/optional_input_maps are provided, their length needs to match. The input is in length of 2, while output_maps is in the length of 2. Please adjust mappings."
+                "when output_maps/optional_output_maps and input_maps/optional_input_maps are provided, their length needs to match. The input is in length of 2, while output_maps is in the length of 3. Please adjust mappings."
             );
 
         }
     }
 
     /**
-         * Tests the creation of the MLInferenceSearchResponseProcessor with optional fields.
-         *
-         * @throws Exception if an error occurs during the test
-         */
+     * Tests the case where only the input maps are provided in the configuration.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    public void testOnlyInputMapsProvided() throws Exception {
+        Map<String, Object> config = new HashMap<>();
+        config.put(MODEL_ID, "model2");
+        List<Map<String, String>> inputMap = new ArrayList<>();
+        Map<String, String> input0 = new HashMap<>();
+        input0.put("inputs", "text");
+        inputMap.add(input0);
+        Map<String, String> input1 = new HashMap<>();
+        input1.put("inputs", "hashtag");
+        inputMap.add(input1);
+        config.put(INPUT_MAP, inputMap);
+        config.put(MAX_PREDICTION_TASKS, 2);
+        String processorTag = randomAlphaOfLength(10);
+
+        factory.create(Collections.emptyMap(), processorTag, null, false, config, null);
+    }
+
+    /**
+     * Tests the case where the input maps and empty output map are provided in the configuration.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    public void testInputMapsEmptyOutputMapProvided() throws Exception {
+        Map<String, Object> config = new HashMap<>();
+        config.put(MODEL_ID, "model2");
+        List<Map<String, String>> inputMap = new ArrayList<>();
+        Map<String, String> input0 = new HashMap<>();
+        input0.put("inputs", "text");
+        inputMap.add(input0);
+        Map<String, String> input1 = new HashMap<>();
+        input1.put("inputs", "hashtag");
+        inputMap.add(input1);
+        config.put(INPUT_MAP, inputMap);
+        config.put(MAX_PREDICTION_TASKS, 2);
+        String processorTag = randomAlphaOfLength(10);
+
+        List<Map<String, String>> outputMap = new ArrayList<>();
+        config.put(OUTPUT_MAP, outputMap);
+
+        factory.create(Collections.emptyMap(), processorTag, null, false, config, null);
+    }
+
+    /**
+     * Tests the case where only the Optional input maps are provided in the configuration.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    public void testOnlyOptionalInputMapsProvided() throws Exception {
+        Map<String, Object> config = new HashMap<>();
+        config.put(MODEL_ID, "model2");
+        List<Map<String, String>> inputMap = new ArrayList<>();
+        Map<String, String> input0 = new HashMap<>();
+        input0.put("inputs", "text");
+        inputMap.add(input0);
+        Map<String, String> input1 = new HashMap<>();
+        input1.put("inputs", "hashtag");
+        inputMap.add(input1);
+        config.put(OPTIONAL_INPUT_MAP, inputMap);
+        config.put(MAX_PREDICTION_TASKS, 2);
+        String processorTag = randomAlphaOfLength(10);
+
+        factory.create(Collections.emptyMap(), processorTag, null, false, config, null);
+
+    }
+
+    /**
+     * Tests the case where only the Optional input maps are provided in the configuration.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    public void testOnlyOptionalInputMapsEmptyOptionalOutputProvided() throws Exception {
+        Map<String, Object> config = new HashMap<>();
+        config.put(MODEL_ID, "model2");
+        List<Map<String, String>> inputMap = new ArrayList<>();
+        Map<String, String> input0 = new HashMap<>();
+        input0.put("inputs", "text");
+        inputMap.add(input0);
+        Map<String, String> input1 = new HashMap<>();
+        input1.put("inputs", "hashtag");
+        inputMap.add(input1);
+        config.put(OPTIONAL_INPUT_MAP, inputMap);
+        config.put(MAX_PREDICTION_TASKS, 2);
+        String processorTag = randomAlphaOfLength(10);
+        List<Map<String, String>> outputMap = new ArrayList<>();
+        config.put(OPTIONAL_OUTPUT_MAP, outputMap);
+        factory.create(Collections.emptyMap(), processorTag, null, false, config, null);
+
+    }
+
+    /**
+     * Tests the case where only the output maps are provided in the configuration.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    public void testOnlyOutputMapsProvided() throws Exception {
+        Map<String, Object> config = new HashMap<>();
+        config.put(MODEL_ID, "model2");
+        List<Map<String, String>> outputMap = new ArrayList<>();
+        Map<String, String> output = new HashMap<>();
+        output.put("text_embedding", "$.inference_results[0].output[0].data");
+        outputMap.add(output);
+        config.put(OUTPUT_MAP, outputMap);
+        config.put(MAX_PREDICTION_TASKS, 2);
+        String processorTag = randomAlphaOfLength(10);
+
+        factory.create(Collections.emptyMap(), processorTag, null, false, config, null);
+    }
+
+    /**
+     * Tests the case where only the output maps are provided in the configuration.
+     *
+     * @throws Exception if an error occurs during the test
+     */
+    public void testOnlyOutputMapsEmptyInputProvided() throws Exception {
+        Map<String, Object> config = new HashMap<>();
+        config.put(MODEL_ID, "model2");
+        List<Map<String, String>> inputMap = new ArrayList<>();
+        List<Map<String, String>> outputMap = new ArrayList<>();
+        Map<String, String> output = new HashMap<>();
+        output.put("text_embedding", "$.inference_results[0].output[0].data");
+        outputMap.add(output);
+        config.put(INPUT_MAP, inputMap);
+        config.put(OUTPUT_MAP, outputMap);
+        config.put(MAX_PREDICTION_TASKS, 2);
+        String processorTag = randomAlphaOfLength(10);
+
+        factory.create(Collections.emptyMap(), processorTag, null, false, config, null);
+    }
+
+    /**
+     * Tests the creation of the MLInferenceSearchResponseProcessor with optional fields.
+     *
+     * @throws Exception if an error occurs during the test
+     */
     public void testCreateOptionalFields() throws Exception {
         Map<String, Object> config = new HashMap<>();
         config.put(MODEL_ID, "model2");


Reviews:
user: login: b4sjoo
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-04-15T02:59:25Z
user: login: Zhangxunmt
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T03:08:06Z
user: login: Zhangxunmt
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-04-15T03:08:11Z
user: login: mingshl
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T03:09:28Z
user: login: b4sjoo
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-15T03:20:21Z
user: login: Zhangxunmt
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-15T03:55:05Z
user: login: jngz-es
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-15T16:13:50Z

Review comments:
diff_hunk: @@ -626,29 +626,29 @@ public void onResponse(Map<Integer, MLOutput> multipleMLOutputs) {
                                             ignoreMissing,
                                             fullResponsePath
                                         );
-                                        Object modelOutputValuePerDoc;
-                                        if (modelOutputValue instanceof List
-                                            && ((List) modelOutputValue).size() == hitCountInPredictions.get(mappingIndex)) {
-                                            Object valuePerDoc = ((List) modelOutputValue)
-                                                .get(MapUtils.getCounter(writeOutputMapDocCounter, mappingIndex, modelOutputFieldName));
-                                            modelOutputValuePerDoc = valuePerDoc;
-                                        } else {
-                                            modelOutputValuePerDoc = modelOutputValue;
-                                        }
                                         // writing to search response extension
                                         if (newDocumentFieldName.startsWith(EXTENSION_PREFIX)) {
                                             Map<String, Object> params = ((MLInferenceSearchResponse) response).getParams();
                                             String paramsName = newDocumentFieldName.replaceFirst(EXTENSION_PREFIX + ".", "");
 
                                             if (params != null) {
-                                                params.put(paramsName, modelOutputValuePerDoc);
+                                                params.put(paramsName, modelOutputValue);
                                                 ((MLInferenceSearchResponse) response).setParams(params);
                                             } else {
                                                 Map<String, Object> newParams = new HashMap<>();
-                                                newParams.put(paramsName, modelOutputValuePerDoc);
+                                                newParams.put(paramsName, modelOutputValue);
                                                 ((MLInferenceSearchResponse) response).setParams(newParams);
                                             }
                                         } else {
+                                            Object modelOutputValuePerDoc;
+                                            if (modelOutputValue instanceof List
+                                                && ((List) modelOutputValue).size() == hitCountInPredictions.get(mappingIndex)) {
path: plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessor.java
user: login: Zhangxunmt
body: If mappingIndex is not in hitCountInPredictions, you'll get a null → NullPointerException on size() == null.


created_at: 2025-04-15T03:08:05Z
author_association: COLLABORATOR
diff_hunk: @@ -626,29 +626,29 @@ public void onResponse(Map<Integer, MLOutput> multipleMLOutputs) {
                                             ignoreMissing,
                                             fullResponsePath
                                         );
-                                        Object modelOutputValuePerDoc;
-                                        if (modelOutputValue instanceof List
-                                            && ((List) modelOutputValue).size() == hitCountInPredictions.get(mappingIndex)) {
-                                            Object valuePerDoc = ((List) modelOutputValue)
-                                                .get(MapUtils.getCounter(writeOutputMapDocCounter, mappingIndex, modelOutputFieldName));
-                                            modelOutputValuePerDoc = valuePerDoc;
-                                        } else {
-                                            modelOutputValuePerDoc = modelOutputValue;
-                                        }
                                         // writing to search response extension
                                         if (newDocumentFieldName.startsWith(EXTENSION_PREFIX)) {
                                             Map<String, Object> params = ((MLInferenceSearchResponse) response).getParams();
                                             String paramsName = newDocumentFieldName.replaceFirst(EXTENSION_PREFIX + ".", "");
 
                                             if (params != null) {
-                                                params.put(paramsName, modelOutputValuePerDoc);
+                                                params.put(paramsName, modelOutputValue);
                                                 ((MLInferenceSearchResponse) response).setParams(params);
                                             } else {
                                                 Map<String, Object> newParams = new HashMap<>();
-                                                newParams.put(paramsName, modelOutputValuePerDoc);
+                                                newParams.put(paramsName, modelOutputValue);
                                                 ((MLInferenceSearchResponse) response).setParams(newParams);
                                             }
                                         } else {
+                                            Object modelOutputValuePerDoc;
+                                            if (modelOutputValue instanceof List
+                                                && ((List) modelOutputValue).size() == hitCountInPredictions.get(mappingIndex)) {
path: plugin/src/main/java/org/opensearch/ml/processor/MLInferenceSearchResponseProcessor.java
user: login: mingshl
body:  I will add this check
created_at: 2025-04-15T03:09:28Z
author_association: COLLABORATOR

PR comments:
user: login: mingshl
created_at: 2025-04-15T03:51:12Z
body: unrelated CI failed
```
> Task :opensearch-ml-plugin:yamlRestTest

PluginClientYamlTestSuiteIT > initializationError FAILED
    java.lang.RuntimeException: Error collecting parameters from: public static java.lang.Iterable org.opensearch.ml.plugin.PluginClientYamlTestSuiteIT.parameters() throws java.lang.Exception
        at com.carrotsearch.randomizedtesting.RandomizedRunner.collectMethodExecutions(RandomizedRunner.java:1506)
        at com.carrotsearch.randomizedtesting.RandomizedRunner.collectTestCandidates(RandomizedRunner.java:1318)
        at com.carrotsearch.randomizedtesting.RandomizedRunner.<init>(RandomizedRunner.java:386)
        at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:501)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:485)
        at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
        at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:70)
        at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:37)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:70)
        at org.junit.internal.requests.ClassRequest.createRunner(ClassRequest.java:28)
        at org.junit.internal.requests.MemoizingRequest.getRunner(MemoizingRequest.java:19)
        at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:77)
	at java.base/java.lang.Thread.run(Thread.java:1575)
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.NoClassDefFoundError: org/opensearch/javaagent/bootstrap/AgentPolicy$AnyCanExit [in thread "Test worker"]
	at org.opensearch.bootstrap.BootstrapForTesting.<clinit>(BootstrapForTesting.java:191)
	at org.opensearch.test.OpenSearchTestCase.<clinit>(OpenSearchTestCase.java:331)
	at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
	at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1161)
	at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:340)
	at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newMethodAccessor(MethodHandleAccessorFactory.java:71)
	at java.base/jdk.internal.reflect.ReflectionFactory.newMethodAccessor(ReflectionFactory.java:154)
	at java.base/java.lang.reflect.Method.acquireMethodAccessor(Method.java:726)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at com.carrotsearch.randomizedtesting.RandomizedRunner.collectMethodExecutions(RandomizedRunner.java:1493)
	at com.carrotsearch.randomizedtesting.RandomizedRunner.collectTestCandidates(RandomizedRunner.java:1318)
	at com.carrotsearch.randomizedtesting.RandomizedRunner.<init>(RandomizedRunner.java:386)
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:501)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:485)
	at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
	at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
	at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:70)
	at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:37)
	at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:70)
	at org.junit.internal.requests.ClassRequest.createRunner(ClassRequest.java:28)
	at org.junit.internal.requests.MemoizingRequest.getRunner(MemoizingRequest.java:19)
```
author_association: COLLABORATOR
user: login: mingshl
created_at: 2025-04-15T19:29:04Z
body: flaky test

```
REPRODUCE WITH: gradlew ':opensearch-ml-plugin:integTest' --tests "org.opensearch.ml.rest.RestBedRockInferenceIT.test_bedrock_embedding_v2_model_with_postProcessFunction" -Dtests.seed=AAE793DDA6D7952F -Dtests.security.manager=false -Dtests.locale=fr-TG -Dtests.timezone=AGT -Druntime.java=23
    	at java.management@23.0.2/sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:518)
    	at java.management@23.0.2/sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:506)
    	at app//com.carrotsearch.randomizedtesting.ThreadLeakControl.formatThreadStacksFull(ThreadLeakControl.java:681)
    	at app//com.carrotsearch.randomizedtesting.ThreadLeakControl.access$1000(ThreadLeakControl.java:65)
    	at app//com.carrotsearch.randomizedtesting.ThreadLeakControl$2.evaluate(ThreadLeakControl.java:415)
    	- locked java.lang.Object@a943412
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner.runSuite(RandomizedRunner.java:708)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner.access$200(RandomizedRunner.java:138)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner$2.run(RandomizedRunner.java:629)

    "TEST-RestBedRockInferenceIT.test_bedrock_embedding_v2_model_with_postProcessFunction-seed#[AAE793DDA6D7952F]" ID=42 TIMED_WAITING
    	at java.base@23.0.2/java.lang.Thread.sleepNanos0(Native Method)
    	at java.base@23.0.2/java.lang.Thread.sleepNanos(Thread.java:496)
    	at java.base@23.0.2/java.lang.Thread.sleep(Thread.java:527)
    	at app//org.opensearch.test.OpenSearchTestCase.waitUntil(OpenSearchTestCase.java:1258)
    	at app//org.opensearch.ml.rest.MLCommonsRestTestCase.waitForTask(MLCommonsRestTestCase.java:969)
    	at app//org.opensearch.ml.rest.MLCommonsRestTestCase.registerRemoteModel(MLCommonsRestTestCase.java:1010)
    	at app//org.opensearch.ml.rest.RestBedRockInferenceIT.test_bedrock_embedding_v2_model_with_postProcessFunction(RestBedRockInferenceIT.java:271)
    	at java.base@23.0.2/java.lang.invoke.LambdaForm$DMH/0x0000021c01150800.invokeVirtual(LambdaForm$DMH)
    	at java.base@23.0.2/java.lang.invoke.LambdaForm$MH/0x0000021c01104800.invoke(LambdaForm$MH)
    	at java.base@23.0.2/java.lang.invoke.Invokers$Holder.invokeExact_MT(Invokers$Holder)
    	at java.base@23.0.2/jdk.internal.reflect.DirectMethodHandleAccessor.invokeImpl(DirectMethodHandleAccessor.java:153)
    	at java.base@23.0.2/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    	at java.base@23.0.2/java.lang.reflect.Method.invoke(Method.java:580)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner.invoke(RandomizedRunner.java:1750)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner$8.evaluate(RandomizedRunner.java:938)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner$9.evaluate(RandomizedRunner.java:974)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner$10.evaluate(RandomizedRunner.java:988)
    	at app//com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
    	at app//org.junit.rules.RunRules.evaluate(RunRules.java:20)
    	at app//org.apache.lucene.tests.util.TestRuleSetupTeardownChained$1.evaluate(TestRuleSetupTeardownChained.java:48)
    	at app//org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
    	at app//org.apache.lucene.tests.util.TestRuleThreadAndTestName$1.evaluate(TestRuleThreadAndTestName.java:45)
    	at app//org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
    	at app//org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
    	at app//org.junit.rules.RunRules.evaluate(RunRules.java:20)
    	at app//com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
    	at app//com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:368)
    	at app//com.carrotsearch.randomizedtesting.ThreadLeakControl.forkTimeoutingTask(ThreadLeakControl.java:817)
    	at app//com.carrotsearch.randomizedtesting.ThreadLeakControl$3.evaluate(ThreadLeakControl.java:468)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner.runSingleTest(RandomizedRunner.java:947)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner$5.evaluate(RandomizedRunner.java:832)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner$6.evaluate(RandomizedRunner.java:883)
    	at app//com.carrotsearch.randomizedtesting.RandomizedRunner$7.evaluate(RandomizedRunner.java:894)
    	at app//or
    g.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
    	at app//com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
    	at app//org.apache.lucene.tests.util.TestRuleStoreClassName$1.evaluate(TestRuleStoreClassName.java:38)
    	at app//com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
    	at app//com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
    	at app//com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
    	at app//com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
    	at app//org.apache.lucene.tests.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:53)
    	at app//org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
    	at app//org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
    	at app//org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
    	at app//org.apache.lucene.tests.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:47)
    	at app//org.junit.rules.RunRules.evaluate(RunRules.java:20)
    	at app//com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
    	at app//com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(Thre
```
author_association: COLLABORATOR
user: login: opensearch-trigger-bot[bot]
created_at: 2025-04-15T19:32:00Z
body: The backport to `2.18` failed:
```
The process '/usr/bin/git' failed with exit code 1
```
To backport manually, run these commands in your terminal:
```bash
# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add .worktrees/backport-2.18 2.18
# Navigate to the new working tree
cd .worktrees/backport-2.18
# Create a new branch
git switch --create backport/backport-3730-to-2.18
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 2da7d7a1e7d5cd80bd9940dd6b7ff1c32c009582
# Push it to GitHub
git push --set-upstream origin backport/backport-3730-to-2.18
# Go back to the original working tree
cd ../..
# Delete the working tree
git worktree remove .worktrees/backport-2.18
```
Then, create a pull request where the `base` branch is `2.18` and the `compare`/`head` branch is `backport/backport-3730-to-2.18`.
author_association: CONTRIBUTOR
user: login: opensearch-trigger-bot[bot]
created_at: 2025-04-15T19:32:05Z
body: The backport to `2.17` failed:
```
The process '/usr/bin/git' failed with exit code 1
```
To backport manually, run these commands in your terminal:
```bash
# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add .worktrees/backport-2.17 2.17
# Navigate to the new working tree
cd .worktrees/backport-2.17
# Create a new branch
git switch --create backport/backport-3730-to-2.17
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 2da7d7a1e7d5cd80bd9940dd6b7ff1c32c009582
# Push it to GitHub
git push --set-upstream origin backport/backport-3730-to-2.17
# Go back to the original working tree
cd ../..
# Delete the working tree
git worktree remove .worktrees/backport-2.17
```
Then, create a pull request where the `base` branch is `2.17` and the `compare`/`head` branch is `backport/backport-3730-to-2.17`.
author_association: CONTRIBUTOR