langwatch-scenario 0.4.0__py3-none-any.whl → 0.7.1__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.
- {langwatch_scenario-0.4.0.dist-info → langwatch_scenario-0.7.1.dist-info}/METADATA +210 -86
- langwatch_scenario-0.7.1.dist-info/RECORD +237 -0
- scenario/__init__.py +12 -118
- scenario/_events/__init__.py +64 -0
- scenario/_events/event_bus.py +185 -0
- scenario/_events/event_reporter.py +83 -0
- scenario/_events/events.py +162 -0
- scenario/_events/messages.py +58 -0
- scenario/_events/utils.py +97 -0
- scenario/_generated/langwatch_api_client/README.md +139 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/__init__.py +13 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/__init__.py +1 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/__init__.py +1 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/delete_api_annotations_id.py +155 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/delete_api_prompts_by_id.py +218 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/delete_api_scenario_events.py +183 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/get_api_annotations.py +136 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/get_api_annotations_id.py +155 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/get_api_annotations_trace_id.py +160 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/get_api_dataset_by_slug_or_id.py +229 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/get_api_prompts.py +188 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/get_api_prompts_by_id.py +218 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/get_api_prompts_by_id_versions.py +218 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/get_api_trace_id.py +155 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/patch_api_annotations_id.py +178 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/post_api_annotations_trace_id.py +178 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/post_api_dataset_by_slug_entries.py +108 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/post_api_prompts.py +187 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/post_api_prompts_by_id_versions.py +241 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/post_api_scenario_events.py +229 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/post_api_trace_id_share.py +155 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/post_api_trace_id_unshare.py +155 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/default/put_api_prompts_by_id.py +241 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/traces/__init__.py +1 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/api/traces/post_api_trace_search.py +168 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/client.py +268 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/errors.py +16 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/__init__.py +455 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/annotation.py +131 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/dataset_post_entries.py +74 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/dataset_post_entries_entries_item.py +44 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_annotations_id_response_200.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_prompts_by_id_response_200.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_prompts_by_id_response_400.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_prompts_by_id_response_400_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_prompts_by_id_response_401.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_prompts_by_id_response_401_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_prompts_by_id_response_404.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_prompts_by_id_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_scenario_events_response_200.py +81 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_scenario_events_response_400.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_scenario_events_response_401.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/delete_api_scenario_events_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/error.py +67 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/evaluation.py +164 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/evaluation_timestamps.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_dataset_by_slug_or_id_response_200.py +75 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_dataset_by_slug_or_id_response_200_data_item.py +109 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_dataset_by_slug_or_id_response_200_data_item_entry.py +44 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_dataset_by_slug_or_id_response_400.py +78 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_dataset_by_slug_or_id_response_401.py +78 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_dataset_by_slug_or_id_response_404.py +78 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_dataset_by_slug_or_id_response_422.py +67 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_dataset_by_slug_or_id_response_500.py +78 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_200.py +172 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_200_messages_item.py +69 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_200_messages_item_role.py +10 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_200_response_format_type_0.py +81 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_200_response_format_type_0_json_schema.py +77 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_200_response_format_type_0_json_schema_schema.py +44 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_200_response_format_type_0_type.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_400.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_400_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_401.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_401_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_404.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200.py +155 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data.py +204 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_demonstrations.py +101 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_demonstrations_columns_item.py +79 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_demonstrations_columns_item_type.py +18 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_demonstrations_rows_item.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_inputs_item.py +71 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_inputs_item_type.py +16 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_messages_item.py +71 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_messages_item_role.py +10 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_outputs_item.py +98 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_outputs_item_json_schema.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_outputs_item_type.py +11 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_200_config_data_prompting_technique.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_400.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_400_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_401.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_401_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_404.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_by_id_versions_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_200_item.py +172 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_200_item_messages_item.py +69 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_200_item_messages_item_role.py +10 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_200_item_response_format_type_0.py +81 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_200_item_response_format_type_0_json_schema.py +77 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_200_item_response_format_type_0_json_schema_schema.py +44 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_200_item_response_format_type_0_type.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_400.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_400_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_401.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_401_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_prompts_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200.py +249 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_error_type_0.py +79 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_evaluations_item.py +152 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_evaluations_item_error.py +79 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_evaluations_item_timestamps.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_input.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_metadata.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_metrics.py +95 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_output.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item.py +271 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item_error_type_0.py +79 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item_input.py +90 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item_input_value_item.py +69 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item_metrics.py +77 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item_output.py +89 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item_output_value_item.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item_params.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_spans_item_timestamps.py +95 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/get_api_trace_id_response_200_timestamps.py +77 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/input_.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/metadata.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/metrics.py +115 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/output.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/pagination.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/patch_api_annotations_id_body.py +77 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/patch_api_annotations_id_response_200.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_annotations_trace_id_body.py +77 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_body.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body.py +147 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data.py +207 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_demonstrations.py +106 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_demonstrations_columns_item.py +79 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_demonstrations_columns_item_type.py +18 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_demonstrations_rows_item.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_inputs_item.py +71 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_inputs_item_type.py +16 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_messages_item.py +71 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_messages_item_role.py +10 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_outputs_item.py +98 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_outputs_item_json_schema.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_outputs_item_type.py +11 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_body_config_data_prompting_technique.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200.py +155 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data.py +206 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_demonstrations.py +101 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_demonstrations_columns_item.py +79 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_demonstrations_columns_item_type.py +18 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_demonstrations_rows_item.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_inputs_item.py +71 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_inputs_item_type.py +16 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_messages_item.py +71 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_messages_item_role.py +10 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_outputs_item.py +98 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_outputs_item_json_schema.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_outputs_item_type.py +11 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_200_config_data_prompting_technique.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_400.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_400_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_401.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_401_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_404.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_by_id_versions_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_200.py +172 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_200_messages_item.py +69 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_200_messages_item_role.py +10 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_200_response_format_type_0.py +81 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_200_response_format_type_0_json_schema.py +77 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_200_response_format_type_0_json_schema_schema.py +44 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_200_response_format_type_0_type.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_400.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_400_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_401.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_401_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_prompts_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_0.py +127 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_0_metadata.py +68 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_1.py +164 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_1_results_type_0.py +98 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_1_results_type_0_verdict.py +10 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_1_status.py +13 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_2.py +245 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_2_messages_item_type_0.py +88 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_2_messages_item_type_1.py +88 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_2_messages_item_type_2.py +120 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_2_messages_item_type_2_tool_calls_item.py +87 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_2_messages_item_type_2_tool_calls_item_function.py +67 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_2_messages_item_type_3.py +88 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_body_type_2_messages_item_type_4.py +85 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_response_201.py +81 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_response_400.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_response_401.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_scenario_events_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_trace_id_share_response_200.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/post_api_trace_id_unshare_response_200.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/put_api_prompts_by_id_body.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/put_api_prompts_by_id_response_200.py +75 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/put_api_prompts_by_id_response_400.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/put_api_prompts_by_id_response_400_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/put_api_prompts_by_id_response_401.py +61 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/put_api_prompts_by_id_response_401_error.py +8 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/put_api_prompts_by_id_response_404.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/put_api_prompts_by_id_response_500.py +59 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/search_request.py +133 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/search_request_filters.py +51 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/search_response.py +93 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/timestamps.py +77 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/models/trace.py +225 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/py.typed +1 -0
- scenario/_generated/langwatch_api_client/lang_watch_api_client/types.py +46 -0
- scenario/_generated/langwatch_api_client/pyproject.toml +27 -0
- scenario/_utils/__init__.py +32 -0
- scenario/_utils/ids.py +58 -0
- scenario/_utils/message_conversion.py +103 -0
- scenario/{utils.py → _utils/utils.py} +21 -110
- scenario/agent_adapter.py +8 -4
- scenario/cache.py +4 -3
- scenario/config.py +7 -5
- scenario/judge_agent.py +13 -29
- scenario/pytest_plugin.py +6 -51
- scenario/scenario_executor.py +372 -215
- scenario/scenario_state.py +6 -6
- scenario/script.py +9 -9
- scenario/types.py +15 -8
- scenario/user_simulator_agent.py +4 -11
- langwatch_scenario-0.4.0.dist-info/RECORD +0 -18
- {langwatch_scenario-0.4.0.dist-info → langwatch_scenario-0.7.1.dist-info}/WHEEL +0 -0
- {langwatch_scenario-0.4.0.dist-info → langwatch_scenario-0.7.1.dist-info}/entry_points.txt +0 -0
- {langwatch_scenario-0.4.0.dist-info → langwatch_scenario-0.7.1.dist-info}/top_level.txt +0 -0
- /scenario/{error_messages.py → _error_messages.py} +0 -0
@@ -0,0 +1,185 @@
|
|
1
|
+
from rx.core.observable.observable import Observable
|
2
|
+
from typing import Optional, Any
|
3
|
+
from .events import ScenarioEvent
|
4
|
+
from .event_reporter import EventReporter
|
5
|
+
|
6
|
+
import asyncio
|
7
|
+
import queue
|
8
|
+
import threading
|
9
|
+
import logging
|
10
|
+
|
11
|
+
class ScenarioEventBus:
|
12
|
+
"""
|
13
|
+
Subscribes to scenario event streams and handles HTTP posting using a dedicated worker thread.
|
14
|
+
|
15
|
+
The EventBus acts as an observer of scenario events, automatically
|
16
|
+
posting them to external APIs. It uses a queue-based threading model
|
17
|
+
where events are processed by a dedicated worker thread.
|
18
|
+
|
19
|
+
Key design principles:
|
20
|
+
- Single worker thread handles all HTTP posting (simplifies concurrency)
|
21
|
+
- Thread created lazily when first event arrives
|
22
|
+
- Thread terminates when queue empty and stream completed
|
23
|
+
- Non-daemon thread ensures all events posted before program exit
|
24
|
+
|
25
|
+
Attributes:
|
26
|
+
_event_reporter: EventReporter instance for HTTP posting of events
|
27
|
+
_max_retries: Maximum number of retry attempts for failed event processing
|
28
|
+
_event_queue: Thread-safe queue for passing events to worker thread
|
29
|
+
_completed: Whether the event stream has completed
|
30
|
+
_subscription: RxPY subscription to the event stream
|
31
|
+
_worker_thread: Dedicated thread for processing events
|
32
|
+
"""
|
33
|
+
|
34
|
+
def __init__(
|
35
|
+
self, event_reporter: Optional[EventReporter] = None, max_retries: int = 3
|
36
|
+
):
|
37
|
+
"""
|
38
|
+
Initialize the event bus with optional event reporter and retry configuration.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
event_reporter: Optional EventReporter for HTTP posting of events.
|
42
|
+
If not provided, a default EventReporter will be created.
|
43
|
+
max_retries: Maximum number of retry attempts for failed event processing.
|
44
|
+
Defaults to 3 attempts with exponential backoff.
|
45
|
+
"""
|
46
|
+
self._event_reporter: EventReporter = event_reporter or EventReporter()
|
47
|
+
self._max_retries = max_retries
|
48
|
+
|
49
|
+
# Custom logger for this class
|
50
|
+
self.logger = logging.getLogger(__name__)
|
51
|
+
|
52
|
+
# Threading infrastructure
|
53
|
+
self._event_queue: queue.Queue[ScenarioEvent] = queue.Queue()
|
54
|
+
self._completed = False
|
55
|
+
self._subscription: Optional[Any] = None
|
56
|
+
self._worker_thread: Optional[threading.Thread] = None
|
57
|
+
self._shutdown_event = threading.Event() # Signal worker to shutdown
|
58
|
+
|
59
|
+
def _get_or_create_worker(self) -> None:
|
60
|
+
"""Lazily create worker thread when first event arrives"""
|
61
|
+
if self._worker_thread is None or not self._worker_thread.is_alive():
|
62
|
+
self.logger.debug("Creating new worker thread")
|
63
|
+
self._worker_thread = threading.Thread(
|
64
|
+
target=self._worker_loop,
|
65
|
+
daemon=False,
|
66
|
+
name="ScenarioEventBus-Worker"
|
67
|
+
)
|
68
|
+
self._worker_thread.start()
|
69
|
+
self.logger.debug("Worker thread started")
|
70
|
+
|
71
|
+
def _worker_loop(self) -> None:
|
72
|
+
"""Main worker thread loop - processes events from queue until shutdown"""
|
73
|
+
self.logger.debug("Worker thread loop started")
|
74
|
+
while True:
|
75
|
+
try:
|
76
|
+
if self._shutdown_event.wait(timeout=0.1):
|
77
|
+
self.logger.debug("Worker thread received shutdown signal")
|
78
|
+
break
|
79
|
+
|
80
|
+
try:
|
81
|
+
event = self._event_queue.get(timeout=0.1)
|
82
|
+
self.logger.debug(f"Worker picked up event: {event.type_} ({event.scenario_run_id})")
|
83
|
+
self._process_event_sync(event)
|
84
|
+
self._event_queue.task_done()
|
85
|
+
except queue.Empty:
|
86
|
+
# Exit if stream completed and no more events
|
87
|
+
if self._completed:
|
88
|
+
self.logger.debug("Stream completed and no more events, worker thread exiting")
|
89
|
+
break
|
90
|
+
continue
|
91
|
+
|
92
|
+
except Exception as e:
|
93
|
+
self.logger.error(f"Worker thread error: {e}")
|
94
|
+
|
95
|
+
self.logger.debug("Worker thread loop ended")
|
96
|
+
|
97
|
+
def _process_event_sync(self, event: ScenarioEvent) -> None:
|
98
|
+
"""
|
99
|
+
Process event synchronously in worker thread with retry logic.
|
100
|
+
"""
|
101
|
+
self.logger.debug(f"Processing HTTP post for {event.type_} ({event.scenario_run_id})")
|
102
|
+
|
103
|
+
try:
|
104
|
+
# Convert async to sync using asyncio.run - this blocks until HTTP completes
|
105
|
+
success = asyncio.run(self._process_event_with_retry(event))
|
106
|
+
if not success:
|
107
|
+
self.logger.warning(f"Failed to process event {event.type_} after {self._max_retries} attempts")
|
108
|
+
else:
|
109
|
+
self.logger.debug(f"Successfully posted {event.type_} ({event.scenario_run_id})")
|
110
|
+
except Exception as e:
|
111
|
+
self.logger.error(f"Error processing event {event.type_}: {e}")
|
112
|
+
|
113
|
+
async def _process_event_with_retry(self, event: ScenarioEvent, attempt: int = 1) -> bool:
|
114
|
+
"""
|
115
|
+
Process a single event with retry logic (now runs in worker thread context).
|
116
|
+
"""
|
117
|
+
try:
|
118
|
+
if self._event_reporter:
|
119
|
+
await self._event_reporter.post_event(event)
|
120
|
+
return True
|
121
|
+
except Exception as e:
|
122
|
+
if attempt >= self._max_retries:
|
123
|
+
return False
|
124
|
+
print(f"Error processing event (attempt {attempt}/{self._max_retries}): {e}")
|
125
|
+
await asyncio.sleep(0.1 * (2 ** (attempt - 1))) # Exponential backoff
|
126
|
+
return await self._process_event_with_retry(event, attempt + 1)
|
127
|
+
|
128
|
+
def subscribe_to_events(self, event_stream: Observable) -> None:
|
129
|
+
"""
|
130
|
+
Subscribe to any observable stream of scenario events.
|
131
|
+
Events are queued for processing by the dedicated worker thread.
|
132
|
+
"""
|
133
|
+
if self._subscription is not None:
|
134
|
+
self.logger.debug("Already subscribed to event stream")
|
135
|
+
return
|
136
|
+
|
137
|
+
def handle_event(event: ScenarioEvent) -> None:
|
138
|
+
self.logger.debug(f"Event received, queuing: {event.type_} ({event.scenario_run_id})")
|
139
|
+
self._get_or_create_worker()
|
140
|
+
self._event_queue.put(event)
|
141
|
+
self.logger.debug(f"Event queued: {event.type_} ({event.scenario_run_id})")
|
142
|
+
|
143
|
+
self.logger.info("Subscribing to event stream")
|
144
|
+
self._subscription = event_stream.subscribe(
|
145
|
+
handle_event,
|
146
|
+
lambda e: self.logger.error(f"Error in event stream: {e}"),
|
147
|
+
lambda: self._set_completed()
|
148
|
+
)
|
149
|
+
|
150
|
+
def _set_completed(self):
|
151
|
+
"""Helper to set completed state with logging"""
|
152
|
+
self.logger.debug("Event stream completed")
|
153
|
+
self._completed = True
|
154
|
+
|
155
|
+
def drain(self) -> None:
|
156
|
+
"""
|
157
|
+
Waits for all queued events to complete processing.
|
158
|
+
|
159
|
+
This method blocks until all events in the queue have been processed.
|
160
|
+
Since _process_event_sync() uses asyncio.run(), HTTP requests complete
|
161
|
+
before task_done() is called, so join() ensures everything is finished.
|
162
|
+
"""
|
163
|
+
self.logger.debug("Drain started - waiting for queue to empty")
|
164
|
+
|
165
|
+
# Wait for all events to be processed - this is sufficient!
|
166
|
+
self._event_queue.join()
|
167
|
+
self.logger.debug("Event queue drained")
|
168
|
+
|
169
|
+
# Signal worker to shutdown and wait for it
|
170
|
+
self._shutdown_event.set()
|
171
|
+
if self._worker_thread and self._worker_thread.is_alive():
|
172
|
+
self.logger.debug("Waiting for worker thread to shutdown...")
|
173
|
+
self._worker_thread.join(timeout=5.0)
|
174
|
+
if self._worker_thread.is_alive():
|
175
|
+
self.logger.warning("Worker thread did not shutdown within timeout")
|
176
|
+
else:
|
177
|
+
self.logger.debug("Worker thread shutdown complete")
|
178
|
+
|
179
|
+
self.logger.info("Drain completed")
|
180
|
+
|
181
|
+
def is_completed(self) -> bool:
|
182
|
+
"""
|
183
|
+
Returns whether all events have been processed.
|
184
|
+
"""
|
185
|
+
return self._completed and self._event_queue.empty()
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import httpx
|
4
|
+
from typing import Optional
|
5
|
+
from .events import ScenarioEvent
|
6
|
+
|
7
|
+
|
8
|
+
class EventReporter:
|
9
|
+
"""
|
10
|
+
Handles HTTP posting of scenario events to external endpoints.
|
11
|
+
|
12
|
+
Single responsibility: Send events via HTTP to configured endpoints
|
13
|
+
with proper authentication and error handling.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
endpoint (str, optional): The base URL to post events to. Defaults to LANGWATCH_ENDPOINT env var.
|
17
|
+
api_key (str, optional): The API key for authentication. Defaults to LANGWATCH_API_KEY env var.
|
18
|
+
|
19
|
+
Example:
|
20
|
+
event = {
|
21
|
+
"type": "SCENARIO_RUN_STARTED",
|
22
|
+
"batch_run_id": "batch-1",
|
23
|
+
"scenario_id": "scenario-1",
|
24
|
+
"scenario_run_id": "run-1",
|
25
|
+
"metadata": {
|
26
|
+
"name": "test",
|
27
|
+
"description": "test scenario"
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
reporter = EventReporter(endpoint="https://api.langwatch.ai", api_key="test-api-key")
|
32
|
+
await reporter.post_event(event)
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self, endpoint: Optional[str] = None, api_key: Optional[str] = None):
|
36
|
+
self.endpoint = endpoint or os.getenv("LANGWATCH_ENDPOINT")
|
37
|
+
self.api_key = api_key or os.getenv("LANGWATCH_API_KEY", "")
|
38
|
+
self.logger = logging.getLogger(__name__)
|
39
|
+
|
40
|
+
async def post_event(self, event: ScenarioEvent):
|
41
|
+
"""
|
42
|
+
Posts an event to the configured endpoint.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
event: A dictionary containing the event data
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
None - logs success/failure internally
|
49
|
+
"""
|
50
|
+
event_type = event.type_
|
51
|
+
self.logger.info(f"[{event_type}] Publishing event ({event.scenario_run_id})")
|
52
|
+
|
53
|
+
if not self.endpoint:
|
54
|
+
self.logger.warning(
|
55
|
+
"No LANGWATCH_ENDPOINT configured, skipping event posting"
|
56
|
+
)
|
57
|
+
return
|
58
|
+
|
59
|
+
try:
|
60
|
+
async with httpx.AsyncClient() as client:
|
61
|
+
response = await client.post(
|
62
|
+
f"{self.endpoint}/api/scenario-events",
|
63
|
+
json=event.to_dict(),
|
64
|
+
headers={
|
65
|
+
"Content-Type": "application/json",
|
66
|
+
"X-Auth-Token": self.api_key,
|
67
|
+
},
|
68
|
+
)
|
69
|
+
self.logger.info(f"[{event_type}] POST response status: {response.status_code} ({event.scenario_run_id})")
|
70
|
+
|
71
|
+
if response.is_success:
|
72
|
+
data = response.json()
|
73
|
+
self.logger.info(f"[{event_type}] POST response: {data} ({event.scenario_run_id})")
|
74
|
+
else:
|
75
|
+
error_text = response.text
|
76
|
+
self.logger.error(
|
77
|
+
f"[{event_type}] Event POST failed: status={response.status_code}, "
|
78
|
+
f"reason={response.reason_phrase}, error={error_text}, "
|
79
|
+
f"event={event}"
|
80
|
+
)
|
81
|
+
except Exception as error:
|
82
|
+
self.logger.error(
|
83
|
+
f"[{event_type}] Event POST error: {error}, event={event}, endpoint={self.endpoint}")
|
@@ -0,0 +1,162 @@
|
|
1
|
+
"""
|
2
|
+
Exports scenario event models from the generated LangWatch API client,
|
3
|
+
renaming the auto-generated types to clean, meaningful names.
|
4
|
+
|
5
|
+
This ensures all event types are always in sync with the OpenAPI spec and
|
6
|
+
the backend, and provides a single import location for event models.
|
7
|
+
|
8
|
+
If you need to add custom logic or helpers, you can extend or wrap these models here.
|
9
|
+
"""
|
10
|
+
|
11
|
+
from typing import Union, Any, Optional, TypeAlias
|
12
|
+
from scenario._generated.langwatch_api_client.lang_watch_api_client.models import (
|
13
|
+
PostApiScenarioEventsBodyType0,
|
14
|
+
PostApiScenarioEventsBodyType0Metadata,
|
15
|
+
PostApiScenarioEventsBodyType1,
|
16
|
+
PostApiScenarioEventsBodyType1ResultsType0,
|
17
|
+
PostApiScenarioEventsBodyType1ResultsType0Verdict,
|
18
|
+
PostApiScenarioEventsBodyType1Status,
|
19
|
+
PostApiScenarioEventsBodyType2,
|
20
|
+
)
|
21
|
+
from .messages import MessageType
|
22
|
+
|
23
|
+
# Create alias for cleaner naming
|
24
|
+
ScenarioRunStartedEventMetadata: TypeAlias = PostApiScenarioEventsBodyType0Metadata
|
25
|
+
ScenarioRunFinishedEventResults: TypeAlias = PostApiScenarioEventsBodyType1ResultsType0
|
26
|
+
ScenarioRunFinishedEventVerdict: TypeAlias = PostApiScenarioEventsBodyType1ResultsType0Verdict
|
27
|
+
ScenarioRunFinishedEventStatus: TypeAlias = PostApiScenarioEventsBodyType1Status
|
28
|
+
|
29
|
+
|
30
|
+
class ScenarioRunStartedEvent(PostApiScenarioEventsBodyType0):
|
31
|
+
"""
|
32
|
+
Event published when a scenario run begins execution.
|
33
|
+
|
34
|
+
Automatically sets type_ to "SCENARIO_RUN_STARTED" and includes metadata
|
35
|
+
about the scenario (name, description, etc.).
|
36
|
+
|
37
|
+
Args:
|
38
|
+
batch_run_id (str): Unique identifier for the batch of scenario runs
|
39
|
+
scenario_id (str): Unique identifier for the scenario definition
|
40
|
+
scenario_run_id (str): Unique identifier for this specific run
|
41
|
+
metadata (ScenarioRunStartedEventMetadata): Scenario details like name and description
|
42
|
+
timestamp (Optional[int], optional): Unix timestamp in milliseconds, auto-generated if not provided
|
43
|
+
raw_event (Optional[Any], optional): Raw event data
|
44
|
+
scenario_set_id (Optional[str], optional): Set identifier, defaults to "default"
|
45
|
+
"""
|
46
|
+
def __init__(
|
47
|
+
self,
|
48
|
+
batch_run_id: str,
|
49
|
+
scenario_id: str,
|
50
|
+
scenario_run_id: str,
|
51
|
+
metadata: ScenarioRunStartedEventMetadata,
|
52
|
+
timestamp: int,
|
53
|
+
raw_event: Optional[Any] = None,
|
54
|
+
scenario_set_id: Optional[str] = "default"
|
55
|
+
):
|
56
|
+
super().__init__(
|
57
|
+
type_="SCENARIO_RUN_STARTED",
|
58
|
+
batch_run_id=batch_run_id,
|
59
|
+
scenario_id=scenario_id,
|
60
|
+
scenario_run_id=scenario_run_id,
|
61
|
+
metadata=metadata,
|
62
|
+
timestamp=timestamp,
|
63
|
+
raw_event=raw_event,
|
64
|
+
scenario_set_id=scenario_set_id or "default"
|
65
|
+
)
|
66
|
+
|
67
|
+
class ScenarioRunFinishedEvent(PostApiScenarioEventsBodyType1):
|
68
|
+
"""
|
69
|
+
Event published when a scenario run completes execution.
|
70
|
+
|
71
|
+
Automatically sets type_ to "SCENARIO_RUN_FINISHED" and includes results
|
72
|
+
with verdict (PASS/FAIL/SUCCESS) and reasoning.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
batch_run_id (str): Unique identifier for the batch of scenario runs
|
76
|
+
scenario_id (str): Unique identifier for the scenario definition
|
77
|
+
scenario_run_id (str): Unique identifier for this specific run
|
78
|
+
status (ScenarioRunFinishedEventStatus): Overall execution status
|
79
|
+
timestamp (Optional[int], optional): Unix timestamp in milliseconds, auto-generated if not provided
|
80
|
+
raw_event (Optional[Any], optional): Raw event data
|
81
|
+
scenario_set_id (Optional[str], optional): Set identifier, defaults to "default"
|
82
|
+
results (Optional[ScenarioRunFinishedEventResults], optional): Verdict and reasoning for the outcome
|
83
|
+
"""
|
84
|
+
def __init__(
|
85
|
+
self,
|
86
|
+
batch_run_id: str,
|
87
|
+
scenario_id: str,
|
88
|
+
scenario_run_id: str,
|
89
|
+
status: ScenarioRunFinishedEventStatus,
|
90
|
+
timestamp: int,
|
91
|
+
results: Optional[ScenarioRunFinishedEventResults] = None,
|
92
|
+
raw_event: Optional[Any] = None,
|
93
|
+
scenario_set_id: Optional[str] = "default",
|
94
|
+
):
|
95
|
+
super().__init__(
|
96
|
+
type_="SCENARIO_RUN_FINISHED",
|
97
|
+
batch_run_id=batch_run_id,
|
98
|
+
scenario_id=scenario_id,
|
99
|
+
scenario_run_id=scenario_run_id,
|
100
|
+
status=status,
|
101
|
+
timestamp=timestamp,
|
102
|
+
raw_event=raw_event,
|
103
|
+
scenario_set_id=scenario_set_id or "default",
|
104
|
+
results=results
|
105
|
+
)
|
106
|
+
|
107
|
+
class ScenarioMessageSnapshotEvent(PostApiScenarioEventsBodyType2):
|
108
|
+
"""
|
109
|
+
Event published to capture intermediate state during scenario execution.
|
110
|
+
|
111
|
+
Automatically sets type_ to "SCENARIO_MESSAGE_SNAPSHOT" and allows tracking
|
112
|
+
of messages, context, or other runtime data during scenario processing.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
batch_run_id (str): Unique identifier for the batch of scenario runs
|
116
|
+
scenario_id (str): Unique identifier for the scenario definition
|
117
|
+
scenario_run_id (str): Unique identifier for this specific run
|
118
|
+
messages (list[MessageType]): List of message objects in the conversation
|
119
|
+
timestamp (Optional[int], optional): Unix timestamp in milliseconds, auto-generated if not provided
|
120
|
+
raw_event (Optional[Any], optional): Raw event data
|
121
|
+
scenario_set_id (Optional[str], optional): Set identifier, defaults to "default"
|
122
|
+
"""
|
123
|
+
def __init__(
|
124
|
+
self,
|
125
|
+
batch_run_id: str,
|
126
|
+
scenario_id: str,
|
127
|
+
scenario_run_id: str,
|
128
|
+
messages: list[MessageType],
|
129
|
+
timestamp: int,
|
130
|
+
raw_event: Optional[Any] = None,
|
131
|
+
scenario_set_id: Optional[str] = "default"
|
132
|
+
):
|
133
|
+
super().__init__(
|
134
|
+
type_="SCENARIO_MESSAGE_SNAPSHOT",
|
135
|
+
batch_run_id=batch_run_id,
|
136
|
+
scenario_id=scenario_id,
|
137
|
+
scenario_run_id=scenario_run_id,
|
138
|
+
messages=messages,
|
139
|
+
timestamp=timestamp,
|
140
|
+
raw_event=raw_event,
|
141
|
+
scenario_set_id=scenario_set_id or "default"
|
142
|
+
)
|
143
|
+
|
144
|
+
# Union type for all supported event types
|
145
|
+
ScenarioEvent = Union[
|
146
|
+
ScenarioRunStartedEvent,
|
147
|
+
ScenarioRunFinishedEvent,
|
148
|
+
ScenarioMessageSnapshotEvent
|
149
|
+
]
|
150
|
+
|
151
|
+
|
152
|
+
__all__ = [
|
153
|
+
"ScenarioEvent",
|
154
|
+
"ScenarioRunStartedEvent",
|
155
|
+
"ScenarioRunStartedEventMetadata",
|
156
|
+
"ScenarioRunFinishedEvent",
|
157
|
+
"ScenarioRunFinishedEventResults",
|
158
|
+
"ScenarioRunFinishedEventVerdict",
|
159
|
+
"ScenarioRunFinishedEventStatus",
|
160
|
+
"ScenarioMessageSnapshotEvent",
|
161
|
+
"MessageType",
|
162
|
+
]
|
@@ -0,0 +1,58 @@
|
|
1
|
+
"""
|
2
|
+
Exports message models from the generated LangWatch API client,
|
3
|
+
renaming the auto-generated types to clean, meaningful names.
|
4
|
+
|
5
|
+
This ensures all message types are always in sync with the OpenAPI spec and
|
6
|
+
the backend, and provides a single import location for message models.
|
7
|
+
|
8
|
+
If you need to add custom logic or helpers, you can extend or wrap these models here.
|
9
|
+
"""
|
10
|
+
|
11
|
+
from typing import Union, TypeAlias
|
12
|
+
from scenario._generated.langwatch_api_client.lang_watch_api_client.models import (
|
13
|
+
PostApiScenarioEventsBodyType2MessagesItemType0,
|
14
|
+
PostApiScenarioEventsBodyType2MessagesItemType1,
|
15
|
+
PostApiScenarioEventsBodyType2MessagesItemType2,
|
16
|
+
PostApiScenarioEventsBodyType2MessagesItemType3,
|
17
|
+
PostApiScenarioEventsBodyType2MessagesItemType4,
|
18
|
+
PostApiScenarioEventsBodyType2MessagesItemType2ToolCallsItem,
|
19
|
+
PostApiScenarioEventsBodyType2MessagesItemType2ToolCallsItemFunction,
|
20
|
+
)
|
21
|
+
|
22
|
+
# Create aliases for cleaner naming
|
23
|
+
DeveloperMessage: TypeAlias = PostApiScenarioEventsBodyType2MessagesItemType0
|
24
|
+
SystemMessage: TypeAlias = PostApiScenarioEventsBodyType2MessagesItemType1
|
25
|
+
AssistantMessage: TypeAlias = PostApiScenarioEventsBodyType2MessagesItemType2
|
26
|
+
UserMessage: TypeAlias = PostApiScenarioEventsBodyType2MessagesItemType3
|
27
|
+
ToolMessage: TypeAlias = PostApiScenarioEventsBodyType2MessagesItemType4
|
28
|
+
ToolCall: TypeAlias = PostApiScenarioEventsBodyType2MessagesItemType2ToolCallsItem
|
29
|
+
FunctionCall: TypeAlias = PostApiScenarioEventsBodyType2MessagesItemType2ToolCallsItemFunction
|
30
|
+
|
31
|
+
# Union type for all supported message types
|
32
|
+
MessageType = Union[
|
33
|
+
DeveloperMessage,
|
34
|
+
SystemMessage,
|
35
|
+
AssistantMessage,
|
36
|
+
UserMessage,
|
37
|
+
ToolMessage,
|
38
|
+
]
|
39
|
+
|
40
|
+
__all__ = [
|
41
|
+
"MessageType",
|
42
|
+
"DeveloperMessage",
|
43
|
+
"SystemMessage",
|
44
|
+
"AssistantMessage",
|
45
|
+
"UserMessage",
|
46
|
+
"ToolMessage",
|
47
|
+
"ToolCall",
|
48
|
+
"FunctionCall",
|
49
|
+
|
50
|
+
# API client models -- Required for PDocs
|
51
|
+
"PostApiScenarioEventsBodyType2MessagesItemType0",
|
52
|
+
"PostApiScenarioEventsBodyType2MessagesItemType1",
|
53
|
+
"PostApiScenarioEventsBodyType2MessagesItemType2",
|
54
|
+
"PostApiScenarioEventsBodyType2MessagesItemType3",
|
55
|
+
"PostApiScenarioEventsBodyType2MessagesItemType4",
|
56
|
+
"PostApiScenarioEventsBodyType2MessagesItemType2ToolCallsItem",
|
57
|
+
"PostApiScenarioEventsBodyType2MessagesItemType2ToolCallsItemFunction",
|
58
|
+
]
|
@@ -0,0 +1,97 @@
|
|
1
|
+
import warnings
|
2
|
+
from openai.types.chat.chat_completion_message_param import ChatCompletionMessageParam
|
3
|
+
from .events import MessageType
|
4
|
+
from .messages import (
|
5
|
+
SystemMessage,
|
6
|
+
AssistantMessage,
|
7
|
+
UserMessage,
|
8
|
+
ToolMessage,
|
9
|
+
ToolCall,
|
10
|
+
FunctionCall,
|
11
|
+
)
|
12
|
+
from typing import List
|
13
|
+
import uuid
|
14
|
+
|
15
|
+
def convert_messages_to_api_client_messages(messages: list[ChatCompletionMessageParam]) -> list[MessageType]:
|
16
|
+
"""
|
17
|
+
Converts OpenAI ChatCompletionMessageParam messages to API client Message format.
|
18
|
+
|
19
|
+
This function transforms messages from OpenAI's format to the API client format
|
20
|
+
that matches the expected schema for ScenarioMessageSnapshotEvent.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
messages: List of OpenAI ChatCompletionMessageParam messages
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
List of API client Message objects
|
27
|
+
|
28
|
+
Raises:
|
29
|
+
ValueError: If message role is not supported or message format is invalid
|
30
|
+
"""
|
31
|
+
|
32
|
+
converted_messages: list[MessageType] = []
|
33
|
+
|
34
|
+
for i, message in enumerate(messages):
|
35
|
+
# Generate unique ID for each message
|
36
|
+
message_id = message.get("id") or str(uuid.uuid4())
|
37
|
+
|
38
|
+
role = message.get("role")
|
39
|
+
content = message.get("content")
|
40
|
+
|
41
|
+
if role == "user":
|
42
|
+
if not content:
|
43
|
+
raise ValueError(f"User message at index {i} missing required content")
|
44
|
+
converted_messages.append(UserMessage(
|
45
|
+
id=message_id,
|
46
|
+
role="user",
|
47
|
+
content=str(content)
|
48
|
+
))
|
49
|
+
elif role == "assistant":
|
50
|
+
# Handle tool calls if present
|
51
|
+
tool_calls = message.get("tool_calls")
|
52
|
+
api_tool_calls: List[ToolCall] = []
|
53
|
+
|
54
|
+
if tool_calls:
|
55
|
+
for tool_call in tool_calls:
|
56
|
+
api_tool_calls.append(ToolCall(
|
57
|
+
id=tool_call.get("id", str(uuid.uuid4())),
|
58
|
+
type_="function",
|
59
|
+
function=FunctionCall(
|
60
|
+
name=tool_call["function"].get("name", "unknown"),
|
61
|
+
arguments=tool_call["function"].get("arguments", "{}")
|
62
|
+
)
|
63
|
+
))
|
64
|
+
|
65
|
+
converted_messages.append(AssistantMessage(
|
66
|
+
id=message_id,
|
67
|
+
role="assistant",
|
68
|
+
content=str(content),
|
69
|
+
tool_calls=api_tool_calls
|
70
|
+
))
|
71
|
+
elif role == "system":
|
72
|
+
if not content:
|
73
|
+
raise ValueError(f"System message at index {i} missing required content")
|
74
|
+
converted_messages.append(SystemMessage(
|
75
|
+
id=message_id,
|
76
|
+
role="system",
|
77
|
+
content=str(content)
|
78
|
+
))
|
79
|
+
elif role == "tool":
|
80
|
+
tool_call_id = message.get("tool_call_id")
|
81
|
+
if not tool_call_id:
|
82
|
+
warnings.warn(f"Tool message at index {i} missing required tool_call_id, skipping tool message")
|
83
|
+
continue
|
84
|
+
if not content:
|
85
|
+
warnings.warn(f"Tool message at index {i} missing required content, skipping tool message")
|
86
|
+
continue
|
87
|
+
|
88
|
+
converted_messages.append(ToolMessage(
|
89
|
+
id=message_id,
|
90
|
+
role="tool",
|
91
|
+
content=str(content),
|
92
|
+
tool_call_id=tool_call_id
|
93
|
+
))
|
94
|
+
else:
|
95
|
+
raise ValueError(f"Unsupported message role '{role}' at index {i}")
|
96
|
+
|
97
|
+
return converted_messages
|