opik 1.9.39__py3-none-any.whl → 1.9.86__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. opik/api_objects/attachment/attachment_context.py +36 -0
  2. opik/api_objects/attachment/attachments_extractor.py +153 -0
  3. opik/api_objects/attachment/client.py +1 -0
  4. opik/api_objects/attachment/converters.py +2 -0
  5. opik/api_objects/attachment/decoder.py +18 -0
  6. opik/api_objects/attachment/decoder_base64.py +83 -0
  7. opik/api_objects/attachment/decoder_helpers.py +137 -0
  8. opik/api_objects/constants.py +2 -0
  9. opik/api_objects/dataset/dataset.py +133 -40
  10. opik/api_objects/dataset/rest_operations.py +2 -0
  11. opik/api_objects/experiment/experiment.py +6 -0
  12. opik/api_objects/helpers.py +8 -4
  13. opik/api_objects/local_recording.py +6 -5
  14. opik/api_objects/observation_data.py +101 -0
  15. opik/api_objects/opik_client.py +78 -45
  16. opik/api_objects/opik_query_language.py +9 -3
  17. opik/api_objects/prompt/chat/chat_prompt.py +18 -1
  18. opik/api_objects/prompt/client.py +8 -1
  19. opik/api_objects/span/span_data.py +3 -88
  20. opik/api_objects/threads/threads_client.py +7 -4
  21. opik/api_objects/trace/trace_data.py +3 -74
  22. opik/api_objects/validation_helpers.py +3 -3
  23. opik/cli/exports/__init__.py +131 -0
  24. opik/cli/exports/dataset.py +278 -0
  25. opik/cli/exports/experiment.py +784 -0
  26. opik/cli/exports/project.py +685 -0
  27. opik/cli/exports/prompt.py +578 -0
  28. opik/cli/exports/utils.py +406 -0
  29. opik/cli/harbor.py +39 -0
  30. opik/cli/imports/__init__.py +439 -0
  31. opik/cli/imports/dataset.py +143 -0
  32. opik/cli/imports/experiment.py +1192 -0
  33. opik/cli/imports/project.py +262 -0
  34. opik/cli/imports/prompt.py +177 -0
  35. opik/cli/imports/utils.py +280 -0
  36. opik/cli/main.py +14 -12
  37. opik/config.py +12 -1
  38. opik/datetime_helpers.py +12 -0
  39. opik/decorator/arguments_helpers.py +4 -1
  40. opik/decorator/base_track_decorator.py +111 -37
  41. opik/decorator/context_manager/span_context_manager.py +5 -1
  42. opik/decorator/generator_wrappers.py +5 -4
  43. opik/decorator/span_creation_handler.py +13 -4
  44. opik/evaluation/engine/engine.py +111 -28
  45. opik/evaluation/engine/evaluation_tasks_executor.py +71 -19
  46. opik/evaluation/evaluator.py +12 -0
  47. opik/evaluation/metrics/conversation/llm_judges/conversational_coherence/metric.py +3 -1
  48. opik/evaluation/metrics/conversation/llm_judges/session_completeness/metric.py +3 -1
  49. opik/evaluation/metrics/conversation/llm_judges/user_frustration/metric.py +3 -1
  50. opik/evaluation/metrics/heuristics/equals.py +11 -7
  51. opik/evaluation/metrics/llm_judges/answer_relevance/metric.py +3 -1
  52. opik/evaluation/metrics/llm_judges/context_precision/metric.py +3 -1
  53. opik/evaluation/metrics/llm_judges/context_recall/metric.py +3 -1
  54. opik/evaluation/metrics/llm_judges/factuality/metric.py +1 -1
  55. opik/evaluation/metrics/llm_judges/g_eval/metric.py +3 -1
  56. opik/evaluation/metrics/llm_judges/hallucination/metric.py +3 -1
  57. opik/evaluation/metrics/llm_judges/moderation/metric.py +3 -1
  58. opik/evaluation/metrics/llm_judges/structure_output_compliance/metric.py +3 -1
  59. opik/evaluation/metrics/llm_judges/syc_eval/metric.py +4 -2
  60. opik/evaluation/metrics/llm_judges/trajectory_accuracy/metric.py +3 -1
  61. opik/evaluation/metrics/llm_judges/usefulness/metric.py +3 -1
  62. opik/evaluation/metrics/ragas_metric.py +43 -23
  63. opik/evaluation/models/litellm/litellm_chat_model.py +7 -2
  64. opik/evaluation/models/litellm/util.py +4 -20
  65. opik/evaluation/models/models_factory.py +19 -5
  66. opik/evaluation/rest_operations.py +3 -3
  67. opik/evaluation/threads/helpers.py +3 -2
  68. opik/file_upload/file_uploader.py +13 -0
  69. opik/file_upload/upload_options.py +2 -0
  70. opik/integrations/adk/legacy_opik_tracer.py +9 -11
  71. opik/integrations/adk/opik_tracer.py +2 -2
  72. opik/integrations/adk/patchers/adk_otel_tracer/opik_adk_otel_tracer.py +2 -2
  73. opik/integrations/dspy/callback.py +100 -14
  74. opik/integrations/dspy/parsers.py +168 -0
  75. opik/integrations/harbor/__init__.py +17 -0
  76. opik/integrations/harbor/experiment_service.py +269 -0
  77. opik/integrations/harbor/opik_tracker.py +528 -0
  78. opik/integrations/haystack/opik_tracer.py +2 -2
  79. opik/integrations/langchain/__init__.py +15 -2
  80. opik/integrations/langchain/langgraph_tracer_injector.py +88 -0
  81. opik/integrations/langchain/opik_tracer.py +258 -160
  82. opik/integrations/langchain/provider_usage_extractors/langchain_run_helpers/helpers.py +7 -4
  83. opik/integrations/llama_index/callback.py +43 -6
  84. opik/integrations/openai/agents/opik_tracing_processor.py +8 -10
  85. opik/integrations/openai/opik_tracker.py +99 -4
  86. opik/integrations/openai/videos/__init__.py +9 -0
  87. opik/integrations/openai/videos/binary_response_write_to_file_decorator.py +88 -0
  88. opik/integrations/openai/videos/videos_create_decorator.py +159 -0
  89. opik/integrations/openai/videos/videos_download_decorator.py +110 -0
  90. opik/message_processing/batching/base_batcher.py +14 -21
  91. opik/message_processing/batching/batch_manager.py +22 -10
  92. opik/message_processing/batching/batchers.py +32 -40
  93. opik/message_processing/batching/flushing_thread.py +0 -3
  94. opik/message_processing/emulation/emulator_message_processor.py +36 -1
  95. opik/message_processing/emulation/models.py +21 -0
  96. opik/message_processing/messages.py +9 -0
  97. opik/message_processing/preprocessing/__init__.py +0 -0
  98. opik/message_processing/preprocessing/attachments_preprocessor.py +70 -0
  99. opik/message_processing/preprocessing/batching_preprocessor.py +53 -0
  100. opik/message_processing/preprocessing/constants.py +1 -0
  101. opik/message_processing/preprocessing/file_upload_preprocessor.py +38 -0
  102. opik/message_processing/preprocessing/preprocessor.py +36 -0
  103. opik/message_processing/processors/__init__.py +0 -0
  104. opik/message_processing/processors/attachments_extraction_processor.py +146 -0
  105. opik/message_processing/{message_processors.py → processors/message_processors.py} +15 -1
  106. opik/message_processing/{message_processors_chain.py → processors/message_processors_chain.py} +3 -2
  107. opik/message_processing/{online_message_processor.py → processors/online_message_processor.py} +11 -9
  108. opik/message_processing/queue_consumer.py +4 -2
  109. opik/message_processing/streamer.py +71 -33
  110. opik/message_processing/streamer_constructors.py +36 -8
  111. opik/plugins/pytest/experiment_runner.py +1 -1
  112. opik/plugins/pytest/hooks.py +5 -3
  113. opik/rest_api/__init__.py +42 -0
  114. opik/rest_api/datasets/client.py +321 -123
  115. opik/rest_api/datasets/raw_client.py +470 -145
  116. opik/rest_api/experiments/client.py +26 -0
  117. opik/rest_api/experiments/raw_client.py +26 -0
  118. opik/rest_api/llm_provider_key/client.py +4 -4
  119. opik/rest_api/llm_provider_key/raw_client.py +4 -4
  120. opik/rest_api/llm_provider_key/types/provider_api_key_write_provider.py +2 -1
  121. opik/rest_api/manual_evaluation/client.py +101 -0
  122. opik/rest_api/manual_evaluation/raw_client.py +172 -0
  123. opik/rest_api/optimizations/client.py +0 -166
  124. opik/rest_api/optimizations/raw_client.py +0 -248
  125. opik/rest_api/projects/client.py +9 -0
  126. opik/rest_api/projects/raw_client.py +13 -0
  127. opik/rest_api/projects/types/project_metric_request_public_metric_type.py +4 -0
  128. opik/rest_api/prompts/client.py +130 -2
  129. opik/rest_api/prompts/raw_client.py +175 -0
  130. opik/rest_api/traces/client.py +101 -0
  131. opik/rest_api/traces/raw_client.py +120 -0
  132. opik/rest_api/types/__init__.py +50 -0
  133. opik/rest_api/types/audio_url.py +19 -0
  134. opik/rest_api/types/audio_url_public.py +19 -0
  135. opik/rest_api/types/audio_url_write.py +19 -0
  136. opik/rest_api/types/automation_rule_evaluator.py +38 -2
  137. opik/rest_api/types/automation_rule_evaluator_object_object_public.py +33 -2
  138. opik/rest_api/types/automation_rule_evaluator_public.py +33 -2
  139. opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python.py +22 -0
  140. opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python_public.py +22 -0
  141. opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python_write.py +22 -0
  142. opik/rest_api/types/automation_rule_evaluator_update.py +27 -1
  143. opik/rest_api/types/automation_rule_evaluator_update_span_user_defined_metric_python.py +22 -0
  144. opik/rest_api/types/automation_rule_evaluator_write.py +27 -1
  145. opik/rest_api/types/dataset.py +2 -0
  146. opik/rest_api/types/dataset_item.py +1 -1
  147. opik/rest_api/types/dataset_item_batch.py +4 -0
  148. opik/rest_api/types/dataset_item_changes_public.py +5 -0
  149. opik/rest_api/types/dataset_item_compare.py +1 -1
  150. opik/rest_api/types/dataset_item_filter.py +4 -0
  151. opik/rest_api/types/dataset_item_page_compare.py +0 -1
  152. opik/rest_api/types/dataset_item_page_public.py +0 -1
  153. opik/rest_api/types/dataset_item_public.py +1 -1
  154. opik/rest_api/types/dataset_public.py +2 -0
  155. opik/rest_api/types/dataset_version_public.py +10 -0
  156. opik/rest_api/types/dataset_version_summary.py +46 -0
  157. opik/rest_api/types/dataset_version_summary_public.py +46 -0
  158. opik/rest_api/types/experiment.py +9 -0
  159. opik/rest_api/types/experiment_public.py +9 -0
  160. opik/rest_api/types/group_content_with_aggregations.py +1 -0
  161. opik/rest_api/types/llm_as_judge_message_content.py +2 -0
  162. opik/rest_api/types/llm_as_judge_message_content_public.py +2 -0
  163. opik/rest_api/types/llm_as_judge_message_content_write.py +2 -0
  164. opik/rest_api/types/manual_evaluation_request_entity_type.py +1 -1
  165. opik/rest_api/types/project.py +1 -0
  166. opik/rest_api/types/project_detailed.py +1 -0
  167. opik/rest_api/types/project_metric_response_public_metric_type.py +4 -0
  168. opik/rest_api/types/project_reference.py +31 -0
  169. opik/rest_api/types/project_reference_public.py +31 -0
  170. opik/rest_api/types/project_stats_summary_item.py +1 -0
  171. opik/rest_api/types/prompt_version.py +1 -0
  172. opik/rest_api/types/prompt_version_detail.py +1 -0
  173. opik/rest_api/types/prompt_version_page_public.py +5 -0
  174. opik/rest_api/types/prompt_version_public.py +1 -0
  175. opik/rest_api/types/prompt_version_update.py +33 -0
  176. opik/rest_api/types/provider_api_key.py +5 -1
  177. opik/rest_api/types/provider_api_key_provider.py +2 -1
  178. opik/rest_api/types/provider_api_key_public.py +5 -1
  179. opik/rest_api/types/provider_api_key_public_provider.py +2 -1
  180. opik/rest_api/types/service_toggles_config.py +11 -1
  181. opik/rest_api/types/span_user_defined_metric_python_code.py +20 -0
  182. opik/rest_api/types/span_user_defined_metric_python_code_public.py +20 -0
  183. opik/rest_api/types/span_user_defined_metric_python_code_write.py +20 -0
  184. opik/types.py +36 -0
  185. opik/validation/chat_prompt_messages.py +241 -0
  186. opik/validation/feedback_score.py +3 -3
  187. opik/validation/validator.py +28 -0
  188. {opik-1.9.39.dist-info → opik-1.9.86.dist-info}/METADATA +7 -7
  189. {opik-1.9.39.dist-info → opik-1.9.86.dist-info}/RECORD +193 -142
  190. opik/cli/export.py +0 -791
  191. opik/cli/import_command.py +0 -575
  192. {opik-1.9.39.dist-info → opik-1.9.86.dist-info}/WHEEL +0 -0
  193. {opik-1.9.39.dist-info → opik-1.9.86.dist-info}/entry_points.txt +0 -0
  194. {opik-1.9.39.dist-info → opik-1.9.86.dist-info}/licenses/LICENSE +0 -0
  195. {opik-1.9.39.dist-info → opik-1.9.86.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@ import abc
2
2
  import logging
3
3
  from typing import List, Optional, TypeVar, Type
4
4
 
5
- from . import messages
5
+ from .. import messages
6
6
 
7
7
  import opik.exceptions
8
8
 
@@ -76,3 +76,17 @@ class ChainedMessageProcessor(BaseMessageProcessor):
76
76
  if isinstance(processor, processor_type):
77
77
  return processor
78
78
  return None
79
+
80
+ def add_first(self, processor: BaseMessageProcessor) -> None:
81
+ """
82
+ Inserts a processor at the first position in the list of processors.
83
+
84
+ This method allows prioritizing a given processor by placing it
85
+ at the beginning of the internal processor list. As a result, the
86
+ provided processor will be executed before others.
87
+
88
+ Args:
89
+ processor: The message processor to be
90
+ added to the front of the processor list.
91
+ """
92
+ self._processors.insert(0, processor)
@@ -1,12 +1,13 @@
1
1
  import logging
2
2
  from typing import Optional
3
3
 
4
+ from opik.rest_api import client as rest_api_client
5
+
4
6
  from . import (
5
7
  message_processors,
6
8
  online_message_processor,
7
9
  )
8
- from .emulation import local_emulator_message_processor
9
- from ..rest_api import client as rest_api_client
10
+ from ..emulation import local_emulator_message_processor
10
11
 
11
12
 
12
13
  LOGGER = logging.getLogger(__name__)
@@ -4,21 +4,18 @@ from typing import Callable, Dict, Type, Any
4
4
  import pydantic
5
5
  import tenacity
6
6
 
7
- from . import encoder_helpers, messages, message_processors
8
- from .. import (
9
- exceptions as exceptions,
10
- logging_messages as logging_messages,
11
- dict_utils,
12
- )
13
- from ..rate_limit import rate_limit
14
- from ..rest_api import client as rest_api_client, core as rest_api_core
15
- from ..rest_api.types import (
7
+ from opik import dict_utils, exceptions, logging_messages
8
+ from opik.rate_limit import rate_limit
9
+ from opik.rest_api import client as rest_api_client, core as rest_api_core
10
+ from opik.rest_api.types import (
16
11
  feedback_score_batch_item,
17
12
  feedback_score_batch_item_thread,
18
13
  guardrail,
19
14
  experiment_item,
20
15
  )
21
16
 
17
+ from . import message_processors
18
+ from .. import encoder_helpers, messages
22
19
 
23
20
  LOGGER = logging.getLogger(__name__)
24
21
 
@@ -49,6 +46,7 @@ class OpikMessageProcessor(message_processors.BaseMessageProcessor):
49
46
  messages.CreateTraceBatchMessage: self._process_create_traces_batch_message, # type: ignore
50
47
  messages.GuardrailBatchMessage: self._process_guardrail_batch_message, # type: ignore
51
48
  messages.CreateExperimentItemsBatchMessage: self._process_create_experiment_items_batch_message, # type: ignore
49
+ messages.AttachmentSupportingMessage: self._noop_handler, # type: ignore
52
50
  }
53
51
 
54
52
  def is_active(self) -> bool:
@@ -304,6 +302,10 @@ class OpikMessageProcessor(message_processors.BaseMessageProcessor):
304
302
  "Sent experiment items batch of size %d", len(experiment_items_batch)
305
303
  )
