opik-optimizer 0.7.2__py3-none-any.whl → 0.7.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,12 +27,14 @@ from opik.evaluation.models.litellm import warning_filters
27
27
  warning_filters.add_warning_filters()
28
28
 
29
29
  from .optimization_result import OptimizationResult
30
+ from opik_optimizer.evolutionary_optimizer.evolutionary_optimizer import EvolutionaryOptimizer
30
31
 
31
32
  __all__ = [
32
33
  "BaseOptimizer",
33
34
  "FewShotBayesianOptimizer",
34
35
  "MetaPromptOptimizer",
35
36
  "MiproOptimizer",
37
+ "EvolutionaryOptimizer",
36
38
  "MetricConfig",
37
39
  "OptimizationConfig",
38
40
  "TaskConfig",
@@ -1,43 +1,43 @@
1
- import threading
1
+ import functools
2
+ import pyrate_limiter
2
3
  import time
3
- import queue
4
- from functools import wraps
4
+ import opik.config
5
+
6
+ from typing import Callable, Any
7
+
5
8
 
6
9
  class RateLimiter:
7
10
  """
8
- Rate limiter that enforces a maximum number of calls across all threads.
11
+ Rate limiter that enforces a maximum number of calls across all threads using pyrate_limiter.
9
12
  """
10
- def __init__(self, max_calls_per_second):
13
+ def __init__(self, max_calls_per_second: int):
11
14
  self.max_calls_per_second = max_calls_per_second
12
- self.interval = 1.0 / max_calls_per_second # Time between allowed calls
13
- self.last_call_time = 0
14
- self.lock = threading.Lock()
15
+ rate = pyrate_limiter.Rate(max_calls_per_second, pyrate_limiter.Duration.SECOND)
16
+
17
+ self.limiter = pyrate_limiter.Limiter(rate, raise_when_fail=False)
18
+ self.bucket_key = "global_rate_limit"
15
19
 
16
- def acquire(self):
17
- """
18
- Wait until a call is allowed according to the global rate limit.
19
- Returns immediately if the call is allowed, otherwise blocks until it's time.
20
- """
21
- with self.lock:
22
- current_time = time.time()
23
- time_since_last = current_time - self.last_call_time
24
-
25
- # If we haven't waited long enough since the last call
26
- if time_since_last < self.interval:
27
- # Calculate how much longer we need to wait
28
- sleep_time = self.interval - time_since_last
29
- time.sleep(sleep_time)
30
-
31
- # Update the last call time (after potential sleep)
32
- self.last_call_time = time.time()
33
-
34
- def rate_limited(limiter):
20
+ def acquire(self) -> None:
21
+ while not self.limiter.try_acquire(self.bucket_key):
22
+ time.sleep(0.01)
23
+
24
+ def rate_limited(limiter: RateLimiter) -> Callable[[Callable], Callable]:
35
25
  """Decorator to rate limit a function using the provided limiter"""
36
- def decorator(func):
37
- @wraps(func)
38
- def wrapper(*args, **kwargs):
26
+
27
+ def decorator(func: Callable) -> Callable:
28
+ @functools.wraps(func)
29
+ def wrapper(*args, **kwargs) -> Any:
39
30
  limiter.acquire()
40
31
  return func(*args, **kwargs)
41
32
  return wrapper
42
33
  return decorator
43
34
 
35
+
36
+ def get_rate_limiter_for_current_opik_installation() -> RateLimiter:
37
+ opik_config = opik.config.OpikConfig()
38
+ max_calls_per_second = (
39
+ 10
40
+ if opik_config.is_cloud_installation
41
+ else 50
42
+ )
43
+ return RateLimiter(max_calls_per_second=max_calls_per_second)
@@ -12,7 +12,7 @@ from .cache_config import initialize_cache
12
12
  from opik.evaluation.models.litellm import opik_monitor as opik_litellm_monitor
13
13
  from .optimization_config.configs import TaskConfig, MetricConfig
14
14
 
15
- limiter = RateLimiter(max_calls_per_second=15)
15
+ limiter = RateLimiter(max_calls_per_second=8)
16
16
 
17
17
  # Don't use unsupported params:
18
18
  litellm.drop_params = True
@@ -141,85 +141,6 @@ class BaseOptimizer:
141
141
  """
142
142
  self._history.append(round_data)
143
143
 
144
- @rate_limited(limiter)
145
- def _call_model(
146
- self,
147
- prompt: str,
148
- system_prompt: Optional[str] = None,
149
- is_reasoning: bool = False,
150
- ) -> str:
151
- """Call the model to get suggestions based on the meta-prompt."""
152
- model = self.reasoning_model if is_reasoning else self.model
153
- messages = []
154
-
155
- if system_prompt:
156
- messages.append({"role": "system", "content": system_prompt})
157
- logger.debug(f"Using custom system prompt: {system_prompt[:100]}...")
158
- else:
159
- messages.append(
160
- {"role": "system", "content": "You are a helpful assistant."}
161
- )
162
-
163
- messages.append({"role": "user", "content": prompt})
164
- logger.debug(f"Calling model {model} with prompt: {prompt[:100]}...")
165
-
166
- api_params = self.model_kwargs.copy()
167
- api_params.update(
168
- {
169
- "model": model,
170
- "messages": messages,
171
- # Ensure required params like 'temperature', 'max_tokens' are present
172
- # Defaults added here for safety, though usually set in __init__ kwargs
173
- "temperature": api_params.get("temperature", 0.3),
174
- "max_tokens": api_params.get("max_tokens", 1000),
175
- }
176
- )
177
-
178
- # Attempt to add Opik monitoring if available
179
- try:
180
- # Assuming opik_litellm_monitor is imported and configured elsewhere
181
- api_params = opik_litellm_monitor.try_add_opik_monitoring_to_params(
182
- api_params
183
- )
184
- logger.debug("Opik monitoring hooks added to LiteLLM params.")
185
- except Exception as e:
186
- logger.warning(f"Could not add Opik monitoring to LiteLLM params: {e}")
187
-
188
- logger.debug(
189
- f"Final API params (excluding messages): { {k:v for k,v in api_params.items() if k != 'messages'} }"
190
- )
191
-
192
- # Increment Counter
193
- self.llm_call_counter += 1
194
- logger.debug(f"LLM Call Count: {self.llm_call_counter}")
195
-
196
- try:
197
- response = litellm.completion(**api_params)
198
- model_output = response.choices[0].message.content.strip()
199
- logger.debug(f"Model response from {model_to_use}: {model_output[:100]}...")
200
- return model_output
201
- except litellm.exceptions.RateLimitError as e:
202
- logger.error(f"LiteLLM Rate Limit Error for model {model_to_use}: {e}")
203
- # Consider adding retry logic here with tenacity
204
- raise
205
- except litellm.exceptions.APIConnectionError as e:
206
- logger.error(f"LiteLLM API Connection Error for model {model_to_use}: {e}")
207
- # Consider adding retry logic here
208
- raise
209
- except litellm.exceptions.ContextWindowExceededError as e:
210
- logger.error(
211
- f"LiteLLM Context Window Exceeded Error for model {model_to_use}. Prompt length: {len(prompt)}. Details: {e}"
212
- )
213
- raise
214
- except litellm.exceptions.APIError as e: # Catch broader API errors
215
- logger.error(f"LiteLLM API Error for model {model_to_use}: {e}")
216
- raise
217
- except Exception as e:
218
- # Catch any other unexpected errors
219
- logger.error(
220
- f"Unexpected error during model call to {model_to_use}: {type(e).__name__} - {e}"
221
- )
222
- raise
223
144
 
224
145
  def update_optimization(self, optimization, status: str) -> None:
225
146
  """