Skip to content
This repository was archived by the owner on Jul 10, 2026. It is now read-only.

feat: add scoped composite rate limiting#102

Closed
kevinmessiaen wants to merge 7 commits into
mainfrom
feature/eng-1272-add-composite-limiter-global-kind-target-to-giskard-agents
Closed

feat: add scoped composite rate limiting#102
kevinmessiaen wants to merge 7 commits into
mainfrom
feature/eng-1272-add-composite-limiter-global-kind-target-to-giskard-agents

Conversation

@kevinmessiaen

@kevinmessiaen kevinmessiaen commented Feb 5, 2026

Copy link
Copy Markdown
Member

Introduce a composable limiter registry for global and targeted throttling, and update generators/tests to use it.


Note

Medium Risk
Touches core throttling behavior and removes the prior rate_limiter configuration/serialization path, which could change runtime request pacing and how downstream code configures limits.

Overview
Introduces a new composable, scoped rate-limiting system (RateLimiter primitives + CompositeRateLimiter + hierarchical registry) that can apply global and target-specific throttles and exposes wait-time details via RateLimitDetails/RateLimitEntry.

Updates LiteLLMGenerator to always use registry-based throttle("llm","litellm",...) instead of a per-generator rate_limiter field, removes the WithRateLimiter mixin/old singleton registry APIs, and rewrites tests (plus adds pytest-timeout) to cover RPM, concurrency, composition, and scoped resolution behavior; generator/workflow serialization tests drop rate-limiter serialization expectations.

Written by Cursor Bugbot for commit d4bc92d. This will update automatically on new commits. Configure here.

Introduce a composable limiter registry for global and targeted throttling, and update generators/tests to use it.
@linear

linear Bot commented Feb 5, 2026

Copy link
Copy Markdown

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @kevinmessiaen, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly overhauls the rate limiting infrastructure, moving from a basic, per-instance approach to a sophisticated, registry-driven system. The core purpose is to provide more granular control over request throttling, allowing for the application of multiple, layered rate limits across different components or external services. This change enhances the system's ability to manage resource consumption and prevent abuse more effectively, particularly for external API calls, without adding complexity to individual generator implementations.