306
304
 
305
+ def _noop_handler(self, message: messages.BaseMessage) -> None:
306
+ # just ignore the message
307
+ pass
308
+
307
309
 
308
310
  def _generate_error_tracking_extra(
309
311
  exception: Exception, message: messages.BaseMessage
@@ -4,7 +4,8 @@ import time
4
4
  from queue import Empty
5
5
  from typing import Optional
6
6
 
7
- from . import message_processors, message_queue, messages
7
+ from . import message_queue, messages
8
+ from .processors import message_processors
8
9
  from .. import exceptions, _logging
9
10
 
10
11
  LOGGER = logging.getLogger(__name__)
@@ -49,7 +50,8 @@ class QueueConsumer(threading.Thread):
49
50
 
50
51
  if message is None:
51
52
  return
52
- elif message.delivery_time <= now:
53
+
54
+ if message.delivery_time <= now:
53
55
  self._process_message(message)
54
56
  else:
55
57
  # put a message back to keep an order in the queue
@@ -4,10 +4,14 @@ import time
4
4
  from typing import List, Optional
5
5
 
6
6
  from . import messages, message_queue, queue_consumer
7
- from .. import synchronization
8
- from .batching import batch_manager
9
- from ..file_upload import base_upload_manager
10
7
  from .. import _logging
8
+ from .. import synchronization
9
+ from .preprocessing import (
10
+ attachments_preprocessor,
11
+ batching_preprocessor,
12
+ file_upload_preprocessor,
13
+ )
14
+
11
15
 
12
16
  LOGGER = logging.getLogger(__name__)
13
17
 
@@ -17,52 +21,74 @@ class Streamer:
17
21
  self,
18
22
  queue: message_queue.MessageQueue[messages.BaseMessage],
19
23
  queue_consumers: List[queue_consumer.QueueConsumer],
20
- batch_manager: Optional[batch_manager.BatchManager],
21
- file_upload_manager: base_upload_manager.BaseFileUploadManager,
24
+ attachments_preprocessor: attachments_preprocessor.AttachmentsPreprocessor,
25
+ batch_preprocessor: batching_preprocessor.BatchingPreprocessor,
26
+ upload_preprocessor: file_upload_preprocessor.FileUploadPreprocessor,
22
27
  ) -> None:
