opik-optimizer 2.1.2__py3-none-any.whl → 2.2.0__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.
Files changed (39) hide show
  1. opik_optimizer/__init__.py +2 -2
  2. opik_optimizer/base_optimizer.py +314 -145
  3. opik_optimizer/evolutionary_optimizer/crossover_ops.py +31 -4
  4. opik_optimizer/evolutionary_optimizer/evaluation_ops.py +23 -3
  5. opik_optimizer/evolutionary_optimizer/evolutionary_optimizer.py +122 -95
  6. opik_optimizer/evolutionary_optimizer/mcp.py +11 -6
  7. opik_optimizer/evolutionary_optimizer/mutation_ops.py +25 -5
  8. opik_optimizer/evolutionary_optimizer/population_ops.py +26 -10
  9. opik_optimizer/evolutionary_optimizer/reporting.py +5 -5
  10. opik_optimizer/few_shot_bayesian_optimizer/few_shot_bayesian_optimizer.py +53 -99
  11. opik_optimizer/few_shot_bayesian_optimizer/reporting.py +4 -4
  12. opik_optimizer/gepa_optimizer/gepa_optimizer.py +183 -172
  13. opik_optimizer/gepa_optimizer/reporting.py +164 -22
  14. opik_optimizer/hierarchical_reflective_optimizer/hierarchical_reflective_optimizer.py +221 -245
  15. opik_optimizer/hierarchical_reflective_optimizer/hierarchical_root_cause_analyzer.py +38 -14
  16. opik_optimizer/hierarchical_reflective_optimizer/prompts.py +7 -1
  17. opik_optimizer/hierarchical_reflective_optimizer/reporting.py +287 -132
  18. opik_optimizer/meta_prompt_optimizer/meta_prompt_optimizer.py +185 -205
  19. opik_optimizer/meta_prompt_optimizer/reporting.py +4 -4
  20. opik_optimizer/mipro_optimizer/__init__.py +2 -2
  21. opik_optimizer/mipro_optimizer/_lm.py +4 -4
  22. opik_optimizer/mipro_optimizer/{_mipro_optimizer_v2.py → mipro_optimizer_v2.py} +1 -7
  23. opik_optimizer/mipro_optimizer/utils.py +1 -0
  24. opik_optimizer/multi_metric_objective.py +33 -0
  25. opik_optimizer/optimizable_agent.py +7 -4
  26. opik_optimizer/optimization_config/chat_prompt.py +7 -10
  27. opik_optimizer/parameter_optimizer/parameter_optimizer.py +188 -40
  28. opik_optimizer/parameter_optimizer/reporting.py +148 -0
  29. opik_optimizer/reporting_utils.py +42 -15
  30. opik_optimizer/task_evaluator.py +26 -9
  31. opik_optimizer/utils/core.py +16 -2
  32. opik_optimizer/utils/prompt_segments.py +1 -2
  33. {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/METADATA +2 -3
  34. {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/RECORD +37 -37
  35. opik_optimizer/evolutionary_optimizer/llm_support.py +0 -136
  36. opik_optimizer/mipro_optimizer/mipro_optimizer.py +0 -680
  37. {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/WHEEL +0 -0
  38. {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/licenses/LICENSE +0 -0
  39. {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/top_level.txt +0 -0
@@ -14,6 +14,24 @@ from .utils import get_optimization_run_url_by_id
14
14
  PANEL_WIDTH = 70
15
15
 
16
16
 
17
+ def safe_percentage_change(current: float, baseline: float) -> tuple[float, bool]:
18
+ """
19
+ Calculate percentage change safely, handling division by zero.
20
+
21
+ Args:
22
+ current: Current value
23
+ baseline: Baseline value to compare against
24
+
25
+ Returns:
26
+ Tuple of (percentage_change, has_percentage) where:
27
+ - percentage_change: The percentage change if calculable, otherwise 0
28
+ - has_percentage: True if percentage was calculated, False if baseline was zero
29
+ """
30
+ if baseline == 0:
31
+ return 0.0, False
32
+ return ((current - baseline) / baseline) * 100, True
33
+
34
+
17
35
  def get_console(*args: Any, **kwargs: Any) -> Console:
18
36
  console = Console(*args, **kwargs)
19
37
  console.is_jupyter = False
@@ -36,35 +54,44 @@ def convert_tqdm_to_rich(description: str | None = None, verbose: int = 1) -> An
36
54
 
37
55
  from opik.evaluation import report
38
56
 
57
+ # Store original functions
58
+ original_display_experiment_results = report.display_experiment_results
59
+ original_display_experiment_link = report.display_experiment_link
60
+
61
+ # Replace with no-ops
39
62
  report.display_experiment_results = lambda *args, **kwargs: None
40
63
  report.display_experiment_link = lambda *args, **kwargs: None
41
64
 
42
65
  try:
43
66
  yield
44
67
  finally:
68
+ # Restore everything
45
69
  opik.evaluation.engine.evaluation_tasks_executor._tqdm = original__tqdm
70
+ report.display_experiment_results = original_display_experiment_results
71
+ report.display_experiment_link = original_display_experiment_link
46
72
 
47
73
 
48
74
  @contextmanager
49
75
  def suppress_opik_logs() -> Any:
50
76
  """Suppress Opik startup logs by temporarily increasing the log level."""
51
- # Optimizer log level
52
- optimizer_logger = logging.getLogger("opik_optimizer")
77
+ # Get all loggers we need to suppress
78
+ opik_client_logger = logging.getLogger("opik.api_objects.opik_client")
79
+ opik_logger = logging.getLogger("opik")
53
80
 
54
- # Get the Opik logger
55
- opik_logger = logging.getLogger("opik.api_objects.opik_client")
81
+ # Store original log levels
82
+ original_client_level = opik_client_logger.level
83
+ original_opik_level = opik_logger.level
56
84
 
57
- # Store original log level
58
- original_level = opik_logger.level
59
-
60
- # Set log level to ERROR to suppress INFO messages
61
- opik_logger.setLevel(optimizer_logger.level)
85
+ # Set log level to WARNING to suppress INFO messages
86
+ opik_client_logger.setLevel(logging.WARNING)
87
+ opik_logger.setLevel(logging.WARNING)
62
88
 
63
89
  try:
64
90
  yield
65
91
  finally:
66
- # Restore original log level
67
- opik_logger.setLevel(original_level)
92
+ # Restore original log levels
93
+ opik_client_logger.setLevel(original_client_level)
94
+ opik_logger.setLevel(original_opik_level)
68
95
 
69
96
 
70
97
  def display_messages(messages: list[dict[str, str]], prefix: str = "") -> None:
@@ -196,18 +223,18 @@ def display_result(
196
223
  content: Text | Panel = []
197
224
 
198
225
  if best_score > initial_score:
199
- if initial_score == 0:
226
+ perc_change, has_percentage = safe_percentage_change(best_score, initial_score)
227
+ if has_percentage:
200
228
  content += [
201
229
  Text(
202
- f"Prompt was optimized and improved from {initial_score:.4f} to {best_score:.4f}",
230
+ f"Prompt was optimized and improved from {initial_score:.4f} to {best_score:.4f} ({perc_change:.2%})",
203
231
  style="bold green",
204
232
  )
205
233
  ]
206
234
  else:
207
- perc_change = (best_score - initial_score) / initial_score
208
235
  content += [
209
236
  Text(
210
- f"Prompt was optimized and improved from {initial_score:.4f} to {best_score:.4f} ({perc_change:.2%})",
237
+ f"Prompt was optimized and improved from {initial_score:.4f} to {best_score:.4f}",
211
238
  style="bold green",
212
239
  )
213
240
  ]
@@ -5,6 +5,7 @@ from collections.abc import Callable
5
5
  import opik
6
6
  from opik.evaluation import evaluator as opik_evaluator
7
7
  from opik.evaluation.metrics import base_metric, score_result
8
+ from . import multi_metric_objective
8
9
 
9
10
  logger = logging.getLogger(__name__)
10
11
 
@@ -14,9 +15,20 @@ def _create_metric_class(metric: Callable) -> base_metric.BaseMetric:
14
15
  def __init__(self) -> None:
15
16
  self.name = metric.__name__
16
17
 
17
- def score(self, llm_output: str, **kwargs: Any) -> score_result.ScoreResult:
18
+ def score(
19
+ self, llm_output: str, **kwargs: Any
20
+ ) -> score_result.ScoreResult | list[score_result.ScoreResult]:
18
21
  try:
19
22
  metric_val = metric(dataset_item=kwargs, llm_output=llm_output)
23
+
24
+ if isinstance(metric, multi_metric_objective.MultiMetricObjective):
25
+ if (
26
+ hasattr(metric_val, "metadata")
27
+ and "raw_score_results" in metric_val.metadata
28
+ ):
29
+ return [metric_val, *metric_val.metadata["raw_score_results"]]
30
+ else:
31
+ return [metric_val]
20
32
  if isinstance(metric_val, score_result.ScoreResult):
21
33
  return score_result.ScoreResult(
22
34
  name=self.name,
@@ -107,15 +119,20 @@ def evaluate(
107
119
  if not result.test_results:
108
120
  return 0.0
109
121
 
110
- # We may allow score aggregation customization.
111
- score_results: list[score_result.ScoreResult] = [
112
- test_result.score_results[0] for test_result in result.test_results
113
- ]
114
- if not score_results:
122
+ # Filter score results to only include the objective metric
123
+ objective_metric_name = metric.__name__
124
+ objective_score_results: list[score_result.ScoreResult] = []
125
+ for test_result in result.test_results:
126
+ for score_result_ in test_result.score_results:
127
+ if score_result_.name == objective_metric_name:
128
+ objective_score_results.append(score_result_)
129
+ break
130
+
131
+ if not objective_score_results:
115
132
  return 0.0
116
133
 
117
- avg_score = sum([score_result_.value for score_result_ in score_results]) / len(
118
- score_results
119
- )
134
+ avg_score = sum(
135
+ [score_result_.value for score_result_ in objective_score_results]
136
+ ) / len(objective_score_results)
120
137
 
121
138
  return avg_score
@@ -327,9 +327,16 @@ def create_litellm_agent_class(
327
327
  class LiteLLMAgent(OptimizableAgent):
328
328
  model = prompt.model
329
329
  model_kwargs = prompt.model_kwargs
330
- project_name = prompt.project_name
331
330
  optimizer = optimizer_ref
332
331
 
332
+ def __init__(
333
+ self, prompt: "ChatPrompt", project_name: str | None = None
334
+ ) -> None:
335
+ # Get project_name from optimizer if available
336
+ if project_name is None and hasattr(self.optimizer, "project_name"):
337
+ project_name = self.optimizer.project_name
338
+ super().__init__(prompt, project_name=project_name)
339
+
333
340
  def invoke(
334
341
  self, messages: list[dict[str, str]], seed: int | None = None
335
342
  ) -> str:
@@ -342,9 +349,16 @@ def create_litellm_agent_class(
342
349
  class LiteLLMAgent(OptimizableAgent): # type: ignore[no-redef]
343
350
  model = prompt.model
344
351
  model_kwargs = prompt.model_kwargs
345
- project_name = prompt.project_name
346
352
  optimizer = optimizer_ref
347
353
 
354
+ def __init__(
355
+ self, prompt: "ChatPrompt", project_name: str | None = None
356
+ ) -> None:
357
+ # Get project_name from optimizer if available
358
+ if project_name is None and hasattr(self.optimizer, "project_name"):
359
+ project_name = self.optimizer.project_name
360
+ super().__init__(prompt, project_name=project_name)
361
+
348
362
  return LiteLLMAgent
349
363
 
350
364
 
@@ -175,8 +175,7 @@ def apply_segment_updates(
175
175
  function_map=prompt.function_map,
176
176
  model=prompt.model,
177
177
  invoke=prompt.invoke,
178
- project_name=prompt.project_name,
179
- **prompt.model_kwargs,
178
+ model_parameters=prompt.model_kwargs,
180
179
  )
181
180
 
182
181
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opik_optimizer
3
- Version: 2.1.2
3
+ Version: 2.2.0
4
4
  Summary: Agent optimization with Opik
5
5
  Home-page: https://github.com/comet-ml/opik
6
6
  Author: Comet ML
@@ -8,13 +8,12 @@ Author-email: Comet ML <support@comet.com>
8
8
  License: Apache 2.0
9
9
  Project-URL: Homepage, https://github.com/comet-ml/opik/blob/main/sdks/opik_optimizer/README.md
10
10
  Project-URL: Repository, https://github.com/comet-ml/opik
11
- Requires-Python: >=3.10,<3.13
11
+ Requires-Python: >=3.10,<3.14
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: datasets
15
15
  Requires-Dist: deap>=1.4.3
16
16
  Requires-Dist: diskcache
17
- Requires-Dist: dspy<3
18
17
  Requires-Dist: gepa>=0.0.7
19
18
  Requires-Dist: ujson
20
19
  Requires-Dist: hf_xet
@@ -1,13 +1,14 @@
1
- opik_optimizer/__init__.py,sha256=VwryQ5bSOmJSl4CiacCIv_UF_In8Zho54fQ3FUR8pyk,1573
1
+ opik_optimizer/__init__.py,sha256=HsEIWyxeUJhzCvuML5SjBHFWtm-b5LSHyE9GRYytyeI,1592
2
2
  opik_optimizer/_throttle.py,sha256=1JXIhYlo0IaqCgwmNB0Hnh9CYhYPkwRFdVGIcE7pVNg,1362
3
- opik_optimizer/base_optimizer.py,sha256=XryBkUTs4FQmHcBtVm63EJIKWrTvwqduUZ6ArHzYQko,21520
3
+ opik_optimizer/base_optimizer.py,sha256=VpH6JSalcoewGkIN0h77_crCAkx5ffQtjNhaD0Xtazg,28350
4
4
  opik_optimizer/cache_config.py,sha256=Xd3NdUsL7bLQWoNe3pESqH4nHucU1iNTSGp-RqbwDog,599
5
5
  opik_optimizer/logging_config.py,sha256=TmxX0C1P20amxoXuiNQvlENOjdSNfWwvL8jFy206VWM,3837
6
- opik_optimizer/optimizable_agent.py,sha256=R0_BdwdHyZGWTw3oSvTg8FULDOYM8XaTiPNR3qV8DkQ,6344
6
+ opik_optimizer/multi_metric_objective.py,sha256=y4jqirnhkfhB7SWonI4ldYg5fWG4JGfAxqu7ylRD1J4,1178
7
+ opik_optimizer/optimizable_agent.py,sha256=gB1ALuVPyEmXOTVYeK2i-inBAO-6JMZSjOrmj37okgQ,6514
7
8
  opik_optimizer/optimization_result.py,sha256=sG-Yr-hOaH9zx_I5S6_W3v6j8nPUhwYdS333jVM4Gus,17218
8
9
  opik_optimizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- opik_optimizer/reporting_utils.py,sha256=dcECFmzZ_J-DKoukMDEE_fm7X8sdQyl_ijTddvQtepE,8287
10
- opik_optimizer/task_evaluator.py,sha256=1hILYwJLtn7XpPX96JjubnlMasmudVTHMVK3pmd22bE,4312
10
+ opik_optimizer/reporting_utils.py,sha256=Gx69W16FfIpavH_o0WvnGJPIpqHAjJm4GNHKcJhtoeU,9443
11
+ opik_optimizer/task_evaluator.py,sha256=7N254DU0UkWJ5saQ5AmYEsHHSrychAJtedmmjNsCOnI,5081
11
12
  opik_optimizer/data/context7_eval.jsonl,sha256=vPR3XRfI0UbZ1hgUGaOdpraFT99RDLU1YWuPFLLQz40,1757
12
13
  opik_optimizer/data/hotpot-500.json,sha256=YXxCtuvYvxSu5u0y4559a6b1qwgAYsWzT_SUKv_21ew,76862
13
14
  opik_optimizer/datasets/__init__.py,sha256=V4LVDOaRjwzaYvhdQ3V6CAwFaeKnxyTV1lp_ES9Z31E,691
@@ -27,29 +28,28 @@ opik_optimizer/demo/__init__.py,sha256=KSpFYhzN7fTmLEsIaciRHwxcJDeAiX5NDmYLdPsfp
27
28
  opik_optimizer/demo/cache.py,sha256=CwjdmVjokVxmPXvgfOutZK8e0sV-PIUz3ou6ODXZBts,3738
28
29
  opik_optimizer/demo/datasets.py,sha256=idod4NYHw1IbxhA8c0XVFD_pGpMZagNGNZuEYDTbbMM,2357
29
30
  opik_optimizer/evolutionary_optimizer/__init__.py,sha256=bDa6FZR9Y_a5z337I4EtvaB69jB542P4dbruhYPHCEU,95
30
- opik_optimizer/evolutionary_optimizer/crossover_ops.py,sha256=7kMvAWOiEA0R5PQMRdnLqbS1uCmIDVzLppNSsPsIO7o,7740
31
- opik_optimizer/evolutionary_optimizer/evaluation_ops.py,sha256=euVbFgrG37zj2GfcjAPMvPtz-52QljFR76OfChYmsKY,4795
32
- opik_optimizer/evolutionary_optimizer/evolutionary_optimizer.py,sha256=2gEny7Q1PMcwzaDOJ2GrXMKZAZThuhV9tGg92JhQ9lI,46260
31
+ opik_optimizer/evolutionary_optimizer/crossover_ops.py,sha256=M-TsQv8EHKt_RiKoEPYTtiP_HL588AyyTuoXNsQpaVA,8883
32
+ opik_optimizer/evolutionary_optimizer/evaluation_ops.py,sha256=XlPVJG_3R0GeYKOHTCdhBE4TvOBMRvyHlXwG2xvroD4,5511
33
+ opik_optimizer/evolutionary_optimizer/evolutionary_optimizer.py,sha256=TVNHtQMZdGL4if_PK4f3230Rg0NFR87kOf0sibOeqbY,48162
33
34
  opik_optimizer/evolutionary_optimizer/helpers.py,sha256=yWYW5JyVbr2smDByc9yaHCYbUS6cw35RBI7lM3pT69A,607
34
- opik_optimizer/evolutionary_optimizer/llm_support.py,sha256=JeghAOwT_nYyOjdUi-xEiDvG-dW0C87UBzGz_xMdHl8,5438
35
- opik_optimizer/evolutionary_optimizer/mcp.py,sha256=QhRPsxbtZJKzhJouJOuNbrNGGjgY4JAMmo-UYiNa0WQ,7794
36
- opik_optimizer/evolutionary_optimizer/mutation_ops.py,sha256=Jj4q297z054LSI0udZmeH_jXQMAxml2_qBoxSIfXNBs,12643
37
- opik_optimizer/evolutionary_optimizer/population_ops.py,sha256=-33oN2aPTF_upJLYDVUTNm1c5bMzWy2krQ3alFCrJlM,10101
35
+ opik_optimizer/evolutionary_optimizer/mcp.py,sha256=OHZ__q4vVInli8qowNh-1uD76dJ871wY6NkU4XhfxAA,8067
36
+ opik_optimizer/evolutionary_optimizer/mutation_ops.py,sha256=ybhammJsY_SWIBsdZlom9H4Uy7-efbukma1j1-75oY4,13196
37
+ opik_optimizer/evolutionary_optimizer/population_ops.py,sha256=ybNFUpfZgOeuWF5IGdtVRLFkiR4H9WsDrsGMPVj3Rk8,10992
38
38
  opik_optimizer/evolutionary_optimizer/prompts.py,sha256=am1nL8oqw3TOVVBDaDn5EoWkjxufEiMQ7E_54Uw8m3s,16204
39
- opik_optimizer/evolutionary_optimizer/reporting.py,sha256=xzvHK2m0Kdf7hhrrdRxXbp-qt8d8j69nnSBIOzhLSms,12090
39
+ opik_optimizer/evolutionary_optimizer/reporting.py,sha256=AxhdiwUSEchKepu8eW6J5DiUYHXKvMDPqVQkrnrt1No,12048
40
40
  opik_optimizer/evolutionary_optimizer/style_ops.py,sha256=XmGFS5s2Qr2DJMZVVsI_C6LqJ5zoyxpeWAtGmdg3TnA,3082
41
41
  opik_optimizer/few_shot_bayesian_optimizer/__init__.py,sha256=VuH7FOROyGcjMPryejtZC-5Y0QHlVTFLTGUDgNqRAFw,113
42
- opik_optimizer/few_shot_bayesian_optimizer/few_shot_bayesian_optimizer.py,sha256=fiCp7_aPpoEf8afBhFo98z1pl1Z50Rs0b-lS6Rij2Ns,27897
43
- opik_optimizer/few_shot_bayesian_optimizer/reporting.py,sha256=OMpLG4xsM6K7oQcP_nbnky47NklVsowNDlK6WliZM10,6311
42
+ opik_optimizer/few_shot_bayesian_optimizer/few_shot_bayesian_optimizer.py,sha256=3lFUcAiRCM6GUV2Aa9rbvF36nFQ1nFRK-ZC2_dkdGIU,26639
43
+ opik_optimizer/few_shot_bayesian_optimizer/reporting.py,sha256=xk7gKaoTrlp1WDpW3mB5Irzty5Z5l9SJygO3PaamOvU,6283
44
44
  opik_optimizer/gepa_optimizer/__init__.py,sha256=XcPah5t4mop7UCFo69E9l45Mem49-itqkQT7_J1aWOA,71
45
45
  opik_optimizer/gepa_optimizer/adapter.py,sha256=KzPa4koq7aJhALMAOKPxAO4yWuEy_YbW7tGnqny3Hfo,5139
46
- opik_optimizer/gepa_optimizer/gepa_optimizer.py,sha256=HBjikhce3K4VIaiIXs7eSagmRyFPdY8h4seoW9F3nQE,26481
47
- opik_optimizer/gepa_optimizer/reporting.py,sha256=F0cxYSjRuFAszgi3rgqwH1A-KH26kZOLtENP7x1xrQs,5154
46
+ opik_optimizer/gepa_optimizer/gepa_optimizer.py,sha256=rWD92KauJbnkXbsftzhTDkBjmgzlwGZcm6t7F-ceh_Q,27055
47
+ opik_optimizer/gepa_optimizer/reporting.py,sha256=ZyD4bwiW6BxmPb-966u3iDAfBsiO56kO2VBxHtNmL-Q,11050
48
48
  opik_optimizer/hierarchical_reflective_optimizer/__init__.py,sha256=9qM3kvfAaFy-Y6Tg19MXHJxpnF5DJQQwzr6oNsxaRBM,133
49
- opik_optimizer/hierarchical_reflective_optimizer/hierarchical_reflective_optimizer.py,sha256=Fs83ztOuPS8mkFvJAVmYok15DaXTk4Jqpoa9ImRl2t4,27256
50
- opik_optimizer/hierarchical_reflective_optimizer/hierarchical_root_cause_analyzer.py,sha256=GSIXUBxoS9LFnCXopS6B6wLSpmCYXA8Cv6ytELgEBoc,12709
51
- opik_optimizer/hierarchical_reflective_optimizer/prompts.py,sha256=XcOEI9eeEbTgKFsFiRWxvHdaByQkiN02bH2gTl3HX-Y,3853
52
- opik_optimizer/hierarchical_reflective_optimizer/reporting.py,sha256=LpHv_WBZCg2a0RhZaGwUmCch_-Dfk_rpuMxTckJMWTU,23234
49
+ opik_optimizer/hierarchical_reflective_optimizer/hierarchical_reflective_optimizer.py,sha256=fhB68XrGNgaHfPwV1JDow-MiAT-jhKDT_Kf-mLLzk0o,27775
50
+ opik_optimizer/hierarchical_reflective_optimizer/hierarchical_root_cause_analyzer.py,sha256=0D5wgx04jZvTJ0Yjqm0jtQvkjrGBB73qgcsSwLBpnv0,13814
51
+ opik_optimizer/hierarchical_reflective_optimizer/prompts.py,sha256=8TsLsJo7KPUNFkxSVGXTpVnr9ax4oTosImky0nlEI40,4376
52
+ opik_optimizer/hierarchical_reflective_optimizer/reporting.py,sha256=frbqEOGsiK-TRPJTtcLhHjPJtQaj4T60cq97QEgcDJ0,29053
53
53
  opik_optimizer/hierarchical_reflective_optimizer/types.py,sha256=bS-JAheX2FpJ4XAxoZi5PfjloG8L-B1LGQA1iLXZhW4,1031
54
54
  opik_optimizer/mcp_utils/__init__.py,sha256=BsWQT8nAa6JV6zcOD__OvPMepUS2IpJD4J2rnAXhpuU,710
55
55
  opik_optimizer/mcp_utils/mcp.py,sha256=UylgpTJsybszS433_kuTAgKH-PPde-VHjHVelMardFs,18466
@@ -57,30 +57,30 @@ opik_optimizer/mcp_utils/mcp_second_pass.py,sha256=p2Knlxg1CKIZVMBbdskdRDqw1BRrn
57
57
  opik_optimizer/mcp_utils/mcp_simulator.py,sha256=bLL7iVAGMRc8Mb2j_XpSjlkr6TvQLI90hkS4ifnwLqs,3427
58
58
  opik_optimizer/mcp_utils/mcp_workflow.py,sha256=R3lqufN35p-OJlGxIxAIOMIAvRTBLGXINzfpoVIq2nw,17885
59
59
  opik_optimizer/meta_prompt_optimizer/__init__.py,sha256=syiN2_fMm5iZDQezZCHYe-ZiGOIPlBkLt49Sa1kuR70,97
60
- opik_optimizer/meta_prompt_optimizer/meta_prompt_optimizer.py,sha256=F3bxrttYAIkzvjduLvVDpFd9xoZkIqqTgqpSjgYDMIw,51435
61
- opik_optimizer/meta_prompt_optimizer/reporting.py,sha256=Py30NDYFNPzb8XrCXibQRtBC3vjjViQG74uG-O6lhXE,7783
62
- opik_optimizer/mipro_optimizer/__init__.py,sha256=7sMq9OSWyjITqK7sVtkO9fhG1w6KRE8bN7V52CKaGvo,94
63
- opik_optimizer/mipro_optimizer/_lm.py,sha256=jgp_bamkG9og8nxVKs6J2qPi6BmTvJD3qVeiorRhszU,17004
64
- opik_optimizer/mipro_optimizer/_mipro_optimizer_v2.py,sha256=bQBJG3wFeNsOF_Yhklx53d4M8kqzBXQ22cOLANXjGJw,39315
65
- opik_optimizer/mipro_optimizer/mipro_optimizer.py,sha256=ei3gON1r0PiCNhJVJA3zhGS4C2QPZOZMivhc2CPJ_R8,27660
66
- opik_optimizer/mipro_optimizer/utils.py,sha256=pP3mvai_GQmwUhcchVOiW1xPI3LatpXclE_5XvBYwTw,2493
60
+ opik_optimizer/meta_prompt_optimizer/meta_prompt_optimizer.py,sha256=Q7wZgqynai6eezTVRAJ-FWPrYP6HlFpwQdiObbnwu7o,51562
61
+ opik_optimizer/meta_prompt_optimizer/reporting.py,sha256=jW3WwMz7ZP7jjpvJLVyVXvlKbyTgMJVTz6mFYJhsmPQ,7755
62
+ opik_optimizer/mipro_optimizer/__init__.py,sha256=Hr5HJT7LBBtbCTqBM0CSrIyYxq-eMfI2vujzEkCejV4,63
63
+ opik_optimizer/mipro_optimizer/_lm.py,sha256=LjFan3--gCaeYxWEKdHswCcea_9jC1nLHK5uXULv-c4,17008
64
+ opik_optimizer/mipro_optimizer/mipro_optimizer_v2.py,sha256=Dt2ETxyQXdkVKE74Zu0D2K90tTnTTIPjtp6uV60HCrc,39212
65
+ opik_optimizer/mipro_optimizer/utils.py,sha256=hzlXYOFjCx1Vc485oby5uqI6Xoqc39x2aEpPT2acsf0,2539
67
66
  opik_optimizer/optimization_config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
- opik_optimizer/optimization_config/chat_prompt.py,sha256=d3jwM1UvUeRQOSsYHa5GD842VO3JWjVDmB3ROUGp57c,7089
67
+ opik_optimizer/optimization_config/chat_prompt.py,sha256=LJGR2o6DMYdTEU2cbhX9dyH-hd9RQ8ngFfJHttBJJo4,6998
69
68
  opik_optimizer/optimization_config/configs.py,sha256=EGacRNnl6TeWuf8RNsxpP6Nh5JhogjK-JxKllK8dQr0,413
70
69
  opik_optimizer/optimization_config/mappers.py,sha256=4uBoPaIvCo4bqt_w-4rJyVe2LMAP_W7p6xxnDmGT-Sk,1724
71
70
  opik_optimizer/parameter_optimizer/__init__.py,sha256=Eg-LEFBJqnOFw7i2B_YH27CoIGDPb5y_q1ar-ZpjtYo,308
72
- opik_optimizer/parameter_optimizer/parameter_optimizer.py,sha256=eDd9tFQinz2lKsEJtikCBVzSWMK4saI9bhUY2NtDEg0,14955
71
+ opik_optimizer/parameter_optimizer/parameter_optimizer.py,sha256=B18H2ZtcaPmrjh7f7_vHA8UewXml_9bw5PDA3nBzgyE,21206
73
72
  opik_optimizer/parameter_optimizer/parameter_search_space.py,sha256=rgTNK8HPbdDiVm4GVX2QESTmQPhPFj4UkxqZfAy9JAA,4659
74
73
  opik_optimizer/parameter_optimizer/parameter_spec.py,sha256=HzYT_dHBTfZtx403mY-Epv_IEqn4kYuYBZ6QUdkFRiY,8064
74
+ opik_optimizer/parameter_optimizer/reporting.py,sha256=-kEe9sQFdkUhxayEamXLR8ukyTLJrGsTs8pbJWmimQ4,4665
75
75
  opik_optimizer/parameter_optimizer/search_space_types.py,sha256=UajTA2QKikEWazokDNO7j141gc2WxxYYiDRnFFjXi6M,512
76
76
  opik_optimizer/parameter_optimizer/sensitivity_analysis.py,sha256=8KEMVMHsmcoiK21Cq1-We6_Pw_6LX9qBX9Az4-tmj_w,2146
77
77
  opik_optimizer/utils/__init__.py,sha256=Ee0SnTPOcwRwp93M6Lh-X913lfSIwnvCiYYh5cpdRQE,486
78
78
  opik_optimizer/utils/colbert.py,sha256=qSrzKUUGw7P92mLy4Ofug5pBGeTsHBLMJXlXSJSfKuo,8147
79
- opik_optimizer/utils/core.py,sha256=5GT1vp6fW8ICO42LHMX14BjR-xEb6afAKjM7b1Evx5M,15298
79
+ opik_optimizer/utils/core.py,sha256=sL9I9kG1Gdjj0b3rBgPpXp7NUaUisD3_ITSkE7w5QhU,16014
80
80
  opik_optimizer/utils/dataset_utils.py,sha256=dqRUGOekjeNWL0J15R8xFwLyKJDJynJXzVyQmt8rhHA,1464
81
- opik_optimizer/utils/prompt_segments.py,sha256=1zUITSccJ82Njac1rmANzim4WWM6rVac61mfluS7lFE,5931
82
- opik_optimizer-2.1.2.dist-info/licenses/LICENSE,sha256=V-0VHJOBdcA_teT8VymvsBUQ1-CZU6yJRmMEjec_8tA,11372
83
- opik_optimizer-2.1.2.dist-info/METADATA,sha256=V2WXOFaF2tc7-xAHcdndLn8pcUp1JHxHEVONa0K8j78,12829
84
- opik_optimizer-2.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
85
- opik_optimizer-2.1.2.dist-info/top_level.txt,sha256=ondOlpq6_yFckqpxoAHSfzZS2N-JfgmA-QQhOJfz7m0,15
86
- opik_optimizer-2.1.2.dist-info/RECORD,,
81
+ opik_optimizer/utils/prompt_segments.py,sha256=eiLYT1iiPxtB7ArriN13-LgI5tID-v7MrjniTAxK2Lo,5904
82
+ opik_optimizer-2.2.0.dist-info/licenses/LICENSE,sha256=V-0VHJOBdcA_teT8VymvsBUQ1-CZU6yJRmMEjec_8tA,11372
83
+ opik_optimizer-2.2.0.dist-info/METADATA,sha256=O8dgJi_lpEyd2kDMCj-AZMK4QthqAaZiR5Afo5WwCZ8,12807
84
+ opik_optimizer-2.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
85
+ opik_optimizer-2.2.0.dist-info/top_level.txt,sha256=ondOlpq6_yFckqpxoAHSfzZS2N-JfgmA-QQhOJfz7m0,15
86
+ opik_optimizer-2.2.0.dist-info/RECORD,,
@@ -1,136 +0,0 @@
1
- from typing import Any, TYPE_CHECKING
2
-
3
- import logging
4
- import os
5
- import time
6
- import random
7
-
8
- import litellm
9
- from litellm import exceptions as litellm_exceptions
10
- from litellm.caching import Cache
11
- from litellm.types.caching import LiteLLMCacheType
12
- from opik.evaluation.models.litellm import opik_monitor as opik_litellm_monitor
13
-
14
- from .. import _throttle
15
-
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
-
20
- # Configure LiteLLM cache with safe fallback
21
- try:
22
- # Prefer a disk cache in a user-writable location
23
- cache_dir = os.path.join(os.path.expanduser("~"), ".cache", "litellm")
24
- os.makedirs(cache_dir, exist_ok=True)
25
- litellm.cache = Cache(type=LiteLLMCacheType.DISK, cache_dir=cache_dir)
26
- except (PermissionError, OSError, FileNotFoundError):
27
- # Fall back to in-memory cache to avoid disk timeouts/locks
28
- litellm.cache = Cache(type=LiteLLMCacheType.MEMORY)
29
-
30
- _rate_limiter = _throttle.get_rate_limiter_for_current_opik_installation()
31
-
32
-
33
- class LlmSupport:
34
- if TYPE_CHECKING:
35
- model: str
36
- llm_call_counter: int
37
- project_name: str | None
38
- disable_litellm_monitoring: bool
39
- temperature: float
40
- max_tokens: int
41
- top_p: float
42
- frequency_penalty: float
43
- presence_penalty: float
44
-
45
- def increment_llm_counter(self) -> None: ...
46
-
47
- @_throttle.rate_limited(_rate_limiter)
48
- def _call_model(
49
- self,
50
- messages: list[dict[str, str]],
51
- is_reasoning: bool = False,
52
- optimization_id: str | None = None,
53
- ) -> str:
54
- """Call the model with the given prompt and return the response string."""
55
- # Build base call params
56
- llm_config_params: dict[str, Any] = {
57
- "temperature": getattr(self, "temperature", 0.3),
58
- "max_tokens": getattr(self, "max_tokens", 1000),
59
- "top_p": getattr(self, "top_p", 1.0),
60
- "frequency_penalty": getattr(self, "frequency_penalty", 0.0),
61
- "presence_penalty": getattr(self, "presence_penalty", 0.0),
62
- }
63
-
64
- # Add Opik metadata unless disabled
65
- try:
66
- disable_monitoring_env = os.getenv(
67
- "OPIK_OPTIMIZER_DISABLE_LITELLM_MONITORING", "0"
68
- )
69
- disable_monitoring = getattr(
70
- self, "disable_litellm_monitoring", False
71
- ) or disable_monitoring_env.lower() in ("1", "true", "yes")
72
-
73
- if not disable_monitoring:
74
- metadata_for_opik: dict[str, Any] = {}
75
- pn = getattr(self, "project_name", None)
76
- if pn:
77
- metadata_for_opik["project_name"] = pn
78
- metadata_for_opik["opik"] = {"project_name": pn}
79
- if optimization_id and "opik" in metadata_for_opik:
80
- metadata_for_opik["opik"]["optimization_id"] = optimization_id
81
- metadata_for_opik["optimizer_name"] = self.__class__.__name__
82
- metadata_for_opik["opik_call_type"] = (
83
- "reasoning" if is_reasoning else "evaluation_llm_task_direct"
84
- )
85
- if metadata_for_opik:
86
- llm_config_params["metadata"] = metadata_for_opik
87
-
88
- # Try to add Opik monitoring callbacks; fall back silently on failure
89
- llm_config_params = (
90
- opik_litellm_monitor.try_add_opik_monitoring_to_params( # type: ignore
91
- llm_config_params.copy()
92
- )
93
- )
94
- except Exception as e:
95
- logger.debug(f"Skipping Opik-LiteLLM monitoring setup: {e}")
96
-
97
- # Retry policy for transient errors
98
- max_retries = int(os.getenv("OPIK_OPTIMIZER_LITELLM_MAX_RETRIES", "3"))
99
- base_sleep = float(os.getenv("OPIK_OPTIMIZER_LITELLM_BACKOFF", "0.5"))
100
-
101
- for attempt in range(max_retries + 1):
102
- try:
103
- logger.debug(
104
- f"Calling model '{self.model}' with messages: {messages}, params: {llm_config_params} (attempt {attempt + 1})"
105
- )
106
- response = litellm.completion(
107
- model=self.model, messages=messages, **llm_config_params
108
- )
109
- self.increment_llm_counter()
110
- return response.choices[0].message.content
111
- except (
112
- litellm_exceptions.RateLimitError,
113
- litellm_exceptions.APIConnectionError,
114
- litellm_exceptions.InternalServerError,
115
- ) as e:
116
- if attempt < max_retries:
117
- sleep_s = min(10.0, base_sleep * (2**attempt)) + random.uniform(
118
- 0, 0.25
119
- )
120
- logger.warning(
121
- f"LiteLLM transient error ({type(e).__name__}): {e}. Retrying in {sleep_s:.2f}s..."
122
- )
123
- time.sleep(sleep_s)
124
- continue
125
- logger.error(f"LiteLLM error (final attempt): {e}")
126
- raise
127
- except litellm_exceptions.ContextWindowExceededError as e:
128
- logger.error(f"LiteLLM Context Window Exceeded Error: {e}")
129
- raise
130
- except Exception as e:
131
- logger.error(
132
- f"Error calling model '{self.model}': {type(e).__name__} - {e}"
133
- )
134
- raise
135
- # Should never reach here
136
- raise RuntimeError("LLM call did not return a response and did not raise")