This is an automated email from the ASF dual-hosted git repository.

jrmccluskey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 2f7d95d1cf9 [Gemini] Add batching support to Java Remote Inference 
(#39067)
2f7d95d1cf9 is described below

commit 2f7d95d1cf9ff136df394f4f56b0a4b84ba467c1
Author: Jack McCluskey <[email protected]>
AuthorDate: Wed Jul 1 11:52:02 2026 -0400

    [Gemini] Add batching support to Java Remote Inference (#39067)
    
    * [Gemini] Add batching support to Java Remote Inference
    
    * Update 
sdks/java/ml/inference/remote/src/test/java/org/apache/beam/sdk/ml/inference/remote/RemoteInferenceTest.java
    
    Co-authored-by: gemini-code-assist[bot] 
<176961590+gemini-code-assist[bot]@users.noreply.github.com>
    
    * Fix batching test issue
    
    ---------
    
    Co-authored-by: gemini-code-assist[bot] 
<176961590+gemini-code-assist[bot]@users.noreply.github.com>
---
 .../sdk/ml/inference/remote/RemoteInference.java   | 33 +++++++++++-------
 .../ml/inference/remote/RemoteInferenceTest.java   | 40 ++++++++++++++++------
 2 files changed, 49 insertions(+), 24 deletions(-)

diff --git 
a/sdks/java/ml/inference/remote/src/main/java/org/apache/beam/sdk/ml/inference/remote/RemoteInference.java
 
b/sdks/java/ml/inference/remote/src/main/java/org/apache/beam/sdk/ml/inference/remote/RemoteInference.java
index 9092fc9910d..193c83d7b3e 100644
--- 
a/sdks/java/ml/inference/remote/src/main/java/org/apache/beam/sdk/ml/inference/remote/RemoteInference.java
+++ 
b/sdks/java/ml/inference/remote/src/main/java/org/apache/beam/sdk/ml/inference/remote/RemoteInference.java
@@ -20,13 +20,11 @@ package org.apache.beam.sdk.ml.inference.remote;
 import static 
org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
 
 import com.google.auto.value.AutoValue;
-import java.util.Collections;
 import java.util.List;
+import org.apache.beam.sdk.transforms.BatchElements;
 import org.apache.beam.sdk.transforms.DoFn;
-import org.apache.beam.sdk.transforms.MapElements;
 import org.apache.beam.sdk.transforms.PTransform;
 import org.apache.beam.sdk.transforms.ParDo;
-import org.apache.beam.sdk.transforms.SimpleFunction;
 import org.apache.beam.sdk.values.PCollection;
 import org.checkerframework.checker.nullness.qual.Nullable;
 
@@ -82,6 +80,8 @@ public class RemoteInference {
 
     abstract @Nullable BaseModelParameters parameters();
 
+    abstract BatchElements.@Nullable BatchConfig batchConfig();
+
     abstract Builder<InputT, OutputT> builder();
 
     @AutoValue.Builder
@@ -92,6 +92,8 @@ public class RemoteInference {
 
       abstract Builder<InputT, OutputT> setParameters(BaseModelParameters 
modelParameters);
 
+      abstract Builder<InputT, OutputT> 
setBatchConfig(BatchElements.BatchConfig batchConfig);
+
       abstract Invoke<InputT, OutputT> build();
     }
 
@@ -106,21 +108,26 @@ public class RemoteInference {
       return builder().setParameters(modelParameters).build();
     }
 
+    /** Configures the batching behavior for the inputs. */
+    public Invoke<InputT, OutputT> withBatchConfig(BatchElements.BatchConfig 
batchConfig) {
+      return builder().setBatchConfig(batchConfig).build();
+    }
+
     @Override
     public PCollection<Iterable<PredictionResult<InputT, OutputT>>> expand(
         PCollection<InputT> input) {
       checkArgument(handler() != null, "handler() is required");
       checkArgument(parameters() != null, "withParameters() is required");
-      return input
-          .apply(
-              "WrapInputInList",
-              MapElements.via(
-                  new SimpleFunction<InputT, List<InputT>>() {
-                    @Override
-                    public List<InputT> apply(InputT element) {
-                      return Collections.singletonList(element);
-                    }
-                  }))
+
+      BatchElements.BatchConfig config = batchConfig();
+      PCollection<List<InputT>> batchedInput;
+      if (config != null) {
+        batchedInput = input.apply("BatchElements", 
BatchElements.withConfig(config));
+      } else {
+        batchedInput = input.apply("BatchElements", 
BatchElements.withDefaults());
+      }
+
+      return batchedInput
           // Pass the list to the inference function
           .apply("RemoteInference", ParDo.of(new RemoteInferenceFn<InputT, 
OutputT>(this)));
     }
diff --git 
a/sdks/java/ml/inference/remote/src/test/java/org/apache/beam/sdk/ml/inference/remote/RemoteInferenceTest.java
 
b/sdks/java/ml/inference/remote/src/test/java/org/apache/beam/sdk/ml/inference/remote/RemoteInferenceTest.java
index eef47dddfcd..261b87bfe3a 100644
--- 
a/sdks/java/ml/inference/remote/src/test/java/org/apache/beam/sdk/ml/inference/remote/RemoteInferenceTest.java
+++ 
b/sdks/java/ml/inference/remote/src/test/java/org/apache/beam/sdk/ml/inference/remote/RemoteInferenceTest.java
@@ -518,36 +518,54 @@ public class RemoteInferenceTest {
     pipeline.run().waitUntilFinish();
   }
 
-  // Temporary behaviour until we introduce java BatchElements transform
-  // to batch elements in RemoteInference
-  @Test
-  public void testMultipleInputsProduceSeparateBatches() {
-    List<TestInput> inputs = Arrays.asList(new TestInput("input1"), new 
TestInput("input2"));
+  private static class GenerateInputsFn
+      extends org.apache.beam.sdk.transforms.DoFn<Integer, TestInput> {
+    @ProcessElement
+    public void processElement(ProcessContext c) {
+      c.output(new TestInput("input1"));
+      c.output(new TestInput("input2"));
+    }
+  }
 
+  @Test
+  public void testBatchingProducesCombinedBatches() {
     TestParameters params = 
TestParameters.builder().setConfig("test-config").build();
 
+    // Use a single element to trigger generation of inputs within the same 
bundle,
+    // ensuring DirectRunner doesn't split them before BatchElements processes 
them.
     PCollection<TestInput> inputCollection =
-        pipeline.apply(
-            "CreateInputs", 
Create.of(inputs).withCoder(SerializableCoder.of(TestInput.class)));
+        pipeline
+            .apply("CreateTrigger", Create.of(1))
+            .apply(
+                "GenerateInputs", org.apache.beam.sdk.transforms.ParDo.of(new 
GenerateInputsFn()))
+            .setCoder(SerializableCoder.of(TestInput.class));
+
+    // Configure BatchElements to force a batch of exactly 2
+    org.apache.beam.sdk.transforms.BatchElements.BatchConfig batchConfig =
+        org.apache.beam.sdk.transforms.BatchElements.BatchConfig.builder()
+            .withMinBatchSize(2)
+            .withMaxBatchSize(2)
+            .build();
 
     PCollection<Iterable<PredictionResult<TestInput, TestOutput>>> results =
         inputCollection.apply(
             "RemoteInference",
             RemoteInference.<TestInput, TestOutput>invoke()
                 .handler(MockSuccessHandler.class)
+                .withBatchConfig(batchConfig)
                 .withParameters(params));
 
     PAssert.that(results)
         .satisfies(
             batches -> {
               int batchCount = 0;
+              int totalElements = 0;
               for (Iterable<PredictionResult<TestInput, TestOutput>> batch : 
batches) {
                 batchCount++;
-                int elementCount = (int) 
StreamSupport.stream(batch.spliterator(), false).count();
-                // Each batch should contain exactly 1 element
-                assertEquals("Each batch should contain 1 element", 1, 
elementCount);
+                totalElements += (int) 
StreamSupport.stream(batch.spliterator(), false).count();
               }
-              assertEquals("Expected 2 batches", 2, batchCount);
+              assertEquals("Expected 1 batch", 1, batchCount);
+              assertEquals("Total output elements should be 2", 2, 
totalElements);
               return null;
             });
 

Reply via email to