themis-eval 0.2.2__py3-none-any.whl → 1.0.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.
- themis/__init__.py +5 -2
- themis/_version.py +14 -1
- themis/api.py +83 -145
- themis/backends/storage.py +5 -0
- themis/cli/commands/info.py +2 -11
- themis/cli/main.py +231 -40
- themis/comparison/engine.py +7 -13
- themis/core/entities.py +4 -0
- themis/evaluation/metric_pipeline.py +12 -0
- themis/evaluation/pipeline.py +22 -0
- themis/evaluation/pipelines/__init__.py +4 -0
- themis/evaluation/pipelines/composable_pipeline.py +55 -0
- themis/evaluation/pipelines/standard_pipeline.py +18 -1
- themis/evaluation/strategies/attempt_aware_evaluation_strategy.py +5 -2
- themis/evaluation/strategies/judge_evaluation_strategy.py +6 -1
- themis/experiment/__init__.py +2 -2
- themis/experiment/cache_manager.py +15 -1
- themis/experiment/definitions.py +1 -1
- themis/experiment/orchestrator.py +21 -11
- themis/experiment/share.py +264 -0
- themis/experiment/storage.py +345 -298
- themis/generation/plan.py +28 -6
- themis/generation/router.py +22 -4
- themis/generation/runner.py +16 -1
- themis/presets/benchmarks.py +602 -17
- themis/server/app.py +38 -26
- themis/session.py +125 -0
- themis/specs/__init__.py +7 -0
- themis/specs/execution.py +26 -0
- themis/specs/experiment.py +33 -0
- themis/specs/storage.py +18 -0
- themis/storage/__init__.py +6 -0
- themis/storage/experiment_storage.py +7 -0
- {themis_eval-0.2.2.dist-info → themis_eval-1.0.0.dist-info}/METADATA +47 -34
- {themis_eval-0.2.2.dist-info → themis_eval-1.0.0.dist-info}/RECORD +38 -31
- {themis_eval-0.2.2.dist-info → themis_eval-1.0.0.dist-info}/WHEEL +1 -1
- themis/experiment/builder.py +0 -151
- themis/experiment/export_csv.py +0 -159
- {themis_eval-0.2.2.dist-info → themis_eval-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {themis_eval-0.2.2.dist-info → themis_eval-1.0.0.dist-info}/top_level.txt +0 -0
themis/generation/plan.py
CHANGED
|
@@ -88,9 +88,20 @@ class GenerationPlan:
|
|
|
88
88
|
}
|
|
89
89
|
if dataset_id is not None:
|
|
90
90
|
metadata["dataset_id"] = dataset_id
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
|
|
92
|
+
# If metadata_fields is explicitly specified, use it as a filter (existing behavior)
|
|
93
|
+
# Otherwise, include all fields by default (new behavior for custom metrics)
|
|
94
|
+
if self.metadata_fields:
|
|
95
|
+
# Explicit filter - only include specified fields
|
|
96
|
+
for field_name in self.metadata_fields:
|
|
97
|
+
if field_name in row:
|
|
98
|
+
metadata[field_name] = row[field_name]
|
|
99
|
+
else:
|
|
100
|
+
# No filter - include all fields except those used for other purposes
|
|
101
|
+
for field_name, field_value in row.items():
|
|
102
|
+
if field_name not in (self.dataset_id_field, self.reference_field):
|
|
103
|
+
metadata[field_name] = field_value
|
|
104
|
+
|
|
94
105
|
return metadata
|
|
95
106
|
|
|
96
107
|
def _build_reference(
|
|
@@ -209,9 +220,20 @@ class CartesianExpansionStrategy:
|
|
|
209
220
|
}
|
|
210
221
|
if dataset_id is not None:
|
|
211
222
|
metadata["dataset_id"] = dataset_id
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
223
|
+
|
|
224
|
+
# If metadata_fields is explicitly specified, use it as a filter (existing behavior)
|
|
225
|
+
# Otherwise, include all fields by default (new behavior for custom metrics)
|
|
226
|
+
if context.metadata_fields:
|
|
227
|
+
# Explicit filter - only include specified fields
|
|
228
|
+
for field_name in context.metadata_fields:
|
|
229
|
+
if field_name in row:
|
|
230
|
+
metadata[field_name] = row[field_name]
|
|
231
|
+
else:
|
|
232
|
+
# No filter - include all fields except those used for other purposes
|
|
233
|
+
for field_name, field_value in row.items():
|
|
234
|
+
if field_name not in (context.dataset_id_field, context.reference_field):
|
|
235
|
+
metadata[field_name] = field_value
|
|
236
|
+
|
|
215
237
|
return metadata
|
|
216
238
|
|
|
217
239
|
def _build_reference(
|
themis/generation/router.py
CHANGED
|
@@ -2,22 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Mapping
|
|
5
|
+
from typing import Mapping, Tuple, Union
|
|
6
6
|
|
|
7
7
|
from themis.core import entities as core_entities
|
|
8
8
|
from themis.interfaces import ModelProvider
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
ProviderKey = Union[str, Tuple[str, str]]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _model_key(provider: str, identifier: str) -> str:
|
|
15
|
+
return f"{provider}:{identifier}"
|
|
16
|
+
|
|
17
|
+
|
|
11
18
|
class ProviderRouter(ModelProvider):
|
|
12
19
|
"""Dispatches generation tasks to concrete providers by model identifier."""
|
|
13
20
|
|
|
14
|
-
def __init__(self, providers: Mapping[
|
|
15
|
-
|
|
21
|
+
def __init__(self, providers: Mapping[ProviderKey, ModelProvider]):
|
|
22
|
+
normalized: dict[str, ModelProvider] = {}
|
|
23
|
+
|
|
24
|
+
for key, provider in providers.items():
|
|
25
|
+
if isinstance(key, tuple):
|
|
26
|
+
provider_name, model_id = key
|
|
27
|
+
normalized[_model_key(provider_name, model_id)] = provider
|
|
28
|
+
else:
|
|
29
|
+
normalized[str(key)] = provider
|
|
30
|
+
|
|
31
|
+
self._providers = normalized
|
|
16
32
|
|
|
17
33
|
def generate(
|
|
18
34
|
self, task: core_entities.GenerationTask
|
|
19
35
|
) -> core_entities.GenerationRecord: # type: ignore[override]
|
|
20
|
-
provider = self._providers.get(task.model.identifier)
|
|
36
|
+
provider = self._providers.get(_model_key(task.model.provider, task.model.identifier))
|
|
37
|
+
if provider is None:
|
|
38
|
+
provider = self._providers.get(task.model.identifier)
|
|
21
39
|
if provider is None:
|
|
22
40
|
known = ", ".join(sorted(self._providers)) or "<none>"
|
|
23
41
|
raise RuntimeError(
|
themis/generation/runner.py
CHANGED
|
@@ -26,6 +26,7 @@ class GenerationRunner:
|
|
|
26
26
|
[core_entities.GenerationTask], strategies.GenerationStrategy
|
|
27
27
|
]
|
|
28
28
|
| None = None,
|
|
29
|
+
execution_backend: object | None = None,
|
|
29
30
|
max_parallel: int = 1,
|
|
30
31
|
max_retries: int = 3,
|
|
31
32
|
retry_initial_delay: float = 0.5,
|
|
@@ -36,6 +37,7 @@ class GenerationRunner:
|
|
|
36
37
|
self._strategy_resolver = strategy_resolver or (
|
|
37
38
|
lambda task: strategies.SingleAttemptStrategy()
|
|
38
39
|
)
|
|
40
|
+
self._execution_backend = execution_backend
|
|
39
41
|
self._max_parallel = max(1, max_parallel)
|
|
40
42
|
self._max_retries = max(1, int(max_retries))
|
|
41
43
|
self._retry_initial_delay = max(0.0, retry_initial_delay)
|
|
@@ -53,7 +55,20 @@ class GenerationRunner:
|
|
|
53
55
|
return
|
|
54
56
|
|
|
55
57
|
logger.info(f"Runner: Starting execution of {len(task_list)} tasks with {self._max_parallel} workers")
|
|
56
|
-
|
|
58
|
+
|
|
59
|
+
if self._execution_backend is not None:
|
|
60
|
+
logger.info("Runner: Using custom execution backend")
|
|
61
|
+
backend = self._execution_backend
|
|
62
|
+
try:
|
|
63
|
+
for result in backend.map(
|
|
64
|
+
self._execute_task, task_list, max_workers=self._max_parallel
|
|
65
|
+
):
|
|
66
|
+
yield result
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(f"Runner: Execution backend failed: {e}")
|
|
69
|
+
raise
|
|
70
|
+
return
|
|
71
|
+
|
|
57
72
|
if self._max_parallel <= 1:
|
|
58
73
|
logger.info("Runner: Using sequential execution (1 worker)")
|
|
59
74
|
for i, task in enumerate(task_list, 1):
|