opik 1.8.39__py3-none-any.whl → 1.9.71__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 (592) hide show
  1. opik/__init__.py +19 -3
  2. opik/anonymizer/__init__.py +5 -0
  3. opik/anonymizer/anonymizer.py +12 -0
  4. opik/anonymizer/factory.py +80 -0
  5. opik/anonymizer/recursive_anonymizer.py +64 -0
  6. opik/anonymizer/rules.py +56 -0
  7. opik/anonymizer/rules_anonymizer.py +35 -0
  8. opik/api_objects/attachment/attachment_context.py +36 -0
  9. opik/api_objects/attachment/attachments_extractor.py +153 -0
  10. opik/api_objects/attachment/client.py +1 -0
  11. opik/api_objects/attachment/converters.py +2 -0
  12. opik/api_objects/attachment/decoder.py +18 -0
  13. opik/api_objects/attachment/decoder_base64.py +83 -0
  14. opik/api_objects/attachment/decoder_helpers.py +137 -0
  15. opik/api_objects/data_helpers.py +79 -0
  16. opik/api_objects/dataset/dataset.py +64 -4
  17. opik/api_objects/dataset/rest_operations.py +11 -2
  18. opik/api_objects/experiment/experiment.py +57 -57
  19. opik/api_objects/experiment/experiment_item.py +2 -1
  20. opik/api_objects/experiment/experiments_client.py +64 -0
  21. opik/api_objects/experiment/helpers.py +35 -11
  22. opik/api_objects/experiment/rest_operations.py +65 -5
  23. opik/api_objects/helpers.py +8 -5
  24. opik/api_objects/local_recording.py +81 -0
  25. opik/api_objects/opik_client.py +600 -108
  26. opik/api_objects/opik_query_language.py +39 -5
  27. opik/api_objects/prompt/__init__.py +12 -2
  28. opik/api_objects/prompt/base_prompt.py +69 -0
  29. opik/api_objects/prompt/base_prompt_template.py +29 -0
  30. opik/api_objects/prompt/chat/__init__.py +1 -0
  31. opik/api_objects/prompt/chat/chat_prompt.py +210 -0
  32. opik/api_objects/prompt/chat/chat_prompt_template.py +350 -0
  33. opik/api_objects/prompt/chat/content_renderer_registry.py +203 -0
  34. opik/api_objects/prompt/client.py +189 -47
  35. opik/api_objects/prompt/text/__init__.py +1 -0
  36. opik/api_objects/prompt/text/prompt.py +174 -0
  37. opik/api_objects/prompt/{prompt_template.py → text/prompt_template.py} +10 -6
  38. opik/api_objects/prompt/types.py +23 -0
  39. opik/api_objects/search_helpers.py +89 -0
  40. opik/api_objects/span/span_data.py +35 -25
  41. opik/api_objects/threads/threads_client.py +39 -5
  42. opik/api_objects/trace/trace_client.py +52 -2
  43. opik/api_objects/trace/trace_data.py +15 -24
  44. opik/api_objects/validation_helpers.py +3 -3
  45. opik/cli/__init__.py +5 -0
  46. opik/cli/__main__.py +6 -0
  47. opik/cli/configure.py +66 -0
  48. opik/cli/exports/__init__.py +131 -0
  49. opik/cli/exports/dataset.py +278 -0
  50. opik/cli/exports/experiment.py +784 -0
  51. opik/cli/exports/project.py +685 -0
  52. opik/cli/exports/prompt.py +578 -0
  53. opik/cli/exports/utils.py +406 -0
  54. opik/cli/harbor.py +39 -0
  55. opik/cli/healthcheck.py +21 -0
  56. opik/cli/imports/__init__.py +439 -0
  57. opik/cli/imports/dataset.py +143 -0
  58. opik/cli/imports/experiment.py +1192 -0
  59. opik/cli/imports/project.py +262 -0
  60. opik/cli/imports/prompt.py +177 -0
  61. opik/cli/imports/utils.py +280 -0
  62. opik/cli/main.py +49 -0
  63. opik/cli/proxy.py +93 -0
  64. opik/cli/usage_report/__init__.py +16 -0
  65. opik/cli/usage_report/charts.py +783 -0
  66. opik/cli/usage_report/cli.py +274 -0
  67. opik/cli/usage_report/constants.py +9 -0
  68. opik/cli/usage_report/extraction.py +749 -0
  69. opik/cli/usage_report/pdf.py +244 -0
  70. opik/cli/usage_report/statistics.py +78 -0
  71. opik/cli/usage_report/utils.py +235 -0
  72. opik/config.py +13 -7
  73. opik/configurator/configure.py +17 -0
  74. opik/datetime_helpers.py +12 -0
  75. opik/decorator/arguments_helpers.py +9 -1
  76. opik/decorator/base_track_decorator.py +205 -133
  77. opik/decorator/context_manager/span_context_manager.py +123 -0
  78. opik/decorator/context_manager/trace_context_manager.py +84 -0
  79. opik/decorator/opik_args/__init__.py +13 -0
  80. opik/decorator/opik_args/api_classes.py +71 -0
  81. opik/decorator/opik_args/helpers.py +120 -0
  82. opik/decorator/span_creation_handler.py +25 -6
  83. opik/dict_utils.py +3 -3
  84. opik/evaluation/__init__.py +13 -2
  85. opik/evaluation/engine/engine.py +272 -75
  86. opik/evaluation/engine/evaluation_tasks_executor.py +6 -3
  87. opik/evaluation/engine/helpers.py +31 -6
  88. opik/evaluation/engine/metrics_evaluator.py +237 -0
  89. opik/evaluation/evaluation_result.py +168 -2
  90. opik/evaluation/evaluator.py +533 -62
  91. opik/evaluation/metrics/__init__.py +103 -4
  92. opik/evaluation/metrics/aggregated_metric.py +35 -6
  93. opik/evaluation/metrics/base_metric.py +1 -1
  94. opik/evaluation/metrics/conversation/__init__.py +48 -0
  95. opik/evaluation/metrics/conversation/conversation_thread_metric.py +56 -2
  96. opik/evaluation/metrics/conversation/g_eval_wrappers.py +19 -0
  97. opik/evaluation/metrics/conversation/helpers.py +14 -15
  98. opik/evaluation/metrics/conversation/heuristics/__init__.py +14 -0
  99. opik/evaluation/metrics/conversation/heuristics/degeneration/__init__.py +3 -0
  100. opik/evaluation/metrics/conversation/heuristics/degeneration/metric.py +189 -0
  101. opik/evaluation/metrics/conversation/heuristics/degeneration/phrases.py +12 -0
  102. opik/evaluation/metrics/conversation/heuristics/knowledge_retention/__init__.py +3 -0
  103. opik/evaluation/metrics/conversation/heuristics/knowledge_retention/metric.py +172 -0
  104. opik/evaluation/metrics/conversation/llm_judges/__init__.py +32 -0
  105. opik/evaluation/metrics/conversation/{conversational_coherence → llm_judges/conversational_coherence}/metric.py +22 -17
  106. opik/evaluation/metrics/conversation/{conversational_coherence → llm_judges/conversational_coherence}/templates.py +1 -1
  107. opik/evaluation/metrics/conversation/llm_judges/g_eval_wrappers.py +442 -0
  108. opik/evaluation/metrics/conversation/{session_completeness → llm_judges/session_completeness}/metric.py +13 -7
  109. opik/evaluation/metrics/conversation/{session_completeness → llm_judges/session_completeness}/templates.py +1 -1
  110. opik/evaluation/metrics/conversation/llm_judges/user_frustration/__init__.py +0 -0
  111. opik/evaluation/metrics/conversation/{user_frustration → llm_judges/user_frustration}/metric.py +21 -14
  112. opik/evaluation/metrics/conversation/{user_frustration → llm_judges/user_frustration}/templates.py +1 -1
  113. opik/evaluation/metrics/conversation/types.py +4 -5
  114. opik/evaluation/metrics/conversation_types.py +9 -0
  115. opik/evaluation/metrics/heuristics/bertscore.py +107 -0
  116. opik/evaluation/metrics/heuristics/bleu.py +35 -15
  117. opik/evaluation/metrics/heuristics/chrf.py +127 -0
  118. opik/evaluation/metrics/heuristics/contains.py +47 -11
  119. opik/evaluation/metrics/heuristics/distribution_metrics.py +331 -0
  120. opik/evaluation/metrics/heuristics/gleu.py +113 -0
  121. opik/evaluation/metrics/heuristics/language_adherence.py +123 -0
  122. opik/evaluation/metrics/heuristics/meteor.py +119 -0
  123. opik/evaluation/metrics/heuristics/prompt_injection.py +150 -0
  124. opik/evaluation/metrics/heuristics/readability.py +129 -0
  125. opik/evaluation/metrics/heuristics/rouge.py +26 -9
  126. opik/evaluation/metrics/heuristics/spearman.py +88 -0
  127. opik/evaluation/metrics/heuristics/tone.py +155 -0
  128. opik/evaluation/metrics/heuristics/vader_sentiment.py +77 -0
  129. opik/evaluation/metrics/llm_judges/answer_relevance/metric.py +20 -5
  130. opik/evaluation/metrics/llm_judges/context_precision/metric.py +20 -6
  131. opik/evaluation/metrics/llm_judges/context_recall/metric.py +20 -6
  132. opik/evaluation/metrics/llm_judges/g_eval/__init__.py +5 -0
  133. opik/evaluation/metrics/llm_judges/g_eval/metric.py +219 -68
  134. opik/evaluation/metrics/llm_judges/g_eval/parser.py +102 -52
  135. opik/evaluation/metrics/llm_judges/g_eval/presets.py +209 -0
  136. opik/evaluation/metrics/llm_judges/g_eval_presets/__init__.py +36 -0
  137. opik/evaluation/metrics/llm_judges/g_eval_presets/agent_assessment.py +77 -0
  138. opik/evaluation/metrics/llm_judges/g_eval_presets/bias_classifier.py +181 -0
  139. opik/evaluation/metrics/llm_judges/g_eval_presets/compliance_risk.py +41 -0
  140. opik/evaluation/metrics/llm_judges/g_eval_presets/prompt_uncertainty.py +41 -0
  141. opik/evaluation/metrics/llm_judges/g_eval_presets/qa_suite.py +146 -0
  142. opik/evaluation/metrics/llm_judges/hallucination/metric.py +16 -3
  143. opik/evaluation/metrics/llm_judges/llm_juries/__init__.py +3 -0
  144. opik/evaluation/metrics/llm_judges/llm_juries/metric.py +76 -0
  145. opik/evaluation/metrics/llm_judges/moderation/metric.py +16 -4
  146. opik/evaluation/metrics/llm_judges/structure_output_compliance/__init__.py +0 -0
  147. opik/evaluation/metrics/llm_judges/structure_output_compliance/metric.py +144 -0
  148. opik/evaluation/metrics/llm_judges/structure_output_compliance/parser.py +79 -0
  149. opik/evaluation/metrics/llm_judges/structure_output_compliance/schema.py +15 -0
  150. opik/evaluation/metrics/llm_judges/structure_output_compliance/template.py +50 -0
  151. opik/evaluation/metrics/llm_judges/syc_eval/__init__.py +0 -0
  152. opik/evaluation/metrics/llm_judges/syc_eval/metric.py +252 -0
  153. opik/evaluation/metrics/llm_judges/syc_eval/parser.py +82 -0
  154. opik/evaluation/metrics/llm_judges/syc_eval/template.py +155 -0
  155. opik/evaluation/metrics/llm_judges/trajectory_accuracy/metric.py +20 -5
  156. opik/evaluation/metrics/llm_judges/usefulness/metric.py +16 -4
  157. opik/evaluation/metrics/ragas_metric.py +43 -23
  158. opik/evaluation/models/__init__.py +8 -0
  159. opik/evaluation/models/base_model.py +107 -1
  160. opik/evaluation/models/langchain/langchain_chat_model.py +15 -7
  161. opik/evaluation/models/langchain/message_converters.py +97 -15
  162. opik/evaluation/models/litellm/litellm_chat_model.py +156 -29
  163. opik/evaluation/models/litellm/util.py +125 -0
  164. opik/evaluation/models/litellm/warning_filters.py +16 -4
  165. opik/evaluation/models/model_capabilities.py +187 -0
  166. opik/evaluation/models/models_factory.py +25 -3
  167. opik/evaluation/preprocessing.py +92 -0
  168. opik/evaluation/report.py +70 -12
  169. opik/evaluation/rest_operations.py +49 -45
  170. opik/evaluation/samplers/__init__.py +4 -0
  171. opik/evaluation/samplers/base_dataset_sampler.py +40 -0
  172. opik/evaluation/samplers/random_dataset_sampler.py +48 -0
  173. opik/evaluation/score_statistics.py +66 -0
  174. opik/evaluation/scorers/__init__.py +4 -0
  175. opik/evaluation/scorers/scorer_function.py +55 -0
  176. opik/evaluation/scorers/scorer_wrapper_metric.py +130 -0
  177. opik/evaluation/test_case.py +3 -2
  178. opik/evaluation/test_result.py +1 -0
  179. opik/evaluation/threads/evaluator.py +31 -3
  180. opik/evaluation/threads/helpers.py +3 -2
  181. opik/evaluation/types.py +9 -1
  182. opik/exceptions.py +33 -0
  183. opik/file_upload/file_uploader.py +13 -0
  184. opik/file_upload/upload_options.py +2 -0
  185. opik/hooks/__init__.py +23 -0
  186. opik/hooks/anonymizer_hook.py +36 -0
  187. opik/hooks/httpx_client_hook.py +112 -0
  188. opik/httpx_client.py +12 -9
  189. opik/id_helpers.py +18 -0
  190. opik/integrations/adk/graph/subgraph_edges_builders.py +1 -2
  191. opik/integrations/adk/helpers.py +16 -7
  192. opik/integrations/adk/legacy_opik_tracer.py +7 -4
  193. opik/integrations/adk/opik_tracer.py +14 -1
  194. opik/integrations/adk/patchers/adk_otel_tracer/opik_adk_otel_tracer.py +7 -3
  195. opik/integrations/adk/recursive_callback_injector.py +4 -7
  196. opik/integrations/bedrock/converse/__init__.py +0 -0
  197. opik/integrations/bedrock/converse/chunks_aggregator.py +188 -0
  198. opik/integrations/bedrock/{converse_decorator.py → converse/converse_decorator.py} +4 -3
  199. opik/integrations/bedrock/invoke_agent_decorator.py +5 -4
  200. opik/integrations/bedrock/invoke_model/__init__.py +0 -0
  201. opik/integrations/bedrock/invoke_model/chunks_aggregator/__init__.py +78 -0
  202. opik/integrations/bedrock/invoke_model/chunks_aggregator/api.py +45 -0
  203. opik/integrations/bedrock/invoke_model/chunks_aggregator/base.py +23 -0
  204. opik/integrations/bedrock/invoke_model/chunks_aggregator/claude.py +121 -0
  205. opik/integrations/bedrock/invoke_model/chunks_aggregator/format_detector.py +107 -0
  206. opik/integrations/bedrock/invoke_model/chunks_aggregator/llama.py +108 -0
  207. opik/integrations/bedrock/invoke_model/chunks_aggregator/mistral.py +118 -0
  208. opik/integrations/bedrock/invoke_model/chunks_aggregator/nova.py +99 -0
  209. opik/integrations/bedrock/invoke_model/invoke_model_decorator.py +178 -0
  210. opik/integrations/bedrock/invoke_model/response_types.py +34 -0
  211. opik/integrations/bedrock/invoke_model/stream_wrappers.py +122 -0
  212. opik/integrations/bedrock/invoke_model/usage_converters.py +87 -0
  213. opik/integrations/bedrock/invoke_model/usage_extraction.py +108 -0
  214. opik/integrations/bedrock/opik_tracker.py +42 -4
  215. opik/integrations/bedrock/types.py +19 -0
  216. opik/integrations/crewai/crewai_decorator.py +8 -51
  217. opik/integrations/crewai/opik_tracker.py +31 -10
  218. opik/integrations/crewai/patchers/__init__.py +5 -0
  219. opik/integrations/crewai/patchers/flow.py +118 -0
  220. opik/integrations/crewai/patchers/litellm_completion.py +30 -0
  221. opik/integrations/crewai/patchers/llm_client.py +207 -0
  222. opik/integrations/dspy/callback.py +80 -17
  223. opik/integrations/dspy/parsers.py +168 -0
  224. opik/integrations/harbor/__init__.py +17 -0
  225. opik/integrations/harbor/experiment_service.py +269 -0
  226. opik/integrations/harbor/opik_tracker.py +528 -0
  227. opik/integrations/haystack/opik_connector.py +2 -2
  228. opik/integrations/haystack/opik_tracer.py +3 -7
  229. opik/integrations/langchain/__init__.py +3 -1
  230. opik/integrations/langchain/helpers.py +96 -0
  231. opik/integrations/langchain/langgraph_async_context_bridge.py +131 -0
  232. opik/integrations/langchain/langgraph_tracer_injector.py +88 -0
  233. opik/integrations/langchain/opik_encoder_extension.py +1 -1
  234. opik/integrations/langchain/opik_tracer.py +474 -229
  235. opik/integrations/litellm/__init__.py +5 -0
  236. opik/integrations/litellm/completion_chunks_aggregator.py +115 -0
  237. opik/integrations/litellm/litellm_completion_decorator.py +242 -0
  238. opik/integrations/litellm/opik_tracker.py +43 -0
  239. opik/integrations/litellm/stream_patchers.py +151 -0
  240. opik/integrations/llama_index/callback.py +146 -107
  241. opik/integrations/openai/agents/opik_tracing_processor.py +1 -2
  242. opik/integrations/openai/openai_chat_completions_decorator.py +2 -16
  243. opik/integrations/openai/opik_tracker.py +1 -1
  244. opik/integrations/sagemaker/auth.py +5 -1
  245. opik/llm_usage/google_usage.py +3 -1
  246. opik/llm_usage/opik_usage.py +7 -8
  247. opik/llm_usage/opik_usage_factory.py +4 -2
  248. opik/logging_messages.py +6 -0
  249. opik/message_processing/batching/base_batcher.py +14 -21
  250. opik/message_processing/batching/batch_manager.py +22 -10
  251. opik/message_processing/batching/batch_manager_constuctors.py +10 -0
  252. opik/message_processing/batching/batchers.py +59 -27
  253. opik/message_processing/batching/flushing_thread.py +0 -3
  254. opik/message_processing/emulation/__init__.py +0 -0
  255. opik/message_processing/emulation/emulator_message_processor.py +578 -0
  256. opik/message_processing/emulation/local_emulator_message_processor.py +140 -0
  257. opik/message_processing/emulation/models.py +162 -0
  258. opik/message_processing/encoder_helpers.py +79 -0
  259. opik/message_processing/messages.py +56 -1
  260. opik/message_processing/preprocessing/__init__.py +0 -0
  261. opik/message_processing/preprocessing/attachments_preprocessor.py +70 -0
  262. opik/message_processing/preprocessing/batching_preprocessor.py +53 -0
  263. opik/message_processing/preprocessing/constants.py +1 -0
  264. opik/message_processing/preprocessing/file_upload_preprocessor.py +38 -0
  265. opik/message_processing/preprocessing/preprocessor.py +36 -0
  266. opik/message_processing/processors/__init__.py +0 -0
  267. opik/message_processing/processors/attachments_extraction_processor.py +146 -0
  268. opik/message_processing/processors/message_processors.py +92 -0
  269. opik/message_processing/processors/message_processors_chain.py +96 -0
  270. opik/message_processing/{message_processors.py → processors/online_message_processor.py} +85 -29
  271. opik/message_processing/queue_consumer.py +9 -3
  272. opik/message_processing/streamer.py +71 -33
  273. opik/message_processing/streamer_constructors.py +43 -10
  274. opik/opik_context.py +16 -4
  275. opik/plugins/pytest/hooks.py +5 -3
  276. opik/rest_api/__init__.py +346 -15
  277. opik/rest_api/alerts/__init__.py +7 -0
  278. opik/rest_api/alerts/client.py +667 -0
  279. opik/rest_api/alerts/raw_client.py +1015 -0
  280. opik/rest_api/alerts/types/__init__.py +7 -0
  281. opik/rest_api/alerts/types/get_webhook_examples_request_alert_type.py +5 -0
  282. opik/rest_api/annotation_queues/__init__.py +4 -0
  283. opik/rest_api/annotation_queues/client.py +668 -0
  284. opik/rest_api/annotation_queues/raw_client.py +1019 -0
  285. opik/rest_api/automation_rule_evaluators/client.py +34 -2
  286. opik/rest_api/automation_rule_evaluators/raw_client.py +24 -0
  287. opik/rest_api/client.py +15 -0
  288. opik/rest_api/dashboards/__init__.py +4 -0
  289. opik/rest_api/dashboards/client.py +462 -0
  290. opik/rest_api/dashboards/raw_client.py +648 -0
  291. opik/rest_api/datasets/client.py +1310 -44
  292. opik/rest_api/datasets/raw_client.py +2269 -358
  293. opik/rest_api/experiments/__init__.py +2 -2
  294. opik/rest_api/experiments/client.py +191 -5
  295. opik/rest_api/experiments/raw_client.py +301 -7
  296. opik/rest_api/experiments/types/__init__.py +4 -1
  297. opik/rest_api/experiments/types/experiment_update_status.py +5 -0
  298. opik/rest_api/experiments/types/experiment_update_type.py +5 -0
  299. opik/rest_api/experiments/types/experiment_write_status.py +5 -0
  300. opik/rest_api/feedback_definitions/types/find_feedback_definitions_request_type.py +1 -1
  301. opik/rest_api/llm_provider_key/client.py +20 -0
  302. opik/rest_api/llm_provider_key/raw_client.py +20 -0
  303. opik/rest_api/llm_provider_key/types/provider_api_key_write_provider.py +1 -1
  304. opik/rest_api/manual_evaluation/__init__.py +4 -0
  305. opik/rest_api/manual_evaluation/client.py +347 -0
  306. opik/rest_api/manual_evaluation/raw_client.py +543 -0
  307. opik/rest_api/optimizations/client.py +145 -9
  308. opik/rest_api/optimizations/raw_client.py +237 -13
  309. opik/rest_api/optimizations/types/optimization_update_status.py +3 -1
  310. opik/rest_api/prompts/__init__.py +2 -2
  311. opik/rest_api/prompts/client.py +227 -6
  312. opik/rest_api/prompts/raw_client.py +331 -2
  313. opik/rest_api/prompts/types/__init__.py +3 -1
  314. opik/rest_api/prompts/types/create_prompt_version_detail_template_structure.py +5 -0
  315. opik/rest_api/prompts/types/prompt_write_template_structure.py +5 -0
  316. opik/rest_api/spans/__init__.py +0 -2
  317. opik/rest_api/spans/client.py +238 -76
  318. opik/rest_api/spans/raw_client.py +307 -95
  319. opik/rest_api/spans/types/__init__.py +0 -2
  320. opik/rest_api/traces/client.py +572 -161
  321. opik/rest_api/traces/raw_client.py +736 -229
  322. opik/rest_api/types/__init__.py +352 -17
  323. opik/rest_api/types/aggregation_data.py +1 -0
  324. opik/rest_api/types/alert.py +33 -0
  325. opik/rest_api/types/alert_alert_type.py +5 -0
  326. opik/rest_api/types/alert_page_public.py +24 -0
  327. opik/rest_api/types/alert_public.py +33 -0
  328. opik/rest_api/types/alert_public_alert_type.py +5 -0
  329. opik/rest_api/types/alert_trigger.py +27 -0
  330. opik/rest_api/types/alert_trigger_config.py +28 -0
  331. opik/rest_api/types/alert_trigger_config_public.py +28 -0
  332. opik/rest_api/types/alert_trigger_config_public_type.py +10 -0
  333. opik/rest_api/types/alert_trigger_config_type.py +10 -0
  334. opik/rest_api/types/alert_trigger_config_write.py +22 -0
  335. opik/rest_api/types/alert_trigger_config_write_type.py +10 -0
  336. opik/rest_api/types/alert_trigger_event_type.py +19 -0
  337. opik/rest_api/types/alert_trigger_public.py +27 -0
  338. opik/rest_api/types/alert_trigger_public_event_type.py +19 -0
  339. opik/rest_api/types/alert_trigger_write.py +23 -0
  340. opik/rest_api/types/alert_trigger_write_event_type.py +19 -0
  341. opik/rest_api/types/alert_write.py +28 -0
  342. opik/rest_api/types/alert_write_alert_type.py +5 -0
  343. opik/rest_api/types/annotation_queue.py +42 -0
  344. opik/rest_api/types/annotation_queue_batch.py +27 -0
  345. opik/rest_api/types/annotation_queue_item_ids.py +19 -0
  346. opik/rest_api/types/annotation_queue_page_public.py +28 -0
  347. opik/rest_api/types/annotation_queue_public.py +38 -0
  348. opik/rest_api/types/annotation_queue_public_scope.py +5 -0
  349. opik/rest_api/types/annotation_queue_reviewer.py +20 -0
  350. opik/rest_api/types/annotation_queue_reviewer_public.py +20 -0
  351. opik/rest_api/types/annotation_queue_scope.py +5 -0
  352. opik/rest_api/types/annotation_queue_write.py +31 -0
  353. opik/rest_api/types/annotation_queue_write_scope.py +5 -0
  354. opik/rest_api/types/audio_url.py +19 -0
  355. opik/rest_api/types/audio_url_public.py +19 -0
  356. opik/rest_api/types/audio_url_write.py +19 -0
  357. opik/rest_api/types/automation_rule_evaluator.py +62 -2
  358. opik/rest_api/types/automation_rule_evaluator_llm_as_judge.py +2 -0
  359. opik/rest_api/types/automation_rule_evaluator_llm_as_judge_public.py +2 -0
  360. opik/rest_api/types/automation_rule_evaluator_llm_as_judge_write.py +2 -0
  361. opik/rest_api/types/automation_rule_evaluator_object_object_public.py +155 -0
  362. opik/rest_api/types/automation_rule_evaluator_page_public.py +3 -2
  363. opik/rest_api/types/automation_rule_evaluator_public.py +57 -2
  364. opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge.py +22 -0
  365. opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge_public.py +22 -0
  366. opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge_write.py +22 -0
  367. opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python.py +22 -0
  368. opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python_public.py +22 -0
  369. opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python_write.py +22 -0
  370. opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge.py +2 -0
  371. opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge_public.py +2 -0
  372. opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge_write.py +2 -0
  373. opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python.py +2 -0
  374. opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python_public.py +2 -0
  375. opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python_write.py +2 -0
  376. opik/rest_api/types/automation_rule_evaluator_update.py +51 -1
  377. opik/rest_api/types/automation_rule_evaluator_update_llm_as_judge.py +2 -0
  378. opik/rest_api/types/automation_rule_evaluator_update_span_llm_as_judge.py +22 -0
  379. opik/rest_api/types/automation_rule_evaluator_update_span_user_defined_metric_python.py +22 -0
  380. opik/rest_api/types/automation_rule_evaluator_update_trace_thread_llm_as_judge.py +2 -0
  381. opik/rest_api/types/automation_rule_evaluator_update_trace_thread_user_defined_metric_python.py +2 -0
  382. opik/rest_api/types/automation_rule_evaluator_update_user_defined_metric_python.py +2 -0
  383. opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python.py +2 -0
  384. opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python_public.py +2 -0
  385. opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python_write.py +2 -0
  386. opik/rest_api/types/automation_rule_evaluator_write.py +51 -1
  387. opik/rest_api/types/boolean_feedback_definition.py +25 -0
  388. opik/rest_api/types/boolean_feedback_definition_create.py +20 -0
  389. opik/rest_api/types/boolean_feedback_definition_public.py +25 -0
  390. opik/rest_api/types/boolean_feedback_definition_update.py +20 -0
  391. opik/rest_api/types/boolean_feedback_detail.py +29 -0
  392. opik/rest_api/types/boolean_feedback_detail_create.py +29 -0
  393. opik/rest_api/types/boolean_feedback_detail_public.py +29 -0
  394. opik/rest_api/types/boolean_feedback_detail_update.py +29 -0
  395. opik/rest_api/types/dashboard_page_public.py +24 -0
  396. opik/rest_api/types/dashboard_public.py +30 -0
  397. opik/rest_api/types/dataset.py +4 -0
  398. opik/rest_api/types/dataset_expansion.py +42 -0
  399. opik/rest_api/types/dataset_expansion_response.py +39 -0
  400. opik/rest_api/types/dataset_item.py +2 -0
  401. opik/rest_api/types/dataset_item_changes_public.py +5 -0
  402. opik/rest_api/types/dataset_item_compare.py +2 -0
  403. opik/rest_api/types/dataset_item_filter.py +27 -0
  404. opik/rest_api/types/dataset_item_filter_operator.py +21 -0
  405. opik/rest_api/types/dataset_item_page_compare.py +5 -0
  406. opik/rest_api/types/dataset_item_page_public.py +5 -0
  407. opik/rest_api/types/dataset_item_public.py +2 -0
  408. opik/rest_api/types/dataset_item_update.py +39 -0
  409. opik/rest_api/types/dataset_item_write.py +1 -0
  410. opik/rest_api/types/dataset_public.py +4 -0
  411. opik/rest_api/types/dataset_public_status.py +5 -0
  412. opik/rest_api/types/dataset_status.py +5 -0
  413. opik/rest_api/types/dataset_version_diff.py +22 -0
  414. opik/rest_api/types/dataset_version_diff_stats.py +24 -0
  415. opik/rest_api/types/dataset_version_page_public.py +23 -0
  416. opik/rest_api/types/dataset_version_public.py +59 -0
  417. opik/rest_api/types/dataset_version_summary.py +46 -0
  418. opik/rest_api/types/dataset_version_summary_public.py +46 -0
  419. opik/rest_api/types/experiment.py +7 -2
  420. opik/rest_api/types/experiment_group_response.py +2 -0
  421. opik/rest_api/types/experiment_public.py +7 -2
  422. opik/rest_api/types/experiment_public_status.py +5 -0
  423. opik/rest_api/types/experiment_score.py +20 -0
  424. opik/rest_api/types/experiment_score_public.py +20 -0
  425. opik/rest_api/types/experiment_score_write.py +20 -0
  426. opik/rest_api/types/experiment_status.py +5 -0
  427. opik/rest_api/types/feedback.py +25 -1
  428. opik/rest_api/types/feedback_create.py +20 -1
  429. opik/rest_api/types/feedback_object_public.py +27 -1
  430. opik/rest_api/types/feedback_public.py +25 -1
  431. opik/rest_api/types/feedback_score_batch_item.py +2 -1
  432. opik/rest_api/types/feedback_score_batch_item_thread.py +2 -1
  433. opik/rest_api/types/feedback_score_public.py +4 -0
  434. opik/rest_api/types/feedback_update.py +20 -1
  435. opik/rest_api/types/group_content_with_aggregations.py +1 -0
  436. opik/rest_api/types/group_detail.py +19 -0
  437. opik/rest_api/types/group_details.py +20 -0
  438. opik/rest_api/types/guardrail.py +1 -0
  439. opik/rest_api/types/guardrail_write.py +1 -0
  440. opik/rest_api/types/ids_holder.py +19 -0
  441. opik/rest_api/types/image_url.py +20 -0
  442. opik/rest_api/types/image_url_public.py +20 -0
  443. opik/rest_api/types/image_url_write.py +20 -0
  444. opik/rest_api/types/llm_as_judge_message.py +5 -1
  445. opik/rest_api/types/llm_as_judge_message_content.py +26 -0
  446. opik/rest_api/types/llm_as_judge_message_content_public.py +26 -0
  447. opik/rest_api/types/llm_as_judge_message_content_write.py +26 -0
  448. opik/rest_api/types/llm_as_judge_message_public.py +5 -1
  449. opik/rest_api/types/llm_as_judge_message_write.py +5 -1
  450. opik/rest_api/types/llm_as_judge_model_parameters.py +3 -0
  451. opik/rest_api/types/llm_as_judge_model_parameters_public.py +3 -0
  452. opik/rest_api/types/llm_as_judge_model_parameters_write.py +3 -0
  453. opik/rest_api/types/manual_evaluation_request.py +38 -0
  454. opik/rest_api/types/manual_evaluation_request_entity_type.py +5 -0
  455. opik/rest_api/types/manual_evaluation_response.py +27 -0
  456. opik/rest_api/types/optimization.py +4 -2
  457. opik/rest_api/types/optimization_public.py +4 -2
  458. opik/rest_api/types/optimization_public_status.py +3 -1
  459. opik/rest_api/types/optimization_status.py +3 -1
  460. opik/rest_api/types/optimization_studio_config.py +27 -0
  461. opik/rest_api/types/optimization_studio_config_public.py +27 -0
  462. opik/rest_api/types/optimization_studio_config_write.py +27 -0
  463. opik/rest_api/types/optimization_studio_log.py +22 -0
  464. opik/rest_api/types/optimization_write.py +4 -2
  465. opik/rest_api/types/optimization_write_status.py +3 -1
  466. opik/rest_api/types/project.py +1 -0
  467. opik/rest_api/types/project_detailed.py +1 -0
  468. opik/rest_api/types/project_reference.py +31 -0
  469. opik/rest_api/types/project_reference_public.py +31 -0
  470. opik/rest_api/types/project_stats_summary_item.py +1 -0
  471. opik/rest_api/types/prompt.py +6 -0
  472. opik/rest_api/types/prompt_detail.py +6 -0
  473. opik/rest_api/types/prompt_detail_template_structure.py +5 -0
  474. opik/rest_api/types/prompt_public.py +6 -0
  475. opik/rest_api/types/prompt_public_template_structure.py +5 -0
  476. opik/rest_api/types/prompt_template_structure.py +5 -0
  477. opik/rest_api/types/prompt_version.py +3 -0
  478. opik/rest_api/types/prompt_version_detail.py +3 -0
  479. opik/rest_api/types/prompt_version_detail_template_structure.py +5 -0
  480. opik/rest_api/types/prompt_version_link.py +1 -0
  481. opik/rest_api/types/prompt_version_link_public.py +1 -0
  482. opik/rest_api/types/prompt_version_page_public.py +5 -0
  483. opik/rest_api/types/prompt_version_public.py +3 -0
  484. opik/rest_api/types/prompt_version_public_template_structure.py +5 -0
  485. opik/rest_api/types/prompt_version_template_structure.py +5 -0
  486. opik/rest_api/types/prompt_version_update.py +33 -0
  487. opik/rest_api/types/provider_api_key.py +9 -0
  488. opik/rest_api/types/provider_api_key_provider.py +1 -1
  489. opik/rest_api/types/provider_api_key_public.py +9 -0
  490. opik/rest_api/types/provider_api_key_public_provider.py +1 -1
  491. opik/rest_api/types/score_name.py +1 -0
  492. opik/rest_api/types/service_toggles_config.py +18 -0
  493. opik/rest_api/types/span.py +1 -2
  494. opik/rest_api/types/span_enrichment_options.py +31 -0
  495. opik/rest_api/types/span_experiment_item_bulk_write_view.py +1 -2
  496. opik/rest_api/types/span_filter.py +23 -0
  497. opik/rest_api/types/span_filter_operator.py +21 -0
  498. opik/rest_api/types/span_filter_write.py +23 -0
  499. opik/rest_api/types/span_filter_write_operator.py +21 -0
  500. opik/rest_api/types/span_llm_as_judge_code.py +27 -0
  501. opik/rest_api/types/span_llm_as_judge_code_public.py +27 -0
  502. opik/rest_api/types/span_llm_as_judge_code_write.py +27 -0
  503. opik/rest_api/types/span_public.py +1 -2
  504. opik/rest_api/types/span_update.py +46 -0
  505. opik/rest_api/types/span_user_defined_metric_python_code.py +20 -0
  506. opik/rest_api/types/span_user_defined_metric_python_code_public.py +20 -0
  507. opik/rest_api/types/span_user_defined_metric_python_code_write.py +20 -0
  508. opik/rest_api/types/span_write.py +1 -2
  509. opik/rest_api/types/studio_evaluation.py +20 -0
  510. opik/rest_api/types/studio_evaluation_public.py +20 -0
  511. opik/rest_api/types/studio_evaluation_write.py +20 -0
  512. opik/rest_api/types/studio_llm_model.py +21 -0
  513. opik/rest_api/types/studio_llm_model_public.py +21 -0
  514. opik/rest_api/types/studio_llm_model_write.py +21 -0
  515. opik/rest_api/types/studio_message.py +20 -0
  516. opik/rest_api/types/studio_message_public.py +20 -0
  517. opik/rest_api/types/studio_message_write.py +20 -0
  518. opik/rest_api/types/studio_metric.py +21 -0
  519. opik/rest_api/types/studio_metric_public.py +21 -0
  520. opik/rest_api/types/studio_metric_write.py +21 -0
  521. opik/rest_api/types/studio_optimizer.py +21 -0
  522. opik/rest_api/types/studio_optimizer_public.py +21 -0
  523. opik/rest_api/types/studio_optimizer_write.py +21 -0
  524. opik/rest_api/types/studio_prompt.py +20 -0
  525. opik/rest_api/types/studio_prompt_public.py +20 -0
  526. opik/rest_api/types/studio_prompt_write.py +20 -0
  527. opik/rest_api/types/trace.py +11 -2
  528. opik/rest_api/types/trace_enrichment_options.py +32 -0
  529. opik/rest_api/types/trace_experiment_item_bulk_write_view.py +1 -2
  530. opik/rest_api/types/trace_filter.py +23 -0
  531. opik/rest_api/types/trace_filter_operator.py +21 -0
  532. opik/rest_api/types/trace_filter_write.py +23 -0
  533. opik/rest_api/types/trace_filter_write_operator.py +21 -0
  534. opik/rest_api/types/trace_public.py +11 -2
  535. opik/rest_api/types/trace_thread_filter_write.py +23 -0
  536. opik/rest_api/types/trace_thread_filter_write_operator.py +21 -0
  537. opik/rest_api/types/trace_thread_identifier.py +1 -0
  538. opik/rest_api/types/trace_update.py +39 -0
  539. opik/rest_api/types/trace_write.py +1 -2
  540. opik/rest_api/types/value_entry.py +2 -0
  541. opik/rest_api/types/value_entry_compare.py +2 -0
  542. opik/rest_api/types/value_entry_experiment_item_bulk_write_view.py +2 -0
  543. opik/rest_api/types/value_entry_public.py +2 -0
  544. opik/rest_api/types/video_url.py +19 -0
  545. opik/rest_api/types/video_url_public.py +19 -0
  546. opik/rest_api/types/video_url_write.py +19 -0
  547. opik/rest_api/types/webhook.py +28 -0
  548. opik/rest_api/types/webhook_examples.py +19 -0
  549. opik/rest_api/types/webhook_public.py +28 -0
  550. opik/rest_api/types/webhook_test_result.py +23 -0
  551. opik/rest_api/types/webhook_test_result_status.py +5 -0
  552. opik/rest_api/types/webhook_write.py +23 -0
  553. opik/rest_api/types/welcome_wizard_tracking.py +22 -0
  554. opik/rest_api/types/workspace_configuration.py +5 -0
  555. opik/rest_api/welcome_wizard/__init__.py +4 -0
  556. opik/rest_api/welcome_wizard/client.py +195 -0
  557. opik/rest_api/welcome_wizard/raw_client.py +208 -0
  558. opik/rest_api/workspaces/client.py +14 -2
  559. opik/rest_api/workspaces/raw_client.py +10 -0
  560. opik/s3_httpx_client.py +14 -1
  561. opik/simulation/__init__.py +6 -0
  562. opik/simulation/simulated_user.py +99 -0
  563. opik/simulation/simulator.py +108 -0
  564. opik/synchronization.py +5 -6
  565. opik/{decorator/tracing_runtime_config.py → tracing_runtime_config.py} +6 -7
  566. opik/types.py +36 -0
  567. opik/validation/chat_prompt_messages.py +241 -0
  568. opik/validation/feedback_score.py +3 -3
  569. opik/validation/validator.py +28 -0
  570. opik-1.9.71.dist-info/METADATA +370 -0
  571. opik-1.9.71.dist-info/RECORD +1110 -0
  572. opik/api_objects/prompt/prompt.py +0 -112
  573. opik/cli.py +0 -193
  574. opik/hooks.py +0 -13
  575. opik/integrations/bedrock/chunks_aggregator.py +0 -55
  576. opik/integrations/bedrock/helpers.py +0 -8
  577. opik/rest_api/types/automation_rule_evaluator_object_public.py +0 -100
  578. opik/rest_api/types/json_node_experiment_item_bulk_write_view.py +0 -5
  579. opik-1.8.39.dist-info/METADATA +0 -339
  580. opik-1.8.39.dist-info/RECORD +0 -790
  581. /opik/{evaluation/metrics/conversation/conversational_coherence → decorator/context_manager}/__init__.py +0 -0
  582. /opik/evaluation/metrics/conversation/{session_completeness → llm_judges/conversational_coherence}/__init__.py +0 -0
  583. /opik/evaluation/metrics/conversation/{conversational_coherence → llm_judges/conversational_coherence}/schema.py +0 -0
  584. /opik/evaluation/metrics/conversation/{user_frustration → llm_judges/session_completeness}/__init__.py +0 -0
  585. /opik/evaluation/metrics/conversation/{session_completeness → llm_judges/session_completeness}/schema.py +0 -0
  586. /opik/evaluation/metrics/conversation/{user_frustration → llm_judges/user_frustration}/schema.py +0 -0
  587. /opik/integrations/bedrock/{stream_wrappers.py → converse/stream_wrappers.py} +0 -0
  588. /opik/rest_api/{spans/types → types}/span_update_type.py +0 -0
  589. {opik-1.8.39.dist-info → opik-1.9.71.dist-info}/WHEEL +0 -0
  590. {opik-1.8.39.dist-info → opik-1.9.71.dist-info}/entry_points.txt +0 -0
  591. {opik-1.8.39.dist-info → opik-1.9.71.dist-info}/licenses/LICENSE +0 -0
  592. {opik-1.8.39.dist-info → opik-1.9.71.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,140 @@
1
+ import datetime
2
+ import logging
3
+ from typing import List, Dict, Optional, Any, Union
4
+
5
+ from opik.types import ErrorInfoDict, SpanType
6
+ from . import models, emulator_message_processor
7
+ from .. import messages
8
+ from ...rest_api.types import span_write, trace_write
9
+
10
+
11
+ LOGGER = logging.getLogger(__name__)
12
+
13
+
14
+ class LocalEmulatorMessageProcessor(
15
+ emulator_message_processor.EmulatorMessageProcessor
16
+ ):
17
+ """This class serves as a replacement for the real backend and collects all logged messages
18
+ locally in memory to be used for evaluation.
19
+ """
20
+
21
+ def __init__(self, active: bool, merge_duplicates: bool = True) -> None:
22
+ super().__init__(active=active, merge_duplicates=merge_duplicates)
23
+
24
+ def process(
25
+ self,
26
+ message: Union[
27
+ messages.BaseMessage, span_write.SpanWrite, trace_write.TraceWrite
28
+ ],
29
+ ) -> None:
30
+ if not self.is_active():
31
+ return
32
+
33
+ if hasattr(message, "delivery_attempts") and message.delivery_attempts > 1:
34
+ # skip retries
35
+ LOGGER.debug("Skipping retry of the message: %s", message)
36
+ return
37
+
38
+ super().process(message)
39
+
40
+ def create_trace_model(
41
+ self,
42
+ trace_id: str,
43
+ start_time: datetime.datetime,
44
+ name: Optional[str],
45
+ project_name: str,
46
+ input: Any,
47
+ output: Any,
48
+ tags: Optional[List[str]],
49
+ metadata: Optional[Dict[str, Any]],
50
+ end_time: Optional[datetime.datetime],
51
+ spans: Optional[List[models.SpanModel]],
52
+ feedback_scores: Optional[List[models.FeedbackScoreModel]],
53
+ error_info: Optional[ErrorInfoDict],
54
+ thread_id: Optional[str],
55
+ last_updated_at: Optional[datetime.datetime] = None,
56
+ ) -> models.TraceModel:
57
+ if spans is None:
58
+ spans = []
59
+ if feedback_scores is None:
60
+ feedback_scores = []
61
+
62
+ return models.TraceModel(
63
+ id=trace_id,
64
+ start_time=start_time,
65
+ name=name,
66
+ project_name=project_name,
67
+ input=input,
68
+ output=output,
69
+ tags=tags,
70
+ metadata=metadata,
71
+ end_time=end_time,
72
+ spans=spans,
73
+ feedback_scores=feedback_scores,
74
+ error_info=error_info,
75
+ thread_id=thread_id,
76
+ last_updated_at=last_updated_at,
77
+ )
78
+
79
+ def create_span_model(
80
+ self,
81
+ span_id: str,
82
+ start_time: datetime.datetime,
83
+ name: Optional[str],
84
+ input: Any,
85
+ output: Any,
86
+ tags: Optional[List[str]],
87
+ metadata: Optional[Dict[str, Any]],
88
+ type: SpanType,
89
+ usage: Optional[Dict[str, Any]],
90
+ end_time: Optional[datetime.datetime],
91
+ project_name: str,
92
+ spans: Optional[List[models.SpanModel]],
93
+ feedback_scores: Optional[List[models.FeedbackScoreModel]],
94
+ model: Optional[str],
95
+ provider: Optional[str],
96
+ error_info: Optional[ErrorInfoDict],
97
+ total_cost: Optional[float],
98
+ last_updated_at: Optional[datetime.datetime],
99
+ ) -> models.SpanModel:
100
+ if spans is None:
101
+ spans = []
102
+ if feedback_scores is None:
103
+ feedback_scores = []
104
+
105
+ return models.SpanModel(
106
+ id=span_id,
107
+ start_time=start_time,
108
+ name=name,
109
+ input=input,
110
+ output=output,
111
+ tags=tags,
112
+ metadata=metadata,
113
+ type=type,
114
+ usage=usage,
115
+ end_time=end_time,
116
+ project_name=project_name,
117
+ spans=spans,
118
+ feedback_scores=feedback_scores,
119
+ model=model,
120
+ provider=provider,
121
+ error_info=error_info,
122
+ total_cost=total_cost,
123
+ last_updated_at=last_updated_at,
124
+ )
125
+
126
+ def create_feedback_score_model(
127
+ self,
128
+ score_id: str,
129
+ name: str,
130
+ value: float,
131
+ category_name: Optional[str],
132
+ reason: Optional[str],
133
+ ) -> models.FeedbackScoreModel:
134
+ return models.FeedbackScoreModel(
135
+ id=score_id,
136
+ name=name,
137
+ value=value,
138
+ category_name=category_name,
139
+ reason=reason,
140
+ )
@@ -0,0 +1,162 @@
1
+ from typing import List, Any, Optional, Dict
2
+
3
+ from opik.config import OPIK_PROJECT_DEFAULT_NAME
4
+ from opik.types import ErrorInfoDict
5
+
6
+ import dataclasses
7
+ import datetime
8
+
9
+
10
+ @dataclasses.dataclass
11
+ class FeedbackScoreModel:
12
+ """
13
+ Represents a model for a feedback score used to evaluate specific spans or traces.
14
+
15
+ This class stores and manages feedback scores linked to defined criteria, including
16
+ identifiers, names, values, categories, and explanations for each score.
17
+
18
+ Attributes:
19
+ id: Unique identifier for the feedback score.
20
+ name: Name associated with the feedback score.
21
+ value: The numerical value of the feedback score.
22
+ category_name: Category to which the feedback score belongs, if any.
23
+ reason: Reason or explanation for the feedback score, if available.
24
+ """
25
+
26
+ id: str
27
+ name: str
28
+ value: float
29
+ category_name: Optional[str] = None
30
+ reason: Optional[str] = None
31
+
32
+
33
+ @dataclasses.dataclass
34
+ class SpanModel:
35
+ """
36
+ Represents a span model used to describe specific points in a process, their metadata,
37
+ and associated data.
38
+
39
+ This class is used to store and manipulate structured data for events or
40
+ spans, including metadata, time markers, associated input/output, tags,
41
+ and additional properties. It serves as a representative structure for recording
42
+ and organizing event-specific information, often used in applications like
43
+ logging, distributed tracing, or data processing pipelines.
44
+
45
+ Attributes:
46
+ id: Unique identifier for the span.
47
+ start_time: Start time of the span.
48
+ name: Name of the span, if provided.
49
+ input: Input data associated with the span, if any.
50
+ output: Output data associated with the span, if any.
51
+ tags: List of tags linked to the span.
52
+ metadata: Additional metadata for the span.
53
+ type: Type of the span, defaulting to "general".
54
+ usage: Usage-related information for the span.
55
+ end_time: End time of the span, if available.
56
+ project_name: Name of the project the span is associated with,
57
+ defaulting to a predefined project name.
58
+ spans: List of nested spans related to this span.
59
+ feedback_scores: List of feedback scores associated
60
+ with the span.
61
+ model: Model identification used, if applicable.
62
+ provider: Provider of the span or associated services, if any.
63
+ error_info: Error information or diagnostics
64
+ for the span, if applicable.
65
+ total_cost: Total cost incurred associated with this span,
66
+ if relevant.
67
+ last_updated_at: Timestamp of when the span was
68
+ last updated, if available.
69
+ """
70
+
71
+ id: str
72
+ start_time: datetime.datetime
73
+ name: Optional[str] = None
74
+ input: Optional[Dict[str, Any]] = None
75
+ output: Optional[Dict[str, Any]] = None
76
+ tags: Optional[List[str]] = None
77
+ metadata: Optional[Dict[str, Any]] = None
78
+ type: str = "general"
79
+ usage: Optional[Dict[str, Any]] = None
80
+ end_time: Optional[datetime.datetime] = None
81
+ project_name: str = OPIK_PROJECT_DEFAULT_NAME
82
+ spans: List["SpanModel"] = dataclasses.field(default_factory=list)
83
+ feedback_scores: List[FeedbackScoreModel] = dataclasses.field(default_factory=list)
84
+ model: Optional[str] = None
85
+ provider: Optional[str] = None
86
+ error_info: Optional[ErrorInfoDict] = None
87
+ total_cost: Optional[float] = None
88
+ last_updated_at: Optional[datetime.datetime] = None
89
+
90
+
91
+ @dataclasses.dataclass
92
+ class ExperimentItemModel:
93
+ """
94
+ Represents an experiment item model that links a trace to a dataset item in an experiment.
95
+
96
+ Attributes:
97
+ id: Unique identifier for the experiment item.
98
+ experiment_id: The ID of the experiment this item belongs to.
99
+ trace_id: The ID of the trace associated with this experiment item.
100
+ dataset_item_id: The ID of the dataset item associated with this experiment item.
101
+ """
102
+
103
+ id: str
104
+ experiment_id: str
105
+ trace_id: str
106
+ dataset_item_id: str
107
+
108
+
109
+ @dataclasses.dataclass
110
+ class TraceModel:
111
+ """
112
+ Represents a trace model that encapsulates data about a trace, its related metadata,
113
+ and associated spans. It is used for tracking and analyzing data during execution
114
+ or processing tasks.
115
+
116
+ This class provides a structure to represent trace information, including the start
117
+ and end times, associated project details, input/output data, feedback scores, error
118
+ information, and thread association. It is designed to handle optional fields for
119
+ flexible use across various scenarios.
120
+
121
+ Attributes:
122
+ id: Unique identifier for the trace.
123
+ start_time: Timestamp representing the start of the trace.
124
+ name: Optional name for the trace, which can provide a descriptive
125
+ label.
126
+ project_name: Name of the project associated with the trace.
127
+ input: Optional dictionary containing the input data
128
+ associated with the trace.
129
+ output: Optional dictionary containing the output data
130
+ generated by the trace.
131
+ tags: Optional list of tags associated with the trace for
132
+ classification or filtering purposes.
133
+ metadata: Optional metadata providing additional
134
+ information about the trace.
135
+ end_time: Timestamp representing the end of the
136
+ trace.
137
+ spans: List of spans associated with the trace, representing
138
+ individual processing parts or segments within the trace.
139
+ feedback_scores: List of feedback scores associated
140
+ with the trace.
141
+ error_info: Optional dictionary containing information
142
+ about errors encountered during the trace.
143
+ thread_id: Optional identifier of the thread associated with the
144
+ trace.
145
+ last_updated_at: Timestamp for when the trace was
146
+ last updated.
147
+ """
148
+
149
+ id: str
150
+ start_time: datetime.datetime
151
+ name: Optional[str]
152
+ project_name: str
153
+ input: Optional[Dict[str, Any]] = None
154
+ output: Optional[Dict[str, Any]] = None
155
+ tags: Optional[List[str]] = None
156
+ metadata: Optional[Dict[str, Any]] = None
157
+ end_time: Optional[datetime.datetime] = None
158
+ spans: List[SpanModel] = dataclasses.field(default_factory=list)
159
+ feedback_scores: List[FeedbackScoreModel] = dataclasses.field(default_factory=list)
160
+ error_info: Optional[ErrorInfoDict] = None
161
+ thread_id: Optional[str] = None
162
+ last_updated_at: Optional[datetime.datetime] = None
@@ -0,0 +1,79 @@
1
+ from typing import Dict, Any, Set, List, Literal
2
+
3
+ import opik.hooks
4
+
5
+ from .. import jsonable_encoder
6
+ from ..anonymizer import anonymizer
7
+
8
+
9
+ def encode_and_anonymize(
10
+ kwargs_dict: Dict[str, Any],
11
+ fields_to_anonymize: Set[str],
12
+ object_type: Literal["span", "trace"],
13
+ ) -> Dict[str, Any]:
14
+ """
15
+ Encodes and anonymizes the data in the given dictionary based on the specified
16
+ fields using registered anonymizers. If no anonymizers are registered, the
17
+ function simply encodes the dictionary without anonymization.
18
+
19
+ Args:
20
+ kwargs_dict: The dictionary containing the data to encode
21
+ and anonymize.
22
+ fields_to_anonymize: The set of fields within the dictionary to
23
+ anonymize.
24
+ object_type: A string indicating the type of object ('span' or 'trace')
25
+ that was used to create the kwargs_dict. This is passed to anonymizers
26
+ to provide context about the source object.
27
+
28
+ Returns:
29
+ A dictionary that has been encoded and, if applicable, anonymized.
30
+ """
31
+ # check if any anonymizer was registered
32
+ encoded_obj = jsonable_encoder.encode(kwargs_dict)
33
+ if not opik.hooks.has_anonymizers():
34
+ return encoded_obj
35
+
36
+ anonymizers = opik.hooks.get_anonymizers()
37
+ return anonymize_encoded_obj(
38
+ obj=encoded_obj,
39
+ fields_to_anonymize=fields_to_anonymize,
40
+ anonymizers=anonymizers,
41
+ object_type=object_type,
42
+ )
43
+
44
+
45
+ def anonymize_encoded_obj(
46
+ obj: Dict[str, Any],
47
+ fields_to_anonymize: Set[str],
48
+ anonymizers: List[anonymizer.Anonymizer],
49
+ object_type: Literal["span", "trace"],
50
+ ) -> Dict[str, Any]:
51
+ """
52
+ Anonymizes specified fields in an encoded dictionary using the provided anonymizers.
53
+ This function iterates over the given set of field names and applies each anonymizer
54
+ to the corresponding field in the dictionary, if present. The anonymizers are expected
55
+ to implement an `anonymize` method that takes the field value, field name, and object type
56
+ as arguments. Only fields present in the dictionary and listed in `fields_to_anonymize`
57
+ are anonymized.
58
+
59
+ Args:
60
+ obj: The encoded dictionary whose fields are to be anonymized.
61
+ fields_to_anonymize: A set of field names within the dictionary to anonymize.
62
+ anonymizers: A list of anonymizer instances to apply to each field.
63
+ object_type: A string indicating the type of object ('span' or 'trace'),
64
+ providing context for anonymization.
65
+
66
+ Returns:
67
+ The dictionary with specified fields anonymized using the provided anonymizers.
68
+ """
69
+ if isinstance(obj, dict):
70
+ for field_name in fields_to_anonymize:
71
+ if field_name in obj:
72
+ for anonymizer_instance in anonymizers:
73
+ obj[field_name] = anonymizer_instance.anonymize(
74
+ obj[field_name],
75
+ field_name=field_name,
76
+ object_type=object_type,
77
+ )
78
+
79
+ return obj
@@ -1,9 +1,10 @@
1
1
  import dataclasses
2
2
  import datetime
3
3
  from dataclasses import field
4
- from typing import Optional, Any, Dict, List, Union, Literal
4
+ from typing import Optional, Any, Dict, List, Union, Literal, Set
5
5
 
6
6
  from . import arguments_utils
7
+ from .preprocessing import constants
7
8
  from ..rest_api.types import span_write, trace_write
8
9
  from ..types import SpanType, ErrorInfoDict, LLMProvider, AttachmentEntityType
9
10
 
@@ -11,6 +12,7 @@ from ..types import SpanType, ErrorInfoDict, LLMProvider, AttachmentEntityType
11
12
  @dataclasses.dataclass
12
13
  class BaseMessage:
13
14
  delivery_time: float = field(init=False, default=0.0)
15
+ delivery_attempts: int = field(init=False, default=1)
14
16
 
15
17
  def as_payload_dict(self) -> Dict[str, Any]:
16
18
  # we are not using dataclasses.as_dict() here
@@ -18,6 +20,10 @@ class BaseMessage:
18
20
  data = {**self.__dict__}
19
21
  if "delivery_time" in data:
20
22
  data.pop("delivery_time")
23
+ if "delivery_attempts" in data:
24
+ data.pop("delivery_attempts")
25
+ if constants.MARKER_ATTRIBUTE_NAME in data:
26
+ data.pop(constants.MARKER_ATTRIBUTE_NAME)
21
27
  return data
22
28
 
23
29
 
@@ -47,6 +53,10 @@ class CreateTraceMessage(BaseMessage):
47
53
  data["id"] = data.pop("trace_id")
48
54
  return data
49
55
 
56
+ @staticmethod
57
+ def fields_to_anonymize() -> Set[str]:
58
+ return {"input", "output", "metadata"}
59
+
50
60
 
51
61
  @dataclasses.dataclass
52
62
  class UpdateTraceMessage(BaseMessage):
@@ -75,6 +85,10 @@ class UpdateTraceMessage(BaseMessage):
75
85
  data["id"] = data.pop("trace_id")
76
86
  return data
77
87
 
88
+ @staticmethod
89
+ def fields_to_anonymize() -> Set[str]:
90
+ return {"input", "output", "metadata"}
91
+
78
92
 
79
93
  @dataclasses.dataclass
80
94
  class CreateSpanMessage(BaseMessage):
@@ -109,6 +123,10 @@ class CreateSpanMessage(BaseMessage):
109
123
  data["total_estimated_cost"] = data.pop("total_cost")
110
124
  return data
111
125
 
126
+ @staticmethod
127
+ def fields_to_anonymize() -> Set[str]:
128
+ return {"input", "output", "metadata"}
129
+
112
130
 
113
131
  @dataclasses.dataclass
114
132
  class UpdateSpanMessage(BaseMessage):
@@ -141,6 +159,10 @@ class UpdateSpanMessage(BaseMessage):
141
159
  data["total_estimated_cost"] = data.pop("total_cost")
142
160
  return data
143
161
 
162
+ @staticmethod
163
+ def fields_to_anonymize() -> Set[str]:
164
+ return {"input", "output", "metadata"}
165
+
144
166
 
145
167
  @dataclasses.dataclass
146
168
  class FeedbackScoreMessage(BaseMessage):
@@ -197,11 +219,19 @@ class AddThreadsFeedbackScoresBatchMessage(BaseMessage):
197
219
  class CreateSpansBatchMessage(BaseMessage):
198
220
  batch: List[span_write.SpanWrite]
199
221
 
222
+ @staticmethod
223
+ def fields_to_anonymize() -> Set[str]:
224
+ return {"input", "output", "metadata"}
225
+
200
226
 
201
227
  @dataclasses.dataclass
202
228
  class CreateTraceBatchMessage(BaseMessage):
203
229
  batch: List[trace_write.TraceWrite]
204
230
 
231
+ @staticmethod
232
+ def fields_to_anonymize() -> Set[str]:
233
+ return {"input", "output", "metadata"}
234
+
205
235
 
206
236
  @dataclasses.dataclass
207
237
  class GuardrailBatchItemMessage(BaseMessage):
@@ -230,6 +260,25 @@ class GuardrailBatchMessage(BaseMessage):
230
260
  return data
231
261
 
232
262
 
263
+ @dataclasses.dataclass
264
+ class ExperimentItemMessage(BaseMessage):
265
+ """
266
+ There is no handler for that in the message processor, it exists
267
+ only as an item of CreateExperimentItemsBatchMessage
268
+ """
269
+
270
+ id: str
271
+ experiment_id: str
272
+ trace_id: str
273
+ dataset_item_id: str
274
+
275
+
276
+ @dataclasses.dataclass
277
+ class CreateExperimentItemsBatchMessage(BaseMessage):
278
+ batch: List[ExperimentItemMessage]
279
+ supports_batching: bool = True
280
+
281
+
233
282
  @dataclasses.dataclass
234
283
  class CreateAttachmentMessage(BaseMessage):
235
284
  file_path: str
@@ -239,3 +288,9 @@ class CreateAttachmentMessage(BaseMessage):
239
288
  entity_id: str
240
289
  project_name: str
241
290
  encoded_url_override: str
291
+ delete_after_upload: bool = False
292
+
293
+
294
+ @dataclasses.dataclass
295
+ class AttachmentSupportingMessage(BaseMessage):
296
+ original_message: BaseMessage
File without changes
@@ -0,0 +1,70 @@
1
+ from typing import Optional, Union
2
+
3
+ from opik.message_processing import messages
4
+
5
+ from . import constants, preprocessor
6
+
7
+
8
+ class AttachmentsPreprocessor(preprocessor.MessagePreprocessor):
9
+ def __init__(self, enabled: bool = True) -> None:
10
+ self._enabled = enabled
11
+
12
+ def preprocess(
13
+ self, message: Optional[messages.BaseMessage]
14
+ ) -> Optional[messages.BaseMessage]:
15
+ """
16
+ Processes a given message and ensures that it is converted into a specialized
17
+ message type if applicable. If the message is already pre-processed, it
18
+ returns the original message to avoid infinite recursion.
19
+
20
+ Args:
21
+ message: The message object to be processed.
22
+
23
+ Returns:
24
+ The processed message, either in its original form
25
+ or converted into a message type supporting embedded attachments.
26
+ """
27
+ if not self._enabled:
28
+ return message
29
+
30
+ if message is None:
31
+ # possibly already pre-processed by other preprocessors
32
+ return None
33
+
34
+ if hasattr(message, constants.MARKER_ATTRIBUTE_NAME):
35
+ # already pre-processed - just return the original message to avoid infinite recursion
36
+ return message
37
+
38
+ if _has_potential_content_with_attachments(message):
39
+ return messages.AttachmentSupportingMessage(message)
40
+ else:
41
+ return message
42
+
43
+
44
+ def _has_potential_content_with_attachments(message: messages.BaseMessage) -> bool:
45
+ # Check if it's an Update message - always process these
46
+ if isinstance(message, (messages.UpdateSpanMessage, messages.UpdateTraceMessage)):
47
+ return _message_has_field_of_interest_set(message)
48
+
49
+ # Check if it's a Create message with end_time set - only process these
50
+ if isinstance(message, (messages.CreateSpanMessage, messages.CreateTraceMessage)):
51
+ if message.end_time is not None:
52
+ return _message_has_field_of_interest_set(message)
53
+
54
+ # All other message types should not be wrapped
55
+ return False
56
+
57
+
58
+ def _message_has_field_of_interest_set(
59
+ message: Union[
60
+ messages.UpdateSpanMessage,
61
+ messages.UpdateTraceMessage,
62
+ messages.CreateSpanMessage,
63
+ messages.CreateTraceMessage,
64
+ ],
65
+ ) -> bool:
66
+ return (
67
+ message.input is not None
68
+ or message.output is not None
69
+ or message.metadata is not None
70
+ )
@@ -0,0 +1,53 @@
1
+ from typing import Optional
2
+
3
+ from . import preprocessor
4
+ from .. import messages
5
+ from ..batching import batch_manager
6
+
7
+
8
+ class BatchingPreprocessor(preprocessor.MessagePreprocessor):
9
+ """
10
+ Handles message batching during preprocessing.
11
+
12
+ The BatchingPreprocessor class processes messages, enabling efficient message
13
+ batching if a batching manager is provided. It supports starting, stopping,
14
+ flushing, and checking the state of the batching manager, ensuring that
15
+ messages are processed or delegated based on their batching capabilities.
16
+ """
17
+
18
+ def __init__(self, batching_manager: Optional[batch_manager.BatchManager]) -> None:
19
+ self._batch_manager = batching_manager
20
+
21
+ def preprocess(
22
+ self, message: Optional[messages.BaseMessage]
23
+ ) -> Optional[messages.BaseMessage]:
24
+ if message is None:
25
+ # possibly already processed
26
+ return None
27
+
28
+ if (
29
+ self._batch_manager is not None
30
+ and self._batch_manager.message_supports_batching(message)
31
+ ):
32
+ self._batch_manager.process_message(message)
33
+ return None
34
+
35
+ return message
36
+
37
+ def start(self) -> None:
38
+ if self._batch_manager is not None:
39
+ self._batch_manager.start()
40
+
41
+ def stop(self) -> None:
42
+ if self._batch_manager is not None:
43
+ self._batch_manager.stop()
44
+
45
+ def flush(self) -> None:
46
+ if self._batch_manager is not None:
47
+ self._batch_manager.flush()
48
+
49
+ def is_empty(self) -> bool:
50
+ if self._batch_manager is not None:
51
+ return self._batch_manager.is_empty()
52
+
53
+ return True
@@ -0,0 +1 @@
1
+ MARKER_ATTRIBUTE_NAME = "_preprocessed_for_attachments"
@@ -0,0 +1,38 @@
1
+ from typing import Optional
2
+
3
+ from opik.file_upload import base_upload_manager
4
+
5
+ from . import preprocessor
6
+ from .. import messages
7
+
8
+
9
+ class FileUploadPreprocessor(preprocessor.MessagePreprocessor):
10
+ """
11
+ Preprocesses messages to handle file uploads.
12
+
13
+ This class is responsible for processing messages to determine if they support
14
+ file uploads and delegating the upload task to a file upload manager. It also
15
+ provides functionality to flush pending uploads with configurable timeout and
16
+ sleep intervals.
17
+ """
18
+
19
+ def __init__(
20
+ self, file_upload_manager: base_upload_manager.BaseFileUploadManager
21
+ ) -> None:
22
+ self._file_upload_manager = file_upload_manager
23
+
24
+ def preprocess(
25
+ self, message: Optional[messages.BaseMessage]
26
+ ) -> Optional[messages.BaseMessage]:
27
+ if message is None:
28
+ # possibly already processed
29
+ return None
30
+
31
+ if base_upload_manager.message_supports_upload(message):
32
+ self._file_upload_manager.upload(message)
33
+ return None
34
+
35
+ return message
36
+
37
+ def flush(self, timeout: Optional[float], sleep_time: int) -> bool:
38
+ return self._file_upload_manager.flush(timeout=timeout, sleep_time=sleep_time)