2121
2222import com .google .auto .value .AutoValue ;
2323import java .util .List ;
24+ import org .apache .beam .sdk .io .components .throttling .ReactiveThrottler ;
2425import org .apache .beam .sdk .transforms .BatchElements ;
2526import org .apache .beam .sdk .transforms .DoFn ;
2627import org .apache .beam .sdk .transforms .PTransform ;
4849 * // Apply remote inference transform
4950 * PCollection<OpenAIModelInput> inputs = pipeline.apply(Create.of(
5051 * OpenAIModelInput.create("An excellent B2B SaaS solution that streamlines business processes efficiently."),
51- * OpenAIModelInput.create("Really impressed with the innovative features!")
52- * ));
52+ * OpenAIModelInput.create("Really impressed with the innovative features!")));
5353 *
54- * PCollection<Iterable<PredictionResult<OpenAIModelInput, OpenAIModelResponse>>> results =
55- * inputs.apply(
56- * RemoteInference.<OpenAIModelInput, OpenAIModelResponse>invoke()
57- * .handler(OpenAIModelHandler.class)
58- * .withParameters(params)
59- * );
54+ * PCollection<Iterable<PredictionResult<OpenAIModelInput, OpenAIModelResponse>>> results = inputs.apply(
55+ * RemoteInference.<OpenAIModelInput, OpenAIModelResponse>invoke()
56+ * .handler(OpenAIModelHandler.class)
57+ * .withParameters(params));
6058 * }</pre>
6159 */
6260@ SuppressWarnings ({"rawtypes" , "unchecked" })
@@ -82,6 +80,14 @@ public abstract static class Invoke<InputT extends BaseInput, OutputT extends Ba
8280
8381 abstract BatchElements .@ Nullable BatchConfig batchConfig ();
8482
83+ abstract @ Nullable Integer throttleDelaySecs ();
84+
85+ abstract @ Nullable Long samplePeriodMs ();
86+
87+ abstract @ Nullable Long sampleUpdateMs ();
88+
89+ abstract @ Nullable Double overloadRatio ();
90+
8591 abstract Builder <InputT , OutputT > builder ();
8692
8793 @ AutoValue .Builder
@@ -94,6 +100,14 @@ abstract Builder<InputT, OutputT> setHandler(
94100
95101 abstract Builder <InputT , OutputT > setBatchConfig (BatchElements .BatchConfig batchConfig );
96102
103+ abstract Builder <InputT , OutputT > setThrottleDelaySecs (Integer throttleDelaySecs );
104+
105+ abstract Builder <InputT , OutputT > setSamplePeriodMs (Long samplePeriodMs );
106+
107+ abstract Builder <InputT , OutputT > setSampleUpdateMs (Long sampleUpdateMs );
108+
109+ abstract Builder <InputT , OutputT > setOverloadRatio (Double overloadRatio );
110+
97111 abstract Invoke <InputT , OutputT > build ();
98112 }
99113
@@ -113,6 +127,42 @@ public Invoke<InputT, OutputT> withBatchConfig(BatchElements.BatchConfig batchCo
113127 return builder ().setBatchConfig (batchConfig ).build ();
114128 }
115129
130+ /**
131+ * Configures the throttling delay when the client is preemptively throttled. Defaults to 5
132+ * seconds. A value of 0 disables throttling. For more context, see {@link ReactiveThrottler}
133+ */
134+ public Invoke <InputT , OutputT > withThrottleDelaySecs (int throttleDelaySecs ) {
135+ checkArgument (throttleDelaySecs >= 0 , "throttleDelaySecs must be non-negative" );
136+ return builder ().setThrottleDelaySecs (throttleDelaySecs ).build ();
137+ }
138+
139+ /**
140+ * Configures the length of history to consider when setting throttling probability. Defaults to
141+ * a sample period of 1000ms. For more context, see {@link AdaptiveThrottler}
142+ */
143+ public Invoke <InputT , OutputT > withSamplePeriodMs (long samplePeriodMs ) {
144+ checkArgument (samplePeriodMs > 0 , "samplePeriodMs must be positive" );
145+ return builder ().setSamplePeriodMs (samplePeriodMs ).build ();
146+ }
147+
148+ /**
149+ * Configures the granularity of time buckets that we store data in for throttling. Defaults to
150+ * a sample period of 1000ms. For more context, see {@link AdaptiveThrottler}
151+ */
152+ public Invoke <InputT , OutputT > withSampleUpdateMs (long sampleUpdateMs ) {
153+ checkArgument (sampleUpdateMs > 0 , "sampleUpdateMs must be positive" );
154+ return builder ().setSampleUpdateMs (sampleUpdateMs ).build ();
155+ }
156+
157+ /**
158+ * Configures the target ratio between requests sent and successful requests. Defaults to an
159+ * overload ratio of 2.0. For more context, see {@link AdaptiveThrottler}
160+ */
161+ public Invoke <InputT , OutputT > withOverloadRatio (double overloadRatio ) {
162+ checkArgument (overloadRatio > 0 , "overloadRatio must be positive" );
163+ return builder ().setOverloadRatio (overloadRatio ).build ();
164+ }
165+
116166 @ Override
117167 public PCollection <Iterable <PredictionResult <InputT , OutputT >>> expand (
118168 PCollection <InputT > input ) {
@@ -151,10 +201,19 @@ static class RemoteInferenceFn<InputT extends BaseInput, OutputT extends BaseRes
151201 private final BaseModelParameters parameters ;
152202 private transient @ Nullable BaseModelHandler modelHandler ;
153203 private final RetryHandler retryHandler ;
204+ private final int throttleDelaySecs ;
205+ private final long samplePeriodMs ;
206+ private final long sampleUpdateMs ;
207+ private final double overloadRatio ;
208+ private transient @ Nullable ReactiveThrottler throttler ;
154209
155210 RemoteInferenceFn (Invoke <InputT , OutputT > spec ) {
156211 this .handlerClass = spec .handler ();
157212 this .parameters = spec .parameters ();
213+ this .throttleDelaySecs = spec .throttleDelaySecs () != null ? spec .throttleDelaySecs () : 5 ;
214+ this .samplePeriodMs = spec .samplePeriodMs () != null ? spec .samplePeriodMs () : 1000L ;
215+ this .sampleUpdateMs = spec .sampleUpdateMs () != null ? spec .sampleUpdateMs () : 1000L ;
216+ this .overloadRatio = spec .overloadRatio () != null ? spec .overloadRatio () : 2.0 ;
158217 retryHandler = RetryHandler .withDefaults ();
159218 }
160219
@@ -164,15 +223,40 @@ public void setupHandler() {
164223 try {
165224 this .modelHandler = handlerClass .getDeclaredConstructor ().newInstance ();
166225 this .modelHandler .createClient (parameters );
226+ if (throttleDelaySecs > 0 ) {
227+ this .throttler =
228+ new ReactiveThrottler (
229+ samplePeriodMs ,
230+ sampleUpdateMs ,
231+ overloadRatio ,
232+ "RemoteInference" ,
233+ throttleDelaySecs );
234+ }
167235 } catch (Exception e ) {
168236 throw new RuntimeException ("Failed to instantiate handler: " + handlerClass .getName (), e );
169237 }
170238 }
239+
171240 /** Perform Inference. */
172241 @ ProcessElement
173242 public void processElement (ProcessContext c ) throws Exception {
174243 Iterable <PredictionResult <InputT , OutputT >> response =
175- retryHandler .execute (() -> modelHandler .request (c .element ()));
244+ retryHandler .execute (
245+ () -> {
246+ if (throttler != null ) {
247+ throttler .throttle ();
248+ }
249+ long reqTime = System .currentTimeMillis ();
250+ if (modelHandler == null ) {
251+ throw new IllegalStateException ("modelHandler is not initialized" );
252+ }
253+ Iterable <PredictionResult <InputT , OutputT >> result =
254+ modelHandler .request (c .element ());
255+ if (throttler != null ) {
256+ throttler .successfulRequest (reqTime );
257+ }
258+ return result ;
259+ });
176260 c .output (response );
177261 }
178262 }
0 commit comments