Highlights

  • New Rate Limiting System: Introduced a new, more flexible rate limiting system that supports composite limiters (combining requests per minute and maximum concurrent requests) and a scoped registry for targeted throttling.
  • Composable Rate Limiters: The new system allows for combining multiple rate limiting policies, such as MaxRequestsPerMinute and MaxConcurrentRequests, into a CompositeRateLimiter.
  • Scoped Registry: A registry (_RateLimiterRegistry) has been implemented to manage rate limiters based on target paths, enabling global and specific (e.g., per LLM provider or model) throttling policies.
  • LiteLLMGenerator Update: The LiteLLMGenerator has been refactored to utilize the new throttle function and the scoped registry, simplifying its internal rate limiting logic and removing the WithRateLimiter mixin.
  • Testing Enhancements: Comprehensive new tests have been added for the MaxRequestsPerMinute, MaxConcurrentRequests, CompositeRateLimiter, and the scoped registry, ensuring robust behavior of the new rate limiting features.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • pyproject.toml
    • Added pytest-timeout>=2.4.0 to development dependencies.
  • src/giskard/agents/init.py
    • Imported new rate limiter classes and functions: CompositeRateLimiter, MaxConcurrentRequests, MaxRequestsPerMinute, NO_WAIT_TIME, RateLimitDetails, RateLimitEntry, register_rate_limiter, scoped_limiter, throttle, unregister_rate_limiter.
    • Removed imports for RateLimiterStrategy, get_or_create_rate_limiter, get_or_create_rate_limiter_from_rpm, get_rate_limiter.
    • Updated __all__ export list to reflect the new rate limiter components and remove deprecated ones.
  • src/giskard/agents/generators/init.py
    • Removed import and export of WithRateLimiter.
  • src/giskard/agents/generators/litellm_generator.py
    • Imported throttle from ..rate_limiter.
    • Removed WithRateLimiter from LiteLLMGenerator's inheritance.
    • Updated the rate limiting context from self._rate_limiter_context() to throttle("llm", "litellm", *self.model.split("/")).
  • src/giskard/agents/generators/mixins.py
    • Removed the WithRateLimiter class and its associated logic.
  • src/giskard/agents/rate_limiter.py
    • Completely refactored the rate limiting logic, introducing RateLimitEntry and RateLimitDetails for detailed wait time reporting.
    • Defined RateLimiter as a base class with acquire and release methods, and factory methods like budget, rpm, max_in_flight, and with_rate_limiters.
    • Implemented MaxRequestsPerMinute and MaxConcurrentRequests as concrete RateLimiter subclasses.
    • Introduced CompositeRateLimiter to combine multiple limiters sequentially.
    • Created a _RateLimiterRegistry class to manage scoped rate limiters.
    • Added global functions throttle, register_rate_limiter, unregister_rate_limiter, and scoped_limiter for interacting with the registry.
    • Removed the old RateLimiterStrategy and the global _rate_limiters dictionary, along with _register_rate_limiter, get_rate_limiter, get_or_create_rate_limiter, and get_or_create_rate_limiter_from_rpm functions.
  • tests/test_generator.py
    • Updated imports for RateLimiter and scoped_limiter, removing _rate_limiters import.
    • Refactored test_litellm_generator_gets_rate_limiter to use scoped_limiter and RateLimiter.budget, and parameterized it for different targets.
    • Removed several old rate limiter-related tests (test_generator_rate_limiter_context, test_generator_with_params_and_rate_limiter, test_generator_serialization_keep_rate_limiter_instance, test_generator_serialization_recreate_rate_limiter_instance_if_not_in_registry).
    • Adjusted test_generator_with_params to reflect the removal of rate_limiter from LiteLLMGenerator.
  • tests/test_rate_limiter.py
    • Rewrote tests to cover the new MaxRequestsPerMinute, MaxConcurrentRequests, CompositeRateLimiter, and _RateLimiterRegistry functionality.
    • Introduced JITTER_TIME constant and new test classes (TestMaxRequestsPerMinute, TestMaxConcurrentRequests, TestCompositeRateLimiter, TestRateLimiterRegistry) with detailed tests for parallel requests, throttling rates, and scoped limiting.
    • Removed all old rate limiter tests.
  • tests/test_serialization.py
    • Removed RateLimiter import.
    • Removed rate_limiter parameter from Generator instantiation in test_generator_serialization and test_chat_workflow_serialization.
    • Removed assertions related to rate_limiter in test_generator_serialization.
Activity
  • The author kevinmessiaen has introduced a new feature for scoped composite rate limiting.
  • This pull request involves a significant refactoring of the rate limiting system, moving from a simpler, per-instance rate limiter to a more sophisticated, registry-based, and composable approach.
  • The changes impact core agent components, particularly the LiteLLMGenerator, requiring updates to its usage and testing.
  • New test cases have been added to validate the behavior of the new composite and scoped rate limiters, ensuring the robustness of the new system.
  • Old rate limiter-related code and tests have been removed or updated to align with the new architecture.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and well-designed refactoring of the rate limiting mechanism, moving to a composable and scoped system. The new RateLimiter primitives and the registry for applying them based on target paths are a great improvement, making the system more flexible and powerful. The code is clean and the accompanying tests are thorough. I've found a few minor issues, mostly related to the test suite and a type hint, which I've detailed in the comments below.

Comment thread tests/test_generator.py Outdated
Comment thread src/giskard/agents/rate_limiter.py Outdated
Comment thread tests/test_rate_limiter.py Outdated
Comment thread tests/test_generator.py
Comment thread tests/test_rate_limiter.py
Comment thread src/giskard/agents/rate_limiter.py Outdated
kevinmessiaen and others added 5 commits February 5, 2026 14:32
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Ensure already-acquired limiters are released if a later acquire fails.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

return self.rate_limiters == other.rate_limiters

def __hash__(self) -> int:
return hash(tuple(self.rate_limiters))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant tuple conversion on already-tuple attribute

Low Severity

The __hash__ method wraps self.rate_limiters in tuple(), but rate_limiters is already declared as tuple[RateLimiter, ...] at line 278. This should simply be return hash(self.rate_limiters).

Fix in Cursor Fix in Web

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant