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.
- opik/__init__.py +19 -3
- opik/anonymizer/__init__.py +5 -0
- opik/anonymizer/anonymizer.py +12 -0
- opik/anonymizer/factory.py +80 -0
- opik/anonymizer/recursive_anonymizer.py +64 -0
- opik/anonymizer/rules.py +56 -0
- opik/anonymizer/rules_anonymizer.py +35 -0
- opik/api_objects/attachment/attachment_context.py +36 -0
- opik/api_objects/attachment/attachments_extractor.py +153 -0
- opik/api_objects/attachment/client.py +1 -0
- opik/api_objects/attachment/converters.py +2 -0
- opik/api_objects/attachment/decoder.py +18 -0
- opik/api_objects/attachment/decoder_base64.py +83 -0
- opik/api_objects/attachment/decoder_helpers.py +137 -0
- opik/api_objects/data_helpers.py +79 -0
- opik/api_objects/dataset/dataset.py +64 -4
- opik/api_objects/dataset/rest_operations.py +11 -2
- opik/api_objects/experiment/experiment.py +57 -57
- opik/api_objects/experiment/experiment_item.py +2 -1
- opik/api_objects/experiment/experiments_client.py +64 -0
- opik/api_objects/experiment/helpers.py +35 -11
- opik/api_objects/experiment/rest_operations.py +65 -5
- opik/api_objects/helpers.py +8 -5
- opik/api_objects/local_recording.py +81 -0
- opik/api_objects/opik_client.py +600 -108
- opik/api_objects/opik_query_language.py +39 -5
- opik/api_objects/prompt/__init__.py +12 -2
- opik/api_objects/prompt/base_prompt.py +69 -0
- opik/api_objects/prompt/base_prompt_template.py +29 -0
- opik/api_objects/prompt/chat/__init__.py +1 -0
- opik/api_objects/prompt/chat/chat_prompt.py +210 -0
- opik/api_objects/prompt/chat/chat_prompt_template.py +350 -0
- opik/api_objects/prompt/chat/content_renderer_registry.py +203 -0
- opik/api_objects/prompt/client.py +189 -47
- opik/api_objects/prompt/text/__init__.py +1 -0
- opik/api_objects/prompt/text/prompt.py +174 -0
- opik/api_objects/prompt/{prompt_template.py → text/prompt_template.py} +10 -6
- opik/api_objects/prompt/types.py +23 -0
- opik/api_objects/search_helpers.py +89 -0
- opik/api_objects/span/span_data.py +35 -25
- opik/api_objects/threads/threads_client.py +39 -5
- opik/api_objects/trace/trace_client.py +52 -2
- opik/api_objects/trace/trace_data.py +15 -24
- opik/api_objects/validation_helpers.py +3 -3
- opik/cli/__init__.py +5 -0
- opik/cli/__main__.py +6 -0
- opik/cli/configure.py +66 -0
- opik/cli/exports/__init__.py +131 -0
- opik/cli/exports/dataset.py +278 -0
- opik/cli/exports/experiment.py +784 -0
- opik/cli/exports/project.py +685 -0
- opik/cli/exports/prompt.py +578 -0
- opik/cli/exports/utils.py +406 -0
- opik/cli/harbor.py +39 -0
- opik/cli/healthcheck.py +21 -0
- opik/cli/imports/__init__.py +439 -0
- opik/cli/imports/dataset.py +143 -0
- opik/cli/imports/experiment.py +1192 -0
- opik/cli/imports/project.py +262 -0
- opik/cli/imports/prompt.py +177 -0
- opik/cli/imports/utils.py +280 -0
- opik/cli/main.py +49 -0
- opik/cli/proxy.py +93 -0
- opik/cli/usage_report/__init__.py +16 -0
- opik/cli/usage_report/charts.py +783 -0
- opik/cli/usage_report/cli.py +274 -0
- opik/cli/usage_report/constants.py +9 -0
- opik/cli/usage_report/extraction.py +749 -0
- opik/cli/usage_report/pdf.py +244 -0
- opik/cli/usage_report/statistics.py +78 -0
- opik/cli/usage_report/utils.py +235 -0
- opik/config.py +13 -7
- opik/configurator/configure.py +17 -0
- opik/datetime_helpers.py +12 -0
- opik/decorator/arguments_helpers.py +9 -1
- opik/decorator/base_track_decorator.py +205 -133
- opik/decorator/context_manager/span_context_manager.py +123 -0
- opik/decorator/context_manager/trace_context_manager.py +84 -0
- opik/decorator/opik_args/__init__.py +13 -0
- opik/decorator/opik_args/api_classes.py +71 -0
- opik/decorator/opik_args/helpers.py +120 -0
- opik/decorator/span_creation_handler.py +25 -6
- opik/dict_utils.py +3 -3
- opik/evaluation/__init__.py +13 -2
- opik/evaluation/engine/engine.py +272 -75
- opik/evaluation/engine/evaluation_tasks_executor.py +6 -3
- opik/evaluation/engine/helpers.py +31 -6
- opik/evaluation/engine/metrics_evaluator.py +237 -0
- opik/evaluation/evaluation_result.py +168 -2
- opik/evaluation/evaluator.py +533 -62
- opik/evaluation/metrics/__init__.py +103 -4
- opik/evaluation/metrics/aggregated_metric.py +35 -6
- opik/evaluation/metrics/base_metric.py +1 -1
- opik/evaluation/metrics/conversation/__init__.py +48 -0
- opik/evaluation/metrics/conversation/conversation_thread_metric.py +56 -2
- opik/evaluation/metrics/conversation/g_eval_wrappers.py +19 -0
- opik/evaluation/metrics/conversation/helpers.py +14 -15
- opik/evaluation/metrics/conversation/heuristics/__init__.py +14 -0
- opik/evaluation/metrics/conversation/heuristics/degeneration/__init__.py +3 -0
- opik/evaluation/metrics/conversation/heuristics/degeneration/metric.py +189 -0
- opik/evaluation/metrics/conversation/heuristics/degeneration/phrases.py +12 -0
- opik/evaluation/metrics/conversation/heuristics/knowledge_retention/__init__.py +3 -0
- opik/evaluation/metrics/conversation/heuristics/knowledge_retention/metric.py +172 -0
- opik/evaluation/metrics/conversation/llm_judges/__init__.py +32 -0
- opik/evaluation/metrics/conversation/{conversational_coherence → llm_judges/conversational_coherence}/metric.py +22 -17
- opik/evaluation/metrics/conversation/{conversational_coherence → llm_judges/conversational_coherence}/templates.py +1 -1
- opik/evaluation/metrics/conversation/llm_judges/g_eval_wrappers.py +442 -0
- opik/evaluation/metrics/conversation/{session_completeness → llm_judges/session_completeness}/metric.py +13 -7
- opik/evaluation/metrics/conversation/{session_completeness → llm_judges/session_completeness}/templates.py +1 -1
- opik/evaluation/metrics/conversation/llm_judges/user_frustration/__init__.py +0 -0
- opik/evaluation/metrics/conversation/{user_frustration → llm_judges/user_frustration}/metric.py +21 -14
- opik/evaluation/metrics/conversation/{user_frustration → llm_judges/user_frustration}/templates.py +1 -1
- opik/evaluation/metrics/conversation/types.py +4 -5
- opik/evaluation/metrics/conversation_types.py +9 -0
- opik/evaluation/metrics/heuristics/bertscore.py +107 -0
- opik/evaluation/metrics/heuristics/bleu.py +35 -15
- opik/evaluation/metrics/heuristics/chrf.py +127 -0
- opik/evaluation/metrics/heuristics/contains.py +47 -11
- opik/evaluation/metrics/heuristics/distribution_metrics.py +331 -0
- opik/evaluation/metrics/heuristics/gleu.py +113 -0
- opik/evaluation/metrics/heuristics/language_adherence.py +123 -0
- opik/evaluation/metrics/heuristics/meteor.py +119 -0
- opik/evaluation/metrics/heuristics/prompt_injection.py +150 -0
- opik/evaluation/metrics/heuristics/readability.py +129 -0
- opik/evaluation/metrics/heuristics/rouge.py +26 -9
- opik/evaluation/metrics/heuristics/spearman.py +88 -0
- opik/evaluation/metrics/heuristics/tone.py +155 -0
- opik/evaluation/metrics/heuristics/vader_sentiment.py +77 -0
- opik/evaluation/metrics/llm_judges/answer_relevance/metric.py +20 -5
- opik/evaluation/metrics/llm_judges/context_precision/metric.py +20 -6
- opik/evaluation/metrics/llm_judges/context_recall/metric.py +20 -6
- opik/evaluation/metrics/llm_judges/g_eval/__init__.py +5 -0
- opik/evaluation/metrics/llm_judges/g_eval/metric.py +219 -68
- opik/evaluation/metrics/llm_judges/g_eval/parser.py +102 -52
- opik/evaluation/metrics/llm_judges/g_eval/presets.py +209 -0
- opik/evaluation/metrics/llm_judges/g_eval_presets/__init__.py +36 -0
- opik/evaluation/metrics/llm_judges/g_eval_presets/agent_assessment.py +77 -0
- opik/evaluation/metrics/llm_judges/g_eval_presets/bias_classifier.py +181 -0
- opik/evaluation/metrics/llm_judges/g_eval_presets/compliance_risk.py +41 -0
- opik/evaluation/metrics/llm_judges/g_eval_presets/prompt_uncertainty.py +41 -0
- opik/evaluation/metrics/llm_judges/g_eval_presets/qa_suite.py +146 -0
- opik/evaluation/metrics/llm_judges/hallucination/metric.py +16 -3
- opik/evaluation/metrics/llm_judges/llm_juries/__init__.py +3 -0
- opik/evaluation/metrics/llm_judges/llm_juries/metric.py +76 -0
- opik/evaluation/metrics/llm_judges/moderation/metric.py +16 -4
- opik/evaluation/metrics/llm_judges/structure_output_compliance/__init__.py +0 -0
- opik/evaluation/metrics/llm_judges/structure_output_compliance/metric.py +144 -0
- opik/evaluation/metrics/llm_judges/structure_output_compliance/parser.py +79 -0
- opik/evaluation/metrics/llm_judges/structure_output_compliance/schema.py +15 -0
- opik/evaluation/metrics/llm_judges/structure_output_compliance/template.py +50 -0
- opik/evaluation/metrics/llm_judges/syc_eval/__init__.py +0 -0
- opik/evaluation/metrics/llm_judges/syc_eval/metric.py +252 -0
- opik/evaluation/metrics/llm_judges/syc_eval/parser.py +82 -0
- opik/evaluation/metrics/llm_judges/syc_eval/template.py +155 -0
- opik/evaluation/metrics/llm_judges/trajectory_accuracy/metric.py +20 -5
- opik/evaluation/metrics/llm_judges/usefulness/metric.py +16 -4
- opik/evaluation/metrics/ragas_metric.py +43 -23
- opik/evaluation/models/__init__.py +8 -0
- opik/evaluation/models/base_model.py +107 -1
- opik/evaluation/models/langchain/langchain_chat_model.py +15 -7
- opik/evaluation/models/langchain/message_converters.py +97 -15
- opik/evaluation/models/litellm/litellm_chat_model.py +156 -29
- opik/evaluation/models/litellm/util.py +125 -0
- opik/evaluation/models/litellm/warning_filters.py +16 -4
- opik/evaluation/models/model_capabilities.py +187 -0
- opik/evaluation/models/models_factory.py +25 -3
- opik/evaluation/preprocessing.py +92 -0
- opik/evaluation/report.py +70 -12
- opik/evaluation/rest_operations.py +49 -45
- opik/evaluation/samplers/__init__.py +4 -0
- opik/evaluation/samplers/base_dataset_sampler.py +40 -0
- opik/evaluation/samplers/random_dataset_sampler.py +48 -0
- opik/evaluation/score_statistics.py +66 -0
- opik/evaluation/scorers/__init__.py +4 -0
- opik/evaluation/scorers/scorer_function.py +55 -0
- opik/evaluation/scorers/scorer_wrapper_metric.py +130 -0
- opik/evaluation/test_case.py +3 -2
- opik/evaluation/test_result.py +1 -0
- opik/evaluation/threads/evaluator.py +31 -3
- opik/evaluation/threads/helpers.py +3 -2
- opik/evaluation/types.py +9 -1
- opik/exceptions.py +33 -0
- opik/file_upload/file_uploader.py +13 -0
- opik/file_upload/upload_options.py +2 -0
- opik/hooks/__init__.py +23 -0
- opik/hooks/anonymizer_hook.py +36 -0
- opik/hooks/httpx_client_hook.py +112 -0
- opik/httpx_client.py +12 -9
- opik/id_helpers.py +18 -0
- opik/integrations/adk/graph/subgraph_edges_builders.py +1 -2
- opik/integrations/adk/helpers.py +16 -7
- opik/integrations/adk/legacy_opik_tracer.py +7 -4
- opik/integrations/adk/opik_tracer.py +14 -1
- opik/integrations/adk/patchers/adk_otel_tracer/opik_adk_otel_tracer.py +7 -3
- opik/integrations/adk/recursive_callback_injector.py +4 -7
- opik/integrations/bedrock/converse/__init__.py +0 -0
- opik/integrations/bedrock/converse/chunks_aggregator.py +188 -0
- opik/integrations/bedrock/{converse_decorator.py → converse/converse_decorator.py} +4 -3
- opik/integrations/bedrock/invoke_agent_decorator.py +5 -4
- opik/integrations/bedrock/invoke_model/__init__.py +0 -0
- opik/integrations/bedrock/invoke_model/chunks_aggregator/__init__.py +78 -0
- opik/integrations/bedrock/invoke_model/chunks_aggregator/api.py +45 -0
- opik/integrations/bedrock/invoke_model/chunks_aggregator/base.py +23 -0
- opik/integrations/bedrock/invoke_model/chunks_aggregator/claude.py +121 -0
- opik/integrations/bedrock/invoke_model/chunks_aggregator/format_detector.py +107 -0
- opik/integrations/bedrock/invoke_model/chunks_aggregator/llama.py +108 -0
- opik/integrations/bedrock/invoke_model/chunks_aggregator/mistral.py +118 -0
- opik/integrations/bedrock/invoke_model/chunks_aggregator/nova.py +99 -0
- opik/integrations/bedrock/invoke_model/invoke_model_decorator.py +178 -0
- opik/integrations/bedrock/invoke_model/response_types.py +34 -0
- opik/integrations/bedrock/invoke_model/stream_wrappers.py +122 -0
- opik/integrations/bedrock/invoke_model/usage_converters.py +87 -0
- opik/integrations/bedrock/invoke_model/usage_extraction.py +108 -0
- opik/integrations/bedrock/opik_tracker.py +42 -4
- opik/integrations/bedrock/types.py +19 -0
- opik/integrations/crewai/crewai_decorator.py +8 -51
- opik/integrations/crewai/opik_tracker.py +31 -10
- opik/integrations/crewai/patchers/__init__.py +5 -0
- opik/integrations/crewai/patchers/flow.py +118 -0
- opik/integrations/crewai/patchers/litellm_completion.py +30 -0
- opik/integrations/crewai/patchers/llm_client.py +207 -0
- opik/integrations/dspy/callback.py +80 -17
- opik/integrations/dspy/parsers.py +168 -0
- opik/integrations/harbor/__init__.py +17 -0
- opik/integrations/harbor/experiment_service.py +269 -0
- opik/integrations/harbor/opik_tracker.py +528 -0
- opik/integrations/haystack/opik_connector.py +2 -2
- opik/integrations/haystack/opik_tracer.py +3 -7
- opik/integrations/langchain/__init__.py +3 -1
- opik/integrations/langchain/helpers.py +96 -0
- opik/integrations/langchain/langgraph_async_context_bridge.py +131 -0
- opik/integrations/langchain/langgraph_tracer_injector.py +88 -0
- opik/integrations/langchain/opik_encoder_extension.py +1 -1
- opik/integrations/langchain/opik_tracer.py +474 -229
- opik/integrations/litellm/__init__.py +5 -0
- opik/integrations/litellm/completion_chunks_aggregator.py +115 -0
- opik/integrations/litellm/litellm_completion_decorator.py +242 -0
- opik/integrations/litellm/opik_tracker.py +43 -0
- opik/integrations/litellm/stream_patchers.py +151 -0
- opik/integrations/llama_index/callback.py +146 -107
- opik/integrations/openai/agents/opik_tracing_processor.py +1 -2
- opik/integrations/openai/openai_chat_completions_decorator.py +2 -16
- opik/integrations/openai/opik_tracker.py +1 -1
- opik/integrations/sagemaker/auth.py +5 -1
- opik/llm_usage/google_usage.py +3 -1
- opik/llm_usage/opik_usage.py +7 -8
- opik/llm_usage/opik_usage_factory.py +4 -2
- opik/logging_messages.py +6 -0
- opik/message_processing/batching/base_batcher.py +14 -21
- opik/message_processing/batching/batch_manager.py +22 -10
- opik/message_processing/batching/batch_manager_constuctors.py +10 -0
- opik/message_processing/batching/batchers.py +59 -27
- opik/message_processing/batching/flushing_thread.py +0 -3
- opik/message_processing/emulation/__init__.py +0 -0
- opik/message_processing/emulation/emulator_message_processor.py +578 -0
- opik/message_processing/emulation/local_emulator_message_processor.py +140 -0
- opik/message_processing/emulation/models.py +162 -0
- opik/message_processing/encoder_helpers.py +79 -0
- opik/message_processing/messages.py +56 -1
- opik/message_processing/preprocessing/__init__.py +0 -0
- opik/message_processing/preprocessing/attachments_preprocessor.py +70 -0
- opik/message_processing/preprocessing/batching_preprocessor.py +53 -0
- opik/message_processing/preprocessing/constants.py +1 -0
- opik/message_processing/preprocessing/file_upload_preprocessor.py +38 -0
- opik/message_processing/preprocessing/preprocessor.py +36 -0
- opik/message_processing/processors/__init__.py +0 -0
- opik/message_processing/processors/attachments_extraction_processor.py +146 -0
- opik/message_processing/processors/message_processors.py +92 -0
- opik/message_processing/processors/message_processors_chain.py +96 -0
- opik/message_processing/{message_processors.py → processors/online_message_processor.py} +85 -29
- opik/message_processing/queue_consumer.py +9 -3
- opik/message_processing/streamer.py +71 -33
- opik/message_processing/streamer_constructors.py +43 -10
- opik/opik_context.py +16 -4
- opik/plugins/pytest/hooks.py +5 -3
- opik/rest_api/__init__.py +346 -15
- opik/rest_api/alerts/__init__.py +7 -0
- opik/rest_api/alerts/client.py +667 -0
- opik/rest_api/alerts/raw_client.py +1015 -0
- opik/rest_api/alerts/types/__init__.py +7 -0
- opik/rest_api/alerts/types/get_webhook_examples_request_alert_type.py +5 -0
- opik/rest_api/annotation_queues/__init__.py +4 -0
- opik/rest_api/annotation_queues/client.py +668 -0
- opik/rest_api/annotation_queues/raw_client.py +1019 -0
- opik/rest_api/automation_rule_evaluators/client.py +34 -2
- opik/rest_api/automation_rule_evaluators/raw_client.py +24 -0
- opik/rest_api/client.py +15 -0
- opik/rest_api/dashboards/__init__.py +4 -0
- opik/rest_api/dashboards/client.py +462 -0
- opik/rest_api/dashboards/raw_client.py +648 -0
- opik/rest_api/datasets/client.py +1310 -44
- opik/rest_api/datasets/raw_client.py +2269 -358
- opik/rest_api/experiments/__init__.py +2 -2
- opik/rest_api/experiments/client.py +191 -5
- opik/rest_api/experiments/raw_client.py +301 -7
- opik/rest_api/experiments/types/__init__.py +4 -1
- opik/rest_api/experiments/types/experiment_update_status.py +5 -0
- opik/rest_api/experiments/types/experiment_update_type.py +5 -0
- opik/rest_api/experiments/types/experiment_write_status.py +5 -0
- opik/rest_api/feedback_definitions/types/find_feedback_definitions_request_type.py +1 -1
- opik/rest_api/llm_provider_key/client.py +20 -0
- opik/rest_api/llm_provider_key/raw_client.py +20 -0
- opik/rest_api/llm_provider_key/types/provider_api_key_write_provider.py +1 -1
- opik/rest_api/manual_evaluation/__init__.py +4 -0
- opik/rest_api/manual_evaluation/client.py +347 -0
- opik/rest_api/manual_evaluation/raw_client.py +543 -0
- opik/rest_api/optimizations/client.py +145 -9
- opik/rest_api/optimizations/raw_client.py +237 -13
- opik/rest_api/optimizations/types/optimization_update_status.py +3 -1
- opik/rest_api/prompts/__init__.py +2 -2
- opik/rest_api/prompts/client.py +227 -6
- opik/rest_api/prompts/raw_client.py +331 -2
- opik/rest_api/prompts/types/__init__.py +3 -1
- opik/rest_api/prompts/types/create_prompt_version_detail_template_structure.py +5 -0
- opik/rest_api/prompts/types/prompt_write_template_structure.py +5 -0
- opik/rest_api/spans/__init__.py +0 -2
- opik/rest_api/spans/client.py +238 -76
- opik/rest_api/spans/raw_client.py +307 -95
- opik/rest_api/spans/types/__init__.py +0 -2
- opik/rest_api/traces/client.py +572 -161
- opik/rest_api/traces/raw_client.py +736 -229
- opik/rest_api/types/__init__.py +352 -17
- opik/rest_api/types/aggregation_data.py +1 -0
- opik/rest_api/types/alert.py +33 -0
- opik/rest_api/types/alert_alert_type.py +5 -0
- opik/rest_api/types/alert_page_public.py +24 -0
- opik/rest_api/types/alert_public.py +33 -0
- opik/rest_api/types/alert_public_alert_type.py +5 -0
- opik/rest_api/types/alert_trigger.py +27 -0
- opik/rest_api/types/alert_trigger_config.py +28 -0
- opik/rest_api/types/alert_trigger_config_public.py +28 -0
- opik/rest_api/types/alert_trigger_config_public_type.py +10 -0
- opik/rest_api/types/alert_trigger_config_type.py +10 -0
- opik/rest_api/types/alert_trigger_config_write.py +22 -0
- opik/rest_api/types/alert_trigger_config_write_type.py +10 -0
- opik/rest_api/types/alert_trigger_event_type.py +19 -0
- opik/rest_api/types/alert_trigger_public.py +27 -0
- opik/rest_api/types/alert_trigger_public_event_type.py +19 -0
- opik/rest_api/types/alert_trigger_write.py +23 -0
- opik/rest_api/types/alert_trigger_write_event_type.py +19 -0
- opik/rest_api/types/alert_write.py +28 -0
- opik/rest_api/types/alert_write_alert_type.py +5 -0
- opik/rest_api/types/annotation_queue.py +42 -0
- opik/rest_api/types/annotation_queue_batch.py +27 -0
- opik/rest_api/types/annotation_queue_item_ids.py +19 -0
- opik/rest_api/types/annotation_queue_page_public.py +28 -0
- opik/rest_api/types/annotation_queue_public.py +38 -0
- opik/rest_api/types/annotation_queue_public_scope.py +5 -0
- opik/rest_api/types/annotation_queue_reviewer.py +20 -0
- opik/rest_api/types/annotation_queue_reviewer_public.py +20 -0
- opik/rest_api/types/annotation_queue_scope.py +5 -0
- opik/rest_api/types/annotation_queue_write.py +31 -0
- opik/rest_api/types/annotation_queue_write_scope.py +5 -0
- opik/rest_api/types/audio_url.py +19 -0
- opik/rest_api/types/audio_url_public.py +19 -0
- opik/rest_api/types/audio_url_write.py +19 -0
- opik/rest_api/types/automation_rule_evaluator.py +62 -2
- opik/rest_api/types/automation_rule_evaluator_llm_as_judge.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_llm_as_judge_public.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_llm_as_judge_write.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_object_object_public.py +155 -0
- opik/rest_api/types/automation_rule_evaluator_page_public.py +3 -2
- opik/rest_api/types/automation_rule_evaluator_public.py +57 -2
- opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge_public.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_span_llm_as_judge_write.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python_public.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_span_user_defined_metric_python_write.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge_public.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_llm_as_judge_write.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python_public.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_trace_thread_user_defined_metric_python_write.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_update.py +51 -1
- opik/rest_api/types/automation_rule_evaluator_update_llm_as_judge.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_update_span_llm_as_judge.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_update_span_user_defined_metric_python.py +22 -0
- opik/rest_api/types/automation_rule_evaluator_update_trace_thread_llm_as_judge.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_update_trace_thread_user_defined_metric_python.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_update_user_defined_metric_python.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python_public.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_user_defined_metric_python_write.py +2 -0
- opik/rest_api/types/automation_rule_evaluator_write.py +51 -1
- opik/rest_api/types/boolean_feedback_definition.py +25 -0
- opik/rest_api/types/boolean_feedback_definition_create.py +20 -0
- opik/rest_api/types/boolean_feedback_definition_public.py +25 -0
- opik/rest_api/types/boolean_feedback_definition_update.py +20 -0
- opik/rest_api/types/boolean_feedback_detail.py +29 -0
- opik/rest_api/types/boolean_feedback_detail_create.py +29 -0
- opik/rest_api/types/boolean_feedback_detail_public.py +29 -0
- opik/rest_api/types/boolean_feedback_detail_update.py +29 -0
- opik/rest_api/types/dashboard_page_public.py +24 -0
- opik/rest_api/types/dashboard_public.py +30 -0
- opik/rest_api/types/dataset.py +4 -0
- opik/rest_api/types/dataset_expansion.py +42 -0
- opik/rest_api/types/dataset_expansion_response.py +39 -0
- opik/rest_api/types/dataset_item.py +2 -0
- opik/rest_api/types/dataset_item_changes_public.py +5 -0
- opik/rest_api/types/dataset_item_compare.py +2 -0
- opik/rest_api/types/dataset_item_filter.py +27 -0
- opik/rest_api/types/dataset_item_filter_operator.py +21 -0
- opik/rest_api/types/dataset_item_page_compare.py +5 -0
- opik/rest_api/types/dataset_item_page_public.py +5 -0
- opik/rest_api/types/dataset_item_public.py +2 -0
- opik/rest_api/types/dataset_item_update.py +39 -0
- opik/rest_api/types/dataset_item_write.py +1 -0
- opik/rest_api/types/dataset_public.py +4 -0
- opik/rest_api/types/dataset_public_status.py +5 -0
- opik/rest_api/types/dataset_status.py +5 -0
- opik/rest_api/types/dataset_version_diff.py +22 -0
- opik/rest_api/types/dataset_version_diff_stats.py +24 -0
- opik/rest_api/types/dataset_version_page_public.py +23 -0
- opik/rest_api/types/dataset_version_public.py +59 -0
- opik/rest_api/types/dataset_version_summary.py +46 -0
- opik/rest_api/types/dataset_version_summary_public.py +46 -0
- opik/rest_api/types/experiment.py +7 -2
- opik/rest_api/types/experiment_group_response.py +2 -0
- opik/rest_api/types/experiment_public.py +7 -2
- opik/rest_api/types/experiment_public_status.py +5 -0
- opik/rest_api/types/experiment_score.py +20 -0
- opik/rest_api/types/experiment_score_public.py +20 -0
- opik/rest_api/types/experiment_score_write.py +20 -0
- opik/rest_api/types/experiment_status.py +5 -0
- opik/rest_api/types/feedback.py +25 -1
- opik/rest_api/types/feedback_create.py +20 -1
- opik/rest_api/types/feedback_object_public.py +27 -1
- opik/rest_api/types/feedback_public.py +25 -1
- opik/rest_api/types/feedback_score_batch_item.py +2 -1
- opik/rest_api/types/feedback_score_batch_item_thread.py +2 -1
- opik/rest_api/types/feedback_score_public.py +4 -0
- opik/rest_api/types/feedback_update.py +20 -1
- opik/rest_api/types/group_content_with_aggregations.py +1 -0
- opik/rest_api/types/group_detail.py +19 -0
- opik/rest_api/types/group_details.py +20 -0
- opik/rest_api/types/guardrail.py +1 -0
- opik/rest_api/types/guardrail_write.py +1 -0
- opik/rest_api/types/ids_holder.py +19 -0
- opik/rest_api/types/image_url.py +20 -0
- opik/rest_api/types/image_url_public.py +20 -0
- opik/rest_api/types/image_url_write.py +20 -0
- opik/rest_api/types/llm_as_judge_message.py +5 -1
- opik/rest_api/types/llm_as_judge_message_content.py +26 -0
- opik/rest_api/types/llm_as_judge_message_content_public.py +26 -0
- opik/rest_api/types/llm_as_judge_message_content_write.py +26 -0
- opik/rest_api/types/llm_as_judge_message_public.py +5 -1
- opik/rest_api/types/llm_as_judge_message_write.py +5 -1
- opik/rest_api/types/llm_as_judge_model_parameters.py +3 -0
- opik/rest_api/types/llm_as_judge_model_parameters_public.py +3 -0
- opik/rest_api/types/llm_as_judge_model_parameters_write.py +3 -0
- opik/rest_api/types/manual_evaluation_request.py +38 -0
- opik/rest_api/types/manual_evaluation_request_entity_type.py +5 -0
- opik/rest_api/types/manual_evaluation_response.py +27 -0
- opik/rest_api/types/optimization.py +4 -2
- opik/rest_api/types/optimization_public.py +4 -2
- opik/rest_api/types/optimization_public_status.py +3 -1
- opik/rest_api/types/optimization_status.py +3 -1
- opik/rest_api/types/optimization_studio_config.py +27 -0
- opik/rest_api/types/optimization_studio_config_public.py +27 -0
- opik/rest_api/types/optimization_studio_config_write.py +27 -0
- opik/rest_api/types/optimization_studio_log.py +22 -0
- opik/rest_api/types/optimization_write.py +4 -2
- opik/rest_api/types/optimization_write_status.py +3 -1
- opik/rest_api/types/project.py +1 -0
- opik/rest_api/types/project_detailed.py +1 -0
- opik/rest_api/types/project_reference.py +31 -0
- opik/rest_api/types/project_reference_public.py +31 -0
- opik/rest_api/types/project_stats_summary_item.py +1 -0
- opik/rest_api/types/prompt.py +6 -0
- opik/rest_api/types/prompt_detail.py +6 -0
- opik/rest_api/types/prompt_detail_template_structure.py +5 -0
- opik/rest_api/types/prompt_public.py +6 -0
- opik/rest_api/types/prompt_public_template_structure.py +5 -0
- opik/rest_api/types/prompt_template_structure.py +5 -0
- opik/rest_api/types/prompt_version.py +3 -0
- opik/rest_api/types/prompt_version_detail.py +3 -0
- opik/rest_api/types/prompt_version_detail_template_structure.py +5 -0
- opik/rest_api/types/prompt_version_link.py +1 -0
- opik/rest_api/types/prompt_version_link_public.py +1 -0
- opik/rest_api/types/prompt_version_page_public.py +5 -0
- opik/rest_api/types/prompt_version_public.py +3 -0
- opik/rest_api/types/prompt_version_public_template_structure.py +5 -0
- opik/rest_api/types/prompt_version_template_structure.py +5 -0
- opik/rest_api/types/prompt_version_update.py +33 -0
- opik/rest_api/types/provider_api_key.py +9 -0
- opik/rest_api/types/provider_api_key_provider.py +1 -1
- opik/rest_api/types/provider_api_key_public.py +9 -0
- opik/rest_api/types/provider_api_key_public_provider.py +1 -1
- opik/rest_api/types/score_name.py +1 -0
- opik/rest_api/types/service_toggles_config.py +18 -0
- opik/rest_api/types/span.py +1 -2
- opik/rest_api/types/span_enrichment_options.py +31 -0
- opik/rest_api/types/span_experiment_item_bulk_write_view.py +1 -2
- opik/rest_api/types/span_filter.py +23 -0
- opik/rest_api/types/span_filter_operator.py +21 -0
- opik/rest_api/types/span_filter_write.py +23 -0
- opik/rest_api/types/span_filter_write_operator.py +21 -0
- opik/rest_api/types/span_llm_as_judge_code.py +27 -0
- opik/rest_api/types/span_llm_as_judge_code_public.py +27 -0
- opik/rest_api/types/span_llm_as_judge_code_write.py +27 -0
- opik/rest_api/types/span_public.py +1 -2
- opik/rest_api/types/span_update.py +46 -0
- opik/rest_api/types/span_user_defined_metric_python_code.py +20 -0
- opik/rest_api/types/span_user_defined_metric_python_code_public.py +20 -0
- opik/rest_api/types/span_user_defined_metric_python_code_write.py +20 -0
- opik/rest_api/types/span_write.py +1 -2
- opik/rest_api/types/studio_evaluation.py +20 -0
- opik/rest_api/types/studio_evaluation_public.py +20 -0
- opik/rest_api/types/studio_evaluation_write.py +20 -0
- opik/rest_api/types/studio_llm_model.py +21 -0
- opik/rest_api/types/studio_llm_model_public.py +21 -0
- opik/rest_api/types/studio_llm_model_write.py +21 -0
- opik/rest_api/types/studio_message.py +20 -0
- opik/rest_api/types/studio_message_public.py +20 -0
- opik/rest_api/types/studio_message_write.py +20 -0
- opik/rest_api/types/studio_metric.py +21 -0
- opik/rest_api/types/studio_metric_public.py +21 -0
- opik/rest_api/types/studio_metric_write.py +21 -0
- opik/rest_api/types/studio_optimizer.py +21 -0
- opik/rest_api/types/studio_optimizer_public.py +21 -0
- opik/rest_api/types/studio_optimizer_write.py +21 -0
- opik/rest_api/types/studio_prompt.py +20 -0
- opik/rest_api/types/studio_prompt_public.py +20 -0
- opik/rest_api/types/studio_prompt_write.py +20 -0
- opik/rest_api/types/trace.py +11 -2
- opik/rest_api/types/trace_enrichment_options.py +32 -0
- opik/rest_api/types/trace_experiment_item_bulk_write_view.py +1 -2
- opik/rest_api/types/trace_filter.py +23 -0
- opik/rest_api/types/trace_filter_operator.py +21 -0
- opik/rest_api/types/trace_filter_write.py +23 -0
- opik/rest_api/types/trace_filter_write_operator.py +21 -0
- opik/rest_api/types/trace_public.py +11 -2
- opik/rest_api/types/trace_thread_filter_write.py +23 -0
- opik/rest_api/types/trace_thread_filter_write_operator.py +21 -0
- opik/rest_api/types/trace_thread_identifier.py +1 -0
- opik/rest_api/types/trace_update.py +39 -0
- opik/rest_api/types/trace_write.py +1 -2
- opik/rest_api/types/value_entry.py +2 -0
- opik/rest_api/types/value_entry_compare.py +2 -0
- opik/rest_api/types/value_entry_experiment_item_bulk_write_view.py +2 -0
- opik/rest_api/types/value_entry_public.py +2 -0
- opik/rest_api/types/video_url.py +19 -0
- opik/rest_api/types/video_url_public.py +19 -0
- opik/rest_api/types/video_url_write.py +19 -0
- opik/rest_api/types/webhook.py +28 -0
- opik/rest_api/types/webhook_examples.py +19 -0
- opik/rest_api/types/webhook_public.py +28 -0
- opik/rest_api/types/webhook_test_result.py +23 -0
- opik/rest_api/types/webhook_test_result_status.py +5 -0
- opik/rest_api/types/webhook_write.py +23 -0
- opik/rest_api/types/welcome_wizard_tracking.py +22 -0
- opik/rest_api/types/workspace_configuration.py +5 -0
- opik/rest_api/welcome_wizard/__init__.py +4 -0
- opik/rest_api/welcome_wizard/client.py +195 -0
- opik/rest_api/welcome_wizard/raw_client.py +208 -0
- opik/rest_api/workspaces/client.py +14 -2
- opik/rest_api/workspaces/raw_client.py +10 -0
- opik/s3_httpx_client.py +14 -1
- opik/simulation/__init__.py +6 -0
- opik/simulation/simulated_user.py +99 -0
- opik/simulation/simulator.py +108 -0
- opik/synchronization.py +5 -6
- opik/{decorator/tracing_runtime_config.py → tracing_runtime_config.py} +6 -7
- opik/types.py +36 -0
- opik/validation/chat_prompt_messages.py +241 -0
- opik/validation/feedback_score.py +3 -3
- opik/validation/validator.py +28 -0
- opik-1.9.71.dist-info/METADATA +370 -0
- opik-1.9.71.dist-info/RECORD +1110 -0
- opik/api_objects/prompt/prompt.py +0 -112
- opik/cli.py +0 -193
- opik/hooks.py +0 -13
- opik/integrations/bedrock/chunks_aggregator.py +0 -55
- opik/integrations/bedrock/helpers.py +0 -8
- opik/rest_api/types/automation_rule_evaluator_object_public.py +0 -100
- opik/rest_api/types/json_node_experiment_item_bulk_write_view.py +0 -5
- opik-1.8.39.dist-info/METADATA +0 -339
- opik-1.8.39.dist-info/RECORD +0 -790
- /opik/{evaluation/metrics/conversation/conversational_coherence → decorator/context_manager}/__init__.py +0 -0
- /opik/evaluation/metrics/conversation/{session_completeness → llm_judges/conversational_coherence}/__init__.py +0 -0
- /opik/evaluation/metrics/conversation/{conversational_coherence → llm_judges/conversational_coherence}/schema.py +0 -0
- /opik/evaluation/metrics/conversation/{user_frustration → llm_judges/session_completeness}/__init__.py +0 -0
- /opik/evaluation/metrics/conversation/{session_completeness → llm_judges/session_completeness}/schema.py +0 -0
- /opik/evaluation/metrics/conversation/{user_frustration → llm_judges/user_frustration}/schema.py +0 -0
- /opik/integrations/bedrock/{stream_wrappers.py → converse/stream_wrappers.py} +0 -0
- /opik/rest_api/{spans/types → types}/span_update_type.py +0 -0
- {opik-1.8.39.dist-info → opik-1.9.71.dist-info}/WHEEL +0 -0
- {opik-1.8.39.dist-info → opik-1.9.71.dist-info}/entry_points.txt +0 -0
- {opik-1.8.39.dist-info → opik-1.9.71.dist-info}/licenses/LICENSE +0 -0
- {opik-1.8.39.dist-info → opik-1.9.71.dist-info}/top_level.txt +0 -0
opik/api_objects/opik_client.py
CHANGED
|
@@ -2,11 +2,32 @@ import atexit
|
|
|
2
2
|
import datetime
|
|
3
3
|
import functools
|
|
4
4
|
import logging
|
|
5
|
-
from typing import Any, Dict, List, Optional, TypeVar, Union, Literal
|
|
5
|
+
from typing import Any, Dict, List, Optional, TypeVar, Union, Literal, cast
|
|
6
6
|
|
|
7
7
|
import httpx
|
|
8
8
|
|
|
9
|
+
from . import (
|
|
10
|
+
constants,
|
|
11
|
+
dataset,
|
|
12
|
+
experiment,
|
|
13
|
+
optimization,
|
|
14
|
+
helpers,
|
|
15
|
+
opik_query_language,
|
|
16
|
+
search_helpers,
|
|
17
|
+
span,
|
|
18
|
+
trace,
|
|
19
|
+
)
|
|
20
|
+
from .attachment import Attachment
|
|
21
|
+
from .attachment import client as attachment_client
|
|
22
|
+
from .attachment import converters as attachment_converters
|
|
23
|
+
from .dataset import rest_operations as dataset_rest_operations
|
|
24
|
+
from .experiment import experiments_client
|
|
25
|
+
from .experiment import helpers as experiment_helpers
|
|
26
|
+
from .experiment import rest_operations as experiment_rest_operations
|
|
27
|
+
from . import prompt as prompt_module
|
|
28
|
+
from .prompt import client as prompt_client
|
|
9
29
|
from .threads import threads_client
|
|
30
|
+
from .trace import migration as trace_migration, trace_client
|
|
10
31
|
from .. import (
|
|
11
32
|
config,
|
|
12
33
|
datetime_helpers,
|
|
@@ -17,8 +38,13 @@ from .. import (
|
|
|
17
38
|
rest_client_configurator,
|
|
18
39
|
url_helpers,
|
|
19
40
|
)
|
|
20
|
-
from ..message_processing import
|
|
41
|
+
from ..message_processing import (
|
|
42
|
+
messages,
|
|
43
|
+
streamer_constructors,
|
|
44
|
+
message_queue,
|
|
45
|
+
)
|
|
21
46
|
from ..message_processing.batching import sequence_splitter
|
|
47
|
+
from ..message_processing.processors import message_processors_chain
|
|
22
48
|
from ..rest_api import client as rest_api_client
|
|
23
49
|
from ..rest_api.core.api_error import ApiError
|
|
24
50
|
from ..rest_api.types import (
|
|
@@ -29,26 +55,13 @@ from ..rest_api.types import (
|
|
|
29
55
|
span_filter_public,
|
|
30
56
|
trace_filter_public,
|
|
31
57
|
)
|
|
32
|
-
from ..types import
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
helpers,
|
|
39
|
-
span,
|
|
40
|
-
trace,
|
|
58
|
+
from ..types import (
|
|
59
|
+
BatchFeedbackScoreDict,
|
|
60
|
+
ErrorInfoDict,
|
|
61
|
+
FeedbackScoreDict,
|
|
62
|
+
LLMProvider,
|
|
63
|
+
SpanType,
|
|
41
64
|
)
|
|
42
|
-
from .attachment import converters as attachment_converters
|
|
43
|
-
from .attachment import Attachment
|
|
44
|
-
from .attachment import client as attachment_client
|
|
45
|
-
from . import rest_stream_parser
|
|
46
|
-
from .dataset import rest_operations as dataset_rest_operations
|
|
47
|
-
from .experiment import helpers as experiment_helpers
|
|
48
|
-
from .experiment import rest_operations as experiment_rest_operations
|
|
49
|
-
from .prompt import Prompt, PromptType
|
|
50
|
-
from .prompt.client import PromptClient
|
|
51
|
-
from .trace import migration as trace_migration
|
|
52
65
|
|
|
53
66
|
LOGGER = logging.getLogger(__name__)
|
|
54
67
|
|
|
@@ -100,13 +113,7 @@ class Opik:
|
|
|
100
113
|
self._use_batching = _use_batching
|
|
101
114
|
|
|
102
115
|
self._initialize_streamer(
|
|
103
|
-
url_override=config_.url_override,
|
|
104
|
-
workers=config_.background_workers,
|
|
105
|
-
file_upload_worker_count=config_.file_upload_background_workers,
|
|
106
|
-
api_key=config_.api_key,
|
|
107
|
-
check_tls_certificate=config_.check_tls_certificate,
|
|
108
116
|
use_batching=_use_batching,
|
|
109
|
-
enable_json_request_compression=config_.enable_json_request_compression,
|
|
110
117
|
)
|
|
111
118
|
atexit.register(self.end, timeout=self._flush_timeout)
|
|
112
119
|
|
|
@@ -145,24 +152,17 @@ class Opik:
|
|
|
145
152
|
|
|
146
153
|
def _initialize_streamer(
|
|
147
154
|
self,
|
|
148
|
-
url_override: str,
|
|
149
|
-
workers: int,
|
|
150
|
-
file_upload_worker_count: int,
|
|
151
|
-
api_key: Optional[str],
|
|
152
|
-
check_tls_certificate: bool,
|
|
153
155
|
use_batching: bool,
|
|
154
|
-
enable_json_request_compression: bool,
|
|
155
156
|
) -> None:
|
|
156
|
-
|
|
157
|
+
self._httpx_client = httpx_client.get(
|
|
157
158
|
workspace=self._workspace,
|
|
158
|
-
api_key=api_key,
|
|
159
|
-
check_tls_certificate=check_tls_certificate,
|
|
160
|
-
compress_json_requests=enable_json_request_compression,
|
|
159
|
+
api_key=self._config.api_key,
|
|
160
|
+
check_tls_certificate=self._config.check_tls_certificate,
|
|
161
|
+
compress_json_requests=self._config.enable_json_request_compression,
|
|
161
162
|
)
|
|
162
|
-
self._httpx_client = httpx_client_
|
|
163
163
|
self._rest_client = rest_api_client.OpikApi(
|
|
164
|
-
base_url=url_override,
|
|
165
|
-
httpx_client=
|
|
164
|
+
base_url=self._config.url_override,
|
|
165
|
+
httpx_client=self._httpx_client,
|
|
166
166
|
)
|
|
167
167
|
self._rest_client._client_wrapper._timeout = (
|
|
168
168
|
httpx.USE_CLIENT_DEFAULT
|
|
@@ -174,13 +174,22 @@ class Opik:
|
|
|
174
174
|
batch_factor=self._config.maximal_queue_size_batch_factor,
|
|
175
175
|
)
|
|
176
176
|
|
|
177
|
+
self.__internal_api__message_processor__ = (
|
|
178
|
+
message_processors_chain.create_message_processors_chain(
|
|
179
|
+
rest_client=self._rest_client
|
|
180
|
+
)
|
|
181
|
+
)
|
|
177
182
|
self._streamer = streamer_constructors.construct_online_streamer(
|
|
178
|
-
n_consumers=
|
|
183
|
+
n_consumers=self._config.background_workers,
|
|
179
184
|
rest_client=self._rest_client,
|
|
180
|
-
httpx_client=
|
|
185
|
+
httpx_client=self._httpx_client,
|
|
181
186
|
use_batching=use_batching,
|
|
182
|
-
|
|
187
|
+
use_attachment_extraction=self._config.is_attachment_extraction_active,
|
|
188
|
+
min_base64_embedded_attachment_size=self._config.min_base64_embedded_attachment_size,
|
|
189
|
+
file_upload_worker_count=self._config.file_upload_background_workers,
|
|
183
190
|
max_queue_size=max_queue_size,
|
|
191
|
+
message_processor=self.__internal_api__message_processor__,
|
|
192
|
+
url_override=self._config.url_override,
|
|
184
193
|
)
|
|
185
194
|
|
|
186
195
|
def _display_trace_url(self, trace_id: str, project_name: str) -> None:
|
|
@@ -282,7 +291,9 @@ class Opik:
|
|
|
282
291
|
for feedback_score in feedback_scores:
|
|
283
292
|
feedback_score["id"] = id
|
|
284
293
|
|
|
285
|
-
self.log_traces_feedback_scores(
|
|
294
|
+
self.log_traces_feedback_scores(
|
|
295
|
+
cast(List[BatchFeedbackScoreDict], feedback_scores), project_name
|
|
296
|
+
)
|
|
286
297
|
|
|
287
298
|
if attachments is not None:
|
|
288
299
|
for attachment_data in attachments:
|
|
@@ -457,7 +468,9 @@ class Opik:
|
|
|
457
468
|
for feedback_score in feedback_scores:
|
|
458
469
|
feedback_score["id"] = id
|
|
459
470
|
|
|
460
|
-
self.log_spans_feedback_scores(
|
|
471
|
+
self.log_spans_feedback_scores(
|
|
472
|
+
cast(List[BatchFeedbackScoreDict], feedback_scores), project_name
|
|
473
|
+
)
|
|
461
474
|
|
|
462
475
|
return span.span_client.create_span(
|
|
463
476
|
trace_id=trace_id,
|
|
@@ -563,24 +576,97 @@ class Opik:
|
|
|
563
576
|
attachments=attachments,
|
|
564
577
|
)
|
|
565
578
|
|
|
579
|
+
def update_trace(
|
|
580
|
+
self,
|
|
581
|
+
trace_id: str,
|
|
582
|
+
project_name: str,
|
|
583
|
+
end_time: Optional[datetime.datetime] = None,
|
|
584
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
585
|
+
input: Optional[Dict[str, Any]] = None,
|
|
586
|
+
output: Optional[Dict[str, Any]] = None,
|
|
587
|
+
tags: Optional[List[Any]] = None,
|
|
588
|
+
error_info: Optional[ErrorInfoDict] = None,
|
|
589
|
+
thread_id: Optional[str] = None,
|
|
590
|
+
) -> None:
|
|
591
|
+
"""
|
|
592
|
+
Update the trace attributes.
|
|
593
|
+
|
|
594
|
+
This method should only be used after the trace has been fully created and stored.
|
|
595
|
+
If called before or immediately after trace creation, the update may silently fail or result in incorrect data.
|
|
596
|
+
|
|
597
|
+
This method uses two parameters to identify the trace:
|
|
598
|
+
- `trace_id`
|
|
599
|
+
- `project_name`
|
|
600
|
+
|
|
601
|
+
These parameters **must match exactly** the values used when the trace was created.
|
|
602
|
+
If any of them are incorrect, the update may not apply and no error will be raised.
|
|
603
|
+
|
|
604
|
+
All other parameters are optional and will update the corresponding fields in the trace.
|
|
605
|
+
If a parameter is not provided, the existing value will remain unchanged.
|
|
606
|
+
|
|
607
|
+
Args:
|
|
608
|
+
trace_id: The unique identifier for the trace.
|
|
609
|
+
project_name: The project name to which the trace belongs.
|
|
610
|
+
end_time: The end time of the trace.
|
|
611
|
+
metadata: Additional metadata to be associated with the trace.
|
|
612
|
+
input: The input data for the trace.
|
|
613
|
+
output: The output data for the trace.
|
|
614
|
+
tags: A list of tags to be associated with the trace.
|
|
615
|
+
error_info: The dictionary with error information (typically used when the trace function has failed).
|
|
616
|
+
thread_id: Used to group multiple traces into a thread.
|
|
617
|
+
The identifier is user-defined and has to be unique per project.
|
|
618
|
+
|
|
619
|
+
Returns:
|
|
620
|
+
None
|
|
621
|
+
"""
|
|
622
|
+
if not trace_id or not project_name:
|
|
623
|
+
raise ValueError(
|
|
624
|
+
"trace_id and project_name must be provided and can not be None or empty, "
|
|
625
|
+
f"trace_id: {trace_id}, project_name: {project_name}"
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
trace_client.update_trace(
|
|
629
|
+
trace_id=trace_id,
|
|
630
|
+
project_name=project_name,
|
|
631
|
+
message_streamer=self._streamer,
|
|
632
|
+
end_time=end_time,
|
|
633
|
+
metadata=metadata,
|
|
634
|
+
input=input,
|
|
635
|
+
output=output,
|
|
636
|
+
tags=tags,
|
|
637
|
+
error_info=error_info,
|
|
638
|
+
thread_id=thread_id,
|
|
639
|
+
)
|
|
640
|
+
|
|
566
641
|
def log_spans_feedback_scores(
|
|
567
|
-
self, scores: List[
|
|
642
|
+
self, scores: List[BatchFeedbackScoreDict], project_name: Optional[str] = None
|
|
568
643
|
) -> None:
|
|
569
644
|
"""
|
|
570
645
|
Log feedback scores for spans.
|
|
571
646
|
|
|
572
647
|
Args:
|
|
573
|
-
scores (List[
|
|
648
|
+
scores (List[BatchFeedbackScoreDict]): A list of feedback score dictionaries.
|
|
574
649
|
Specifying a span id via `id` key for each score is mandatory.
|
|
575
650
|
project_name: The name of the project in which the spans are logged. If not set, the project name
|
|
576
651
|
which was configured when the Opik instance was created will be used.
|
|
652
|
+
Deprecated: use `project_name` in the feedback score dictionary that's listed in the `scores` parameter.
|
|
577
653
|
|
|
578
654
|
Returns:
|
|
579
655
|
None
|
|
656
|
+
|
|
657
|
+
Example:
|
|
658
|
+
>>> from opik import Opik
|
|
659
|
+
>>> client = Opik()
|
|
660
|
+
>>> # Batch logging across multiple projects
|
|
661
|
+
>>> scores = [
|
|
662
|
+
>>> {"id": span1_id, "name": "accuracy", "value": 0.95, "project_name": "project-A"},
|
|
663
|
+
>>> {"id": span2_id, "name": "accuracy", "value": 0.88, "project_name": "project-B"},
|
|
664
|
+
>>> ]
|
|
665
|
+
>>> client.log_spans_feedback_scores(scores=scores)
|
|
580
666
|
"""
|
|
581
667
|
score_messages = helpers.parse_feedback_score_messages(
|
|
582
668
|
scores=scores,
|
|
583
|
-
project_name=project_name or self.
|
|
669
|
+
project_name=project_name or self.project_name,
|
|
584
670
|
parsed_item_class=messages.FeedbackScoreMessage,
|
|
585
671
|
logger=LOGGER,
|
|
586
672
|
)
|
|
@@ -602,23 +688,34 @@ class Opik:
|
|
|
602
688
|
self._streamer.put(add_span_feedback_scores_batch_message)
|
|
603
689
|
|
|
604
690
|
def log_traces_feedback_scores(
|
|
605
|
-
self, scores: List[
|
|
691
|
+
self, scores: List[BatchFeedbackScoreDict], project_name: Optional[str] = None
|
|
606
692
|
) -> None:
|
|
607
693
|
"""
|
|
608
694
|
Log feedback scores for traces.
|
|
609
695
|
|
|
610
696
|
Args:
|
|
611
|
-
scores (List[
|
|
697
|
+
scores (List[BatchFeedbackScoreDict]): A list of feedback score dictionaries.
|
|
612
698
|
Specifying a trace id via `id` key for each score is mandatory.
|
|
613
699
|
project_name: The name of the project in which the traces are logged. If not set, the project name
|
|
614
700
|
which was configured when the Opik instance was created will be used.
|
|
701
|
+
Deprecated: use `project_name` in the feedback score dictionary that's listed in the `scores` parameter.
|
|
615
702
|
|
|
616
703
|
Returns:
|
|
617
704
|
None
|
|
705
|
+
|
|
706
|
+
Example:
|
|
707
|
+
>>> from opik import Opik
|
|
708
|
+
>>> client = Opik()
|
|
709
|
+
>>> # Batch logging across multiple projects
|
|
710
|
+
>>> scores = [
|
|
711
|
+
>>> {"id": trace1_id, "name": "accuracy", "value": 0.95, "project_name": "project-A"},
|
|
712
|
+
>>> {"id": trace2_id, "name": "accuracy", "value": 0.88, "project_name": "project-B"},
|
|
713
|
+
>>> ]
|
|
714
|
+
>>> client.log_traces_feedback_scores(scores=scores)
|
|
618
715
|
"""
|
|
619
716
|
score_messages = helpers.parse_feedback_score_messages(
|
|
620
717
|
scores=scores,
|
|
621
|
-
project_name=project_name or self.
|
|
718
|
+
project_name=project_name or self.project_name,
|
|
622
719
|
parsed_item_class=messages.FeedbackScoreMessage,
|
|
623
720
|
logger=LOGGER,
|
|
624
721
|
)
|
|
@@ -640,6 +737,36 @@ class Opik:
|
|
|
640
737
|
|
|
641
738
|
self._streamer.put(add_trace_feedback_scores_batch_message)
|
|
642
739
|
|
|
740
|
+
def log_threads_feedback_scores(
|
|
741
|
+
self, scores: List[BatchFeedbackScoreDict], project_name: Optional[str] = None
|
|
742
|
+
) -> None:
|
|
743
|
+
"""
|
|
744
|
+
Log feedback scores for threads.
|
|
745
|
+
|
|
746
|
+
Args:
|
|
747
|
+
scores (List[BatchFeedbackScoreDict]): A list of feedback score dictionaries.
|
|
748
|
+
Specifying a thread id via `id` key for each score is mandatory.
|
|
749
|
+
project_name: The name of the project in which the threads are logged. If not set, the project name
|
|
750
|
+
which was configured when the Opik instance was created will be used.
|
|
751
|
+
Deprecated: use `project_name` in the feedback score dictionary that's listed in the `scores` parameter.
|
|
752
|
+
|
|
753
|
+
Returns:
|
|
754
|
+
None
|
|
755
|
+
|
|
756
|
+
Example:
|
|
757
|
+
>>> from opik import Opik
|
|
758
|
+
>>> client = Opik()
|
|
759
|
+
>>> # Batch logging across multiple projects
|
|
760
|
+
>>> scores = [
|
|
761
|
+
>>> {"id": "thread_123", "name": "user_satisfaction", "value": 0.85, "project_name": "project-A"},
|
|
762
|
+
>>> {"id": "thread_456", "name": "user_satisfaction", "value": 0.92, "project_name": "project-B"},
|
|
763
|
+
>>> ]
|
|
764
|
+
>>> client.log_threads_feedback_scores(scores=scores)
|
|
765
|
+
"""
|
|
766
|
+
self.get_threads_client().log_threads_feedback_scores(
|
|
767
|
+
scores=scores, project_name=project_name
|
|
768
|
+
)
|
|
769
|
+
|
|
643
770
|
def delete_trace_feedback_score(self, trace_id: str, name: str) -> None:
|
|
644
771
|
"""
|
|
645
772
|
Deletes a feedback score associated with a specific trace.
|
|
@@ -740,8 +867,13 @@ class Opik:
|
|
|
740
867
|
self._rest_client, dataset_name
|
|
741
868
|
)
|
|
742
869
|
|
|
870
|
+
experiments_client = self.get_experiments_client()
|
|
743
871
|
experiments = dataset_rest_operations.get_dataset_experiments(
|
|
744
|
-
self._rest_client,
|
|
872
|
+
rest_client=self._rest_client,
|
|
873
|
+
dataset_id=dataset_id,
|
|
874
|
+
max_results=max_results,
|
|
875
|
+
streamer=self._streamer,
|
|
876
|
+
experiments_client=experiments_client,
|
|
745
877
|
)
|
|
746
878
|
|
|
747
879
|
return experiments
|
|
@@ -805,8 +937,8 @@ class Opik:
|
|
|
805
937
|
dataset_name: str,
|
|
806
938
|
name: Optional[str] = None,
|
|
807
939
|
experiment_config: Optional[Dict[str, Any]] = None,
|
|
808
|
-
prompt: Optional[
|
|
809
|
-
prompts: Optional[List[
|
|
940
|
+
prompt: Optional[prompt_module.base_prompt.BasePrompt] = None,
|
|
941
|
+
prompts: Optional[List[prompt_module.base_prompt.BasePrompt]] = None,
|
|
810
942
|
type: Literal["regular", "trial", "mini-batch"] = "regular",
|
|
811
943
|
optimization_id: Optional[str] = None,
|
|
812
944
|
) -> experiment.Experiment:
|
|
@@ -853,11 +985,49 @@ class Opik:
|
|
|
853
985
|
name=name,
|
|
854
986
|
dataset_name=dataset_name,
|
|
855
987
|
rest_client=self._rest_client,
|
|
988
|
+
streamer=self._streamer,
|
|
989
|
+
experiments_client=self.get_experiments_client(),
|
|
856
990
|
prompts=checked_prompts,
|
|
857
991
|
)
|
|
858
992
|
|
|
859
993
|
return experiment_
|
|
860
994
|
|
|
995
|
+
def update_experiment(
|
|
996
|
+
self,
|
|
997
|
+
id: str,
|
|
998
|
+
name: Optional[str] = None,
|
|
999
|
+
experiment_config: Optional[Dict[str, Any]] = None,
|
|
1000
|
+
) -> None:
|
|
1001
|
+
"""
|
|
1002
|
+
Update an experiment's name and/or configuration.
|
|
1003
|
+
|
|
1004
|
+
Args:
|
|
1005
|
+
id: The experiment ID.
|
|
1006
|
+
name: The new name for the experiment. If None, the name will not be updated.
|
|
1007
|
+
experiment_config: The new configuration for the experiment. If None, the configuration will not be updated.
|
|
1008
|
+
|
|
1009
|
+
Raises:
|
|
1010
|
+
ValueError: if id is None or empty, or if both name and experiment_config are None
|
|
1011
|
+
"""
|
|
1012
|
+
if not id:
|
|
1013
|
+
raise ValueError(
|
|
1014
|
+
f"id must be provided and can not be None or empty, id: {id}"
|
|
1015
|
+
)
|
|
1016
|
+
|
|
1017
|
+
if name is None and experiment_config is None:
|
|
1018
|
+
raise ValueError(
|
|
1019
|
+
"At least one of 'name' or 'experiment_config' must be provided"
|
|
1020
|
+
)
|
|
1021
|
+
|
|
1022
|
+
# Only include parameters that are provided to avoid clearing fields
|
|
1023
|
+
request_params: Dict[str, Any] = {}
|
|
1024
|
+
if name is not None:
|
|
1025
|
+
request_params["name"] = name
|
|
1026
|
+
if experiment_config is not None:
|
|
1027
|
+
request_params["metadata"] = experiment_config
|
|
1028
|
+
|
|
1029
|
+
self._rest_client.experiments.update_experiment(id, **request_params)
|
|
1030
|
+
|
|
861
1031
|
def get_experiment_by_name(self, name: str) -> experiment.Experiment:
|
|
862
1032
|
"""
|
|
863
1033
|
Returns an existing experiment by its name.
|
|
@@ -877,18 +1047,20 @@ class Opik:
|
|
|
877
1047
|
|
|
878
1048
|
return experiment.Experiment(
|
|
879
1049
|
id=experiment_public.id,
|
|
880
|
-
name=name,
|
|
1050
|
+
name=experiment_public.name,
|
|
881
1051
|
dataset_name=experiment_public.dataset_name,
|
|
882
1052
|
rest_client=self._rest_client,
|
|
883
|
-
|
|
1053
|
+
streamer=self._streamer,
|
|
1054
|
+
experiments_client=self.get_experiments_client(),
|
|
884
1055
|
)
|
|
885
1056
|
|
|
886
1057
|
def get_experiments_by_name(self, name: str) -> List[experiment.Experiment]:
|
|
887
1058
|
"""
|
|
888
|
-
Returns a list of existing experiments
|
|
1059
|
+
Returns a list of existing experiments containing the given string in their name.
|
|
1060
|
+
Search is case-insensitive.
|
|
889
1061
|
|
|
890
1062
|
Args:
|
|
891
|
-
name: The
|
|
1063
|
+
name: The string to search for in the experiment names.
|
|
892
1064
|
|
|
893
1065
|
Returns:
|
|
894
1066
|
List[experiment.Experiment]: List of existing experiments.
|
|
@@ -901,9 +1073,11 @@ class Opik:
|
|
|
901
1073
|
for public_experiment in experiments_public:
|
|
902
1074
|
experiment_ = experiment.Experiment(
|
|
903
1075
|
id=public_experiment.id,
|
|
1076
|
+
name=public_experiment.name,
|
|
904
1077
|
dataset_name=public_experiment.dataset_name,
|
|
905
|
-
name=name,
|
|
906
1078
|
rest_client=self._rest_client,
|
|
1079
|
+
streamer=self._streamer,
|
|
1080
|
+
experiments_client=self.get_experiments_client(),
|
|
907
1081
|
)
|
|
908
1082
|
result.append(experiment_)
|
|
909
1083
|
|
|
@@ -935,7 +1109,8 @@ class Opik:
|
|
|
935
1109
|
name=experiment_public.name,
|
|
936
1110
|
dataset_name=experiment_public.dataset_name,
|
|
937
1111
|
rest_client=self._rest_client,
|
|
938
|
-
|
|
1112
|
+
streamer=self._streamer,
|
|
1113
|
+
experiments_client=self.get_experiments_client(),
|
|
939
1114
|
)
|
|
940
1115
|
|
|
941
1116
|
def end(self, timeout: Optional[int] = None) -> None:
|
|
@@ -970,34 +1145,90 @@ class Opik:
|
|
|
970
1145
|
filter_string: Optional[str] = None,
|
|
971
1146
|
max_results: int = 1000,
|
|
972
1147
|
truncate: bool = True,
|
|
1148
|
+
wait_for_at_least: Optional[int] = None,
|
|
1149
|
+
wait_for_timeout: int = httpx_client.READ_TIMEOUT_SECONDS,
|
|
973
1150
|
) -> List[trace_public.TracePublic]:
|
|
974
1151
|
"""
|
|
975
|
-
Search for traces in the given project.
|
|
1152
|
+
Search for traces in the given project. Optionally, you can wait for at least a certain number of traces
|
|
1153
|
+
to be found before returning within the specified timeout. If wait_for_at_least number of traces are not found
|
|
1154
|
+
within the specified timeout, an exception will be raised.
|
|
976
1155
|
|
|
977
1156
|
Args:
|
|
978
1157
|
project_name: The name of the project to search traces in. If not provided, will search across the project name configured when the Client was created which defaults to the `Default Project`.
|
|
979
|
-
filter_string: A filter string to narrow down the search
|
|
1158
|
+
filter_string: A filter string to narrow down the search using Opik Query Language (OQL).
|
|
1159
|
+
The format is: "<COLUMN> <OPERATOR> <VALUE> [AND <COLUMN> <OPERATOR> <VALUE>]*"
|
|
1160
|
+
|
|
1161
|
+
Supported columns include:
|
|
1162
|
+
- `id`, `name`, `created_by`, `thread_id`, `type`, `model`, `provider`: String fields with full operator support
|
|
1163
|
+
- `status`: String field (=, contains, not_contains only)
|
|
1164
|
+
- `start_time`, `end_time`: DateTime fields (use ISO 8601 format, e.g., "2024-01-01T00:00:00Z")
|
|
1165
|
+
- `input`, `output`: String fields for content (=, contains, not_contains only)
|
|
1166
|
+
- `metadata`: Dictionary field (use dot notation, e.g., "metadata.model")
|
|
1167
|
+
- `feedback_scores`: Numeric field (use dot notation, e.g., "feedback_scores.accuracy")
|
|
1168
|
+
- `tags`: List field (use "contains" operator only)
|
|
1169
|
+
- `usage.total_tokens`, `usage.prompt_tokens`, `usage.completion_tokens`: Numeric usage fields
|
|
1170
|
+
- `duration`, `number_of_messages`, `total_estimated_cost`: Numeric fields
|
|
1171
|
+
|
|
1172
|
+
Supported operators by column:
|
|
1173
|
+
- `id`, `name`, `created_by`, `thread_id`, `type`, `model`, `provider`: =, !=, contains, not_contains, starts_with, ends_with, >, <
|
|
1174
|
+
- `status`: =, contains, not_contains
|
|
1175
|
+
- `start_time`, `end_time`: =, >, <, >=, <=
|
|
1176
|
+
- `input`, `output`: =, contains, not_contains
|
|
1177
|
+
- `metadata`: =, contains, >, <
|
|
1178
|
+
- `feedback_scores`: =, >, <, >=, <=, is_empty, is_not_empty
|
|
1179
|
+
- `tags`: contains (only)
|
|
1180
|
+
- `usage.total_tokens`, `usage.prompt_tokens`, `usage.completion_tokens`, `duration`, `number_of_messages`, `total_estimated_cost`: =, !=, >, <, >=, <=
|
|
1181
|
+
|
|
1182
|
+
Examples:
|
|
1183
|
+
- `start_time >= "2024-01-01T00:00:00Z"` - Filter by start date
|
|
1184
|
+
- `start_time > "2024-01-01T00:00:00Z" AND start_time < "2024-02-01T00:00:00Z"` - Date range
|
|
1185
|
+
- `input contains "question"` - Filter by input content
|
|
1186
|
+
- `usage.total_tokens > 1000` - Filter by token usage
|
|
1187
|
+
- `feedback_scores.accuracy > 0.8` - Filter by feedback score
|
|
1188
|
+
- `feedback_scores.my_metric is_empty` - Filter traces with empty feedback score
|
|
1189
|
+
- `feedback_scores.my_metric is_not_empty` - Filter traces with non-empty feedback score
|
|
1190
|
+
- `tags contains "production"` - Filter by tag
|
|
1191
|
+
- `metadata.model = "gpt-4"` - Filter by metadata field
|
|
1192
|
+
- `thread_id = "thread_123"` - Filter by thread ID
|
|
1193
|
+
|
|
1194
|
+
If not provided, all traces in the project will be returned up to the limit.
|
|
980
1195
|
max_results: The maximum number of traces to return.
|
|
981
1196
|
truncate: Whether to truncate image data stored in input, output, or metadata
|
|
1197
|
+
wait_for_at_least: The minimum number of traces to wait for before returning.
|
|
1198
|
+
wait_for_timeout: The timeout for waiting for traces.
|
|
1199
|
+
|
|
1200
|
+
Raises:
|
|
1201
|
+
exceptions.SearchTimeoutError if wait_for_at_least traces are not found within the specified timeout.
|
|
982
1202
|
"""
|
|
983
1203
|
filters_ = helpers.parse_filter_expressions(
|
|
984
1204
|
filter_string, parsed_item_class=trace_filter_public.TraceFilterPublic
|
|
985
1205
|
)
|
|
986
1206
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
limit=current_batch_size,
|
|
993
|
-
truncate=truncate,
|
|
994
|
-
last_retrieved_id=last_retrieved_id,
|
|
995
|
-
),
|
|
1207
|
+
search_functor = functools.partial(
|
|
1208
|
+
search_helpers.search_traces_with_filters,
|
|
1209
|
+
rest_client=self._rest_client,
|
|
1210
|
+
project_name=project_name or self._project_name,
|
|
1211
|
+
filters=filters_,
|
|
996
1212
|
max_results=max_results,
|
|
997
|
-
|
|
1213
|
+
truncate=truncate,
|
|
1214
|
+
)
|
|
1215
|
+
|
|
1216
|
+
if wait_for_at_least is None:
|
|
1217
|
+
return search_functor()
|
|
1218
|
+
|
|
1219
|
+
# do synchronization with backend if wait_for_at_least is provided until a specific number of traces are found
|
|
1220
|
+
result = search_helpers.search_and_wait_for_done(
|
|
1221
|
+
search_functor=search_functor,
|
|
1222
|
+
wait_for_at_least=wait_for_at_least,
|
|
1223
|
+
wait_for_timeout=wait_for_timeout,
|
|
1224
|
+
sleep_time=5,
|
|
998
1225
|
)
|
|
1226
|
+
if len(result) < wait_for_at_least:
|
|
1227
|
+
raise exceptions.SearchTimeoutError(
|
|
1228
|
+
f"Timeout after {wait_for_timeout} seconds: expected {wait_for_at_least} traces, but only {len(result)} were found."
|
|
1229
|
+
)
|
|
999
1230
|
|
|
1000
|
-
return
|
|
1231
|
+
return result
|
|
1001
1232
|
|
|
1002
1233
|
def search_spans(
|
|
1003
1234
|
self,
|
|
@@ -1006,37 +1237,93 @@ class Opik:
|
|
|
1006
1237
|
filter_string: Optional[str] = None,
|
|
1007
1238
|
max_results: int = 1000,
|
|
1008
1239
|
truncate: bool = True,
|
|
1240
|
+
wait_for_at_least: Optional[int] = None,
|
|
1241
|
+
wait_for_timeout: int = httpx_client.READ_TIMEOUT_SECONDS,
|
|
1009
1242
|
) -> List[span_public.SpanPublic]:
|
|
1010
1243
|
"""
|
|
1011
1244
|
Search for spans in the given trace. This allows you to search spans based on the span input, output,
|
|
1012
|
-
metadata, tags, etc. or based on the trace ID.
|
|
1245
|
+
metadata, tags, etc. or based on the trace ID. Also, you can wait for at least a certain number of spans
|
|
1246
|
+
to be found before returning within the specified timeout. If wait_for_at_least number of spans are not found
|
|
1247
|
+
within the specified timeout, an exception will be raised.
|
|
1013
1248
|
|
|
1014
1249
|
Args:
|
|
1015
1250
|
project_name: The name of the project to search spans in. If not provided, will search across the project name configured when the Client was created which defaults to the `Default Project`.
|
|
1016
1251
|
trace_id: The ID of the trace to search spans in. If provided, the search will be limited to the spans in the given trace.
|
|
1017
|
-
filter_string: A filter string to narrow down the search.
|
|
1252
|
+
filter_string: A filter string to narrow down the search using Opik Query Language (OQL).
|
|
1253
|
+
The format is: "<COLUMN> <OPERATOR> <VALUE> [AND <COLUMN> <OPERATOR> <VALUE>]*"
|
|
1254
|
+
|
|
1255
|
+
Supported columns include:
|
|
1256
|
+
- `id`, `name`, `created_by`, `thread_id`, `type`, `model`, `provider`: String fields with full operator support
|
|
1257
|
+
- `status`: String field (=, contains, not_contains only)
|
|
1258
|
+
- `start_time`, `end_time`: DateTime fields (use ISO 8601 format, e.g., "2024-01-01T00:00:00Z")
|
|
1259
|
+
- `input`, `output`: String fields for content (=, contains, not_contains only)
|
|
1260
|
+
- `metadata`: Dictionary field (use dot notation, e.g., "metadata.model")
|
|
1261
|
+
- `feedback_scores`: Numeric field (use dot notation, e.g., "feedback_scores.accuracy")
|
|
1262
|
+
- `tags`: List field (use "contains" operator only)
|
|
1263
|
+
- `usage.total_tokens`, `usage.prompt_tokens`, `usage.completion_tokens`: Numeric usage fields
|
|
1264
|
+
- `duration`, `number_of_messages`, `total_estimated_cost`: Numeric fields
|
|
1265
|
+
|
|
1266
|
+
Supported operators by column:
|
|
1267
|
+
- `id`, `name`, `created_by`, `thread_id`, `type`, `model`, `provider`: =, !=, contains, not_contains, starts_with, ends_with, >, <
|
|
1268
|
+
- `status`: =, contains, not_contains
|
|
1269
|
+
- `start_time`, `end_time`: =, >, <, >=, <=
|
|
1270
|
+
- `input`, `output`: =, contains, not_contains
|
|
1271
|
+
- `metadata`: =, contains, >, <
|
|
1272
|
+
- `feedback_scores`: =, >, <, >=, <=, is_empty, is_not_empty
|
|
1273
|
+
- `tags`: contains (only)
|
|
1274
|
+
- `usage.total_tokens`, `usage.prompt_tokens`, `usage.completion_tokens`, `duration`, `number_of_messages`, `total_estimated_cost`: =, !=, >, <, >=, <=
|
|
1275
|
+
|
|
1276
|
+
Examples:
|
|
1277
|
+
- `start_time >= "2024-01-01T00:00:00Z"` - Filter by start date
|
|
1278
|
+
- `start_time > "2024-01-01T00:00:00Z" AND start_time < "2024-02-01T00:00:00Z"` - Date range
|
|
1279
|
+
- `input contains "question"` - Filter by input content
|
|
1280
|
+
- `usage.total_tokens > 1000` - Filter by token usage
|
|
1281
|
+
- `feedback_scores.accuracy > 0.8` - Filter by feedback score
|
|
1282
|
+
- `feedback_scores.my_metric is_empty` - Filter spans with empty feedback score
|
|
1283
|
+
- `feedback_scores.my_metric is_not_empty` - Filter spans with non-empty feedback score
|
|
1284
|
+
- `tags contains "production"` - Filter by tag
|
|
1285
|
+
- `metadata.model = "gpt-4"` - Filter by metadata field
|
|
1286
|
+
- `thread_id = "thread_123"` - Filter by thread ID
|
|
1287
|
+
|
|
1288
|
+
If not provided, all spans in the project/trace will be returned up to the limit.
|
|
1018
1289
|
max_results: The maximum number of spans to return.
|
|
1019
1290
|
truncate: Whether to truncate image data stored in input, output, or metadata
|
|
1291
|
+
wait_for_at_least: The minimum number of spans to wait for before returning.
|
|
1292
|
+
wait_for_timeout: The timeout for waiting for spans.
|
|
1293
|
+
|
|
1294
|
+
Raises:
|
|
1295
|
+
exceptions.SearchTimeoutError if wait_for_at_least spans are not found within the specified timeout.
|
|
1020
1296
|
"""
|
|
1021
1297
|
filters = helpers.parse_filter_expressions(
|
|
1022
1298
|
filter_string, parsed_item_class=span_filter_public.SpanFilterPublic
|
|
1023
1299
|
)
|
|
1024
1300
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
limit=current_batch_size,
|
|
1032
|
-
truncate=truncate,
|
|
1033
|
-
last_retrieved_id=last_retrieved_id,
|
|
1034
|
-
),
|
|
1301
|
+
search_functor = functools.partial(
|
|
1302
|
+
search_helpers.search_spans_with_filters,
|
|
1303
|
+
rest_client=self._rest_client,
|
|
1304
|
+
project_name=project_name or self._project_name,
|
|
1305
|
+
trace_id=trace_id,
|
|
1306
|
+
filters=filters,
|
|
1035
1307
|
max_results=max_results,
|
|
1036
|
-
|
|
1308
|
+
truncate=truncate,
|
|
1037
1309
|
)
|
|
1038
1310
|
|
|
1039
|
-
|
|
1311
|
+
if wait_for_at_least is None:
|
|
1312
|
+
return search_functor()
|
|
1313
|
+
|
|
1314
|
+
# do synchronization with backend if wait_for_at_least is provided until a specific number of spans are found
|
|
1315
|
+
result = search_helpers.search_and_wait_for_done(
|
|
1316
|
+
search_functor=search_functor,
|
|
1317
|
+
wait_for_at_least=wait_for_at_least,
|
|
1318
|
+
wait_for_timeout=wait_for_timeout,
|
|
1319
|
+
sleep_time=5,
|
|
1320
|
+
)
|
|
1321
|
+
if len(result) < wait_for_at_least:
|
|
1322
|
+
raise exceptions.SearchTimeoutError(
|
|
1323
|
+
f"Timeout after {wait_for_timeout} seconds: expected {wait_for_at_least} spans, but only {len(result)} were found."
|
|
1324
|
+
)
|
|
1325
|
+
|
|
1326
|
+
return result
|
|
1040
1327
|
|
|
1041
1328
|
def get_trace_content(self, id: str) -> trace_public.TracePublic:
|
|
1042
1329
|
"""
|
|
@@ -1131,77 +1418,273 @@ class Opik:
|
|
|
1131
1418
|
name: str,
|
|
1132
1419
|
prompt: str,
|
|
1133
1420
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1134
|
-
type: PromptType = PromptType.MUSTACHE,
|
|
1135
|
-
) -> Prompt:
|
|
1421
|
+
type: prompt_module.PromptType = prompt_module.PromptType.MUSTACHE,
|
|
1422
|
+
) -> prompt_module.Prompt:
|
|
1136
1423
|
"""
|
|
1137
|
-
Creates a new prompt with the given name and template.
|
|
1138
|
-
If a prompt with the same name already exists, it will create a new version of the existing prompt if the templates differ.
|
|
1424
|
+
Creates a new text prompt with the given name and template.
|
|
1425
|
+
If a text prompt with the same name already exists, it will create a new version of the existing prompt if the templates differ.
|
|
1139
1426
|
|
|
1140
1427
|
Parameters:
|
|
1141
1428
|
name: The name of the prompt.
|
|
1142
1429
|
prompt: The template content of the prompt.
|
|
1143
1430
|
metadata: Optional metadata to be included in the prompt.
|
|
1431
|
+
type: The template type (MUSTACHE or JINJA2).
|
|
1144
1432
|
|
|
1145
1433
|
Returns:
|
|
1146
1434
|
A Prompt object containing details of the created or retrieved prompt.
|
|
1147
1435
|
|
|
1148
1436
|
Raises:
|
|
1149
|
-
|
|
1437
|
+
PromptTemplateStructureMismatch: If a chat prompt with the same name already exists (template structure is immutable).
|
|
1438
|
+
ApiError: If there is an error during the creation of the prompt.
|
|
1150
1439
|
"""
|
|
1151
|
-
|
|
1152
|
-
prompt_version =
|
|
1440
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1441
|
+
prompt_version = prompt_client_.create_prompt(
|
|
1153
1442
|
name=name, prompt=prompt, metadata=metadata, type=type
|
|
1154
1443
|
)
|
|
1155
|
-
return Prompt.from_fern_prompt_version(name, prompt_version)
|
|
1444
|
+
return prompt_module.Prompt.from_fern_prompt_version(name, prompt_version)
|
|
1445
|
+
|
|
1446
|
+
def create_chat_prompt(
|
|
1447
|
+
self,
|
|
1448
|
+
name: str,
|
|
1449
|
+
messages: List[Dict[str, Any]],
|
|
1450
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
1451
|
+
type: prompt_module.PromptType = prompt_module.PromptType.MUSTACHE,
|
|
1452
|
+
) -> prompt_module.ChatPrompt:
|
|
1453
|
+
"""
|
|
1454
|
+
Creates a new chat prompt with the given name and message templates.
|
|
1455
|
+
If a chat prompt with the same name already exists, it will create a new version if the messages differ.
|
|
1456
|
+
|
|
1457
|
+
Parameters:
|
|
1458
|
+
name: The name of the chat prompt.
|
|
1459
|
+
messages: List of message dictionaries with 'role' and 'content' fields.
|
|
1460
|
+
metadata: Optional metadata to be included in the prompt.
|
|
1461
|
+
type: The template type (MUSTACHE or JINJA2).
|
|
1462
|
+
|
|
1463
|
+
Returns:
|
|
1464
|
+
A ChatPrompt object containing details of the created or retrieved chat prompt.
|
|
1465
|
+
|
|
1466
|
+
Raises:
|
|
1467
|
+
PromptTemplateStructureMismatch: If a text prompt with the same name already exists (template structure is immutable).
|
|
1468
|
+
ApiError: If there is an error during the creation of the prompt.
|
|
1469
|
+
"""
|
|
1470
|
+
return prompt_module.ChatPrompt(
|
|
1471
|
+
name=name, messages=messages, metadata=metadata, type=type
|
|
1472
|
+
)
|
|
1156
1473
|
|
|
1157
1474
|
def get_prompt(
|
|
1158
1475
|
self,
|
|
1159
1476
|
name: str,
|
|
1160
1477
|
commit: Optional[str] = None,
|
|
1161
|
-
) -> Optional[Prompt]:
|
|
1478
|
+
) -> Optional[prompt_module.Prompt]:
|
|
1162
1479
|
"""
|
|
1163
|
-
Retrieve
|
|
1480
|
+
Retrieve a text prompt by name and optional commit version.
|
|
1481
|
+
|
|
1482
|
+
This method only returns text prompts.
|
|
1164
1483
|
|
|
1165
1484
|
Parameters:
|
|
1166
1485
|
name: The name of the prompt.
|
|
1167
1486
|
commit: An optional commit version of the prompt. If not provided, the latest version is retrieved.
|
|
1168
1487
|
|
|
1169
1488
|
Returns:
|
|
1170
|
-
Prompt: The details of the specified prompt.
|
|
1489
|
+
Prompt: The details of the specified text prompt, or None if not found.
|
|
1490
|
+
|
|
1491
|
+
Raises:
|
|
1492
|
+
PromptTemplateStructureMismatch: If the prompt exists but is a chat prompt (template structure mismatch).
|
|
1171
1493
|
"""
|
|
1172
|
-
|
|
1173
|
-
fern_prompt_version =
|
|
1494
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1495
|
+
fern_prompt_version = prompt_client_.get_prompt(
|
|
1496
|
+
name=name, commit=commit, raise_if_not_template_structure="text"
|
|
1497
|
+
)
|
|
1498
|
+
|
|
1499
|
+
if fern_prompt_version is None:
|
|
1500
|
+
return None
|
|
1501
|
+
|
|
1502
|
+
return prompt_module.Prompt.from_fern_prompt_version(name, fern_prompt_version)
|
|
1503
|
+
|
|
1504
|
+
def get_chat_prompt(
|
|
1505
|
+
self,
|
|
1506
|
+
name: str,
|
|
1507
|
+
commit: Optional[str] = None,
|
|
1508
|
+
) -> Optional[prompt_module.ChatPrompt]:
|
|
1509
|
+
"""
|
|
1510
|
+
Retrieve a chat prompt by name and optional commit version.
|
|
1511
|
+
|
|
1512
|
+
This method only returns chat prompts.
|
|
1513
|
+
|
|
1514
|
+
Parameters:
|
|
1515
|
+
name: The name of the prompt.
|
|
1516
|
+
commit: An optional commit version of the prompt. If not provided, the latest version is retrieved.
|
|
1517
|
+
|
|
1518
|
+
Returns:
|
|
1519
|
+
ChatPrompt: The details of the specified chat prompt, or None if not found.
|
|
1520
|
+
|
|
1521
|
+
Raises:
|
|
1522
|
+
PromptTemplateStructureMismatch: If the prompt exists but is a text prompt (template structure mismatch).
|
|
1523
|
+
"""
|
|
1524
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1525
|
+
fern_prompt_version = prompt_client_.get_prompt(
|
|
1526
|
+
name=name, commit=commit, raise_if_not_template_structure="chat"
|
|
1527
|
+
)
|
|
1528
|
+
|
|
1174
1529
|
if fern_prompt_version is None:
|
|
1175
1530
|
return None
|
|
1176
1531
|
|
|
1177
|
-
return
|
|
1532
|
+
return prompt_module.ChatPrompt.from_fern_prompt_version(
|
|
1533
|
+
name, fern_prompt_version
|
|
1534
|
+
)
|
|
1178
1535
|
|
|
1179
|
-
def
|
|
1536
|
+
def get_prompt_history(self, name: str) -> List[prompt_module.Prompt]:
|
|
1180
1537
|
"""
|
|
1181
|
-
Retrieve all
|
|
1538
|
+
Retrieve all text prompt versions history for a given prompt name.
|
|
1182
1539
|
|
|
1183
1540
|
Parameters:
|
|
1184
1541
|
name: The name of the prompt.
|
|
1185
1542
|
|
|
1186
1543
|
Returns:
|
|
1187
|
-
List[Prompt]: A list of
|
|
1544
|
+
List[Prompt]: A list of text Prompt instances for the given name, or an empty list if not found.
|
|
1545
|
+
|
|
1546
|
+
Raises:
|
|
1547
|
+
PromptTemplateStructureMismatch: If the prompt exists but is a chat prompt (template structure mismatch).
|
|
1188
1548
|
"""
|
|
1189
|
-
|
|
1190
|
-
|
|
1549
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1550
|
+
|
|
1551
|
+
# First, validate that this is a text prompt by trying to get the latest version
|
|
1552
|
+
# Let PromptTemplateStructureMismatch exception propagate - this is a hard error
|
|
1553
|
+
latest_version = prompt_client_.get_prompt(
|
|
1554
|
+
name=name, raise_if_not_template_structure="text"
|
|
1555
|
+
)
|
|
1556
|
+
|
|
1557
|
+
if latest_version is None:
|
|
1558
|
+
return []
|
|
1559
|
+
|
|
1560
|
+
# Now get all versions (we know it's a text prompt)
|
|
1561
|
+
fern_prompt_versions = prompt_client_.get_all_prompt_versions(name=name)
|
|
1562
|
+
|
|
1191
1563
|
result = [
|
|
1192
|
-
Prompt.from_fern_prompt_version(name, version)
|
|
1564
|
+
prompt_module.Prompt.from_fern_prompt_version(name, version)
|
|
1193
1565
|
for version in fern_prompt_versions
|
|
1194
1566
|
]
|
|
1195
1567
|
return result
|
|
1196
1568
|
|
|
1569
|
+
def get_chat_prompt_history(self, name: str) -> List[prompt_module.ChatPrompt]:
|
|
1570
|
+
"""
|
|
1571
|
+
Retrieve all chat prompt versions history for a given prompt name.
|
|
1572
|
+
|
|
1573
|
+
Parameters:
|
|
1574
|
+
name: The name of the prompt.
|
|
1575
|
+
|
|
1576
|
+
Returns:
|
|
1577
|
+
List[ChatPrompt]: A list of ChatPrompt instances for the given name, or an empty list if not found.
|
|
1578
|
+
|
|
1579
|
+
Raises:
|
|
1580
|
+
PromptTemplateStructureMismatch: If the prompt exists but is a text prompt (template structure mismatch).
|
|
1581
|
+
"""
|
|
1582
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1583
|
+
|
|
1584
|
+
# First, validate that this is a chat prompt by trying to get the latest version
|
|
1585
|
+
# Let PromptTemplateStructureMismatch exception propagate - this is a hard error
|
|
1586
|
+
latest_version = prompt_client_.get_prompt(
|
|
1587
|
+
name=name, raise_if_not_template_structure="chat"
|
|
1588
|
+
)
|
|
1589
|
+
|
|
1590
|
+
if latest_version is None:
|
|
1591
|
+
return []
|
|
1592
|
+
|
|
1593
|
+
# Now get all versions (we know it's a chat prompt)
|
|
1594
|
+
fern_prompt_versions = prompt_client_.get_all_prompt_versions(name=name)
|
|
1595
|
+
|
|
1596
|
+
result = [
|
|
1597
|
+
prompt_module.ChatPrompt.from_fern_prompt_version(name, version)
|
|
1598
|
+
for version in fern_prompt_versions
|
|
1599
|
+
]
|
|
1600
|
+
return result
|
|
1601
|
+
|
|
1602
|
+
def get_all_prompts(self, name: str) -> List[prompt_module.Prompt]:
|
|
1603
|
+
"""
|
|
1604
|
+
DEPRECATED: Please use Opik.get_prompt_history() instead.
|
|
1605
|
+
Retrieve all the prompt versions history for a given prompt name.
|
|
1606
|
+
|
|
1607
|
+
Parameters:
|
|
1608
|
+
name: The name of the prompt.
|
|
1609
|
+
|
|
1610
|
+
Returns:
|
|
1611
|
+
List[prompt_module.Prompt]: A list of Prompt instances for the given name.
|
|
1612
|
+
"""
|
|
1613
|
+
LOGGER.warning(
|
|
1614
|
+
"Opik.get_all_prompts() is deprecated. Please use Opik.get_prompt_history() instead."
|
|
1615
|
+
)
|
|
1616
|
+
return self.get_prompt_history(name)
|
|
1617
|
+
|
|
1618
|
+
def search_prompts(
|
|
1619
|
+
self, filter_string: Optional[str] = None
|
|
1620
|
+
) -> List[Union[prompt_module.Prompt, prompt_module.ChatPrompt]]:
|
|
1621
|
+
"""
|
|
1622
|
+
Retrieve the latest prompt versions (both string and chat prompts) for the given search parameters.
|
|
1623
|
+
|
|
1624
|
+
Parameters:
|
|
1625
|
+
filter_string: A filter string to narrow down the search using Opik Query Language (OQL).
|
|
1626
|
+
The format is: "<COLUMN> <OPERATOR> <VALUE> [AND <COLUMN> <OPERATOR> <VALUE>]*"
|
|
1627
|
+
|
|
1628
|
+
Supported columns include:
|
|
1629
|
+
- `id`, `name`: String fields
|
|
1630
|
+
- `tags`: List field (use "contains" operator only)
|
|
1631
|
+
- `created_by`: String field
|
|
1632
|
+
- `template_structure`: String field ("string" or "chat")
|
|
1633
|
+
|
|
1634
|
+
Supported operators by column:
|
|
1635
|
+
- `id`: =, !=, contains, not_contains, starts_with, ends_with, >, <
|
|
1636
|
+
- `name`: =, !=, contains, not_contains, starts_with, ends_with, >, <
|
|
1637
|
+
- `created_by`: =, !=, contains, not_contains, starts_with, ends_with, >, <
|
|
1638
|
+
- `template_structure`: =, !=
|
|
1639
|
+
- `tags`: contains (only)
|
|
1640
|
+
|
|
1641
|
+
Examples:
|
|
1642
|
+
- `tags contains "alpha"` - Filter by tag
|
|
1643
|
+
- `tags contains "alpha" AND tags contains "beta"` - Filter by multiple tags
|
|
1644
|
+
- `name contains "summary"` - Filter by name substring
|
|
1645
|
+
- `created_by = "user@example.com"` - Filter by creator
|
|
1646
|
+
- `id starts_with "prompt_"` - Filter by ID prefix
|
|
1647
|
+
- `template_structure = "text"` - Only text prompts
|
|
1648
|
+
- `template_structure = "chat"` - Only chat prompts
|
|
1649
|
+
|
|
1650
|
+
If not provided, all prompts (both text and chat) will be returned.
|
|
1651
|
+
|
|
1652
|
+
Returns:
|
|
1653
|
+
List[Union[Prompt, ChatPrompt]]: A list of Prompt and/or ChatPrompt instances found.
|
|
1654
|
+
"""
|
|
1655
|
+
oql = opik_query_language.OpikQueryLanguage(filter_string or "")
|
|
1656
|
+
parsed_filters = oql.get_filter_expressions()
|
|
1657
|
+
|
|
1658
|
+
prompt_client_ = prompt_client.PromptClient(self._rest_client)
|
|
1659
|
+
search_results = prompt_client_.search_prompts(parsed_filters=parsed_filters)
|
|
1660
|
+
|
|
1661
|
+
# Convert to Prompt or ChatPrompt objects based on template_structure
|
|
1662
|
+
prompts: List[Union[prompt_module.Prompt, prompt_module.ChatPrompt]] = []
|
|
1663
|
+
for result in search_results:
|
|
1664
|
+
if result.template_structure == "chat":
|
|
1665
|
+
prompts.append(
|
|
1666
|
+
prompt_module.ChatPrompt.from_fern_prompt_version(
|
|
1667
|
+
result.name, result.prompt_version_detail
|
|
1668
|
+
)
|
|
1669
|
+
)
|
|
1670
|
+
else:
|
|
1671
|
+
prompts.append(
|
|
1672
|
+
prompt_module.Prompt.from_fern_prompt_version(
|
|
1673
|
+
result.name, result.prompt_version_detail
|
|
1674
|
+
)
|
|
1675
|
+
)
|
|
1676
|
+
|
|
1677
|
+
return prompts
|
|
1678
|
+
|
|
1197
1679
|
def create_optimization(
|
|
1198
1680
|
self,
|
|
1199
1681
|
dataset_name: str,
|
|
1200
1682
|
objective_name: str,
|
|
1201
1683
|
name: Optional[str] = None,
|
|
1202
1684
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1685
|
+
optimization_id: Optional[str] = None,
|
|
1203
1686
|
) -> optimization.Optimization:
|
|
1204
|
-
id = id_helpers.generate_id()
|
|
1687
|
+
id = optimization_id or id_helpers.generate_id()
|
|
1205
1688
|
|
|
1206
1689
|
self._rest_client.optimizations.create_optimization(
|
|
1207
1690
|
id=id,
|
|
@@ -1224,6 +1707,15 @@ class Opik:
|
|
|
1224
1707
|
_ = self._rest_client.optimizations.get_optimization_by_id(id)
|
|
1225
1708
|
return optimization.Optimization(id=id, rest_client=self._rest_client)
|
|
1226
1709
|
|
|
1710
|
+
def get_experiments_client(self) -> experiments_client.ExperimentsClient:
|
|
1711
|
+
"""
|
|
1712
|
+
Retrieves an instance of `ExperimentsClient`.
|
|
1713
|
+
|
|
1714
|
+
Returns:
|
|
1715
|
+
An instance of the ExperimentsClient initialized with a cached REST client.
|
|
1716
|
+
"""
|
|
1717
|
+
return experiments_client.ExperimentsClient(self._rest_client)
|
|
1718
|
+
|
|
1227
1719
|
|
|
1228
1720
|
@functools.lru_cache()
|
|
1229
1721
|
def get_client_cached() -> Opik:
|