23
28
  self._lock = threading.RLock()
24
29
  self._message_queue = queue
25
30
  self._queue_consumers = queue_consumers
26
- self._batch_manager = batch_manager
27
- self._file_upload_manager = file_upload_manager
31
+ self._attachments_preprocessor = attachments_preprocessor
32
+ self._batch_preprocessor = batch_preprocessor
33
+ self._upload_preprocessor = upload_preprocessor
28
34
 
29
35
  self._drain = False
30
36
 
31
- self._start_queue_consumers()
37
+ self._idle = True
32
38
 
33
- if self._batch_manager is not None:
34
- self._batch_manager.start()
39
+ self._start_queue_consumers()
40
+ self._batch_preprocessor.start()
35
41
 
36
42
  def put(self, message: messages.BaseMessage) -> None:
37
43
  with self._lock:
38
44
  if self._drain:
39
45
  return
40
46
 
41
- if (
42
- self._batch_manager is not None
43
- and self._batch_manager.message_supports_batching(message)
44
- ):
45
- self._batch_manager.process_message(message)
46
- elif base_upload_manager.message_supports_upload(message):
47
- self._file_upload_manager.upload(message)
48
- else:
49
- if self._message_queue.accept_put_without_discarding() is False:
50
- _logging.log_once_at_level(
51
- logging.WARNING,
52
- "The message queue size limit has been reached. The new message has been added to the queue, and the oldest message has been discarded.",
53
- logger=LOGGER,
54
- )
55
- self._message_queue.put(message)
47
+ self._idle = False
48
+ try:
49
+ # do embedded attachments pre-processing first (MUST ALWAYS BE DONE FIRST)
50
+ preprocessed_message = self._attachments_preprocessor.preprocess(
51
+ message
52
+ )
53
+
54
+ # do file uploads pre-processing second
55
+ preprocessed_message = self._upload_preprocessor.preprocess(
56
+ preprocessed_message
57
+ )
58
+
59
+ # do batching pre-processing third
60
+ preprocessed_message = self._batch_preprocessor.preprocess(
61
+ preprocessed_message
62
+ )
63
+
64
+ # work with resulting message if not fully consumed by preprocessors
65
+ if preprocessed_message is not None:
66
+ if self._message_queue.accept_put_without_discarding() is False:
67
+ _logging.log_once_at_level(
68
+ logging.WARNING,
69
+ "The message queue size limit has been reached. The new message has been added to the queue, and the oldest message has been discarded.",
70
+ logger=LOGGER,
71
+ )
72
+ self._message_queue.put(preprocessed_message)
73
+ except Exception as ex:
74
+ LOGGER.error(
75
+ "Failed to process message by streamer: %s", ex, exc_info=ex
76
+ )
77
+ self._idle = True
56
78
 
