opik 1.9.26__py3-none-any.whl → 1.9.39__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/__init__.py +10 -3
- opik/api_objects/dataset/rest_operations.py +2 -0
- opik/api_objects/experiment/experiment.py +31 -5
- opik/api_objects/experiment/helpers.py +34 -10
- opik/api_objects/local_recording.py +8 -3
- opik/api_objects/opik_client.py +218 -46
- opik/api_objects/opik_query_language.py +9 -0
- opik/api_objects/prompt/__init__.py +11 -3
- opik/api_objects/prompt/base_prompt.py +69 -0
- opik/api_objects/prompt/base_prompt_template.py +29 -0
- opik/api_objects/prompt/chat/__init__.py +1 -0
- opik/api_objects/prompt/chat/chat_prompt.py +193 -0
- opik/api_objects/prompt/chat/chat_prompt_template.py +350 -0
- opik/api_objects/prompt/{chat_content_renderer_registry.py → chat/content_renderer_registry.py} +31 -34
- opik/api_objects/prompt/client.py +101 -30
- opik/api_objects/prompt/text/__init__.py +1 -0
- opik/api_objects/prompt/{prompt.py → text/prompt.py} +55 -32
- opik/api_objects/prompt/{prompt_template.py → text/prompt_template.py} +8 -5
- opik/cli/export.py +6 -2
- opik/config.py +0 -5
- opik/decorator/base_track_decorator.py +37 -40
- opik/evaluation/__init__.py +13 -2
- opik/evaluation/engine/engine.py +195 -223
- opik/evaluation/engine/helpers.py +8 -7
- opik/evaluation/engine/metrics_evaluator.py +237 -0
- opik/evaluation/evaluation_result.py +35 -1
- opik/evaluation/evaluator.py +309 -23
- opik/evaluation/models/litellm/util.py +78 -6
- opik/evaluation/report.py +14 -2
- opik/evaluation/rest_operations.py +6 -9
- opik/evaluation/test_case.py +2 -2
- opik/evaluation/types.py +9 -1
- opik/exceptions.py +17 -0
- opik/id_helpers.py +18 -0
- opik/integrations/adk/helpers.py +16 -7
- opik/integrations/adk/legacy_opik_tracer.py +7 -4
- opik/integrations/adk/opik_tracer.py +3 -1
- opik/integrations/adk/patchers/adk_otel_tracer/opik_adk_otel_tracer.py +7 -3
- opik/integrations/dspy/callback.py +1 -4
- opik/integrations/haystack/opik_connector.py +2 -2
- opik/integrations/haystack/opik_tracer.py +2 -4
- opik/integrations/langchain/opik_tracer.py +1 -4
- opik/integrations/llama_index/callback.py +2 -4
- opik/integrations/openai/agents/opik_tracing_processor.py +1 -2
- opik/integrations/openai/opik_tracker.py +1 -1
- opik/opik_context.py +7 -7
- opik/rest_api/__init__.py +123 -11
- opik/rest_api/dashboards/client.py +65 -2
- opik/rest_api/dashboards/raw_client.py +82 -0
- opik/rest_api/datasets/client.py +441 -2
- opik/rest_api/datasets/raw_client.py +1225 -505
- opik/rest_api/experiments/client.py +30 -2
- opik/rest_api/experiments/raw_client.py +26 -0
- opik/rest_api/optimizations/client.py +302 -0
- opik/rest_api/optimizations/raw_client.py +463 -0
- opik/rest_api/optimizations/types/optimization_update_status.py +3 -1
- opik/rest_api/prompts/__init__.py +2 -2
- opik/rest_api/prompts/client.py +34 -4
- opik/rest_api/prompts/raw_client.py +32 -2
- opik/rest_api/prompts/types/__init__.py +3 -1
- opik/rest_api/prompts/types/create_prompt_version_detail_template_structure.py +5 -0
- opik/rest_api/prompts/types/prompt_write_template_structure.py +5 -0
- opik/rest_api/traces/client.py +6 -6
- opik/rest_api/traces/raw_client.py +4 -4
- opik/rest_api/types/__init__.py +121 -11
- opik/rest_api/types/aggregation_data.py +1 -0
- opik/rest_api/types/automation_rule_evaluator.py +23 -1
- opik/rest_api/types/automation_rule_evaluator_llm_as_judge.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_llm_as_judge_public.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_llm_as_judge_write.py +2 -0
- opik/rest_api/types/{automation_rule_evaluator_object_public.py → automation_rule_evaluator_object_object_public.py} +32 -10
- opik/rest_api/types/automation_rule_evaluator_page_public.py +2 -2
- opik/rest_api/types/automation_rule_evaluator_public.py +23 -1
- opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge_public.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge_write.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge_public.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge_write.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python_public.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python_write.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_update.py +23 -1
- opik/rest_api/types/automation_rule_evaluator_update_llm_as_judge.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_update_span_llm_as_judge.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_update_trace_thread_llm_as_judge.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_update_trace_thread_user_defined_metric_python.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_update_user_defined_metric_python.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python_public.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python_write.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_write.py +23 -1
- opik/rest_api/types/dashboard_page_public.py +1 -0
- opik/rest_api/types/dataset.py +2 -0
- opik/rest_api/types/dataset_item.py +1 -0
- opik/rest_api/types/dataset_item_compare.py +1 -0
- opik/rest_api/types/dataset_item_page_compare.py +1 -0
- opik/rest_api/types/dataset_item_page_public.py +1 -0
- opik/rest_api/types/dataset_item_public.py +1 -0
- opik/rest_api/types/dataset_public.py +2 -0
- opik/rest_api/types/dataset_public_status.py +5 -0
- opik/rest_api/types/dataset_status.py +5 -0
- opik/rest_api/types/dataset_version_diff.py +22 -0
- opik/rest_api/types/dataset_version_diff_stats.py +24 -0
- opik/rest_api/types/dataset_version_page_public.py +23 -0
- opik/rest_api/types/dataset_version_public.py +49 -0
- opik/rest_api/types/experiment.py +2 -0
- opik/rest_api/types/experiment_public.py +2 -0
- opik/rest_api/types/experiment_score.py +20 -0
- opik/rest_api/types/experiment_score_public.py +20 -0
- opik/rest_api/types/experiment_score_write.py +20 -0
- opik/rest_api/types/feedback_score_public.py +4 -0
- opik/rest_api/types/optimization.py +2 -0
- opik/rest_api/types/optimization_public.py +2 -0
- opik/rest_api/types/optimization_public_status.py +3 -1
- opik/rest_api/types/optimization_status.py +3 -1
- opik/rest_api/types/optimization_studio_config.py +27 -0
- opik/rest_api/types/optimization_studio_config_public.py +27 -0
- opik/rest_api/types/optimization_studio_config_write.py +27 -0
- opik/rest_api/types/optimization_studio_log.py +22 -0
- opik/rest_api/types/optimization_write.py +2 -0
- opik/rest_api/types/optimization_write_status.py +3 -1
- opik/rest_api/types/prompt.py +6 -0
- opik/rest_api/types/prompt_detail.py +6 -0
- opik/rest_api/types/prompt_detail_template_structure.py +5 -0
- opik/rest_api/types/prompt_public.py +6 -0
- opik/rest_api/types/prompt_public_template_structure.py +5 -0
- opik/rest_api/types/prompt_template_structure.py +5 -0
- opik/rest_api/types/prompt_version.py +2 -0
- opik/rest_api/types/prompt_version_detail.py +2 -0
- opik/rest_api/types/prompt_version_detail_template_structure.py +5 -0
- opik/rest_api/types/prompt_version_public.py +2 -0
- opik/rest_api/types/prompt_version_public_template_structure.py +5 -0
- opik/rest_api/types/prompt_version_template_structure.py +5 -0
- opik/rest_api/types/score_name.py +1 -0
- opik/rest_api/types/service_toggles_config.py +5 -0
- opik/rest_api/types/span_filter.py +23 -0
- opik/rest_api/types/span_filter_operator.py +21 -0
- opik/rest_api/types/span_filter_write.py +23 -0
- opik/rest_api/types/span_filter_write_operator.py +21 -0
- opik/rest_api/types/span_llm_as_judge_code.py +27 -0
- opik/rest_api/types/span_llm_as_judge_code_public.py +27 -0
- opik/rest_api/types/span_llm_as_judge_code_write.py +27 -0
- opik/rest_api/types/studio_evaluation.py +20 -0
- opik/rest_api/types/studio_evaluation_public.py +20 -0
- opik/rest_api/types/studio_evaluation_write.py +20 -0
- opik/rest_api/types/studio_llm_model.py +21 -0
- opik/rest_api/types/studio_llm_model_public.py +21 -0
- opik/rest_api/types/studio_llm_model_write.py +21 -0
- opik/rest_api/types/studio_message.py +20 -0
- opik/rest_api/types/studio_message_public.py +20 -0
- opik/rest_api/types/studio_message_write.py +20 -0
- opik/rest_api/types/studio_metric.py +21 -0
- opik/rest_api/types/studio_metric_public.py +21 -0
- opik/rest_api/types/studio_metric_write.py +21 -0
- opik/rest_api/types/studio_optimizer.py +21 -0
- opik/rest_api/types/studio_optimizer_public.py +21 -0
- opik/rest_api/types/studio_optimizer_write.py +21 -0
- opik/rest_api/types/studio_prompt.py +20 -0
- opik/rest_api/types/studio_prompt_public.py +20 -0
- opik/rest_api/types/studio_prompt_write.py +20 -0
- opik/rest_api/types/trace.py +6 -0
- opik/rest_api/types/trace_public.py +6 -0
- opik/rest_api/types/trace_thread_filter_write.py +23 -0
- opik/rest_api/types/trace_thread_filter_write_operator.py +21 -0
- opik/rest_api/types/value_entry.py +2 -0
- opik/rest_api/types/value_entry_compare.py +2 -0
- opik/rest_api/types/value_entry_experiment_item_bulk_write_view.py +2 -0
- opik/rest_api/types/value_entry_public.py +2 -0
- opik/synchronization.py +5 -6
- opik/{decorator/tracing_runtime_config.py → tracing_runtime_config.py} +6 -7
- {opik-1.9.26.dist-info → opik-1.9.39.dist-info}/METADATA +2 -1
- {opik-1.9.26.dist-info → opik-1.9.39.dist-info}/RECORD +177 -119
- opik/api_objects/prompt/chat_prompt_template.py +0 -200
- {opik-1.9.26.dist-info → opik-1.9.39.dist-info}/WHEEL +0 -0
- {opik-1.9.26.dist-info → opik-1.9.39.dist-info}/entry_points.txt +0 -0
- {opik-1.9.26.dist-info → opik-1.9.39.dist-info}/licenses/LICENSE +0 -0
- {opik-1.9.26.dist-info → opik-1.9.39.dist-info}/top_level.txt +0 -0
opik/__init__.py
CHANGED
|
@@ -6,18 +6,23 @@ from .api_objects.experiment.experiment_item import (
|
|
|
6
6
|
ExperimentItemReferences,
|
|
7
7
|
)
|
|
8
8
|
from .api_objects.opik_client import Opik
|
|
9
|
-
from .api_objects.prompt import Prompt
|
|
9
|
+
from .api_objects.prompt import Prompt, ChatPrompt
|
|
10
10
|
from .api_objects.prompt.types import PromptType
|
|
11
11
|
from .api_objects.span import Span
|
|
12
12
|
from .api_objects.trace import Trace
|
|
13
13
|
from .configurator.configure import configure
|
|
14
14
|
from .decorator.tracker import flush_tracker, track
|
|
15
|
-
from .evaluation import
|
|
15
|
+
from .evaluation import (
|
|
16
|
+
evaluate,
|
|
17
|
+
evaluate_experiment,
|
|
18
|
+
evaluate_on_dict_items,
|
|
19
|
+
evaluate_prompt,
|
|
20
|
+
)
|
|
16
21
|
from .integrations.sagemaker import auth as sagemaker_auth
|
|
17
22
|
from .plugins.pytest.decorator import llm_unit
|
|
18
23
|
from .types import LLMProvider
|
|
19
24
|
from . import opik_context
|
|
20
|
-
from .
|
|
25
|
+
from .tracing_runtime_config import (
|
|
21
26
|
is_tracing_active,
|
|
22
27
|
reset_tracing_to_config_default,
|
|
23
28
|
set_tracing_active,
|
|
@@ -37,6 +42,7 @@ __all__ = [
|
|
|
37
42
|
"evaluate",
|
|
38
43
|
"evaluate_prompt",
|
|
39
44
|
"evaluate_experiment",
|
|
45
|
+
"evaluate_on_dict_items",
|
|
40
46
|
"ExperimentItemContent",
|
|
41
47
|
"ExperimentItemReferences",
|
|
42
48
|
"track",
|
|
@@ -49,6 +55,7 @@ __all__ = [
|
|
|
49
55
|
"llm_unit",
|
|
50
56
|
"configure",
|
|
51
57
|
"Prompt",
|
|
58
|
+
"ChatPrompt",
|
|
52
59
|
"PromptType",
|
|
53
60
|
"LLMProvider",
|
|
54
61
|
"reset_tracing_to_config_default",
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import logging
|
|
3
|
-
from typing import List, Optional
|
|
3
|
+
from typing import List, Optional, TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from opik.message_processing.batching import sequence_splitter
|
|
6
6
|
from opik.message_processing import messages, streamer
|
|
7
7
|
from opik.rest_api import client as rest_api_client
|
|
8
|
-
from opik.rest_api
|
|
8
|
+
from opik.rest_api import types as rest_api_types
|
|
9
9
|
from . import experiment_item, experiments_client
|
|
10
10
|
from .. import constants, helpers
|
|
11
|
-
from ...api_objects.prompt import
|
|
11
|
+
from ...api_objects.prompt import base_prompt
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from opik.evaluation.metrics import score_result
|
|
12
15
|
|
|
13
16
|
LOGGER = logging.getLogger(__name__)
|
|
14
17
|
|
|
@@ -22,7 +25,7 @@ class Experiment:
|
|
|
22
25
|
rest_client: rest_api_client.OpikApi,
|
|
23
26
|
streamer: streamer.Streamer,
|
|
24
27
|
experiments_client: experiments_client.ExperimentsClient,
|
|
25
|
-
prompts: Optional[List[
|
|
28
|
+
prompts: Optional[List[base_prompt.BasePrompt]] = None,
|
|
26
29
|
) -> None:
|
|
27
30
|
self._id = id
|
|
28
31
|
self._name = name
|
|
@@ -60,7 +63,7 @@ class Experiment:
|
|
|
60
63
|
def experiments_rest_client(self) -> rest_api_client.ExperimentsClient:
|
|
61
64
|
return self._rest_client.experiments
|
|
62
65
|
|
|
63
|
-
def get_experiment_data(self) -> experiment_public.ExperimentPublic:
|
|
66
|
+
def get_experiment_data(self) -> rest_api_types.experiment_public.ExperimentPublic:
|
|
64
67
|
return self._rest_client.experiments.get_experiment_by_id(id=self.id)
|
|
65
68
|
|
|
66
69
|
def insert(
|
|
@@ -123,3 +126,26 @@ class Experiment:
|
|
|
123
126
|
truncate=truncate,
|
|
124
127
|
max_results=max_results,
|
|
125
128
|
)
|
|
129
|
+
|
|
130
|
+
def log_experiment_scores(
|
|
131
|
+
self,
|
|
132
|
+
score_results: List["score_result.ScoreResult"],
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Log experiment-level scores to the backend."""
|
|
135
|
+
experiment_scores: List[rest_api_types.ExperimentScore] = []
|
|
136
|
+
|
|
137
|
+
for score_result_ in score_results:
|
|
138
|
+
if score_result_.scoring_failed:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
experiment_score = rest_api_types.ExperimentScore(
|
|
142
|
+
name=score_result_.name,
|
|
143
|
+
value=score_result_.value,
|
|
144
|
+
)
|
|
145
|
+
experiment_scores.append(experiment_score)
|
|
146
|
+
|
|
147
|
+
if experiment_scores:
|
|
148
|
+
self._rest_client.experiments.update_experiment(
|
|
149
|
+
id=self.id,
|
|
150
|
+
experiment_scores=experiment_scores,
|
|
151
|
+
)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import copy
|
|
1
2
|
import logging
|
|
2
|
-
import opik.jsonable_encoder as jsonable_encoder
|
|
3
3
|
from typing import Any, Dict, List, Mapping, Optional, Tuple
|
|
4
|
-
|
|
5
|
-
from
|
|
4
|
+
|
|
5
|
+
from opik import id_helpers
|
|
6
|
+
import opik.jsonable_encoder as jsonable_encoder
|
|
7
|
+
|
|
8
|
+
from ..prompt import base_prompt
|
|
9
|
+
|
|
6
10
|
|
|
7
11
|
LOGGER = logging.getLogger(__name__)
|
|
8
12
|
|
|
@@ -11,7 +15,7 @@ PromptVersion = Dict[str, str]
|
|
|
11
15
|
|
|
12
16
|
def build_metadata_and_prompt_versions(
|
|
13
17
|
experiment_config: Optional[Dict[str, Any]],
|
|
14
|
-
prompts: Optional[List[
|
|
18
|
+
prompts: Optional[List[base_prompt.BasePrompt]],
|
|
15
19
|
) -> Tuple[Optional[Dict[str, Any]], Optional[List[PromptVersion]]]:
|
|
16
20
|
prompt_versions: Optional[List[PromptVersion]] = None
|
|
17
21
|
|
|
@@ -37,9 +41,20 @@ def build_metadata_and_prompt_versions(
|
|
|
37
41
|
prompt_versions = []
|
|
38
42
|
experiment_config["prompts"] = {}
|
|
39
43
|
|
|
40
|
-
for
|
|
41
|
-
prompt_versions.append({"id":
|
|
42
|
-
|
|
44
|
+
for prompt_obj in prompts:
|
|
45
|
+
prompt_versions.append({"id": prompt_obj.__internal_api__version_id__})
|
|
46
|
+
# Use __internal_api__to_info_dict__() to get the prompt content in a consistent way
|
|
47
|
+
prompt_info = prompt_obj.__internal_api__to_info_dict__()
|
|
48
|
+
# Extract the template/messages from the version dict
|
|
49
|
+
if "version" in prompt_info:
|
|
50
|
+
if "template" in prompt_info["version"]:
|
|
51
|
+
experiment_config["prompts"][prompt_obj.name] = prompt_info[
|
|
52
|
+
"version"
|
|
53
|
+
]["template"]
|
|
54
|
+
elif "messages" in prompt_info["version"]:
|
|
55
|
+
experiment_config["prompts"][prompt_obj.name] = prompt_info[
|
|
56
|
+
"version"
|
|
57
|
+
]["messages"]
|
|
43
58
|
|
|
44
59
|
if experiment_config == {}:
|
|
45
60
|
return None, None
|
|
@@ -50,9 +65,9 @@ def build_metadata_and_prompt_versions(
|
|
|
50
65
|
|
|
51
66
|
|
|
52
67
|
def handle_prompt_args(
|
|
53
|
-
prompt: Optional[
|
|
54
|
-
prompts: Optional[List[
|
|
55
|
-
) -> Optional[List[
|
|
68
|
+
prompt: Optional[base_prompt.BasePrompt] = None,
|
|
69
|
+
prompts: Optional[List[base_prompt.BasePrompt]] = None,
|
|
70
|
+
) -> Optional[List[base_prompt.BasePrompt]]:
|
|
56
71
|
if prompts is not None and len(prompts) > 0 and prompt is not None:
|
|
57
72
|
LOGGER.warning(
|
|
58
73
|
"Arguments `prompt` and `prompts` are mutually exclusive, `prompts` will be used`."
|
|
@@ -63,3 +78,12 @@ def handle_prompt_args(
|
|
|
63
78
|
prompts = None
|
|
64
79
|
|
|
65
80
|
return prompts
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def generate_unique_experiment_name(experiment_name_prefix: Optional[str]) -> str:
|
|
84
|
+
if experiment_name_prefix is None:
|
|
85
|
+
return id_helpers.generate_random_alphanumeric_string(12)
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
f"{experiment_name_prefix}-{id_helpers.generate_random_alphanumeric_string(6)}"
|
|
89
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
from typing import Iterator, List
|
|
3
|
-
|
|
3
|
+
from typing import Optional
|
|
4
4
|
from . import opik_client
|
|
5
5
|
from ..message_processing import message_processors_chain
|
|
6
6
|
from ..message_processing.emulation import local_emulator_message_processor, models
|
|
@@ -33,9 +33,13 @@ class _LocalRecordingHandle:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
@contextlib.contextmanager
|
|
36
|
-
def record_traces_locally(
|
|
36
|
+
def record_traces_locally(
|
|
37
|
+
client: Optional[opik_client.Opik] = None,
|
|
38
|
+
) -> Iterator[_LocalRecordingHandle]:
|
|
37
39
|
"""Enable local recording of traces/spans within the context.
|
|
38
40
|
|
|
41
|
+
Args:
|
|
42
|
+
client: Optional Opik client to use for recording. If not provided, the default session client will be used.
|
|
39
43
|
Usage:
|
|
40
44
|
with opik.record_traces_locally() as storage:
|
|
41
45
|
# your code that creates traces/spans
|
|
@@ -44,7 +48,8 @@ def record_traces_locally() -> Iterator[_LocalRecordingHandle]:
|
|
|
44
48
|
Yields a handle with `span_trees` and `trace_trees` properties that flush
|
|
45
49
|
the client before reading, ensuring all events are captured.
|
|
46
50
|
"""
|
|
47
|
-
client
|
|
51
|
+
if client is None:
|
|
52
|
+
client = opik_client.get_client_cached()
|
|
48
53
|
|
|
49
54
|
# Disallow nested/local concurrent recordings in the same process
|
|
50
55
|
existing_local = message_processors_chain.get_local_emulator_message_processor(
|
opik/api_objects/opik_client.py
CHANGED
|
@@ -24,8 +24,8 @@ from .dataset import rest_operations as dataset_rest_operations
|
|
|
24
24
|
from .experiment import experiments_client
|
|
25
25
|
from .experiment import helpers as experiment_helpers
|
|
26
26
|
from .experiment import rest_operations as experiment_rest_operations
|
|
27
|
-
from .
|
|
28
|
-
from .prompt
|
|
27
|
+
from . import prompt as prompt_module
|
|
28
|
+
from .prompt import client as prompt_client
|
|
29
29
|
from .threads import threads_client
|
|
30
30
|
from .trace import migration as trace_migration, trace_client
|
|
31
31
|
from .. import (
|
|
@@ -917,8 +917,8 @@ class Opik:
|
|
|
917
917
|
dataset_name: str,
|
|
918
918
|
name: Optional[str] = None,
|
|
919
919
|
experiment_config: Optional[Dict[str, Any]] = None,
|
|
920
|
-
prompt: Optional[
|
|
921
|
-
prompts: Optional[List[
|
|
920
|
+
prompt: Optional[prompt_module.base_prompt.BasePrompt] = None,
|
|
921
|
+
prompts: Optional[List[prompt_module.base_prompt.BasePrompt]] = None,
|
|
922
922
|
type: Literal["regular", "trial", "mini-batch"] = "regular",
|
|
923
923
|
optimization_id: Optional[str] = None,
|
|
924
924
|
) -> experiment.Experiment:
|
|
@@ -972,6 +972,42 @@ class Opik:
|
|
|
972
972
|
|
|
973
973
|
return experiment_
|
|
974
974
|
|
|
975
|
+
def update_experiment(
|
|
976
|
+
self,
|
|
977
|
+
id: str,
|
|
978
|
+
name: Optional[str] = None,
|
|
979
|
+
experiment_config: Optional[Dict[str, Any]] = None,
|
|
980
|
+
) -> None:
|
|
981
|
+
"""
|
|
982
|
+
Update an experiment's name and/or configuration.
|
|
983
|
+
|
|
984
|
+
Args:
|
|
985
|
+
id: The experiment ID.
|
|
986
|
+
name: The new name for the experiment. If None, the name will not be updated.
|
|
987
|
+
experiment_config: The new configuration for the experiment. If None, the configuration will not be updated.
|
|
988
|
+
|
|
989
|
+
Raises:
|
|
990
|
+
ValueError: if id is None or empty, or if both name and experiment_config are None
|
|
991
|
+
"""
|
|
992
|
+
if not id:
|
|
993
|
+
raise ValueError(
|
|
994
|
+
f"id must be provided and can not be None or empty, id: {id}"
|
|
995
|
+
)
|
|
996
|
+
|
|
997
|
+
if name is None and experiment_config is None:
|
|
998
|
+
raise ValueError(
|
|
999
|
+
"At least one of 'name' or 'experiment_config' must be provided"
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
# Only include parameters that are provided to avoid clearing fields
|
|
1003
|
+
request_params: Dict[str, Any] = {}
|
|
1004
|
+
if name is not None:
|
|
1005
|
+
request_params["name"] = name
|
|
1006
|
+
if experiment_config is not None:
|
|
1007
|
+
request_params["metadata"] = experiment_config
|
|
1008
|
+
|
|
1009
|
+
self._rest_client.experiments.update_experiment(id, **request_params)
|
|
1010
|
+
|
|
975
1011
|
def get_experiment_by_name(self, name: str) -> experiment.Experiment:
|
|
976
1012
|
"""
|
|
977
1013
|
Returns an existing experiment by its name.
|
|
@@ -991,7 +1027,7 @@ class Opik:
|
|
|
991
1027
|
|
|
992
1028
|
return experiment.Experiment(
|
|
993
1029
|
id=experiment_public.id,
|
|
994
|
-
name=name,
|
|
1030
|
+
name=experiment_public.name,
|
|
995
1031
|
dataset_name=experiment_public.dataset_name,
|
|
996
1032
|
rest_client=self._rest_client,
|
|
997
1033
|
streamer=self._streamer,
|
|
@@ -1000,10 +1036,11 @@ class Opik:
|
|
|
1000
1036
|
|
|
1001
1037
|
def get_experiments_by_name(self, name: str) -> List[experiment.Experiment]:
|
|
1002
1038
|
"""
|
|
1003
|
-
Returns a list of existing experiments
|
|
1039
|
+
Returns a list of existing experiments containing the given string in their name.
|
|
1040
|
+
Search is case-insensitive.
|
|
1004
1041
|
|
|
1005
1042
|
Args:
|
|
1006
|
-
name: The
|
|
1043
|
+
name: The string to search for in the experiment names.
|
|
1007
1044
|
|
|
1008
1045
|
Returns:
|
|
1009
1046
|
List[experiment.Experiment]: List of existing experiments.
|
|
@@ -1016,7 +1053,7 @@ class Opik:
|
|
|
1016
1053
|
for public_experiment in experiments_public:
|
|
1017
1054
|
experiment_ = experiment.Experiment(
|
|
1018
1055
|
id=public_experiment.id,
|
|
1019
|
-
name=name,
|
|
1056
|
+
name=public_experiment.name,
|
|
1020
1057
|
dataset_name=public_experiment.dataset_name,
|
|
1021
1058
|
rest_client=self._rest_client,
|
|
1022
1059
|
streamer=self._streamer,
|
|
@@ -1357,70 +1394,188 @@ class Opik:
|
|
|
1357
1394
|
name: str,
|
|
1358
1395
|
prompt: str,
|
|
1359
1396
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1360
|
-
type: PromptType = PromptType.MUSTACHE,
|
|
1361
|
-
) -> Prompt:
|
|
1397
|
+
type: prompt_module.PromptType = prompt_module.PromptType.MUSTACHE,
|
|
1398
|
+
) -> prompt_module.Prompt:
|
|
1362
1399
|
"""
|
|
1363
|
-
Creates a new prompt with the given name and template.
|
|
1364
|
-
If a prompt with the same name already exists, it will create a new version of the existing prompt if the templates differ.
|
|
1400
|
+
Creates a new text prompt with the given name and template.
|
|
1401
|
+
If a text prompt with the same name already exists, it will create a new version of the existing prompt if the templates differ.
|
|
1365
1402
|
|
|
1366
1403
|
Parameters:
|
|
1367
1404
|
name: The name of the prompt.
|
|
1368
1405
|
prompt: The template content of the prompt.
|
|
1369
1406
|
metadata: Optional metadata to be included in the prompt.
|
|
1407
|
+
type: The template type (MUSTACHE or JINJA2).
|
|
1370
1408
|
|
|
1371
1409
|
Returns:
|
|
1372
1410
|
A Prompt object containing details of the created or retrieved prompt.
|
|
1373
1411
|
|
|
1374
1412
|
Raises:
|
|
1375
|
-
|
|
1413
|
+
PromptTemplateStructureMismatch: If a chat prompt with the same name already exists (template structure is immutable).
|
|
1414
|
+
ApiError: If there is an error during the creation of the prompt.
|
|
1376
1415
|
"""
|
|
1377
|
-
|
|
1378
|
-
prompt_version =
|
|
1416
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1417
|
+
prompt_version = prompt_client_.create_prompt(
|
|
1379
1418
|
name=name, prompt=prompt, metadata=metadata, type=type
|
|
1380
1419
|
)
|
|
1381
|
-
return Prompt.from_fern_prompt_version(name, prompt_version)
|
|
1420
|
+
return prompt_module.Prompt.from_fern_prompt_version(name, prompt_version)
|
|
1421
|
+
|
|
1422
|
+
def create_chat_prompt(
|
|
1423
|
+
self,
|
|
1424
|
+
name: str,
|
|
1425
|
+
messages: List[Dict[str, Any]],
|
|
1426
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
1427
|
+
type: prompt_module.PromptType = prompt_module.PromptType.MUSTACHE,
|
|
1428
|
+
) -> prompt_module.ChatPrompt:
|
|
1429
|
+
"""
|
|
1430
|
+
Creates a new chat prompt with the given name and message templates.
|
|
1431
|
+
If a chat prompt with the same name already exists, it will create a new version if the messages differ.
|
|
1432
|
+
|
|
1433
|
+
Parameters:
|
|
1434
|
+
name: The name of the chat prompt.
|
|
1435
|
+
messages: List of message dictionaries with 'role' and 'content' fields.
|
|
1436
|
+
metadata: Optional metadata to be included in the prompt.
|
|
1437
|
+
type: The template type (MUSTACHE or JINJA2).
|
|
1438
|
+
|
|
1439
|
+
Returns:
|
|
1440
|
+
A ChatPrompt object containing details of the created or retrieved chat prompt.
|
|
1441
|
+
|
|
1442
|
+
Raises:
|
|
1443
|
+
PromptTemplateStructureMismatch: If a text prompt with the same name already exists (template structure is immutable).
|
|
1444
|
+
ApiError: If there is an error during the creation of the prompt.
|
|
1445
|
+
"""
|
|
1446
|
+
return prompt_module.ChatPrompt(
|
|
1447
|
+
name=name, messages=messages, metadata=metadata, type=type
|
|
1448
|
+
)
|
|
1382
1449
|
|
|
1383
1450
|
def get_prompt(
|
|
1384
1451
|
self,
|
|
1385
1452
|
name: str,
|
|
1386
1453
|
commit: Optional[str] = None,
|
|
1387
|
-
) -> Optional[Prompt]:
|
|
1454
|
+
) -> Optional[prompt_module.Prompt]:
|
|
1388
1455
|
"""
|
|
1389
|
-
Retrieve
|
|
1456
|
+
Retrieve a text prompt by name and optional commit version.
|
|
1457
|
+
|
|
1458
|
+
This method only returns text prompts.
|
|
1390
1459
|
|
|
1391
1460
|
Parameters:
|
|
1392
1461
|
name: The name of the prompt.
|
|
1393
1462
|
commit: An optional commit version of the prompt. If not provided, the latest version is retrieved.
|
|
1394
1463
|
|
|
1395
1464
|
Returns:
|
|
1396
|
-
Prompt: The details of the specified prompt.
|
|
1465
|
+
Prompt: The details of the specified text prompt, or None if not found.
|
|
1466
|
+
|
|
1467
|
+
Raises:
|
|
1468
|
+
PromptTemplateStructureMismatch: If the prompt exists but is a chat prompt (template structure mismatch).
|
|
1397
1469
|
"""
|
|
1398
|
-
|
|
1399
|
-
fern_prompt_version =
|
|
1470
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1471
|
+
fern_prompt_version = prompt_client_.get_prompt(
|
|
1472
|
+
name=name, commit=commit, raise_if_not_template_structure="text"
|
|
1473
|
+
)
|
|
1474
|
+
|
|
1400
1475
|
if fern_prompt_version is None:
|
|
1401
1476
|
return None
|
|
1402
1477
|
|
|
1403
|
-
return Prompt.from_fern_prompt_version(name, fern_prompt_version)
|
|
1478
|
+
return prompt_module.Prompt.from_fern_prompt_version(name, fern_prompt_version)
|
|
1404
1479
|
|
|
1405
|
-
def
|
|
1480
|
+
def get_chat_prompt(
|
|
1481
|
+
self,
|
|
1482
|
+
name: str,
|
|
1483
|
+
commit: Optional[str] = None,
|
|
1484
|
+
) -> Optional[prompt_module.ChatPrompt]:
|
|
1406
1485
|
"""
|
|
1407
|
-
Retrieve
|
|
1486
|
+
Retrieve a chat prompt by name and optional commit version.
|
|
1487
|
+
|
|
1488
|
+
This method only returns chat prompts.
|
|
1489
|
+
|
|
1490
|
+
Parameters:
|
|
1491
|
+
name: The name of the prompt.
|
|
1492
|
+
commit: An optional commit version of the prompt. If not provided, the latest version is retrieved.
|
|
1493
|
+
|
|
1494
|
+
Returns:
|
|
1495
|
+
ChatPrompt: The details of the specified chat prompt, or None if not found.
|
|
1496
|
+
|
|
1497
|
+
Raises:
|
|
1498
|
+
PromptTemplateStructureMismatch: If the prompt exists but is a text prompt (template structure mismatch).
|
|
1499
|
+
"""
|
|
1500
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1501
|
+
fern_prompt_version = prompt_client_.get_prompt(
|
|
1502
|
+
name=name, commit=commit, raise_if_not_template_structure="chat"
|
|
1503
|
+
)
|
|
1504
|
+
|
|
1505
|
+
if fern_prompt_version is None:
|
|
1506
|
+
return None
|
|
1507
|
+
|
|
1508
|
+
return prompt_module.ChatPrompt.from_fern_prompt_version(
|
|
1509
|
+
name, fern_prompt_version
|
|
1510
|
+
)
|
|
1511
|
+
|
|
1512
|
+
def get_prompt_history(self, name: str) -> List[prompt_module.Prompt]:
|
|
1513
|
+
"""
|
|
1514
|
+
Retrieve all text prompt versions history for a given prompt name.
|
|
1408
1515
|
|
|
1409
1516
|
Parameters:
|
|
1410
1517
|
name: The name of the prompt.
|
|
1411
1518
|
|
|
1412
1519
|
Returns:
|
|
1413
|
-
List[Prompt]: A list of Prompt instances for the given name.
|
|
1520
|
+
List[Prompt]: A list of text Prompt instances for the given name, or an empty list if not found.
|
|
1521
|
+
|
|
1522
|
+
Raises:
|
|
1523
|
+
PromptTemplateStructureMismatch: If the prompt exists but is a chat prompt (template structure mismatch).
|
|
1414
1524
|
"""
|
|
1415
|
-
|
|
1416
|
-
|
|
1525
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1526
|
+
|
|
1527
|
+
# First, validate that this is a text prompt by trying to get the latest version
|
|
1528
|
+
# Let PromptTemplateStructureMismatch exception propagate - this is a hard error
|
|
1529
|
+
latest_version = prompt_client_.get_prompt(
|
|
1530
|
+
name=name, raise_if_not_template_structure="text"
|
|
1531
|
+
)
|
|
1532
|
+
|
|
1533
|
+
if latest_version is None:
|
|
1534
|
+
return []
|
|
1535
|
+
|
|
1536
|
+
# Now get all versions (we know it's a text prompt)
|
|
1537
|
+
fern_prompt_versions = prompt_client_.get_all_prompt_versions(name=name)
|
|
1538
|
+
|
|
1417
1539
|
result = [
|
|
1418
|
-
Prompt.from_fern_prompt_version(name, version)
|
|
1540
|
+
prompt_module.Prompt.from_fern_prompt_version(name, version)
|
|
1419
1541
|
for version in fern_prompt_versions
|
|
1420
1542
|
]
|
|
1421
1543
|
return result
|
|
1422
1544
|
|
|
1423
|
-
def
|
|
1545
|
+
def get_chat_prompt_history(self, name: str) -> List[prompt_module.ChatPrompt]:
|
|
1546
|
+
"""
|
|
1547
|
+
Retrieve all chat prompt versions history for a given prompt name.
|
|
1548
|
+
|
|
1549
|
+
Parameters:
|
|
1550
|
+
name: The name of the prompt.
|
|
1551
|
+
|
|
1552
|
+
Returns:
|
|
1553
|
+
List[ChatPrompt]: A list of ChatPrompt instances for the given name, or an empty list if not found.
|
|
1554
|
+
|
|
1555
|
+
Raises:
|
|
1556
|
+
PromptTemplateStructureMismatch: If the prompt exists but is a text prompt (template structure mismatch).
|
|
1557
|
+
"""
|
|
1558
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1559
|
+
|
|
1560
|
+
# First, validate that this is a chat prompt by trying to get the latest version
|
|
1561
|
+
# Let PromptTemplateStructureMismatch exception propagate - this is a hard error
|
|
1562
|
+
latest_version = prompt_client_.get_prompt(
|
|
1563
|
+
name=name, raise_if_not_template_structure="chat"
|
|
1564
|
+
)
|
|
1565
|
+
|
|
1566
|
+
if latest_version is None:
|
|
1567
|
+
return []
|
|
1568
|
+
|
|
1569
|
+
# Now get all versions (we know it's a chat prompt)
|
|
1570
|
+
fern_prompt_versions = prompt_client_.get_all_prompt_versions(name=name)
|
|
1571
|
+
|
|
1572
|
+
result = [
|
|
1573
|
+
prompt_module.ChatPrompt.from_fern_prompt_version(name, version)
|
|
1574
|
+
for version in fern_prompt_versions
|
|
1575
|
+
]
|
|
1576
|
+
return result
|
|
1577
|
+
|
|
1578
|
+
def get_all_prompts(self, name: str) -> List[prompt_module.Prompt]:
|
|
1424
1579
|
"""
|
|
1425
1580
|
DEPRECATED: Please use Opik.get_prompt_history() instead.
|
|
1426
1581
|
Retrieve all the prompt versions history for a given prompt name.
|
|
@@ -1429,16 +1584,18 @@ class Opik:
|
|
|
1429
1584
|
name: The name of the prompt.
|
|
1430
1585
|
|
|
1431
1586
|
Returns:
|
|
1432
|
-
List[Prompt]: A list of Prompt instances for the given name.
|
|
1587
|
+
List[prompt_module.Prompt]: A list of Prompt instances for the given name.
|
|
1433
1588
|
"""
|
|
1434
1589
|
LOGGER.warning(
|
|
1435
1590
|
"Opik.get_all_prompts() is deprecated. Please use Opik.get_prompt_history() instead."
|
|
1436
1591
|
)
|
|
1437
1592
|
return self.get_prompt_history(name)
|
|
1438
1593
|
|
|
1439
|
-
def search_prompts(
|
|
1594
|
+
def search_prompts(
|
|
1595
|
+
self, filter_string: Optional[str] = None
|
|
1596
|
+
) -> List[Union[prompt_module.Prompt, prompt_module.ChatPrompt]]:
|
|
1440
1597
|
"""
|
|
1441
|
-
Retrieve the latest prompt versions for the given search parameters.
|
|
1598
|
+
Retrieve the latest prompt versions (both string and chat prompts) for the given search parameters.
|
|
1442
1599
|
|
|
1443
1600
|
Parameters:
|
|
1444
1601
|
filter_string: A filter string to narrow down the search using Opik Query Language (OQL).
|
|
@@ -1448,11 +1605,13 @@ class Opik:
|
|
|
1448
1605
|
- `id`, `name`: String fields
|
|
1449
1606
|
- `tags`: List field (use "contains" operator only)
|
|
1450
1607
|
- `created_by`: String field
|
|
1608
|
+
- `template_structure`: String field ("string" or "chat")
|
|
1451
1609
|
|
|
1452
1610
|
Supported operators by column:
|
|
1453
1611
|
- `id`: =, !=, contains, not_contains, starts_with, ends_with, >, <
|
|
1454
1612
|
- `name`: =, !=, contains, not_contains, starts_with, ends_with, >, <
|
|
1455
1613
|
- `created_by`: =, !=, contains, not_contains, starts_with, ends_with, >, <
|
|
1614
|
+
- `template_structure`: =, !=
|
|
1456
1615
|
- `tags`: contains (only)
|
|
1457
1616
|
|
|
1458
1617
|
Examples:
|
|
@@ -1461,23 +1620,36 @@ class Opik:
|
|
|
1461
1620
|
- `name contains "summary"` - Filter by name substring
|
|
1462
1621
|
- `created_by = "user@example.com"` - Filter by creator
|
|
1463
1622
|
- `id starts_with "prompt_"` - Filter by ID prefix
|
|
1623
|
+
- `template_structure = "text"` - Only text prompts
|
|
1624
|
+
- `template_structure = "chat"` - Only chat prompts
|
|
1464
1625
|
|
|
1465
|
-
If not provided, all prompts
|
|
1626
|
+
If not provided, all prompts (both text and chat) will be returned.
|
|
1466
1627
|
|
|
1467
1628
|
Returns:
|
|
1468
|
-
List[Prompt]: A list of Prompt instances found.
|
|
1469
|
-
"""
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
prompts: List[Prompt] = [
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1629
|
+
List[Union[Prompt, ChatPrompt]]: A list of Prompt and/or ChatPrompt instances found.
|
|
1630
|
+
"""
|
|
1631
|
+
oql = opik_query_language.OpikQueryLanguage(filter_string or "")
|
|
1632
|
+
parsed_filters = oql.get_filter_expressions()
|
|
1633
|
+
|
|
1634
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1635
|
+
search_results = prompt_client_.search_prompts(parsed_filters=parsed_filters)
|
|
1636
|
+
|
|
1637
|
+
# Convert to Prompt or ChatPrompt objects based on template_structure
|
|
1638
|
+
prompts: List[Union[prompt_module.Prompt, prompt_module.ChatPrompt]] = []
|
|
1639
|
+
for result in search_results:
|
|
1640
|
+
if result.template_structure == "chat":
|
|
1641
|
+
prompts.append(
|
|
1642
|
+
prompt_module.ChatPrompt.from_fern_prompt_version(
|
|
1643
|
+
result.name, result.prompt_version_detail
|
|
1644
|
+
)
|
|
1645
|
+
)
|
|
1646
|
+
else:
|
|
1647
|
+
prompts.append(
|
|
1648
|
+
prompt_module.Prompt.from_fern_prompt_version(
|
|
1649
|
+
result.name, result.prompt_version_detail
|
|
1650
|
+
)
|
|
1651
|
+
)
|
|
1652
|
+
|
|
1481
1653
|
return prompts
|
|
1482
1654
|
|
|
1483
1655
|
def create_optimization(
|
|
@@ -29,6 +29,7 @@ COLUMNS = {
|
|
|
29
29
|
"type": "string",
|
|
30
30
|
"model": "string",
|
|
31
31
|
"provider": "string",
|
|
32
|
+
"template_structure": "string",
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
SUPPORTED_OPERATORS = {
|
|
@@ -121,6 +122,14 @@ SUPPORTED_OPERATORS = {
|
|
|
121
122
|
">",
|
|
122
123
|
"<",
|
|
123
124
|
],
|
|
125
|
+
"template_structure": [
|
|
126
|
+
"=",
|
|
127
|
+
"contains",
|
|
128
|
+
"not_contains",
|
|
129
|
+
"starts_with",
|
|
130
|
+
"ends_with",
|
|
131
|
+
"!=",
|
|
132
|
+
],
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
from .prompt import Prompt
|
|
1
|
+
from .text.prompt import Prompt
|
|
2
|
+
from .text.prompt_template import PromptTemplate
|
|
3
|
+
from .chat.chat_prompt import ChatPrompt
|
|
4
|
+
from .chat.chat_prompt_template import ChatPromptTemplate
|
|
2
5
|
from .types import PromptType
|
|
3
|
-
from .
|
|
6
|
+
from .base_prompt import BasePrompt
|
|
7
|
+
from .base_prompt_template import BasePromptTemplate
|
|
4
8
|
|
|
5
9
|
__all__ = [
|
|
6
|
-
"Prompt",
|
|
7
10
|
"PromptType",
|
|
11
|
+
"Prompt",
|
|
12
|
+
"ChatPrompt",
|
|
13
|
+
"PromptTemplate",
|
|
8
14
|
"ChatPromptTemplate",
|
|
15
|
+
"BasePrompt",
|
|
16
|
+
"BasePromptTemplate",
|
|
9
17
|
]
|