opik-optimizer 0.7.2__tar.gz → 0.7.4__tar.gz
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-0.7.2/src/opik_optimizer.egg-info → opik_optimizer-0.7.4}/PKG-INFO +30 -28
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/README.md +27 -27
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/setup.py +6 -1
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/__init__.py +2 -0
- opik_optimizer-0.7.4/src/opik_optimizer/_throttle.py +43 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/base_optimizer.py +1 -80
- opik_optimizer-0.7.4/src/opik_optimizer/data/hotpot-500.json +2502 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/demo/datasets.py +15 -32
- opik_optimizer-0.7.4/src/opik_optimizer/evolutionary_optimizer/__init__.py +1 -0
- opik_optimizer-0.7.4/src/opik_optimizer/evolutionary_optimizer/evolutionary_optimizer.py +1447 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/few_shot_bayesian_optimizer/few_shot_bayesian_optimizer.py +7 -7
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/meta_prompt_optimizer.py +3 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/mipro_optimizer/_lm.py +2 -2
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4/src/opik_optimizer.egg-info}/PKG-INFO +30 -28
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer.egg-info/SOURCES.txt +3 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer.egg-info/requires.txt +2 -0
- opik_optimizer-0.7.2/src/opik_optimizer/_throttle.py +0 -43
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/LICENSE +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/setup.cfg +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/cache_config.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/demo/__init__.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/demo/cache.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/few_shot_bayesian_optimizer/__init__.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/few_shot_bayesian_optimizer/prompt_parameter.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/few_shot_bayesian_optimizer/prompt_templates.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/integrations/__init__.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/logging_config.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/mipro_optimizer/__init__.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/mipro_optimizer/_mipro_optimizer_v2.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/mipro_optimizer/mipro_optimizer.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/mipro_optimizer/utils.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/optimization_config/__init__.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/optimization_config/configs.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/optimization_config/mappers.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/optimization_result.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/task_evaluator.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer/utils.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer.egg-info/dependency_links.txt +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/src/opik_optimizer.egg-info/top_level.txt +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/tests/test_base_optimizer.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/tests/test_example.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/tests/test_few_shot_bayesian_optimizer.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/tests/test_mappers.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/tests/test_optimization_dsl.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/tests/test_optimization_result.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/tests/test_task_evaluator.py +0 -0
- {opik_optimizer-0.7.2 → opik_optimizer-0.7.4}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: opik_optimizer
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.4
|
4
4
|
Summary: Agent optimization with Opik
|
5
5
|
Home-page: https://github.com/comet-ml/opik
|
6
6
|
Author: Comet ML
|
@@ -21,6 +21,8 @@ Requires-Dist: optuna
|
|
21
21
|
Requires-Dist: pydantic
|
22
22
|
Requires-Dist: pandas
|
23
23
|
Requires-Dist: hf_xet
|
24
|
+
Requires-Dist: pyrate-limiter
|
25
|
+
Requires-Dist: deap>=1.4.3
|
24
26
|
Provides-Extra: dev
|
25
27
|
Requires-Dist: adalflow; extra == "dev"
|
26
28
|
Requires-Dist: pytest; extra == "dev"
|
@@ -72,7 +74,7 @@ from your LLMs. You can use a variety of algorithms, including:
|
|
72
74
|
|
73
75
|
3. Install the package:
|
74
76
|
```bash
|
75
|
-
pip install
|
77
|
+
pip install opik-optimizer
|
76
78
|
```
|
77
79
|
|
78
80
|
You'll need:
|
@@ -94,11 +96,10 @@ You can see how to use those below:
|
|
94
96
|
|
95
97
|
```python
|
96
98
|
from opik.evaluation.metrics import LevenshteinRatio
|
97
|
-
from opik_optimizer
|
99
|
+
from opik_optimizer import FewShotBayesianOptimizer
|
98
100
|
from opik_optimizer.demo import get_or_create_dataset
|
99
101
|
|
100
102
|
from opik_optimizer import (
|
101
|
-
OptimizationConfig,
|
102
103
|
MetricConfig,
|
103
104
|
TaskConfig,
|
104
105
|
from_dataset_field,
|
@@ -111,40 +112,41 @@ hot_pot_dataset = get_or_create_dataset("hotpot-300")
|
|
111
112
|
prompt_instruction = """
|
112
113
|
Answer the question.
|
113
114
|
"""
|
114
|
-
|
115
|
-
initial_prompt_no_examples = [
|
116
|
-
{"role": "system", "content": prompt_instruction},
|
117
|
-
{"role": "user", "content": "{{question}}"},
|
118
|
-
]
|
115
|
+
project_name = "optimize-few-shot-bayesian-hotpot"
|
119
116
|
|
120
117
|
optimizer = FewShotBayesianOptimizer(
|
121
118
|
model="gpt-4o-mini",
|
122
|
-
project_name=
|
119
|
+
project_name=project_name,
|
123
120
|
min_examples=3,
|
124
121
|
max_examples=8,
|
125
122
|
n_threads=16,
|
126
123
|
seed=42,
|
127
124
|
)
|
128
125
|
|
129
|
-
|
126
|
+
metric_config = MetricConfig(
|
127
|
+
metric=LevenshteinRatio(project_name=project_name),
|
128
|
+
inputs={
|
129
|
+
"output": from_llm_response_text(),
|
130
|
+
"reference": from_dataset_field(name="answer"),
|
131
|
+
},
|
132
|
+
)
|
133
|
+
|
134
|
+
task_config = TaskConfig(
|
135
|
+
instruction_prompt=prompt_instruction,
|
136
|
+
input_dataset_fields=["question"],
|
137
|
+
output_dataset_field="answer",
|
138
|
+
use_chat_prompt=True,
|
139
|
+
)
|
140
|
+
|
141
|
+
result = optimizer.optimize_prompt(
|
130
142
|
dataset=hot_pot_dataset,
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
"reference": from_dataset_field(name="answer"),
|
136
|
-
},
|
137
|
-
),
|
138
|
-
task=TaskConfig(
|
139
|
-
instruction_prompt=prompt_instruction,
|
140
|
-
input_dataset_fields=["question"],
|
141
|
-
output_dataset_field="answer",
|
142
|
-
use_chat_prompt=True,
|
143
|
-
),
|
143
|
+
metric_config=metric_config,
|
144
|
+
task_config=task_config,
|
145
|
+
n_trials=10,
|
146
|
+
n_samples=150,
|
144
147
|
)
|
145
148
|
|
146
|
-
result
|
147
|
-
print(result)
|
149
|
+
result.display()
|
148
150
|
```
|
149
151
|
|
150
152
|
More examples can be found in the `scripts` folder.
|
@@ -152,7 +154,7 @@ More examples can be found in the `scripts` folder.
|
|
152
154
|
## Installation
|
153
155
|
|
154
156
|
```bash
|
155
|
-
pip install
|
157
|
+
pip install opik-optimizer
|
156
158
|
```
|
157
159
|
|
158
160
|
## Development
|
@@ -167,6 +169,6 @@ pip install -e .
|
|
167
169
|
|
168
170
|
## Requirements
|
169
171
|
|
170
|
-
- Python 3.10+
|
172
|
+
- Python 3.10+ < 3.13
|
171
173
|
- Opik API key
|
172
174
|
- OpenAI API key (or other LLM provider)
|
@@ -33,7 +33,7 @@ from your LLMs. You can use a variety of algorithms, including:
|
|
33
33
|
|
34
34
|
3. Install the package:
|
35
35
|
```bash
|
36
|
-
pip install
|
36
|
+
pip install opik-optimizer
|
37
37
|
```
|
38
38
|
|
39
39
|
You'll need:
|
@@ -55,11 +55,10 @@ You can see how to use those below:
|
|
55
55
|
|
56
56
|
```python
|
57
57
|
from opik.evaluation.metrics import LevenshteinRatio
|
58
|
-
from opik_optimizer
|
58
|
+
from opik_optimizer import FewShotBayesianOptimizer
|
59
59
|
from opik_optimizer.demo import get_or_create_dataset
|
60
60
|
|
61
61
|
from opik_optimizer import (
|
62
|
-
OptimizationConfig,
|
63
62
|
MetricConfig,
|
64
63
|
TaskConfig,
|
65
64
|
from_dataset_field,
|
@@ -72,40 +71,41 @@ hot_pot_dataset = get_or_create_dataset("hotpot-300")
|
|
72
71
|
prompt_instruction = """
|
73
72
|
Answer the question.
|
74
73
|
"""
|
75
|
-
|
76
|
-
initial_prompt_no_examples = [
|
77
|
-
{"role": "system", "content": prompt_instruction},
|
78
|
-
{"role": "user", "content": "{{question}}"},
|
79
|
-
]
|
74
|
+
project_name = "optimize-few-shot-bayesian-hotpot"
|
80
75
|
|
81
76
|
optimizer = FewShotBayesianOptimizer(
|
82
77
|
model="gpt-4o-mini",
|
83
|
-
project_name=
|
78
|
+
project_name=project_name,
|
84
79
|
min_examples=3,
|
85
80
|
max_examples=8,
|
86
81
|
n_threads=16,
|
87
82
|
seed=42,
|
88
83
|
)
|
89
84
|
|
90
|
-
|
85
|
+
metric_config = MetricConfig(
|
86
|
+
metric=LevenshteinRatio(project_name=project_name),
|
87
|
+
inputs={
|
88
|
+
"output": from_llm_response_text(),
|
89
|
+
"reference": from_dataset_field(name="answer"),
|
90
|
+
},
|
91
|
+
)
|
92
|
+
|
93
|
+
task_config = TaskConfig(
|
94
|
+
instruction_prompt=prompt_instruction,
|
95
|
+
input_dataset_fields=["question"],
|
96
|
+
output_dataset_field="answer",
|
97
|
+
use_chat_prompt=True,
|
98
|
+
)
|
99
|
+
|
100
|
+
result = optimizer.optimize_prompt(
|
91
101
|
dataset=hot_pot_dataset,
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
"reference": from_dataset_field(name="answer"),
|
97
|
-
},
|
98
|
-
),
|
99
|
-
task=TaskConfig(
|
100
|
-
instruction_prompt=prompt_instruction,
|
101
|
-
input_dataset_fields=["question"],
|
102
|
-
output_dataset_field="answer",
|
103
|
-
use_chat_prompt=True,
|
104
|
-
),
|
102
|
+
metric_config=metric_config,
|
103
|
+
task_config=task_config,
|
104
|
+
n_trials=10,
|
105
|
+
n_samples=150,
|
105
106
|
)
|
106
107
|
|
107
|
-
result
|
108
|
-
print(result)
|
108
|
+
result.display()
|
109
109
|
```
|
110
110
|
|
111
111
|
More examples can be found in the `scripts` folder.
|
@@ -113,7 +113,7 @@ More examples can be found in the `scripts` folder.
|
|
113
113
|
## Installation
|
114
114
|
|
115
115
|
```bash
|
116
|
-
pip install
|
116
|
+
pip install opik-optimizer
|
117
117
|
```
|
118
118
|
|
119
119
|
## Development
|
@@ -128,6 +128,6 @@ pip install -e .
|
|
128
128
|
|
129
129
|
## Requirements
|
130
130
|
|
131
|
-
- Python 3.10+
|
131
|
+
- Python 3.10+ < 3.13
|
132
132
|
- Opik API key
|
133
133
|
- OpenAI API key (or other LLM provider)
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
2
2
|
|
3
3
|
setup(
|
4
4
|
name="opik_optimizer",
|
5
|
-
version="0.7.
|
5
|
+
version="0.7.4",
|
6
6
|
description="Agent optimization with Opik",
|
7
7
|
author="Comet ML",
|
8
8
|
author_email="support@comet.com",
|
@@ -11,6 +11,9 @@ setup(
|
|
11
11
|
url="https://github.com/comet-ml/opik",
|
12
12
|
packages=find_packages(where="src"),
|
13
13
|
package_dir={"": "src"},
|
14
|
+
package_data={
|
15
|
+
'opik_optimizer': ['data/*.json'],
|
16
|
+
},
|
14
17
|
python_requires=">=3.9,<3.13",
|
15
18
|
install_requires=[
|
16
19
|
"opik>=1.7.17",
|
@@ -22,6 +25,8 @@ setup(
|
|
22
25
|
"pydantic",
|
23
26
|
"pandas",
|
24
27
|
"hf_xet",
|
28
|
+
"pyrate-limiter",
|
29
|
+
"deap>=1.4.3",
|
25
30
|
],
|
26
31
|
# dev requirements
|
27
32
|
extras_require={
|
@@ -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",
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import functools
|
2
|
+
import pyrate_limiter
|
3
|
+
import time
|
4
|
+
import opik.config
|
5
|
+
|
6
|
+
from typing import Callable, Any
|
7
|
+
|
8
|
+
|
9
|
+
class RateLimiter:
|
10
|
+
"""
|
11
|
+
Rate limiter that enforces a maximum number of calls across all threads using pyrate_limiter.
|
12
|
+
"""
|
13
|
+
def __init__(self, max_calls_per_second: int):
|
14
|
+
self.max_calls_per_second = max_calls_per_second
|
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"
|
19
|
+
|
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]:
|
25
|
+
"""Decorator to rate limit a function using the provided limiter"""
|
26
|
+
|
27
|
+
def decorator(func: Callable) -> Callable:
|
28
|
+
@functools.wraps(func)
|
29
|
+
def wrapper(*args, **kwargs) -> Any:
|
30
|
+
limiter.acquire()
|
31
|
+
return func(*args, **kwargs)
|
32
|
+
return wrapper
|
33
|
+
return decorator
|
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
|
+
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
|
"""
|