57
79
  def close(self, timeout: Optional[int]) -> bool:
58
80
  """
59
- Stops data sending threads
81
+ Stops data processing threads
60
82
  """
61
83
  with self._lock:
84
+ synchronization.wait_for_done(
85
+ check_function=lambda: self._idle,
86
+ timeout=timeout,
87
+ sleep_time=0.1,
88
+ )
62
89
  self._drain = True
63
90
 
64
- if self._batch_manager is not None:
65
- self._batch_manager.stop() # stopping causes adding remaining batch messages to the queue
91
+ self._batch_preprocessor.stop() # stopping causes adding remaining batch messages to the queue
66
92
 
67
93
  self.flush(timeout)
68
94
  self._close_queue_consumers()
@@ -70,8 +96,17 @@ class Streamer:
70
96
  return self._message_queue.empty()
71
97
 
72
98
  def flush(self, timeout: Optional[float], upload_sleep_time: int = 5) -> bool:
73
- if self._batch_manager is not None:
74
- self._batch_manager.flush()
99
+ # wait for current pending messages processing to be completed
100
+ # this should be done before flushing batch preprocessor because some
101
+ # batch messages may be added to the queue during processing
102
+ with self._lock:
103
+ synchronization.wait_for_done(
104
+ check_function=lambda: self._idle,
105
+ timeout=timeout,
106
+ sleep_time=0.1,
107
+ )
108
+
109
+ self._batch_preprocessor.flush()
75
110
 
