1+ #ifndef PERPLEXITY_CPP_RATELIMITER_H
2+ #define PERPLEXITY_CPP_RATELIMITER_H
3+ #pragma once
4+
5+ #include < chrono>
6+ #include < mutex>
7+ #include < deque>
8+ #include < thread>
9+ #include " exceptions.h"
10+
11+ namespace perplexity {
12+
13+ /* *
14+ * @brief Rate limiter for controlling the frequency of API requests
15+ *
16+ * Uses the Token Bucket algorithm to limit the rate.
17+ * Thread-safe implementation using mutex.
18+ */
19+ class RateLimiter {
20+ private:
21+ mutable std::mutex mutex_;
22+ std::deque<std::chrono::steady_clock::time_point> request_times_;
23+ int max_requests_per_minute_;
24+ bool enabled_;
25+
26+ void cleanup_old_requests () {
27+ auto now = std::chrono::steady_clock::now ();
28+ auto cutoff = now - std::chrono::minutes (1 );
29+
30+ while (!request_times_.empty () && request_times_.front () < cutoff) {
31+ request_times_.pop_front ();
32+ }
33+ }
34+
35+ public:
36+ RateLimiter (int max_requests_per_minute = 60 , bool enabled = true )
37+ : max_requests_per_minute_(max_requests_per_minute)
38+ , enabled_(enabled) {
39+ if (max_requests_per_minute <= 0 ) {
40+ throw ConfigurationError (" Max requests per minute must be positive" );
41+ }
42+ }
43+
44+ /* *
45+ * @brief Waiting before the next request (if necessary)
46+ *
47+ * Blocks execution if the limit is reached.
48+ */
49+ void wait_if_needed () {
50+ if (!enabled_) {
51+ return ;
52+ }
53+
54+ std::unique_lock<std::mutex> lock (mutex_);
55+
56+ cleanup_old_requests ();
57+
58+ while (request_times_.size () >= static_cast <size_t >(max_requests_per_minute_)) {
59+ auto oldest = request_times_.front ();
60+ auto wait_until = oldest + std::chrono::minutes (1 );
61+ auto now = std::chrono::steady_clock::now ();
62+
63+ if (wait_until > now) {
64+ auto wait_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
65+ wait_until - now
66+ );
67+
68+ lock.unlock ();
69+ std::this_thread::sleep_for (wait_duration);
70+ lock.lock ();
71+
72+ cleanup_old_requests ();
73+ } else {
74+ break ;
75+ }
76+ }
77+
78+ // Registering a new request
79+ request_times_.push_back (std::chrono::steady_clock::now ());
80+ }
81+
82+ /* *
83+ * @brief Checking if it is possible to make a request now
84+ */
85+ bool can_make_request () const {
86+ if (!enabled_) {
87+ return true ;
88+ }
89+
90+ std::lock_guard<std::mutex> lock (mutex_);
91+ const_cast <RateLimiter*>(this )->cleanup_old_requests ();
92+
93+ return request_times_.size () <static_cast <size_t >(max_requests_per_minute_);
94+ }
95+
96+ /* *
97+ * @brief Getting the current number of requests in the last minute
98+ */
99+ size_t get_current_request_count () const {
100+ std::lock_guard<std::mutex> lock (mutex_);
101+ const_cast <RateLimiter*>(this )->cleanup_old_requests ();
102+ return request_times_.size ();
103+ }
104+
105+ /* *
106+ * @brief Enabling/disabling rate limiting
107+ */
108+ void set_enabled (bool enabled) {
109+ std::lock_guard<std::mutex> lock (mutex_);
110+ enabled_ = enabled;
111+ }
112+
113+ /* *
114+ * @brief Setting a new limit
115+ */
116+ void set_limit (int max_requests_per_minute) {
117+ if (max_requests_per_minute <= 0 ) {
118+ throw ConfigurationError (" Max requests per minute must be positive" );
119+ }
120+ std::lock_guard<std::mutex> lock (mutex_);
121+ max_requests_per_minute_ = max_requests_per_minute;
122+ }
123+
124+ /* *
125+ * @brief Resetting the request counter
126+ */
127+ void reset () {
128+ std::lock_guard<std::mutex> lock (mutex_);
129+ request_times_.clear ();
130+ }
131+ };
132+
133+ } // namespace perplexity
134+ #endif // PERPLEXITY_CPP_RATELIMITER_H
0 commit comments