@@ -28,6 +28,17 @@ class LlmApi(TypedDict):
28
28
ApiT = LlmApi # In the future, this can be union of different API types
29
29
30
30
31
+ class FlowControl (TypedDict , total = False ):
32
+ key : str
33
+ """flow control key"""
34
+
35
+ parallelism : Optional [int ]
36
+ """number of requests which can be active with the same key"""
37
+
38
+ rate_per_second : Optional [int ]
39
+ """number of requests to activate per second with the same key"""
40
+
41
+
31
42
@dataclasses .dataclass
32
43
class PublishResponse :
33
44
message_id : str
@@ -168,6 +179,12 @@ class BatchRequest(TypedDict, total=False):
168
179
an integer, which will be interpreted as timeout in seconds.
169
180
"""
170
181
182
+ flow_control : Optional [FlowControl ]
183
+ """
184
+ Settings for controlling the number of active requests and number of requests
185
+ per second with the same key.
186
+ """
187
+
171
188
172
189
class BatchJsonRequest (TypedDict , total = False ):
173
190
queue : str
@@ -254,6 +271,12 @@ class BatchJsonRequest(TypedDict, total=False):
254
271
set according to the LLM provider.
255
272
"""
256
273
274
+ flow_control : Optional [FlowControl ]
275
+ """
276
+ Settings for controlling the number of active requests and number of requests
277
+ per second with the same key.
278
+ """
279
+
257
280
258
281
@dataclasses .dataclass
259
282
class Message :
@@ -317,6 +340,15 @@ class Message:
317
340
caller_ip : Optional [str ]
318
341
"""IP address of the publisher of this message."""
319
342
343
+ flow_control_key : Optional [str ]
344
+ """flow control key"""
345
+
346
+ parallelism : Optional [int ]
347
+ """number of requests which can be active with the same flow control key"""
348
+
349
+ rate_per_second : Optional [int ]
350
+ """number of requests to activate per second with the same flow control key"""
351
+
320
352
321
353
def get_destination (
322
354
* ,
@@ -367,6 +399,7 @@ def prepare_headers(
367
399
deduplication_id : Optional [str ],
368
400
content_based_deduplication : Optional [bool ],
369
401
timeout : Optional [Union [str , int ]],
402
+ flow_control : Optional [FlowControl ],
370
403
) -> Dict [str , str ]:
371
404
h = {}
372
405
@@ -413,6 +446,21 @@ def prepare_headers(
413
446
else :
414
447
h ["Upstash-Timeout" ] = timeout
415
448
449
+ if flow_control and "key" in flow_control :
450
+ control_values = []
451
+ if "parallelism" in flow_control :
452
+ control_values .append (f"parallelism={ flow_control ['parallelism' ]} " )
453
+ if "rate_per_second" in flow_control :
454
+ control_values .append (f"rate={ flow_control ['rate_per_second' ]} " )
455
+
456
+ if not control_values :
457
+ raise QStashError (
458
+ "Provide at least one of parallelism or rate_per_second for flow_control"
459
+ )
460
+
461
+ h ["Upstash-Flow-Control-Key" ] = flow_control ["key" ]
462
+ h ["Upstash-Flow-Control-Value" ] = ", " .join (control_values )
463
+
416
464
return h
417
465
418
466
@@ -484,6 +532,7 @@ def prepare_batch_message_body(messages: List[BatchRequest]) -> str:
484
532
deduplication_id = msg .get ("deduplication_id" ),
485
533
content_based_deduplication = msg .get ("content_based_deduplication" ),
486
534
timeout = msg .get ("timeout" ),
535
+ flow_control = msg .get ("flow_control" ),
487
536
)
488
537
489
538
batch_messages .append (
@@ -581,6 +630,9 @@ def convert_to_batch_messages(
581
630
if "timeout" in msg :
582
631
batch_msg ["timeout" ] = msg ["timeout" ]
583
632
633
+ if "flow_control" in msg :
634
+ batch_msg ["flow_control" ] = msg ["flow_control" ]
635
+
584
636
batch_messages .append (batch_msg )
585
637
586
638
return batch_messages
@@ -605,6 +657,9 @@ def parse_message_response(response: Dict[str, Any]) -> Message:
605
657
failure_callback = response .get ("failureCallback" ),
606
658
schedule_id = response .get ("scheduleId" ),
607
659
caller_ip = response .get ("callerIP" ),
660
+ flow_control_key = response .get ("flowControlKey" ),
661
+ parallelism = response .get ("parallelism" ),
662
+ rate_per_second = response .get ("rate" ),
608
663
)
609
664
610
665
@@ -630,6 +685,7 @@ def publish(
630
685
deduplication_id : Optional [str ] = None ,
631
686
content_based_deduplication : Optional [bool ] = None ,
632
687
timeout : Optional [Union [str , int ]] = None ,
688
+ flow_control : Optional [FlowControl ] = None ,
633
689
) -> Union [PublishResponse , List [PublishUrlGroupResponse ]]:
634
690
"""
635
691
Publishes a message to QStash.
@@ -667,6 +723,8 @@ def publish(
667
723
When a timeout is specified, it will be used instead of the maximum timeout
668
724
value permitted by the QStash plan. It is useful in scenarios, where a message
669
725
should be delivered with a shorter timeout.
726
+ :param flow_control: Settings for controlling the number of active requests and
727
+ number of requests per second with the same key.
670
728
"""
671
729
headers = headers or {}
672
730
destination = get_destination (
@@ -688,6 +746,7 @@ def publish(
688
746
deduplication_id = deduplication_id ,
689
747
content_based_deduplication = content_based_deduplication ,
690
748
timeout = timeout ,
749
+ flow_control = flow_control ,
691
750
)
692
751
693
752
response = self ._http .request (
@@ -716,6 +775,7 @@ def publish_json(
716
775
deduplication_id : Optional [str ] = None ,
717
776
content_based_deduplication : Optional [bool ] = None ,
718
777
timeout : Optional [Union [str , int ]] = None ,
778
+ flow_control : Optional [FlowControl ] = None ,
719
779
) -> Union [PublishResponse , List [PublishUrlGroupResponse ]]:
720
780
"""
721
781
Publish a message to QStash, automatically serializing the
@@ -754,6 +814,8 @@ def publish_json(
754
814
When a timeout is specified, it will be used instead of the maximum timeout
755
815
value permitted by the QStash plan. It is useful in scenarios, where a message
756
816
should be delivered with a shorter timeout.
817
+ :param flow_control: Settings for controlling the number of active requests and
818
+ number of requests per second with the same key.
757
819
"""
758
820
return self .publish (
759
821
url = url ,
@@ -771,6 +833,7 @@ def publish_json(
771
833
deduplication_id = deduplication_id ,
772
834
content_based_deduplication = content_based_deduplication ,
773
835
timeout = timeout ,
836
+ flow_control = flow_control ,
774
837
)
775
838
776
839
def enqueue (
@@ -843,6 +906,7 @@ def enqueue(
843
906
deduplication_id = deduplication_id ,
844
907
content_based_deduplication = content_based_deduplication ,
845
908
timeout = timeout ,
909
+ flow_control = None ,
846
910
)
847
911
848
912
response = self ._http .request (
0 commit comments