76
111
  start_time = time.time()
77
112
 
@@ -88,17 +123,20 @@ class Streamer:
88
123
  timeout = 1.0
89
124
 
90
125
  # flushing upload manager is blocking operation
91
- upload_flushed = self._file_upload_manager.flush(
126
+ upload_flushed = self._upload_preprocessor.flush(
92
127
  timeout=timeout, sleep_time=upload_sleep_time
93
128
  )
94
129
 
95
- return upload_flushed and self._all_done()
130
+ flushed = upload_flushed and self._all_done()
131
+ LOGGER.debug(f"Streamer flushed completely: {flushed}")
132
+
133
+ return flushed
96
134
 
97
135
  def _all_done(self) -> bool:
98
136
  return (
99
137
  self.workers_idling()
100
138
  and self._message_queue.empty()
101
- and (self._batch_manager is None or self._batch_manager.is_empty())
139
+ and self._batch_preprocessor.is_empty()
102
140
  )
103
141
 
104
142
  def workers_idling(self) -> bool:
@@ -5,12 +5,17 @@ import httpx
5
5
  from . import (
6
6
  queue_consumer,
7
7
  messages,
8
- message_processors,
9
8
  message_queue,
10
9
  streamer,
11
10
  )
12
11
  from .batching import batch_manager_constuctors
13
- from ..file_upload import upload_manager, base_upload_manager
12
+ from .preprocessing import (
13
+ attachments_preprocessor,
14
+ batching_preprocessor,
15
+ file_upload_preprocessor,
16
+ )
17
+ from .processors import attachments_extraction_processor, message_processors
18
+ from ..file_upload import upload_manager
14
19
  from ..rest_api import client as rest_api_client
15
20
 
16
21
 
@@ -18,10 +23,13 @@ def construct_online_streamer(
18
23
  rest_client: rest_api_client.OpikApi,
19
24
  httpx_client: httpx.Client,
20
25
  use_batching: bool,
26
+ use_attachment_extraction: bool,
27
+ min_base64_embedded_attachment_size: int,
21
28
  file_upload_worker_count: int,
22
29
  n_consumers: int,
23
30
  max_queue_size: int,
24
31
  message_processor: message_processors.ChainedMessageProcessor,
32
+ url_override: str,
25
33
  ) -> streamer.Streamer:
26
34
  file_uploader = upload_manager.FileUploadManager(
27
35
  rest_client=rest_client,
@@ -29,20 +37,37 @@ def construct_online_streamer(
29
37
  worker_count=file_upload_worker_count,
30
38
  )
31
39
 
32
- return construct_streamer(
40
+ streamer = construct_streamer(
33
41
  message_processor=message_processor,
34
- file_upload_manager=file_uploader,
42
+ upload_preprocessor=file_upload_preprocessor.FileUploadPreprocessor(
43
+ file_uploader
44
+ ),
35
45
  n_consumers=n_consumers,
36
46
  use_batching=use_batching,
47
+ use_attachment_extraction=use_attachment_extraction,
37
48
  max_queue_size=max_queue_size,
38
49
  )
39
50
 
51
+ # add attachment extraction processor to the beginning of the processing chain
52
+ attachment_extraction = (
53
+ attachments_extraction_processor.AttachmentsExtractionProcessor(
54
+ messages_streamer=streamer,
55
+ min_attachment_size=min_base64_embedded_attachment_size,
56
+ url_override=url_override,
57
+ is_active=use_attachment_extraction,
58
+ )
59
+ )
60
+ message_processor.add_first(attachment_extraction)
61
+
62
+ return streamer
63
+
40
64
 
41
65
  def construct_streamer(
42
- message_processor: message_processors.ChainedMessageProcessor,
43
- file_upload_manager: base_upload_manager.BaseFileUploadManager,
66
+ message_processor: message_processors.BaseMessageProcessor,
67
+ upload_preprocessor: file_upload_preprocessor.FileUploadPreprocessor,
44
68
  n_consumers: int,
45
69
  use_batching: bool,
70
+ use_attachment_extraction: bool,
46
71
  max_queue_size: Optional[int],
47
72
  ) -> streamer.Streamer:
48
73
  message_queue_: message_queue.MessageQueue[messages.BaseMessage] = (
@@ -67,8 +92,11 @@ def construct_streamer(
67
92
  streamer_ = streamer.Streamer(
68
93
  queue=message_queue_,
69
94
  queue_consumers=queue_consumers,
70
- batch_manager=batch_manager,
71
- file_upload_manager=file_upload_manager,
95
+ batch_preprocessor=batching_preprocessor.BatchingPreprocessor(batch_manager),
96
+ upload_preprocessor=upload_preprocessor,
97
+ attachments_preprocessor=attachments_preprocessor.AttachmentsPreprocessor(
98
+ use_attachment_extraction
99
+ ),
72
100
  )
73
101
 
74
102
  return streamer_
@@ -41,7 +41,7 @@ def run(client: opik_client.Opik, test_items: List[Item]) -> None:
41
41
  except Exception:
42
42
  dataset = client.create_dataset("tests")
43
43
 
44
- dataset_items = dataset.__internal_api__get_items_as_dataclasses__()
44
+ dataset_items = list(dataset.__internal_api__stream_items_as_dataclasses__())
45
45
  dataset_item_id_finder = get_dataset_item_id_finder(
46
46
  existing_dataset_items=dataset_items
47
47
  )
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  import opik._logging as _logging
3
3
  from typing import List, Any, Generator, TYPE_CHECKING
4
- from opik.types import FeedbackScoreDict
4
+ from opik.types import BatchFeedbackScoreDict
5
5
 
6
6
  from opik.api_objects import opik_client
7
7
  from . import test_runs_storage, experiment_runner, summary
@@ -48,13 +48,15 @@ def pytest_sessionfinish(session: "pytest.Session", exitstatus: Any) -> None:
48
48
  return
49
49
 
50
50
  try:
51
- traces_feedback_scores: List[FeedbackScoreDict] = []
51
+ traces_feedback_scores: List[BatchFeedbackScoreDict] = []
52
52
 
53
53
  for item in llm_test_items:
54
54
  report: "pytest.TestReport" = item.report
55
55
  trace_id = test_runs_storage.TEST_RUNS_TO_TRACE_DATA[item.nodeid].id
56
56
  traces_feedback_scores.append(
57
- {"id": trace_id, "name": "Passed", "value": report.passed}
57
+ BatchFeedbackScoreDict(
58
+ id=trace_id, name="Passed", value=float(report.passed)
59
+ )
58
60
  )
59
61
 
60
62
  client = opik_client.get_client_cached()
opik/rest_api/__init__.py CHANGED
@@ -38,6 +38,9 @@ from .types import (
38
38
  AssistantMessageRole,
39
39
  Attachment,
40
40
  AttachmentPage,
41
+ AudioUrl,
42
+ AudioUrlPublic,
43
+ AudioUrlWrite,
41
44
  AuthDetailsHolder,
42
45
  AutomationRuleEvaluator,
43
46
  AutomationRuleEvaluatorLlmAsJudge,
@@ -46,6 +49,7 @@ from .types import (
46
49
  AutomationRuleEvaluatorObjectObjectPublic,
47
50
  AutomationRuleEvaluatorObjectObjectPublic_LlmAsJudge,
48
51
  AutomationRuleEvaluatorObjectObjectPublic_SpanLlmAsJudge,
52
+ AutomationRuleEvaluatorObjectObjectPublic_SpanUserDefinedMetricPython,
49
53
  AutomationRuleEvaluatorObjectObjectPublic_TraceThreadLlmAsJudge,
50
54
  AutomationRuleEvaluatorObjectObjectPublic_TraceThreadUserDefinedMetricPython,
51
55
  AutomationRuleEvaluatorObjectObjectPublic_UserDefinedMetricPython,
@@ -53,12 +57,16 @@ from .types import (
53
57
  AutomationRuleEvaluatorPublic,
54
58
  AutomationRuleEvaluatorPublic_LlmAsJudge,
55
59
  AutomationRuleEvaluatorPublic_SpanLlmAsJudge,
60
+ AutomationRuleEvaluatorPublic_SpanUserDefinedMetricPython,
56
61
  AutomationRuleEvaluatorPublic_TraceThreadLlmAsJudge,
57
62
  AutomationRuleEvaluatorPublic_TraceThreadUserDefinedMetricPython,
58
63
  AutomationRuleEvaluatorPublic_UserDefinedMetricPython,
59
64
  AutomationRuleEvaluatorSpanLlmAsJudge,
60
65
  AutomationRuleEvaluatorSpanLlmAsJudgePublic,
61
66
  AutomationRuleEvaluatorSpanLlmAsJudgeWrite,
67
+ AutomationRuleEvaluatorSpanUserDefinedMetricPython,
68
+ AutomationRuleEvaluatorSpanUserDefinedMetricPythonPublic,
69
+ AutomationRuleEvaluatorSpanUserDefinedMetricPythonWrite,
62
70
  AutomationRuleEvaluatorTraceThreadLlmAsJudge,
63
71
  AutomationRuleEvaluatorTraceThreadLlmAsJudgePublic,
64
72
  AutomationRuleEvaluatorTraceThreadLlmAsJudgeWrite,
@@ -68,11 +76,13 @@ from .types import (
68
76
  AutomationRuleEvaluatorUpdate,
69
77
  AutomationRuleEvaluatorUpdateLlmAsJudge,
70
78
  AutomationRuleEvaluatorUpdateSpanLlmAsJudge,
79
+ AutomationRuleEvaluatorUpdateSpanUserDefinedMetricPython,
71
80
  AutomationRuleEvaluatorUpdateTraceThreadLlmAsJudge,
72
81
  AutomationRuleEvaluatorUpdateTraceThreadUserDefinedMetricPython,
73
82
  AutomationRuleEvaluatorUpdateUserDefinedMetricPython,
74
83
  AutomationRuleEvaluatorUpdate_LlmAsJudge,
75
84
  AutomationRuleEvaluatorUpdate_SpanLlmAsJudge,
85
+ AutomationRuleEvaluatorUpdate_SpanUserDefinedMetricPython,
76
86
  AutomationRuleEvaluatorUpdate_TraceThreadLlmAsJudge,
77
87
  AutomationRuleEvaluatorUpdate_TraceThreadUserDefinedMetricPython,
78
88
  AutomationRuleEvaluatorUpdate_UserDefinedMetricPython,
@@ -82,11 +92,13 @@ from .types import (
82
92
  AutomationRuleEvaluatorWrite,
83
93
  AutomationRuleEvaluatorWrite_LlmAsJudge,
84
94
  AutomationRuleEvaluatorWrite_SpanLlmAsJudge,
95
+ AutomationRuleEvaluatorWrite_SpanUserDefinedMetricPython,
85
96
  AutomationRuleEvaluatorWrite_TraceThreadLlmAsJudge,
86
97
  AutomationRuleEvaluatorWrite_TraceThreadUserDefinedMetricPython,
87
98
  AutomationRuleEvaluatorWrite_UserDefinedMetricPython,
88
99
  AutomationRuleEvaluator_LlmAsJudge,
89
100
  AutomationRuleEvaluator_SpanLlmAsJudge,
101
+ AutomationRuleEvaluator_SpanUserDefinedMetricPython,
90
102
  AutomationRuleEvaluator_TraceThreadLlmAsJudge,
91
103
  AutomationRuleEvaluator_TraceThreadUserDefinedMetricPython,
92
104
  AutomationRuleEvaluator_UserDefinedMetricPython,
@@ -145,6 +157,7 @@ from .types import (
145
157
  DatasetExpansionResponse,
146
158
  DatasetItem,
147
159
  DatasetItemBatch,
160
+ DatasetItemChangesPublic,
148
161
  DatasetItemCompare,
149
162
  DatasetItemCompareSource,
150
163
  DatasetItemFilter,
@@ -166,6 +179,8 @@ from .types import (
166
179
  DatasetVersionDiffStats,
167
180
  DatasetVersionPagePublic,
168
181
  DatasetVersionPublic,
182
+ DatasetVersionSummary,
183
+ DatasetVersionSummaryPublic,
169
184
  DatasetVisibility,
170
185
  DeleteAttachmentsRequest,
171
186
  DeleteAttachmentsRequestEntityType,
@@ -333,6 +348,8 @@ from .types import (
333
348
  ProjectPagePublic,
334
349
  ProjectPublic,
335
350
  ProjectPublicVisibility,
351
+ ProjectReference,
352
+ ProjectReferencePublic,
336
353
  ProjectStatItemObjectPublic,
337
354
  ProjectStatItemObjectPublic_Avg,
338
355
  ProjectStatItemObjectPublic_Count,
@@ -363,6 +380,7 @@ from .types import (
363
380
  PromptVersionPublicType,
364
381
  PromptVersionTemplateStructure,
365
382
  PromptVersionType,
383
+ PromptVersionUpdate,
366
384
  ProviderApiKey,
367
385
  ProviderApiKeyPagePublic,
368
386
  ProviderApiKeyProvider,
@@ -394,6 +412,9 @@ from .types import (
394
412
  SpanType,
395
413
  SpanUpdate,
396
414
  SpanUpdateType,
415
+ SpanUserDefinedMetricPythonCode,
416
+ SpanUserDefinedMetricPythonCodePublic,
417
+ SpanUserDefinedMetricPythonCodeWrite,
397
418
  SpanWrite,
398
419
  SpanWriteType,
399
420
  SpansCountResponse,
@@ -585,6 +606,9 @@ __all__ = [
585
606
  "Attachment",
586
607
  "AttachmentListRequestEntityType",
587
608
  "AttachmentPage",
609
+ "AudioUrl",
610
+ "AudioUrlPublic",
611
+ "AudioUrlWrite",
588
612
  "AuthDetailsHolder",
589
613
  "AutomationRuleEvaluator",
590
614
  "AutomationRuleEvaluatorLlmAsJudge",
@@ -593,6 +617,7 @@ __all__ = [
593
617
  "AutomationRuleEvaluatorObjectObjectPublic",
594
618
  "AutomationRuleEvaluatorObjectObjectPublic_LlmAsJudge",
595
619
  "AutomationRuleEvaluatorObjectObjectPublic_SpanLlmAsJudge",
620
+ "AutomationRuleEvaluatorObjectObjectPublic_SpanUserDefinedMetricPython",
596
621
  "AutomationRuleEvaluatorObjectObjectPublic_TraceThreadLlmAsJudge",
597
622
  "AutomationRuleEvaluatorObjectObjectPublic_TraceThreadUserDefinedMetricPython",
598
623
  "AutomationRuleEvaluatorObjectObjectPublic_UserDefinedMetricPython",
@@ -600,12 +625,16 @@ __all__ = [
600
625
  "AutomationRuleEvaluatorPublic",
601
626
  "AutomationRuleEvaluatorPublic_LlmAsJudge",
602
627
  "AutomationRuleEvaluatorPublic_SpanLlmAsJudge",
628
+ "AutomationRuleEvaluatorPublic_SpanUserDefinedMetricPython",
603
629
  "AutomationRuleEvaluatorPublic_TraceThreadLlmAsJudge",
604
630
  "AutomationRuleEvaluatorPublic_TraceThreadUserDefinedMetricPython",
605
631
  "AutomationRuleEvaluatorPublic_UserDefinedMetricPython",
606
632
  "AutomationRuleEvaluatorSpanLlmAsJudge",
607
633
  "AutomationRuleEvaluatorSpanLlmAsJudgePublic",
608
634
  "AutomationRuleEvaluatorSpanLlmAsJudgeWrite",
635
+ "AutomationRuleEvaluatorSpanUserDefinedMetricPython",
636
+ "AutomationRuleEvaluatorSpanUserDefinedMetricPythonPublic",
637
+ "AutomationRuleEvaluatorSpanUserDefinedMetricPythonWrite",
609
638
  "AutomationRuleEvaluatorTraceThreadLlmAsJudge",
610
639
  "AutomationRuleEvaluatorTraceThreadLlmAsJudgePublic",
611
640
  "AutomationRuleEvaluatorTraceThreadLlmAsJudgeWrite",
@@ -615,11 +644,13 @@ __all__ = [
615
644
  "AutomationRuleEvaluatorUpdate",
616
645
  "AutomationRuleEvaluatorUpdateLlmAsJudge",
617
646
  "AutomationRuleEvaluatorUpdateSpanLlmAsJudge",
647
+ "AutomationRuleEvaluatorUpdateSpanUserDefinedMetricPython",
618
648
  "AutomationRuleEvaluatorUpdateTraceThreadLlmAsJudge",
619
649
  "AutomationRuleEvaluatorUpdateTraceThreadUserDefinedMetricPython",
620
650
  "AutomationRuleEvaluatorUpdateUserDefinedMetricPython",
621
651
  "AutomationRuleEvaluatorUpdate_LlmAsJudge",
622
652
  "AutomationRuleEvaluatorUpdate_SpanLlmAsJudge",
653
+ "AutomationRuleEvaluatorUpdate_SpanUserDefinedMetricPython",
623
654
  "AutomationRuleEvaluatorUpdate_TraceThreadLlmAsJudge",
624
655
  "AutomationRuleEvaluatorUpdate_TraceThreadUserDefinedMetricPython",
625
656
  "AutomationRuleEvaluatorUpdate_UserDefinedMetricPython",
@@ -629,11 +660,13 @@ __all__ = [
629
660
  "AutomationRuleEvaluatorWrite",
630
661
  "AutomationRuleEvaluatorWrite_LlmAsJudge",
631
662
  "AutomationRuleEvaluatorWrite_SpanLlmAsJudge",
663
+ "AutomationRuleEvaluatorWrite_SpanUserDefinedMetricPython",
632
664
  "AutomationRuleEvaluatorWrite_TraceThreadLlmAsJudge",
633
665
  "AutomationRuleEvaluatorWrite_TraceThreadUserDefinedMetricPython",
634
666
  "AutomationRuleEvaluatorWrite_UserDefinedMetricPython",
635
667
  "AutomationRuleEvaluator_LlmAsJudge",
636
668
  "AutomationRuleEvaluator_SpanLlmAsJudge",
669
+ "AutomationRuleEvaluator_SpanUserDefinedMetricPython",
637
670
  "AutomationRuleEvaluator_TraceThreadLlmAsJudge",
638
671
  "AutomationRuleEvaluator_TraceThreadUserDefinedMetricPython",
639
672
  "AutomationRuleEvaluator_UserDefinedMetricPython",
@@ -695,6 +728,7 @@ __all__ = [
695
728
  "DatasetExpansionResponse",
696
729
  "DatasetItem",
697
730
  "DatasetItemBatch",
731
+ "DatasetItemChangesPublic",
698
732
  "DatasetItemCompare",
699
733
  "DatasetItemCompareSource",
700
734
  "DatasetItemFilter",
@@ -717,6 +751,8 @@ __all__ = [
717
751
  "DatasetVersionDiffStats",
718
752
  "DatasetVersionPagePublic",
719
753
  "DatasetVersionPublic",
754
+ "DatasetVersionSummary",
755
+ "DatasetVersionSummaryPublic",
720
756
  "DatasetVisibility",
721
757
  "DatasetWriteVisibility",
722
758
  "DeleteAttachmentsRequest",
@@ -903,6 +939,8 @@ __all__ = [
903
939
  "ProjectPagePublic",
904
940
  "ProjectPublic",
905
941
  "ProjectPublicVisibility",
942
+ "ProjectReference",
943
+ "ProjectReferencePublic",
906
944
  "ProjectStatItemObjectPublic",
907
945
  "ProjectStatItemObjectPublic_Avg",
908
946
  "ProjectStatItemObjectPublic_Count",
@@ -935,6 +973,7 @@ __all__ = [
935
973
  "PromptVersionPublicType",
936
974
  "PromptVersionTemplateStructure",
937
975
  "PromptVersionType",
976
+ "PromptVersionUpdate",
938
977
  "PromptWriteTemplateStructure",
939
978
  "PromptWriteType",
940
979
  "ProviderApiKey",
@@ -970,6 +1009,9 @@ __all__ = [
970
1009
  "SpanType",
971
1010
  "SpanUpdate",
972
1011
  "SpanUpdateType",
1012
+ "SpanUserDefinedMetricPythonCode",
1013
+ "SpanUserDefinedMetricPythonCodePublic",
1014
+ "SpanUserDefinedMetricPythonCodeWrite",
973
1015
  "SpanWrite",
974
1016
  "SpanWriteType",
975
1017
  "SpansCountResponse",