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.
- opik_optimizer/__init__.py +2 -2
- opik_optimizer/base_optimizer.py +314 -145
- opik_optimizer/evolutionary_optimizer/crossover_ops.py +31 -4
- opik_optimizer/evolutionary_optimizer/evaluation_ops.py +23 -3
- opik_optimizer/evolutionary_optimizer/evolutionary_optimizer.py +122 -95
- opik_optimizer/evolutionary_optimizer/mcp.py +11 -6
- opik_optimizer/evolutionary_optimizer/mutation_ops.py +25 -5
- opik_optimizer/evolutionary_optimizer/population_ops.py +26 -10
- opik_optimizer/evolutionary_optimizer/reporting.py +5 -5
- opik_optimizer/few_shot_bayesian_optimizer/few_shot_bayesian_optimizer.py +53 -99
- opik_optimizer/few_shot_bayesian_optimizer/reporting.py +4 -4
- opik_optimizer/gepa_optimizer/gepa_optimizer.py +183 -172
- opik_optimizer/gepa_optimizer/reporting.py +164 -22
- opik_optimizer/hierarchical_reflective_optimizer/hierarchical_reflective_optimizer.py +221 -245
- opik_optimizer/hierarchical_reflective_optimizer/hierarchical_root_cause_analyzer.py +38 -14
- opik_optimizer/hierarchical_reflective_optimizer/prompts.py +7 -1
- opik_optimizer/hierarchical_reflective_optimizer/reporting.py +287 -132
- opik_optimizer/meta_prompt_optimizer/meta_prompt_optimizer.py +185 -205
- opik_optimizer/meta_prompt_optimizer/reporting.py +4 -4
- opik_optimizer/mipro_optimizer/__init__.py +2 -2
- opik_optimizer/mipro_optimizer/_lm.py +4 -4
- opik_optimizer/mipro_optimizer/{_mipro_optimizer_v2.py → mipro_optimizer_v2.py} +1 -7
- opik_optimizer/mipro_optimizer/utils.py +1 -0
- opik_optimizer/multi_metric_objective.py +33 -0
- opik_optimizer/optimizable_agent.py +7 -4
- opik_optimizer/optimization_config/chat_prompt.py +7 -10
- opik_optimizer/parameter_optimizer/parameter_optimizer.py +188 -40
- opik_optimizer/parameter_optimizer/reporting.py +148 -0
- opik_optimizer/reporting_utils.py +42 -15
- opik_optimizer/task_evaluator.py +26 -9
- opik_optimizer/utils/core.py +16 -2
- opik_optimizer/utils/prompt_segments.py +1 -2
- {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/METADATA +2 -3
- {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/RECORD +37 -37
- opik_optimizer/evolutionary_optimizer/llm_support.py +0 -136
- opik_optimizer/mipro_optimizer/mipro_optimizer.py +0 -680
- {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/WHEEL +0 -0
- {opik_optimizer-2.1.2.dist-info → opik_optimizer-2.2.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
#
|
|
52
|
-
|
|
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
|
-
#
|
|
55
|
-
|
|
81
|
+
# Store original log levels
|
|
82
|
+
original_client_level = opik_client_logger.level
|
|
83
|
+
original_opik_level = opik_logger.level
|
|
56
84
|
|
|
57
|
-
#
|
|
58
|
-
|
|
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
|
|
67
|
-
|
|
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
|
-
|
|
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}
|
|
237
|
+
f"Prompt was optimized and improved from {initial_score:.4f} to {best_score:.4f}",
|
|
211
238
|
style="bold green",
|
|
212
239
|
)
|
|
213
240
|
]
|
opik_optimizer/task_evaluator.py
CHANGED
|
@@ -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(
|
|
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
|
-
#
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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(
|
|
118
|
-
|
|
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
|
opik_optimizer/utils/core.py
CHANGED
|
@@ -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
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opik_optimizer
|
|
3
|
-
Version: 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.
|
|
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=
|
|
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=
|
|
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/
|
|
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=
|
|
10
|
-
opik_optimizer/task_evaluator.py,sha256=
|
|
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=
|
|
31
|
-
opik_optimizer/evolutionary_optimizer/evaluation_ops.py,sha256=
|
|
32
|
-
opik_optimizer/evolutionary_optimizer/evolutionary_optimizer.py,sha256=
|
|
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/
|
|
35
|
-
opik_optimizer/evolutionary_optimizer/
|
|
36
|
-
opik_optimizer/evolutionary_optimizer/
|
|
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=
|
|
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=
|
|
43
|
-
opik_optimizer/few_shot_bayesian_optimizer/reporting.py,sha256=
|
|
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=
|
|
47
|
-
opik_optimizer/gepa_optimizer/reporting.py,sha256=
|
|
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=
|
|
50
|
-
opik_optimizer/hierarchical_reflective_optimizer/hierarchical_root_cause_analyzer.py,sha256=
|
|
51
|
-
opik_optimizer/hierarchical_reflective_optimizer/prompts.py,sha256=
|
|
52
|
-
opik_optimizer/hierarchical_reflective_optimizer/reporting.py,sha256=
|
|
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=
|
|
61
|
-
opik_optimizer/meta_prompt_optimizer/reporting.py,sha256=
|
|
62
|
-
opik_optimizer/mipro_optimizer/__init__.py,sha256=
|
|
63
|
-
opik_optimizer/mipro_optimizer/_lm.py,sha256=
|
|
64
|
-
opik_optimizer/mipro_optimizer/
|
|
65
|
-
opik_optimizer/mipro_optimizer/
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
82
|
-
opik_optimizer-2.
|
|
83
|
-
opik_optimizer-2.
|
|
84
|
-
opik_optimizer-2.
|
|
85
|
-
opik_optimizer-2.
|
|
86
|
-
opik_optimizer-2.
|
|
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")
|