├── .devcontainer ├── devcontainer.json └── on-create.sh ├── .dockerignore ├── .dvc ├── .gitignore └── config ├── .dvcignore ├── .gitbook.yaml ├── .github ├── scripts │ ├── jupyter-to-html.sh │ └── transform-jupyter-python.mjs ├── share-actions │ ├── get-bikes-dataset-cached │ │ └── action.yml │ ├── ui-node-pnpm-install │ │ └── action.yml │ └── ui-types-from-backend │ │ └── action.yml └── workflows │ ├── docker.yml │ ├── examples.yml │ ├── main.yml │ ├── release.yml │ └── ui.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── biome.jsonc ├── config.json ├── config.yaml ├── docker ├── Dockerfile.service ├── Dockerfile.service.dev └── Makefile ├── example_test.py ├── examples ├── cookbook │ ├── descriptors.ipynb │ └── metrics.ipynb ├── future_examples │ ├── cloud_sdk.ipynb │ ├── future_dashboads.ipynb │ ├── future_reviews.py │ ├── list_metrics.ipynb │ ├── metric_workbench.ipynb │ ├── prompt_registry.ipynb │ └── upload_snapshots.ipynb ├── readme.md └── service │ ├── .gitignore │ ├── README.md │ ├── remote_demo_project.py │ ├── run_service.sh │ └── workspace_tutorial.ipynb ├── images └── gh_header.png ├── requirements.dev.txt ├── requirements.min.txt ├── ruff.toml ├── setup.cfg ├── setup.py ├── setupbase.py ├── src └── evidently │ ├── __init__.py │ ├── _pydantic_compat.py │ ├── _registry.py │ ├── _version.py │ ├── cli │ ├── __init__.py │ ├── __main__.py │ ├── demo_project.py │ ├── legacy_ui.py │ ├── main.py │ └── ui.py │ ├── core │ ├── __init__.py │ ├── _utils.py │ ├── base_types.py │ ├── compare.py │ ├── container.py │ ├── datasets.py │ ├── metric_types.py │ ├── preset_types.py │ ├── registries │ │ ├── __init__.py │ │ ├── bound_tests.py │ │ ├── column_conditions.py │ │ ├── descriptors.py │ │ ├── metric_results.py │ │ ├── metric_tests.py │ │ ├── metrics.py │ │ └── presets.py │ ├── report.py │ ├── serialization.py │ └── tests.py │ ├── descriptors │ ├── __init__.py │ ├── _context_relevance.py │ ├── _custom_descriptors.py │ ├── _generate_descriptors.py │ ├── _text_length.py │ ├── generated_descriptors.py │ └── llm_judges.py │ ├── errors.py │ ├── future │ ├── __init__.py │ ├── datasets.py │ ├── descriptors │ │ └── __init__.py │ ├── generators │ │ └── __init__.py │ ├── metric_types.py │ ├── metrics │ │ └── __init__.py │ ├── presets │ │ └── __init__.py │ ├── report.py │ └── tests │ │ └── __init__.py │ ├── generators │ ├── __init__.py │ └── column.py │ ├── legacy │ ├── __init__.py │ ├── __main__.py │ ├── _config.py │ ├── _registry.py │ ├── base_metric.py │ ├── calculation_engine │ │ ├── __init__.py │ │ ├── engine.py │ │ ├── metric_implementation.py │ │ └── python_engine.py │ ├── calculations │ │ ├── __init__.py │ │ ├── classification_performance.py │ │ ├── data_drift.py │ │ ├── data_integration.py │ │ ├── data_quality.py │ │ ├── recommender_systems.py │ │ ├── regression_performance.py │ │ ├── stattests │ │ │ ├── __init__.py │ │ │ ├── anderson_darling_stattest.py │ │ │ ├── chisquare_stattest.py │ │ │ ├── cramer_von_mises_stattest.py │ │ │ ├── energy_distance.py │ │ │ ├── epps_singleton_stattest.py │ │ │ ├── fisher_exact_stattest.py │ │ │ ├── g_stattest.py │ │ │ ├── hellinger_distance.py │ │ │ ├── jensenshannon.py │ │ │ ├── kl_div.py │ │ │ ├── ks_stattest.py │ │ │ ├── mann_whitney_urank_stattest.py │ │ │ ├── mmd_stattest.py │ │ │ ├── psi.py │ │ │ ├── registry.py │ │ │ ├── t_test.py │ │ │ ├── text_content_drift.py │ │ │ ├── text_content_drift_abs.py │ │ │ ├── tvd_stattest.py │ │ │ ├── utils.py │ │ │ ├── wasserstein_distance_norm.py │ │ │ └── z_stattest.py │ │ └── utils.py │ ├── cli │ │ ├── __init__.py │ │ ├── collector.py │ │ ├── main.py │ │ └── ui.py │ ├── collector │ │ ├── __init__.py │ │ ├── app.py │ │ ├── client.py │ │ ├── config.py │ │ └── storage.py │ ├── core.py │ ├── dashboard │ │ └── tabs │ │ │ └── quality_metrics_options_example.ipynb │ ├── descriptors │ │ ├── BERTScore_descriptor.py │ │ ├── __init__.py │ │ ├── _registry.py │ │ ├── contains_link_descriptor.py │ │ ├── custom_descriptor.py │ │ ├── exact_match_descriptor.py │ │ ├── hf_descriptor.py │ │ ├── is_valid_json_descriptor.py │ │ ├── is_valid_python_descriptor.py │ │ ├── is_valid_sql_descriptor.py │ │ ├── json_match_descriptor.py │ │ ├── json_schema_match_descriptor.py │ │ ├── llm_judges.py │ │ ├── non_letter_character_percentage_descriptor.py │ │ ├── oov_words_percentage_descriptor.py │ │ ├── openai_descriptor.py │ │ ├── regexp_descriptor.py │ │ ├── semantic_similarity.py │ │ ├── sentence_count_descriptor.py │ │ ├── sentiment_descriptor.py │ │ ├── text_contains_descriptor.py │ │ ├── text_length_descriptor.py │ │ ├── text_part_descriptor.py │ │ ├── trigger_words_presence_descriptor.py │ │ ├── word_count_descriptor.py │ │ └── words_descriptor.py │ ├── experimental │ │ ├── __init__.py │ │ └── report_set.py │ ├── features │ │ ├── BERTScore_feature.py │ │ ├── OOV_words_percentage_feature.py │ │ ├── __init__.py │ │ ├── _registry.py │ │ ├── contains_link_feature.py │ │ ├── custom_feature.py │ │ ├── exact_match_feature.py │ │ ├── feature_generator.py │ │ ├── generated_features.py │ │ ├── hf_feature.py │ │ ├── is_valid_json_feature.py │ │ ├── is_valid_python_feature.py │ │ ├── is_valid_sql_feature.py │ │ ├── json_match_feature.py │ │ ├── json_schema_match_feature.py │ │ ├── llm_judge.py │ │ ├── non_letter_character_percentage_feature.py │ │ ├── openai_feature.py │ │ ├── regexp_feature.py │ │ ├── semantic_similarity_feature.py │ │ ├── sentence_count_feature.py │ │ ├── sentiment_feature.py │ │ ├── text_contains_feature.py │ │ ├── text_length_feature.py │ │ ├── text_part_feature.py │ │ ├── trigger_words_presence_feature.py │ │ ├── word_count_feature.py │ │ └── words_feature.py │ ├── metric_preset │ │ ├── __init__.py │ │ ├── _registry.py │ │ ├── classification_performance.py │ │ ├── data_drift.py │ │ ├── data_quality.py │ │ ├── metric_preset.py │ │ ├── recsys.py │ │ ├── regression_performance.py │ │ ├── target_drift.py │ │ └── text_evals.py │ ├── metric_results.py │ ├── metrics │ │ ├── __init__.py │ │ ├── _registry.py │ │ ├── base_metric.py │ │ ├── classification_performance │ │ │ ├── __init__.py │ │ │ ├── base_classification_metric.py │ │ │ ├── class_balance_metric.py │ │ │ ├── class_separation_metric.py │ │ │ ├── classification_dummy_metric.py │ │ │ ├── classification_quality_metric.py │ │ │ ├── confusion_matrix_metric.py │ │ │ ├── lift_curve_metric.py │ │ │ ├── lift_table_metric.py │ │ │ ├── objects.py │ │ │ ├── pr_curve_metric.py │ │ │ ├── pr_table_metric.py │ │ │ ├── probability_distribution_metric.py │ │ │ ├── quality_by_class_metric.py │ │ │ ├── quality_by_feature_table.py │ │ │ └── roc_curve_metric.py │ │ ├── custom_metric.py │ │ ├── data_drift │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── column_drift_metric.py │ │ │ ├── column_interaction_plot.py │ │ │ ├── column_value_plot.py │ │ │ ├── data_drift_table.py │ │ │ ├── dataset_drift_metric.py │ │ │ ├── embedding_drift_methods.py │ │ │ ├── embeddings_drift.py │ │ │ ├── feature_importance.py │ │ │ ├── target_by_features_table.py │ │ │ ├── text_descriptors_drift_metric.py │ │ │ ├── text_domain_classifier_drift_metric.py │ │ │ └── text_metric.py │ │ ├── data_integrity │ │ │ ├── __init__.py │ │ │ ├── column_missing_values_metric.py │ │ │ ├── column_regexp_metric.py │ │ │ ├── column_summary_metric.py │ │ │ ├── dataset_missing_values_metric.py │ │ │ └── dataset_summary_metric.py │ │ ├── data_quality │ │ │ ├── __init__.py │ │ │ ├── column_category_metric.py │ │ │ ├── column_correlations_metric.py │ │ │ ├── column_distribution_metric.py │ │ │ ├── column_quantile_metric.py │ │ │ ├── column_value_list_metric.py │ │ │ ├── column_value_range_metric.py │ │ │ ├── conflict_prediction_metric.py │ │ │ ├── conflict_target_metric.py │ │ │ ├── dataset_correlations_metric.py │ │ │ ├── stability_metric.py │ │ │ ├── text_descriptors_correlation_metric.py │ │ │ └── text_descriptors_distribution.py │ │ ├── recsys │ │ │ ├── __init__.py │ │ │ ├── base_top_k.py │ │ │ ├── diversity.py │ │ │ ├── f_beta_top_k.py │ │ │ ├── hit_rate_k.py │ │ │ ├── item_bias.py │ │ │ ├── map_k.py │ │ │ ├── mar_k.py │ │ │ ├── mrr.py │ │ │ ├── ndcg_k.py │ │ │ ├── novelty.py │ │ │ ├── pairwise_distance.py │ │ │ ├── personalisation.py │ │ │ ├── popularity_bias.py │ │ │ ├── precision_recall_k.py │ │ │ ├── precision_top_k.py │ │ │ ├── rec_examples.py │ │ │ ├── recall_top_k.py │ │ │ ├── scores_distribution.py │ │ │ ├── serendipity.py │ │ │ ├── train_stats.py │ │ │ └── user_bias.py │ │ ├── regression_performance │ │ │ ├── __init__.py │ │ │ ├── abs_perc_error_in_time.py │ │ │ ├── error_bias_table.py │ │ │ ├── error_distribution.py │ │ │ ├── error_in_time.py │ │ │ ├── error_normality.py │ │ │ ├── objects.py │ │ │ ├── predicted_and_actual_in_time.py │ │ │ ├── predicted_vs_actual.py │ │ │ ├── regression_dummy_metric.py │ │ │ ├── regression_performance_metrics.py │ │ │ ├── regression_quality.py │ │ │ ├── top_error.py │ │ │ ├── utils.py │ │ │ └── visualization.py │ │ └── utils.py │ ├── model │ │ ├── __init__.py │ │ ├── dashboard.py │ │ └── widget.py │ ├── options │ │ ├── __init__.py │ │ ├── agg_data.py │ │ ├── base.py │ │ ├── color_scheme.py │ │ ├── data_drift.py │ │ ├── option.py │ │ └── quality_metrics.py │ ├── pipeline │ │ ├── __init__.py │ │ └── column_mapping.py │ ├── renderers │ │ ├── __init__.py │ │ ├── base_renderer.py │ │ ├── html_widgets.py │ │ └── render_utils.py │ ├── report │ │ ├── __init__.py │ │ └── report.py │ ├── runner │ │ ├── __init__.py │ │ ├── loader.py │ │ └── runner.py │ ├── spark │ │ ├── __init__.py │ │ ├── base.py │ │ ├── calculations │ │ │ ├── __init__.py │ │ │ ├── data_drift.py │ │ │ ├── histogram.py │ │ │ └── stattests │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── chisquare.py │ │ │ │ ├── jensenshannon.py │ │ │ │ ├── psi.py │ │ │ │ ├── utils.py │ │ │ │ └── wasserstein.py │ │ ├── engine.py │ │ ├── metrics │ │ │ ├── __init__.py │ │ │ ├── data_drift.py │ │ │ └── feature_importance.py │ │ ├── utils.py │ │ └── visualizations.py │ ├── suite │ │ ├── __init__.py │ │ └── base_suite.py │ ├── test_preset │ │ ├── __init__.py │ │ ├── _registry.py │ │ ├── classification_binary.py │ │ ├── classification_binary_topk.py │ │ ├── classification_multiclass.py │ │ ├── data_drift.py │ │ ├── data_quality.py │ │ ├── data_stability.py │ │ ├── no_target_performance.py │ │ ├── recsys.py │ │ ├── regression.py │ │ └── test_preset.py │ ├── test_suite │ │ ├── __init__.py │ │ └── test_suite.py │ ├── tests │ │ ├── __init__.py │ │ ├── _registry.py │ │ ├── base_test.py │ │ ├── classification_performance_tests.py │ │ ├── custom_test.py │ │ ├── data_drift_tests.py │ │ ├── data_integrity_tests.py │ │ ├── data_quality_tests.py │ │ ├── recsys_tests.py │ │ ├── regression_performance_tests.py │ │ └── utils.py │ ├── ui │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ ├── projects.py │ │ │ ├── service.py │ │ │ └── static.py │ │ ├── app.py │ │ ├── assets │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── robots.txt │ │ │ └── static │ │ │ │ ├── css │ │ │ │ └── index-CJbKDbyR.css │ │ │ │ └── js │ │ │ │ ├── WidgetsContent-CSRajGBd.js │ │ │ │ ├── dashboard-main-BGHDi9oT.js │ │ │ │ ├── index-Nx_mkSx_.js │ │ │ │ └── snapshot-view-main-Z97zaUJO.js │ │ ├── base.py │ │ ├── components │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── local_storage.py │ │ │ ├── security.py │ │ │ ├── storage.py │ │ │ └── telemetry.py │ │ ├── config.py │ │ ├── dashboards │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── reports.py │ │ │ ├── test_suites.py │ │ │ └── utils.py │ │ ├── datasets.py │ │ ├── demo_projects │ │ │ ├── __init__.py │ │ │ ├── adult.py │ │ │ ├── base.py │ │ │ ├── bikes.py │ │ │ ├── bikes_v2.py │ │ │ ├── reviews.py │ │ │ ├── reviews_v2.py │ │ │ └── simple.py │ │ ├── errors.py │ │ ├── example.ipynb │ │ ├── local_service.py │ │ ├── managers │ │ │ ├── __init__.py │ │ │ ├── auth.py │ │ │ ├── base.py │ │ │ └── projects.py │ │ ├── remote.py │ │ ├── security │ │ │ ├── __init__.py │ │ │ ├── no_security.py │ │ │ ├── service.py │ │ │ └── token.py │ │ ├── storage │ │ │ ├── __init__.py │ │ │ ├── common.py │ │ │ ├── config.py │ │ │ ├── local │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ └── watcher.py │ │ │ └── utils.py │ │ ├── type_aliases.py │ │ ├── utils.py │ │ └── workspace │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── cloud.py │ │ │ ├── remote.py │ │ │ └── view.py │ └── utils │ │ ├── __init__.py │ │ ├── dashboard.py │ │ ├── data_drift_utils.py │ │ ├── data_operations.py │ │ ├── data_preprocessing.py │ │ ├── generators.py │ │ ├── llm │ │ ├── __init__.py │ │ ├── _registry.py │ │ ├── base.py │ │ ├── errors.py │ │ ├── prompts.py │ │ └── wrapper.py │ │ ├── numpy_encoder.py │ │ ├── sync.py │ │ ├── types.py │ │ └── visualizations.py │ ├── llm │ ├── __init__.py │ ├── options.py │ └── templates.py │ ├── metrics │ ├── __init__.py │ ├── _legacy.py │ ├── classification.py │ ├── column_statistics.py │ ├── dataset_statistics.py │ ├── group_by.py │ ├── recsys.py │ └── regression.py │ ├── nbextension │ ├── __init__.py │ └── static │ │ ├── extension.js │ │ ├── index.js │ │ ├── index.js.LICENSE.txt │ │ └── material-ui-icons.woff2 │ ├── presets │ ├── __init__.py │ ├── classification.py │ ├── dataset_stats.py │ ├── drift.py │ └── regression.py │ ├── pydantic_utils.py │ ├── sdk │ ├── __init__.py │ ├── models.py │ ├── panels.py │ └── prompts.py │ ├── telemetry.py │ ├── tests │ ├── __init__.py │ ├── aliases.py │ ├── categorical_tests.py │ ├── descriptors.py │ ├── numerical_tests.py │ └── reference.py │ ├── ui │ ├── __init__.py │ ├── backport.py │ ├── service │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ ├── projects.py │ │ │ ├── service.py │ │ │ └── static.py │ │ ├── app.py │ │ ├── assets │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── robots.txt │ │ │ └── static │ │ │ │ ├── css │ │ │ │ └── index-CJbKDbyR.css │ │ │ │ └── js │ │ │ │ ├── dashboard-main-CItiR2E4.js │ │ │ │ ├── defaultLocale-B_SPPAJR.js │ │ │ │ ├── index-AnvlphjY.js │ │ │ │ └── snapshot-view-main-C7vBwLih.js │ │ ├── base.py │ │ ├── components │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── dashboard.py │ │ │ ├── local_storage.py │ │ │ ├── security.py │ │ │ ├── storage.py │ │ │ └── telemetry.py │ │ ├── config.py │ │ ├── datasets.py │ │ ├── demo_projects │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ └── bikes.py │ │ ├── errors.py │ │ ├── local_service.py │ │ ├── managers │ │ │ ├── __init__.py │ │ │ ├── auth.py │ │ │ ├── base.py │ │ │ └── projects.py │ │ ├── security │ │ │ ├── __init__.py │ │ │ ├── no_security.py │ │ │ ├── service.py │ │ │ └── token.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ └── dashbord │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ └── file.py │ │ ├── storage │ │ │ ├── __init__.py │ │ │ ├── common.py │ │ │ ├── config.py │ │ │ ├── fslocation.py │ │ │ ├── local │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ └── watcher.py │ │ │ └── utils.py │ │ ├── type_aliases.py │ │ ├── utils.py │ │ └── workspace │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ └── view.py │ ├── storage │ │ ├── __init__.py │ │ └── local │ │ │ ├── __init__.py │ │ │ └── base.py │ ├── utils.py │ └── workspace.py │ └── utils │ ├── __init__.py │ └── llm │ ├── __init__.py │ └── wrapper.py ├── test_data ├── adults.CITATION ├── adults.parquet └── reviews.parquet ├── tests ├── __init__.py ├── calculation_engine │ ├── __init__.py │ └── test_python_engine.py ├── calculations │ ├── __init__.py │ ├── stattests │ │ ├── __init__.py │ │ ├── test_get_stattest.py │ │ └── test_utils.py │ ├── test_classification_performance.py │ ├── test_data_clean.py │ ├── test_data_drift.py │ ├── test_data_integration.py │ ├── test_data_quality.py │ └── test_recommender_systems.py ├── cli │ ├── __init__.py │ └── test_ui.py ├── collector │ ├── __init__.py │ ├── conftest.py │ ├── test_app.py │ ├── test_client.py │ └── test_config.py ├── conftest.py ├── dataset_generator │ └── __init__.py ├── features │ ├── __init__.py │ ├── test_OOV_words_percentage_feature.py │ ├── test_bertscore_feature.py │ ├── test_contains_link_feature.py │ ├── test_custom_feature.py │ ├── test_exact_feature.py │ ├── test_is_valid_json_feature.py │ ├── test_is_valid_python_feature.py │ ├── test_is_valid_sql_feature.py │ ├── test_json_match.py │ ├── test_json_schema_match_feature.py │ ├── test_llm_judge.py │ ├── test_multicolumn.py │ ├── test_non_letter_character_percentage_feature.py │ ├── test_text_contains_feature.py │ ├── test_text_length_feature.py │ ├── test_text_part_feature.py │ ├── test_trigger_words_present_feature.py │ └── test_words_feature.py ├── future │ ├── __init__.py │ ├── descriptors │ │ ├── __init__.py │ │ ├── test_conditions.py │ │ ├── test_descriptors.py │ │ └── test_feature_descriptors.py │ ├── generators │ │ ├── __init__.py │ │ └── test_generator.py │ ├── metrics │ │ ├── __init__.py │ │ ├── all_metrics_tests.py │ │ ├── category_count.py │ │ ├── test_in_range_metric.py │ │ ├── test_list_metrics.py │ │ ├── test_min_metric.py │ │ ├── test_missing_count.py │ │ ├── test_out_range_metric.py │ │ └── test_test_fields.py │ ├── presets │ │ ├── __init__.py │ │ ├── dataset_stats.py │ │ ├── regression.py │ │ ├── test_serialization.py │ │ └── test_test_fields.py │ ├── report │ │ ├── __init__.py │ │ └── test_report.py │ ├── test_data_definition.py │ └── test_ui │ │ ├── __init__.py │ │ └── test_workspace │ │ ├── __init__.py │ │ └── test_local.py ├── metric_preset │ ├── __init__.py │ ├── test_all_metrics_presets.py │ ├── test_data_quality.py │ └── test_target_drift.py ├── metrics │ ├── __init__.py │ ├── data_drift │ │ ├── __init__.py │ │ ├── test_column_drift_metric.py │ │ ├── test_column_value_plot.py │ │ ├── test_data_drift_table.py │ │ ├── test_dataset_drift_metric.py │ │ └── test_target_by_features_table.py │ ├── data_interity │ │ ├── __init__.py │ │ ├── test_column_missing_values_metric.py │ │ ├── test_column_regexp_metric.py │ │ ├── test_column_summary_metric.py │ │ ├── test_dataset_missing_values_metric.py │ │ └── test_dataset_summary_metric.py │ ├── data_quality │ │ ├── __init__.py │ │ ├── test_column_correlations_metric.py │ │ ├── test_column_distribution_metric.py │ │ ├── test_column_quantile_metric.py │ │ ├── test_column_value_list_metric.py │ │ ├── test_column_value_range_metric.py │ │ ├── test_dataset_correlations_metric.py │ │ └── test_stability_metric.py │ ├── recsys │ │ ├── __init__.py │ │ ├── test_diversity.py │ │ ├── test_f_beta_top_k.py │ │ ├── test_hit_rate_k.py │ │ ├── test_map_k.py │ │ ├── test_mar_k.py │ │ ├── test_mrr.py │ │ ├── test_ndcg_k.py │ │ ├── test_novelty.py │ │ ├── test_pairwise_distances.py │ │ ├── test_personalisation.py │ │ ├── test_popularity_bias.py │ │ ├── test_precision_top_k.py │ │ ├── test_recall_top_k.py │ │ ├── test_scores_distribution.py │ │ └── test_serendipity.py │ ├── regression_performance │ │ ├── __init__.py │ │ ├── test_error_bias_table.py │ │ └── test_regression_performance_metrics.py │ ├── test_base_metric.py │ └── test_metrics_json_representation.py ├── multitest │ ├── __init__.py │ ├── conftest.py │ ├── datasets.py │ └── metrics │ │ ├── __init__.py │ │ ├── classification.py │ │ ├── conftest.py │ │ ├── custom.py │ │ ├── data_drift.py │ │ ├── data_integrity.py │ │ ├── data_quality.py │ │ ├── recsys.py │ │ ├── regression.py │ │ └── test_all.py ├── options │ ├── __init__.py │ ├── test_base.py │ └── test_data_drift.py ├── report │ ├── __init__.py │ ├── test_report.py │ └── test_report_profile.py ├── spark │ ├── __init__.py │ └── metrics │ │ ├── __init__.py │ │ └── test_data_drift.py ├── stattests │ ├── __init__.py │ ├── test_registry.py │ └── test_stattests.py ├── test_core.py ├── test_metric_results.py ├── test_preset │ ├── __init__.py │ ├── test_classification_multiclass.py │ ├── test_data_drift.py │ ├── test_data_drift_preset.py │ ├── test_data_quality.py │ ├── test_data_stability.py │ ├── test_no_target_performance.py │ └── test_no_target_performance_preset.py ├── test_pydantic_aliases.py ├── test_pydantic_compat.py ├── test_setup.py ├── test_suite │ ├── __init__.py │ ├── test_test_suite.py │ └── test_test_suite_loading.py ├── test_torch_numpy.py ├── test_utils.py ├── tests │ ├── __init__.py │ ├── test_base_tests.py │ ├── test_classification_performance_tests.py │ ├── test_custom_test.py │ ├── test_data_drift_tests.py │ ├── test_data_integrity_tests.py │ ├── test_data_quality_tests.py │ ├── test_regression_performance_tests.py │ └── test_utils.py ├── ui │ ├── __init__.py │ ├── conftest.py │ ├── test_app.py │ ├── test_dashboards.py │ ├── test_demo_project.py │ └── test_ui_basic.py └── utils │ ├── __init__.py │ ├── test_data_preprocessing.py │ ├── test_numpy_encoder.py │ └── test_pydantic_utils.py └── ui ├── .gitignore ├── README.md ├── html-visual-testing ├── .gitignore ├── README.md ├── package.json ├── playwright.config.ts └── tests │ ├── .gitignore │ ├── helpers.ts │ ├── visual.spec.ts │ └── visual.spec.ts-snapshots.dvc ├── package.json ├── packages └── evidently-ui-lib │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src │ ├── api │ │ ├── JsonParser.ts │ │ ├── client-heplers.ts │ │ ├── index.tsx │ │ ├── types │ │ │ ├── index.ts │ │ │ ├── utils.ts │ │ │ └── v2.ts │ │ └── utils.ts │ ├── components │ │ ├── AlertThemed.tsx │ │ ├── AutoTabs.tsx │ │ ├── BaseTabs.tsx │ │ ├── DashboardDateFilter.tsx │ │ ├── DashboardWidgets.tsx │ │ ├── DiscordSvg.tsx │ │ ├── DownloadButton.tsx │ │ ├── HidedTags.tsx │ │ ├── JsonView.tsx │ │ ├── LoadableVIew.tsx │ │ ├── LogoSvg.tsx │ │ ├── OnClickedPoint.tsx │ │ ├── Plot.tsx │ │ ├── ProjectCard.tsx │ │ ├── ProjectLayout.tsx │ │ ├── ServiceHeader.tsx │ │ ├── TextWithCopyIcon.tsx │ │ ├── ThemeToggle.tsx │ │ ├── WidgetGroup.tsx │ │ ├── WidgetsContent.tsx │ │ └── v2 │ │ │ └── Dashboard │ │ │ ├── HelperComponents │ │ │ ├── DrawDashboardPanels.tsx │ │ │ └── RenderPanelByDataFetchGeneralComponent.tsx │ │ │ ├── Panels │ │ │ ├── DashboardPanel.tsx │ │ │ ├── Skeleton.tsx │ │ │ ├── implementations │ │ │ │ ├── Counter.tsx │ │ │ │ ├── Pie.tsx │ │ │ │ ├── Plot.tsx │ │ │ │ ├── Text.tsx │ │ │ │ └── helpers │ │ │ │ │ ├── general.tsx │ │ │ │ │ ├── mui.tsx │ │ │ │ │ └── utils.tsx │ │ │ └── types.tsx │ │ │ └── utils.tsx │ ├── contexts │ │ ├── DashboardContext.tsx │ │ ├── DashboardViewParams.tsx │ │ ├── DashboardViewParamsV2.tsx │ │ └── WidgetWrapper.tsx │ ├── hooks │ │ ├── index.tsx │ │ ├── theme.ts │ │ └── useUpdateQueryStringValueWithoutNavigation.tsx │ ├── router-utils │ │ ├── components │ │ │ ├── breadcrumbs.tsx │ │ │ ├── error.tsx │ │ │ ├── navigation-progress.tsx │ │ │ └── navigations.tsx │ │ ├── fetchers.tsx │ │ ├── hooks.tsx │ │ ├── router-builder.tsx │ │ ├── types.ts │ │ └── utils.tsx │ ├── routes-components │ │ ├── dashboard │ │ │ └── index.tsx │ │ ├── snapshotId │ │ │ └── index.tsx │ │ └── snapshots │ │ │ └── index.tsx │ ├── shared-dependencies │ │ ├── mui-icons-material.tsx │ │ ├── mui-material.tsx │ │ ├── openapi-fetch.tsx │ │ ├── react-hook-form.tsx │ │ ├── react-router-dom.tsx │ │ └── zod.tsx │ ├── standalone │ │ └── app.tsx │ ├── theme │ │ └── index.tsx │ ├── utils │ │ └── index.tsx │ └── widgets │ │ ├── AlertBlock.tsx │ │ ├── AlertStatBlock.tsx │ │ ├── BigGraphWidgetContent.tsx │ │ ├── BigTableWidget │ │ ├── BigTableDetails.tsx │ │ ├── BigTableWidgetContent.tsx │ │ ├── GraphDetails.tsx │ │ ├── HistogramGraphColumn.tsx │ │ ├── LineGraphColumn.tsx │ │ └── ScatterGraphColumn.tsx │ │ ├── CounterWidgetContent.tsx │ │ ├── InsightBlock.tsx │ │ ├── NotImplementedWidgetContent.tsx │ │ ├── ProgressWidgetContent.tsx │ │ ├── RichDataWidget.tsx │ │ ├── TabbedGraphWidgetContent.tsx │ │ ├── TabbedWidgetContent.tsx │ │ ├── TableWidgetContent.tsx │ │ ├── TestSuiteWidget │ │ ├── TestData.tsx │ │ └── TestSuiteWidgetContent.tsx │ │ ├── TextWidgetContent.tsx │ │ ├── Widget.tsx │ │ ├── WidgetList.tsx │ │ ├── WidgetPanel.tsx │ │ └── WidgetRenderer.tsx │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── service ├── .gitignore ├── README.md ├── index.html ├── package.json ├── playwright.config.ts ├── public │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── manifest.json │ └── robots.txt ├── src │ ├── api │ │ └── index.ts │ ├── index.css │ ├── main.tsx │ ├── routes │ │ ├── components.tsx │ │ ├── hooks.tsx │ │ ├── router.tsx │ │ ├── src │ │ │ ├── dashboard │ │ │ │ ├── dashboard-main.tsx │ │ │ │ └── import.tsx │ │ │ ├── home │ │ │ │ ├── components.tsx │ │ │ │ ├── home-main.tsx │ │ │ │ └── import.tsx │ │ │ ├── index.tsx │ │ │ ├── project │ │ │ │ ├── import.tsx │ │ │ │ └── project-main.tsx │ │ │ ├── projects-layout │ │ │ │ ├── import.tsx │ │ │ │ └── projects-layout-main.tsx │ │ │ ├── projects-list │ │ │ │ ├── components.tsx │ │ │ │ ├── import.tsx │ │ │ │ └── projects-list-main.tsx │ │ │ ├── reports-layout │ │ │ │ ├── import.tsx │ │ │ │ └── reports-layout-main.tsx │ │ │ ├── reports-list │ │ │ │ ├── import.tsx │ │ │ │ └── reports-list-main.tsx │ │ │ ├── snapshot-view │ │ │ │ ├── import.tsx │ │ │ │ └── snapshot-view-main.tsx │ │ │ ├── test-suites-layout │ │ │ │ ├── import.tsx │ │ │ │ └── test-suites-layout-main.tsx │ │ │ └── test-suites-list │ │ │ │ ├── import.tsx │ │ │ │ └── test-suites-list-main.tsx │ │ └── types.tsx │ └── vite-env.d.ts ├── tests │ ├── .gitignore │ ├── smoke.spec.ts │ ├── visual.spec.ts │ └── visual.spec.ts-snapshots.dvc ├── tsconfig.json └── vite.config.ts ├── service_v2 ├── .gitignore ├── README.md ├── index.html ├── package.json ├── playwright.config.ts ├── public │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── manifest.json │ └── robots.txt ├── src │ ├── Components │ │ ├── DashboardPanel.tsx │ │ └── GoToSnapshotButton.tsx │ ├── api │ │ └── index.ts │ ├── contexts │ │ └── project.tsx │ ├── index.css │ ├── main.tsx │ ├── routes │ │ ├── components.tsx │ │ ├── hooks.tsx │ │ ├── router.tsx │ │ ├── src │ │ │ ├── dashboard │ │ │ │ ├── dashboard-main.tsx │ │ │ │ └── import.tsx │ │ │ ├── home │ │ │ │ ├── components.tsx │ │ │ │ ├── home-main.tsx │ │ │ │ └── import.tsx │ │ │ ├── index.tsx │ │ │ ├── load-panel-points │ │ │ │ ├── import.tsx │ │ │ │ └── load-panel-points-main.tsx │ │ │ ├── project │ │ │ │ ├── import.tsx │ │ │ │ └── project-main.tsx │ │ │ ├── projects-layout │ │ │ │ ├── import.tsx │ │ │ │ └── projects-layout-main.tsx │ │ │ ├── projects-list │ │ │ │ ├── components.tsx │ │ │ │ ├── import.tsx │ │ │ │ └── projects-list-main.tsx │ │ │ ├── reports-layout │ │ │ │ ├── import.tsx │ │ │ │ └── reports-layout-main.tsx │ │ │ ├── reports-list │ │ │ │ ├── import.tsx │ │ │ │ └── reports-list-main.tsx │ │ │ └── snapshot-view │ │ │ │ ├── import.tsx │ │ │ │ └── snapshot-view-main.tsx │ │ └── types.tsx │ └── vite-env.d.ts ├── tests │ ├── .gitignore │ ├── smoke.spec.ts │ ├── visual.spec.ts │ └── visual.spec.ts-snapshots.dvc ├── tsconfig.json └── vite.config.ts ├── standalone ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── main.tsx │ └── vite-env.d.ts ├── tsconfig.json └── vite.config.ts └── tsconfig.base.ui.json /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Default Linux Universal", 3 | "image": "mcr.microsoft.com/devcontainers/universal:2", 4 | "onCreateCommand": "./.devcontainer/on-create.sh", 5 | "postAttachCommand": { 6 | "ui": "source ./venv/bin/activate && evidently ui --port 8000 --demo-projects all --workspace test-workspace" 7 | }, 8 | "forwardPorts": [8000], 9 | "portsAttributes": { 10 | "8000": { 11 | "label": "evidently UI", 12 | "onAutoForward": "openBrowser" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.devcontainer/on-create.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | virtualenv venv 5 | source ./venv/bin/activate 6 | pip install -e ".[dev]" 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | ui 2 | -------------------------------------------------------------------------------- /.dvc/.gitignore: -------------------------------------------------------------------------------- 1 | /config.local 2 | /tmp 3 | /cache 4 | -------------------------------------------------------------------------------- /.dvc/config: -------------------------------------------------------------------------------- 1 | [core] 2 | remote = gs 3 | ['remote "gs"'] 4 | url = gs://evidently-service-visual-testing 5 | -------------------------------------------------------------------------------- /.dvcignore: -------------------------------------------------------------------------------- 1 | # Add patterns of files dvc should ignore, which could improve 2 | # the performance. Learn more at 3 | # https://dvc.org/doc/user-guide/dvcignore 4 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./docs/book/ 2 | 3 | structure: 4 | readme: README.md 5 | summary: SUMMARY.md 6 | -------------------------------------------------------------------------------- /.github/scripts/transform-jupyter-python.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync, lstatSync } from 'node:fs' 2 | 3 | const filePath = process.argv[2] 4 | 5 | if (!lstatSync(filePath).isFile()) { 6 | throw `${filePath} is not a file` 7 | } 8 | 9 | const pythonFile = readFileSync(filePath, { encoding: 'utf-8' }) 10 | 11 | const pythonFileTransformed = pythonFile 12 | .replace(/^(.+) = (.+.run\(.+)\n^\1/gm, '$1 = $2\n$1.save_html("$1.html")') 13 | 14 | writeFileSync(filePath, pythonFileTransformed) 15 | -------------------------------------------------------------------------------- /.github/share-actions/get-bikes-dataset-cached/action.yml: -------------------------------------------------------------------------------- 1 | name: Get bikes dataset cached 2 | runs: 3 | using: "composite" 4 | steps: 5 | - uses: actions/cache@v4 6 | id: cache-bikes-dataset 7 | env: 8 | cache-name: cache-bikes-dataset 9 | with: 10 | path: Bike-Sharing-Dataset.zip 11 | key: cache-bikes-dataset 12 | enableCrossOsArchive: true 13 | 14 | - name: Download test data 15 | if: ${{ steps.cache-bikes-dataset.outputs.cache-hit != 'true' }} 16 | run: curl -k https://archive.ics.uci.edu/static/public/275/bike+sharing+dataset.zip -o Bike-Sharing-Dataset.zip 17 | shell: bash 18 | -------------------------------------------------------------------------------- /.github/share-actions/ui-node-pnpm-install/action.yml: -------------------------------------------------------------------------------- 1 | name: UI install 2 | inputs: 3 | args: 4 | type: string 5 | description: "" 6 | required: false 7 | default: "" 8 | runs: 9 | using: "composite" 10 | steps: 11 | - uses: pnpm/action-setup@v3 12 | with: 13 | version: 9 14 | 15 | - name: Use Node.js 20 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version: 20 19 | cache: "pnpm" 20 | cache-dependency-path: ui/pnpm-lock.yaml 21 | 22 | - name: 📥 Install node dependencies 23 | working-directory: ui 24 | run: pnpm i --frozen-lockfile --ignore-scripts ${{ inputs.args }} 25 | shell: bash 26 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Publish on Docker Hub 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | latest: 7 | type: boolean 8 | default: false 9 | description: "Add latest tag" 10 | 11 | jobs: 12 | build_and_pub_docker: 13 | runs-on: [ ubuntu-22.04 ] 14 | steps: 15 | - name: Login to docker.io 16 | run: echo ${{ secrets.DOCKER_PWD }} | docker login -u ${{ secrets.DOCKER_LOGIN }} --password-stdin 17 | - uses: actions/checkout@master 18 | - name: setup builder 19 | run: docker buildx create --name mybuilder --bootstrap --use 20 | - name: Build and release latest image 21 | if: ${{ inputs.latest }} 22 | run: cd docker && make release 23 | - name: Build and push image 24 | if: ${{ ! inputs.latest }} 25 | run: cd docker && make buildx 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | venv 3 | .venv* 4 | .vscode 5 | .DS_Store 6 | examples/.DS_Store 7 | example_scripts 8 | .ipynb_checkpoints 9 | .idea 10 | .mypy_cache 11 | .pytest_cache 12 | dist 13 | build 14 | MANIFEST 15 | 16 | profile.json 17 | 18 | __pycache__ 19 | 20 | evidently-openapi-schema.yml 21 | evidently-v2-openapi-schema 22 | 23 | # see .devcontainer/devcontainer.json 24 | test-workspace 25 | workspace-for-visual-testing 26 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: "v4.6.0" 4 | hooks: 5 | - id: check-case-conflict 6 | - id: check-merge-conflict 7 | - id: check-toml 8 | - id: check-yaml 9 | exclude: '(docs|examples)/.*' 10 | - id: end-of-file-fixer 11 | exclude: '(docs|examples)/.*' 12 | - id: trailing-whitespace 13 | exclude: '(docs|examples)/.*' 14 | 15 | - repo: https://github.com/astral-sh/ruff-pre-commit 16 | rev: "v0.3.7" 17 | hooks: 18 | - id: ruff 19 | args: [--exit-non-zero-on-fix, --fix] 20 | - id: ruff-format 21 | 22 | - repo: https://github.com/biomejs/pre-commit 23 | rev: "v0.4.0" 24 | hooks: 25 | - id: biome-check 26 | additional_dependencies: ["@biomejs/biome@1.8.3"] 27 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include setupbase.py 2 | include README.md 3 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "data_format": { 3 | "separator": ",", 4 | "header": true, 5 | "date_column": "dteday" 6 | }, 7 | "column_mapping" : {}, 8 | "dashboard_tabs": { 9 | "data_drift": { 10 | }, 11 | "cat_target_drift":{ 12 | "verbose_level": 0 13 | } 14 | }, 15 | "options": { 16 | "data_drift": { 17 | "confidence": 0.95, 18 | "drift_share": 0.5, 19 | "nbinsx": null, 20 | "xbins": null 21 | } 22 | }, 23 | "pretty_print": true, 24 | "sampling": { 25 | "reference": { 26 | "type": "none", 27 | "n": 1, 28 | "ratio": 0.1 29 | }, 30 | "current": { 31 | "type": "nth", 32 | "n": 2, 33 | "ratio": 0.1 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | data_format: 2 | separator: "," 3 | header: true 4 | date_column: "dteday" 5 | column_mapping: {} 6 | profile_sections: 7 | - data_drift: 8 | xbins: 9 | nbinsx: 10 | drift_share: 0.5 11 | pretty_print: true 12 | sampling: 13 | reference: 14 | type: "simple" # could be "none", "simple", "random" 15 | n: 5 # used with simple sampling, number of rows to skip 16 | ratio: 0.1 # used with random sampling, part of data to take from chunk 17 | random_seed: 4 # used with random sampling, used as seed for random generator 18 | current: 19 | type: "nth" # could be "none", "simple", "random" 20 | n: 5 # used with simple sampling, number of rows to skip 21 | ratio: 0.1 # used with random sampling, part of data to take from chunk 22 | -------------------------------------------------------------------------------- /docker/Dockerfile.service: -------------------------------------------------------------------------------- 1 | FROM python:3.10-slim 2 | ARG BUILD_DATE 3 | ARG VCS_REF 4 | ARG VERSION 5 | LABEL org.label-schema.build-date=$BUILD_DATE \ 6 | org.label-schema.name="Evidently AI Service" \ 7 | org.label-schema.url="https://github.com/evidentlyai/evidently" \ 8 | org.label-schema.vcs-ref=$VCS_REF \ 9 | org.label-schema.vcs-url="https://github.com/evidentlyai/evidently" \ 10 | org.label-schema.version=$VERSION \ 11 | org.label-schema.schema-version="1.0" 12 | LABEL maintainer="mike0sv@evidentlyai.com" 13 | 14 | 15 | 16 | WORKDIR /src 17 | COPY setup.py . 18 | COPY setupbase.py . 19 | COPY README.md . 20 | COPY src ./src 21 | RUN apt-get update && \ 22 | apt-get install -y \ 23 | build-essential \ 24 | make \ 25 | gcc \ 26 | && pip install . \ 27 | && apt-get remove -y --purge make gcc build-essential \ 28 | && apt-get autoremove -y \ 29 | && rm -rf /var/lib/apt/lists/* \ 30 | && rm -rf /src 31 | 32 | WORKDIR /app 33 | ENTRYPOINT ["evidently", "ui", "--host", "0.0.0.0"] 34 | -------------------------------------------------------------------------------- /docker/Dockerfile.service.dev: -------------------------------------------------------------------------------- 1 | FROM python:3.10-slim 2 | ARG BUILD_DATE 3 | ARG VCS_REF 4 | ARG VERSION 5 | LABEL org.label-schema.build-date=$BUILD_DATE \ 6 | org.label-schema.name="Evidently AI Service" \ 7 | org.label-schema.url="https://github.com/evidentlyai/evidently" \ 8 | org.label-schema.vcs-ref=$VCS_REF \ 9 | org.label-schema.vcs-url="https://github.com/evidentlyai/evidently" \ 10 | org.label-schema.version=$VERSION \ 11 | org.label-schema.schema-version="1.0" 12 | LABEL maintainer="mike0sv@evidentlyai.com" 13 | 14 | 15 | RUN apt-get update && \ 16 | apt-get install -y \ 17 | build-essential \ 18 | make \ 19 | gcc 20 | 21 | WORKDIR /src 22 | COPY setup.py . 23 | COPY setupbase.py . 24 | COPY README.md . 25 | COPY src ./src 26 | 27 | RUN pip install . \ 28 | && rm -rf /src 29 | 30 | WORKDIR /app 31 | CMD ["evidently", "ui", "--host", "0.0.0.0"] 32 | -------------------------------------------------------------------------------- /examples/service/.gitignore: -------------------------------------------------------------------------------- 1 | workspace -------------------------------------------------------------------------------- /examples/service/remote_demo_project.py: -------------------------------------------------------------------------------- 1 | from evidently.ui.workspace import RemoteWorkspace 2 | from evidently.ui.service.demo_projects import DEMO_PROJECTS 3 | DEMO_PROJECT_NAME = "bikes" 4 | 5 | def main(): 6 | workspace = "http://localhost:8000" 7 | ws = RemoteWorkspace(workspace) 8 | demo_project = DEMO_PROJECTS[DEMO_PROJECT_NAME] 9 | has_demo_project = any(p.name == demo_project.name for p in ws.list_projects()) 10 | if not has_demo_project: 11 | print("Generating demo project...") 12 | demo_project.create(ws) 13 | 14 | demo_project = ws.search_project(demo_project.name)[0] 15 | print(demo_project.id) 16 | 17 | 18 | if __name__ == '__main__': 19 | main() -------------------------------------------------------------------------------- /examples/service/run_service.sh: -------------------------------------------------------------------------------- 1 | docker run -p 8000:8000 \ 2 | -v $(pwd)/workspace:/app/workspace \ 3 | --name evidently-service \ 4 | --detach \ 5 | evidently/evidently-service:latest -------------------------------------------------------------------------------- /images/gh_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/images/gh_header.png -------------------------------------------------------------------------------- /requirements.dev.txt: -------------------------------------------------------------------------------- 1 | # keep in sync with setup.py extra_require block. 2 | wheel==0.38.1 3 | setuptools==65.5.1; python_version < '3.12' 4 | setuptools==68.2.2; python_version >= '3.12' 5 | jupyter==1.0.0 6 | mypy==1.1.1 7 | pandas-stubs 8 | pytest==7.4.4 9 | pytest-asyncio==0.23.7 10 | pytest-mock==3.14.0 11 | types-PyYAML==6.0.1 12 | types-requests==2.26.0 13 | types-dataclasses==0.6 14 | types-python-dateutil==2.8.19 15 | types-ujson==5.4.0 16 | pillow==10.3.0 17 | pip-audit 18 | pyspark 19 | ruff==0.3.7 20 | pre-commit==3.5.0 21 | 22 | # service dependencies 23 | litestar>=2.7.1 24 | dynaconf 25 | uvicorn 26 | pyarrow 27 | httpx 28 | -------------------------------------------------------------------------------- /requirements.min.txt: -------------------------------------------------------------------------------- 1 | # keep in sync with setup.py install_require block (but there should be set as lower bound). 2 | plotly==5.10.0 3 | statsmodels==0.12.2 4 | scikit-learn==1.0.1 5 | pandas[parquet]==1.3.5 6 | numpy==1.22.0 7 | nltk==3.6.7 8 | scipy==1.10.0 9 | requests==2.32.0 10 | PyYAML==5.4 11 | pydantic==1.10.16 12 | litestar==2.8.3 13 | typing-inspect==0.9.0 14 | uvicorn==0.22.0 15 | watchdog==3.0.0 16 | typer==0.3 17 | rich==13 18 | iterative-telemetry==0.0.5 19 | dynaconf==3.2.4 20 | pyarrow==14.0.1 21 | pyspark==3.4.0 22 | fsspec==2024.6.1 23 | certifi==2024.7.4 24 | urllib3==1.26.19 25 | ujson==5.4.0 26 | deprecation==2.1.0 27 | uuid6==2024.7.10 28 | cryptography==43.0.1 29 | 30 | openai==1.16.2 31 | evaluate==0.4.1 32 | transformers[torch]==4.39.3 33 | sentence-transformers==2.7.0 34 | sqlvalidator==0.0.20 35 | -------------------------------------------------------------------------------- /src/evidently/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | from evidently.core.compare import compare 4 | from evidently.core.datasets import BinaryClassification 5 | from evidently.core.datasets import DataDefinition 6 | from evidently.core.datasets import Dataset 7 | from evidently.core.datasets import MulticlassClassification 8 | from evidently.core.datasets import Recsys 9 | from evidently.core.datasets import Regression 10 | from evidently.core.report import Report 11 | from evidently.legacy.core import ColumnType 12 | 13 | from . import _registry 14 | from ._version import __version__ 15 | from ._version import version_info 16 | from .nbextension import _jupyter_nbextension_paths 17 | 18 | __all__ = [ 19 | "__version__", 20 | "version_info", 21 | "_jupyter_nbextension_paths", 22 | "_registry", 23 | "Report", 24 | "Dataset", 25 | "DataDefinition", 26 | "BinaryClassification", 27 | "MulticlassClassification", 28 | "Regression", 29 | "Recsys", 30 | "compare", 31 | "ColumnType", # legacy support 32 | ] 33 | -------------------------------------------------------------------------------- /src/evidently/_registry.py: -------------------------------------------------------------------------------- 1 | from .core import registries as future_registry 2 | from .legacy import _registry as legacy_registry 3 | 4 | __all__ = ["future_registry", "legacy_registry"] 5 | -------------------------------------------------------------------------------- /src/evidently/_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | version_info = (0, 7, 6) 5 | __version__ = ".".join(map(str, version_info)) 6 | 7 | 8 | if __name__ == "__main__": 9 | print(__version__) 10 | -------------------------------------------------------------------------------- /src/evidently/cli/__init__.py: -------------------------------------------------------------------------------- 1 | from evidently.cli.demo_project import generate_demo_project 2 | from evidently.cli.legacy_ui import legacy_ui 3 | from evidently.cli.main import app 4 | from evidently.cli.ui import ui 5 | 6 | __all__ = ["app", "ui", "legacy_ui", "generate_demo_project"] 7 | 8 | if __name__ == "__main__": 9 | app() 10 | -------------------------------------------------------------------------------- /src/evidently/cli/__main__.py: -------------------------------------------------------------------------------- 1 | from evidently.cli import app 2 | from evidently.cli.ui import ui 3 | 4 | __all__ = ["app", "ui"] 5 | 6 | app() 7 | -------------------------------------------------------------------------------- /src/evidently/cli/demo_project.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from typer import Option 4 | from typer import echo 5 | 6 | from evidently.cli.main import app 7 | from evidently.ui.service.demo_projects import DEMO_PROJECTS 8 | 9 | 10 | @app.command("demo_project") 11 | def generate_demo_project( 12 | project: str = Option("all", help="Project to generate"), 13 | path: str = Option("workspace", help="Workspace path"), 14 | ): 15 | _project = DEMO_PROJECTS.get(project) 16 | if _project is None: 17 | echo(f"Demo project {project} not found.") 18 | sys.exit(1) 19 | _project.create(path) 20 | -------------------------------------------------------------------------------- /src/evidently/cli/main.py: -------------------------------------------------------------------------------- 1 | from typer import Typer 2 | 3 | app = Typer( 4 | context_settings={"help_option_names": ["-h", "--help"]}, 5 | ) 6 | 7 | 8 | @app.callback(no_args_is_help=True, invoke_without_command=True) 9 | def evidently_callback(): 10 | """\b 11 | Evidently is tool to help you evaluate, test and monitor your data and ML models. 12 | Documentation: https://docs.evidentlyai.com 13 | """ 14 | -------------------------------------------------------------------------------- /src/evidently/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/core/__init__.py -------------------------------------------------------------------------------- /src/evidently/core/_utils.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from typing import Iterable 3 | from typing import List 4 | from typing import Tuple 5 | 6 | 7 | def not_implemented(self_obj: object): 8 | currentframe = inspect.currentframe() 9 | name = "(unknown)" 10 | if currentframe is not None and currentframe.f_back: 11 | name = currentframe.f_back.f_code.co_name 12 | return NotImplementedError(f"Metric Type: {type(self_obj)} should implement {name}()") 13 | 14 | 15 | def _flatten(obj: object, paths: List[str] = None) -> Iterable[Tuple[str, float]]: 16 | paths = paths or [] 17 | if isinstance(obj, float): 18 | yield ".".join(paths), obj 19 | return 20 | if isinstance(obj, dict): 21 | for k, v in obj.items(): 22 | yield from _flatten(v, paths + [str(k)]) 23 | return 24 | if isinstance(obj, list): 25 | for i, v in enumerate(obj): 26 | yield from _flatten(v, paths + [str(i)]) 27 | return 28 | raise NotImplementedError("Not implemented for {}".format(type(obj))) 29 | -------------------------------------------------------------------------------- /src/evidently/core/base_types.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from evidently._pydantic_compat import StrictBool 4 | 5 | Label = Union[StrictBool, int, str, None] 6 | -------------------------------------------------------------------------------- /src/evidently/core/preset_types.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import List 3 | 4 | from evidently.core.metric_types import render_widgets 5 | from evidently.legacy.model.widget import BaseWidgetInfo 6 | 7 | 8 | @dataclasses.dataclass 9 | class PresetResult: 10 | widget: List[BaseWidgetInfo] 11 | 12 | def _repr_html_(self): 13 | return render_widgets(self.widget, as_iframe=True) 14 | -------------------------------------------------------------------------------- /src/evidently/core/registries/__init__.py: -------------------------------------------------------------------------------- 1 | from . import bound_tests 2 | from . import column_conditions 3 | from . import descriptors 4 | from . import metric_results 5 | from . import metric_tests 6 | from . import metrics 7 | from . import presets 8 | 9 | __all__ = ["descriptors", "presets", "metrics", "metric_tests", "metric_results", "bound_tests", "column_conditions"] 10 | -------------------------------------------------------------------------------- /src/evidently/core/registries/bound_tests.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa: E501 2 | # fmt: off 3 | from evidently.core.metric_types import BoundTest 4 | from evidently.pydantic_utils import register_type_alias 5 | 6 | register_type_alias(BoundTest, "evidently.core.metric_types.ByLabelBoundTest", "evidently:bound_test:ByLabelBoundTest") 7 | register_type_alias(BoundTest, "evidently.core.metric_types.ByLabelCountBoundTest", "evidently:bound_test:ByLabelCountBoundTest") 8 | register_type_alias(BoundTest, "evidently.core.metric_types.CountBoundTest", "evidently:bound_test:CountBoundTest") 9 | register_type_alias(BoundTest, "evidently.core.metric_types.MeanStdBoundTest", "evidently:bound_test:MeanStdBoundTest") 10 | register_type_alias(BoundTest, "evidently.core.metric_types.SingleValueBoundTest", "evidently:bound_test:SingleValueBoundTest") 11 | register_type_alias(BoundTest, "evidently.metrics.column_statistics.ValueDriftBoundTest", "evidently:bound_test:ValueDriftBoundTest") 12 | -------------------------------------------------------------------------------- /src/evidently/core/registries/descriptors.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa: E501 2 | # fmt: off 3 | from evidently.core.datasets import Descriptor 4 | from evidently.pydantic_utils import register_type_alias 5 | 6 | register_type_alias(Descriptor, "evidently.core.datasets.FeatureDescriptor", "evidently:descriptor_v2:FeatureDescriptor") 7 | register_type_alias(Descriptor, "evidently.descriptors._context_relevance.ContextRelevance", "evidently:descriptor_v2:ContextRelevance") 8 | register_type_alias(Descriptor, "evidently.descriptors._custom_descriptors.CustomColumnDescriptor", "evidently:descriptor_v2:CustomColumnDescriptor") 9 | register_type_alias(Descriptor, "evidently.descriptors._custom_descriptors.CustomDescriptor", "evidently:descriptor_v2:CustomDescriptor") 10 | register_type_alias(Descriptor, "evidently.descriptors._text_length.TextLength", "evidently:descriptor_v2:TextLength") 11 | 12 | register_type_alias(Descriptor, "evidently.core.datasets.ColumnTest", "evidently:descriptor_v2:ColumnTest") 13 | register_type_alias(Descriptor, "evidently.core.datasets.SingleInputDescriptor", "evidently:descriptor_v2:SingleInputDescriptor") 14 | register_type_alias(Descriptor, "evidently.core.datasets.TestSummary", "evidently:descriptor_v2:TestSummary") 15 | -------------------------------------------------------------------------------- /src/evidently/core/registries/metric_results.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa: E501 2 | # fmt: off 3 | from evidently.core.metric_types import MetricResult 4 | from evidently.pydantic_utils import register_type_alias 5 | 6 | register_type_alias(MetricResult, "evidently.core.metric_types.ByLabelCountValue", "evidently:metric_result_v2:ByLabelCountValue") 7 | register_type_alias(MetricResult, "evidently.core.metric_types.ByLabelValue", "evidently:metric_result_v2:ByLabelValue") 8 | register_type_alias(MetricResult, "evidently.core.metric_types.CountValue", "evidently:metric_result_v2:CountValue") 9 | register_type_alias(MetricResult, "evidently.core.metric_types.MeanStdValue", "evidently:metric_result_v2:MeanStdValue") 10 | register_type_alias(MetricResult, "evidently.core.metric_types.SingleValue", "evidently:metric_result_v2:SingleValue") 11 | -------------------------------------------------------------------------------- /src/evidently/core/serialization.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Dict 3 | from typing import List 4 | from typing import Union 5 | 6 | from evidently._pydantic_compat import BaseModel 7 | from evidently.core.metric_types import MetricId 8 | from evidently.core.metric_types import MetricResult 9 | from evidently.legacy.model.widget import BaseWidgetInfo 10 | from evidently.legacy.suite.base_suite import MetadataValueType 11 | 12 | 13 | class MetricReportItem(BaseModel): 14 | metric_id: MetricId 15 | params: dict 16 | 17 | 18 | class PresetReportItem(BaseModel): 19 | pass # TODO: support presets 20 | 21 | 22 | class ReportModel(BaseModel): 23 | items: List[Union[MetricReportItem, PresetReportItem]] 24 | 25 | 26 | class SnapshotModel(BaseModel): 27 | report: ReportModel 28 | timestamp: datetime 29 | metadata: Dict[str, MetadataValueType] 30 | tags: List[str] 31 | metric_results: Dict[MetricId, MetricResult] 32 | top_level_metrics: List[MetricId] 33 | widgets: List[BaseWidgetInfo] 34 | tests_widgets: List[BaseWidgetInfo] 35 | -------------------------------------------------------------------------------- /src/evidently/descriptors/llm_judges.py: -------------------------------------------------------------------------------- 1 | from evidently.llm.templates import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/errors.py: -------------------------------------------------------------------------------- 1 | class EvidentlyError(Exception): 2 | def get_message(self): 3 | return f"{self.__class__.__name__}: {self}" 4 | -------------------------------------------------------------------------------- /src/evidently/future/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/future/__init__.py -------------------------------------------------------------------------------- /src/evidently/future/datasets.py: -------------------------------------------------------------------------------- 1 | from evidently.core.datasets import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/future/descriptors/__init__.py: -------------------------------------------------------------------------------- 1 | from evidently.descriptors import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/future/generators/__init__.py: -------------------------------------------------------------------------------- 1 | from evidently.generators import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/future/metric_types.py: -------------------------------------------------------------------------------- 1 | from evidently.core.metric_types import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/future/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | from evidently.metrics import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/future/presets/__init__.py: -------------------------------------------------------------------------------- 1 | from evidently.presets import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/future/report.py: -------------------------------------------------------------------------------- 1 | from evidently.core.report import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/future/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from evidently.tests import * # noqa: F403 2 | -------------------------------------------------------------------------------- /src/evidently/generators/__init__.py: -------------------------------------------------------------------------------- 1 | from .column import ColumnMetricGenerator 2 | 3 | __all__ = ["ColumnMetricGenerator"] 4 | -------------------------------------------------------------------------------- /src/evidently/legacy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | TELEMETRY_ADDRESS = "https://telemetry-321112.ew.r.appspot.com/post_data" 4 | TELEMETRY_ENABLED = not os.getenv("EVIDENTLY_DISABLE_TELEMETRY", "0") == "1" 5 | -------------------------------------------------------------------------------- /src/evidently/legacy/_registry.py: -------------------------------------------------------------------------------- 1 | from .descriptors import _registry as descriptors_registry 2 | from .features import _registry as features_registry 3 | from .metric_preset import _registry as metric_preset_registry 4 | from .metrics import _registry as metrics_registry 5 | from .test_preset import _registry as test_preset_registry 6 | from .tests import _registry as tests_registry 7 | from .utils.llm import _registry as llm_registry 8 | 9 | __all__ = [ 10 | "tests_registry", 11 | "metrics_registry", 12 | "descriptors_registry", 13 | "features_registry", 14 | "llm_registry", 15 | "metric_preset_registry", 16 | "test_preset_registry", 17 | ] 18 | -------------------------------------------------------------------------------- /src/evidently/legacy/calculation_engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/calculation_engine/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/calculation_engine/metric_implementation.py: -------------------------------------------------------------------------------- 1 | import abc 2 | 3 | 4 | class MetricImplementation: 5 | @abc.abstractmethod 6 | def calculate(self, context, data): 7 | raise NotImplementedError() 8 | 9 | @classmethod 10 | @abc.abstractmethod 11 | def supported_engines(cls): 12 | raise NotImplementedError() 13 | -------------------------------------------------------------------------------- /src/evidently/legacy/calculations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/calculations/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/calculations/stattests/text_content_drift.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | import pandas as pd 4 | 5 | from evidently.legacy.calculations.stattests.registry import StatTest 6 | from evidently.legacy.calculations.stattests.registry import register_stattest 7 | from evidently.legacy.core import ColumnType 8 | from evidently.legacy.utils.data_drift_utils import calculate_text_drift_score 9 | 10 | 11 | def _perc_text_content_drift( 12 | reference_data: pd.Series, current_data: pd.Series, feature_type: ColumnType, threshold: float 13 | ) -> Tuple[float, bool]: 14 | return calculate_text_drift_score(reference_data, current_data, bootstrap=True, p_value=1 - threshold) 15 | 16 | 17 | perc_text_content_drift_stat_test = StatTest( 18 | name="perc_text_content_drift", 19 | display_name="Percentile text content drift", 20 | allowed_feature_types=[ColumnType.Text], 21 | default_threshold=0.95, 22 | ) 23 | 24 | register_stattest(perc_text_content_drift_stat_test, _perc_text_content_drift) 25 | -------------------------------------------------------------------------------- /src/evidently/legacy/calculations/stattests/text_content_drift_abs.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | import pandas as pd 4 | 5 | from evidently.legacy.calculations.stattests.registry import StatTest 6 | from evidently.legacy.calculations.stattests.registry import register_stattest 7 | from evidently.legacy.core import ColumnType 8 | from evidently.legacy.utils.data_drift_utils import calculate_text_drift_score 9 | 10 | 11 | def _abs_text_content_drift( 12 | reference_data: pd.Series, current_data: pd.Series, feature_type: ColumnType, threshold: float 13 | ) -> Tuple[float, bool]: 14 | return calculate_text_drift_score(reference_data, current_data, bootstrap=False, threshold=threshold) 15 | 16 | 17 | abs_text_content_drift_stat_test = StatTest( 18 | name="abs_text_content_drift", 19 | display_name="Absolute text content drift", 20 | allowed_feature_types=[ColumnType.Text], 21 | default_threshold=0.55, 22 | ) 23 | 24 | register_stattest(abs_text_content_drift_stat_test, _abs_text_content_drift) 25 | -------------------------------------------------------------------------------- /src/evidently/legacy/cli/__init__.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.cli.collector import collector 2 | from evidently.legacy.cli.main import app 3 | from evidently.legacy.cli.ui import ui 4 | 5 | __all__ = ["app", "ui", "collector"] 6 | 7 | 8 | def main(): 9 | app() 10 | 11 | 12 | if __name__ == "__main__": 13 | main() 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/cli/collector.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from typer import Option 4 | 5 | from evidently.legacy.cli.main import app 6 | 7 | 8 | @app.command("collector") 9 | def collector( 10 | host: str = Option("127.0.0.1", help="Collector host"), 11 | port: int = Option(8001, help="Collector port"), 12 | config_path: str = Option(None, help="Path to config file"), 13 | secret: Optional[str] = Option(None, help="Secret for writing operations"), 14 | ): 15 | """Start Evidently collector service""" 16 | from evidently.legacy.collector.app import run 17 | from evidently.legacy.collector.config import CONFIG_PATH 18 | 19 | run(host, port, config_path or CONFIG_PATH, secret) 20 | -------------------------------------------------------------------------------- /src/evidently/legacy/cli/main.py: -------------------------------------------------------------------------------- 1 | from typer import Typer 2 | 3 | app = Typer( 4 | context_settings={"help_option_names": ["-h", "--help"]}, 5 | ) 6 | 7 | 8 | @app.callback(no_args_is_help=True, invoke_without_command=True) 9 | def evidently_callback(): 10 | """\b 11 | Evidently is tool to help you evaluate, test and monitor your data and ML models. 12 | Documentation: https://docs.evidentlyai.com 13 | """ 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/collector/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/collector/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/collector/client.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from typing import Dict 3 | 4 | import pandas as pd 5 | 6 | from evidently.legacy.collector.config import CollectorConfig 7 | from evidently.legacy.ui.utils import RemoteClientBase 8 | 9 | 10 | class CollectorClient(RemoteClientBase): 11 | def create_collector(self, id: str, collector: CollectorConfig) -> Dict[str, Any]: 12 | return self._request(f"/{id}", "POST", body=collector.dict()).json() 13 | 14 | def send_data(self, id: str, data: pd.DataFrame) -> Dict[str, Any]: 15 | return self._request(f"/{id}/data", "POST", body=data.to_dict()).json() 16 | 17 | def set_reference(self, id: str, reference: pd.DataFrame) -> Dict[str, Any]: 18 | return self._request(f"/{id}/reference", "POST", body=reference.to_dict()).json() 19 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/BERTScore_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features.BERTScore_feature import BERTScoreFeature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeatures 4 | 5 | 6 | class BERTScore(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:BERTScore" 9 | 10 | with_column: str 11 | 12 | def feature(self, column_name: str) -> GeneratedFeatures: 13 | return BERTScoreFeature(columns=[column_name, self.with_column], display_name=self.display_name) 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/contains_link_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import contains_link_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class ContainsLink(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:ContainsLink" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return contains_link_feature.ContainsLink(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/exact_match_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features.exact_match_feature import ExactMatchFeature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeatures 4 | 5 | 6 | class ExactMatch(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:ExactMatch" 9 | 10 | with_column: str 11 | 12 | def feature(self, column_name: str) -> GeneratedFeatures: 13 | return ExactMatchFeature(columns=[column_name, self.with_column], display_name=self.display_name) 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/is_valid_json_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import is_valid_json_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class IsValidJSON(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:IsValidJSON" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return is_valid_json_feature.IsValidJSON(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/is_valid_python_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import is_valid_python_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class IsValidPython(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:IsValidPython" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return is_valid_python_feature.IsValidPython(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/is_valid_sql_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import is_valid_sql_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class IsValidSQL(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:IsValidSQL" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return is_valid_sql_feature.IsValidSQL(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/json_match_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import json_match_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class JSONMatch(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:JSONMatch" 9 | 10 | with_column: str 11 | 12 | def feature(self, column_name: str) -> GeneratedFeature: 13 | return json_match_feature.JSONMatch(first_column=column_name, second_column=self.with_column) 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/json_schema_match_descriptor.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from evidently.legacy.features import json_schema_match_feature 4 | from evidently.legacy.features.generated_features import FeatureDescriptor 5 | from evidently.legacy.features.generated_features import GeneratedFeature 6 | 7 | 8 | class JSONSchemaMatch(FeatureDescriptor): 9 | class Config: 10 | type_alias = "evidently:descriptor:JSONSchemaMatch" 11 | 12 | expected_schema: Dict[str, type] 13 | validate_types: bool = False 14 | exact_match: bool = False 15 | 16 | def feature(self, column_name: str) -> GeneratedFeature: 17 | return json_schema_match_feature.JSONSchemaMatch( 18 | column_name=column_name, 19 | expected_schema=self.expected_schema, 20 | validate_types=self.validate_types, 21 | exact_match=self.exact_match, 22 | display_name=self.display_name, 23 | ) 24 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/non_letter_character_percentage_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import non_letter_character_percentage_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class NonLetterCharacterPercentage(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:NonLetterCharacterPercentage" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return non_letter_character_percentage_feature.NonLetterCharacterPercentage(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/oov_words_percentage_descriptor.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from evidently.legacy.features.generated_features import FeatureDescriptor 4 | from evidently.legacy.features.generated_features import GeneratedFeature 5 | from evidently.legacy.features.OOV_words_percentage_feature import OOVWordsPercentage 6 | 7 | 8 | class OOV(FeatureDescriptor): 9 | class Config: 10 | type_alias = "evidently:descriptor:OOV" 11 | 12 | ignore_words: Tuple = () 13 | 14 | def feature(self, column_name: str) -> GeneratedFeature: 15 | return OOVWordsPercentage(column_name, self.ignore_words, self.display_name) 16 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/regexp_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import regexp_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class RegExp(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:RegExp" 9 | 10 | reg_exp: str 11 | 12 | def feature(self, column_name: str) -> GeneratedFeature: 13 | return regexp_feature.RegExp(column_name, self.reg_exp, self.display_name) 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/semantic_similarity.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features.generated_features import FeatureDescriptor 2 | from evidently.legacy.features.generated_features import GeneratedFeatures 3 | from evidently.legacy.features.semantic_similarity_feature import SemanticSimilarityFeature 4 | 5 | 6 | class SemanticSimilarity(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:SemanticSimilarity" 9 | 10 | with_column: str 11 | 12 | def feature(self, column_name: str) -> GeneratedFeatures: 13 | return SemanticSimilarityFeature(columns=[column_name, self.with_column], display_name=self.display_name) 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/sentence_count_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import sentence_count_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class SentenceCount(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:SentenceCount" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return sentence_count_feature.SentenceCount(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/sentiment_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import sentiment_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class Sentiment(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:Sentiment" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return sentiment_feature.Sentiment(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/text_length_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import text_length_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class TextLength(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:TextLength" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return text_length_feature.TextLength(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/text_part_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import text_part_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class BeginsWith(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:BeginsWith" 9 | 10 | prefix: str 11 | case_sensitive: bool = True 12 | 13 | def feature(self, column_name: str) -> GeneratedFeature: 14 | return text_part_feature.BeginsWith( 15 | column_name, 16 | self.prefix, 17 | self.case_sensitive, 18 | self.display_name, 19 | ) 20 | 21 | 22 | class EndsWith(FeatureDescriptor): 23 | class Config: 24 | type_alias = "evidently:descriptor:EndsWith" 25 | 26 | suffix: str 27 | case_sensitive: bool = True 28 | 29 | def feature(self, column_name: str) -> GeneratedFeature: 30 | return text_part_feature.EndsWith( 31 | column_name, 32 | self.suffix, 33 | self.case_sensitive, 34 | self.display_name, 35 | ) 36 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/trigger_words_presence_descriptor.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from evidently.legacy.features import trigger_words_presence_feature 4 | from evidently.legacy.features.generated_features import FeatureDescriptor 5 | from evidently.legacy.features.generated_features import GeneratedFeature 6 | 7 | 8 | class TriggerWordsPresence(FeatureDescriptor): 9 | class Config: 10 | type_alias = "evidently:descriptor:TriggerWordsPresence" 11 | 12 | words_list: List[str] 13 | lemmatize: bool = True 14 | 15 | def feature(self, column_name: str) -> GeneratedFeature: 16 | return trigger_words_presence_feature.TriggerWordsPresent( 17 | column_name, 18 | self.words_list, 19 | self.lemmatize, 20 | self.display_name, 21 | ) 22 | -------------------------------------------------------------------------------- /src/evidently/legacy/descriptors/word_count_descriptor.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.features import word_count_feature 2 | from evidently.legacy.features.generated_features import FeatureDescriptor 3 | from evidently.legacy.features.generated_features import GeneratedFeature 4 | 5 | 6 | class WordCount(FeatureDescriptor): 7 | class Config: 8 | type_alias = "evidently:descriptor:WordCount" 9 | 10 | def feature(self, column_name: str) -> GeneratedFeature: 11 | return word_count_feature.WordCount(column_name, self.display_name) 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/experimental/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/experimental/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/experimental/report_set.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | from typing import Dict 4 | from typing import Optional 5 | 6 | from evidently._pydantic_compat import ValidationError 7 | from evidently.legacy.suite.base_suite import Snapshot 8 | from evidently.legacy.ui.type_aliases import SnapshotID 9 | 10 | 11 | def load_snapshots( 12 | path: str, 13 | date_from: Optional[datetime.datetime] = None, 14 | date_to: Optional[datetime.datetime] = None, 15 | skip_errors: bool = False, 16 | ) -> Dict[SnapshotID, Snapshot]: 17 | result = {} 18 | for file in os.listdir(path): 19 | filepath = os.path.join(path, file) 20 | try: 21 | suite = Snapshot.load(filepath) 22 | except ValidationError: 23 | if skip_errors: 24 | continue 25 | raise 26 | if date_from is not None and suite.timestamp < date_from: 27 | continue 28 | if date_to is not None and suite.timestamp > date_to: 29 | continue 30 | result[suite.id] = suite 31 | return result 32 | -------------------------------------------------------------------------------- /src/evidently/legacy/features/__init__.py: -------------------------------------------------------------------------------- 1 | from . import _registry 2 | 3 | __all__ = ["_registry"] 4 | -------------------------------------------------------------------------------- /src/evidently/legacy/features/exact_match_feature.py: -------------------------------------------------------------------------------- 1 | from typing import ClassVar 2 | from typing import List 3 | 4 | import pandas as pd 5 | 6 | from evidently.legacy.base_metric import ColumnName 7 | from evidently.legacy.core import ColumnType 8 | from evidently.legacy.features.generated_features import GeneratedFeature 9 | from evidently.legacy.utils.data_preprocessing import DataDefinition 10 | 11 | 12 | class ExactMatchFeature(GeneratedFeature): 13 | class Config: 14 | type_alias = "evidently:feature:ExactMatchFeature" 15 | 16 | __feature_type__: ClassVar = ColumnType.Categorical 17 | columns: List[str] 18 | 19 | def generate_feature(self, data: pd.DataFrame, data_definition: DataDefinition) -> pd.DataFrame: 20 | return pd.DataFrame({self._feature_name(): data[self.columns[0]] == data[self.columns[1]]}) 21 | 22 | def _feature_name(self): 23 | return "|".join(self.columns) 24 | 25 | def _as_column(self) -> "ColumnName": 26 | return self._create_column( 27 | self._feature_name(), 28 | default_display_name=f"Exact Match for {' '.join(self.columns)}.", 29 | ) 30 | -------------------------------------------------------------------------------- /src/evidently/legacy/features/is_valid_json_feature.py: -------------------------------------------------------------------------------- 1 | import json 2 | from typing import Any 3 | from typing import ClassVar 4 | from typing import Optional 5 | 6 | from evidently.legacy.core import ColumnType 7 | from evidently.legacy.features.generated_features import ApplyColumnGeneratedFeature 8 | 9 | 10 | class IsValidJSON(ApplyColumnGeneratedFeature): 11 | class Config: 12 | type_alias = "evidently:feature:IsValidJSON" 13 | 14 | __feature_type__: ClassVar = ColumnType.Categorical 15 | display_name_template: ClassVar = "JSON valid for {column_name}" 16 | 17 | def __init__(self, column_name: str, display_name: Optional[str] = None): 18 | self.display_name = display_name 19 | super().__init__(column_name=column_name) 20 | 21 | def apply(self, value: Any): 22 | try: 23 | json.loads(value) 24 | except ValueError: 25 | return False 26 | return True 27 | -------------------------------------------------------------------------------- /src/evidently/legacy/features/is_valid_python_feature.py: -------------------------------------------------------------------------------- 1 | import ast 2 | from typing import Any 3 | from typing import ClassVar 4 | from typing import Optional 5 | 6 | from evidently.legacy.core import ColumnType 7 | from evidently.legacy.features.generated_features import ApplyColumnGeneratedFeature 8 | 9 | 10 | class IsValidPython(ApplyColumnGeneratedFeature): 11 | class Config: 12 | type_alias = "evidently:feature:IsValidPython" 13 | 14 | __feature_type__: ClassVar = ColumnType.Categorical 15 | display_name_template: ClassVar = "Valid Python for {column_name}" 16 | 17 | def __init__(self, column_name: str, display_name: Optional[str] = None): 18 | self.display_name = display_name 19 | super().__init__(column_name=column_name) 20 | 21 | def apply(self, value: Any) -> bool: 22 | try: 23 | ast.parse(value) 24 | return True 25 | except (SyntaxError, TypeError): 26 | return False 27 | -------------------------------------------------------------------------------- /src/evidently/legacy/features/sentence_count_feature.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import Any 3 | from typing import ClassVar 4 | from typing import Optional 5 | 6 | import numpy as np 7 | 8 | from evidently.legacy.core import ColumnType 9 | from evidently.legacy.features.generated_features import ApplyColumnGeneratedFeature 10 | 11 | 12 | class SentenceCount(ApplyColumnGeneratedFeature): 13 | class Config: 14 | type_alias = "evidently:feature:SentenceCount" 15 | 16 | __feature_type__: ClassVar = ColumnType.Numerical 17 | _reg: ClassVar[re.Pattern] = re.compile(r"(? List[AnyMetric]: 26 | raise NotImplementedError() 27 | -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/base_metric.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from typing import Optional 3 | from typing import Type 4 | from typing import Union 5 | 6 | from evidently.legacy.base_metric import Metric 7 | from evidently.legacy.utils.generators import BaseGenerator 8 | from evidently.legacy.utils.generators import make_generator_by_columns 9 | 10 | 11 | def generate_column_metrics( 12 | metric_class: Type[Metric], 13 | columns: Optional[Union[str, list]] = None, 14 | parameters: Optional[Dict] = None, 15 | skip_id_column: bool = False, 16 | ) -> BaseGenerator[Metric]: 17 | """Function for generating metrics for columns""" 18 | return make_generator_by_columns( 19 | base_class=metric_class, 20 | columns=columns, 21 | parameters=parameters, 22 | skip_id_column=skip_id_column, 23 | ) 24 | -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/classification_performance/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/metrics/classification_performance/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/data_drift/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/metrics/data_drift/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/data_integrity/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/metrics/data_integrity/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/data_quality/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/metrics/data_quality/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/recsys/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/metrics/recsys/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/recsys/map_k.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.metrics.recsys.base_top_k import TopKMetric 2 | from evidently.legacy.metrics.recsys.base_top_k import TopKMetricRenderer 3 | from evidently.legacy.renderers.base_renderer import default_renderer 4 | 5 | 6 | class MAPKMetric(TopKMetric): 7 | class Config: 8 | type_alias = "evidently:metric:MAPKMetric" 9 | 10 | def key(self): 11 | return "map" 12 | 13 | 14 | @default_renderer(wrap_type=MAPKMetric) 15 | class MAPKMetricRenderer(TopKMetricRenderer): 16 | yaxis_name = "map@k" 17 | header = "MAP" 18 | -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/recsys/mar_k.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.metrics.recsys.base_top_k import TopKMetric 2 | from evidently.legacy.metrics.recsys.base_top_k import TopKMetricRenderer 3 | from evidently.legacy.renderers.base_renderer import default_renderer 4 | 5 | 6 | class MARKMetric(TopKMetric): 7 | class Config: 8 | type_alias = "evidently:metric:MARKMetric" 9 | 10 | def key(self): 11 | return "mar" 12 | 13 | 14 | @default_renderer(wrap_type=MARKMetric) 15 | class MARKMetricRenderer(TopKMetricRenderer): 16 | yaxis_name = "mar@k" 17 | header = "MAR" 18 | -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/recsys/precision_top_k.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.metrics.recsys.base_top_k import TopKMetric 2 | from evidently.legacy.metrics.recsys.base_top_k import TopKMetricRenderer 3 | from evidently.legacy.renderers.base_renderer import default_renderer 4 | 5 | 6 | class PrecisionTopKMetric(TopKMetric): 7 | class Config: 8 | type_alias = "evidently:metric:PrecisionTopKMetric" 9 | 10 | def key(self): 11 | return "precision" 12 | 13 | 14 | @default_renderer(wrap_type=PrecisionTopKMetric) 15 | class PrecisionTopKMetricRenderer(TopKMetricRenderer): 16 | yaxis_name = "precision@k" 17 | header = "Precision" 18 | -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/recsys/recall_top_k.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.metrics.recsys.base_top_k import TopKMetric 2 | from evidently.legacy.metrics.recsys.base_top_k import TopKMetricRenderer 3 | from evidently.legacy.renderers.base_renderer import default_renderer 4 | 5 | 6 | class RecallTopKMetric(TopKMetric): 7 | class Config: 8 | type_alias = "evidently:metric:RecallTopKMetric" 9 | 10 | def key(self): 11 | return "recall" 12 | 13 | 14 | @default_renderer(wrap_type=RecallTopKMetric) 15 | class RecallTopKMetricRenderer(TopKMetricRenderer): 16 | yaxis_name = "recall@k" 17 | header = "Recall" 18 | -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/regression_performance/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/metrics/regression_performance/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/metrics/regression_performance/utils.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.metrics.regression_performance.objects import IntervalSeries 2 | from evidently.legacy.metrics.regression_performance.objects import RegressionMetricScatter 3 | 4 | 5 | def apply_func_to_binned_data( 6 | df_for_bins, func, target_column, preds_column, is_ref_data=False 7 | ) -> RegressionMetricScatter: 8 | def _apply(x): 9 | if x.shape[0] == 0: 10 | return None 11 | return func(x[target_column], x[preds_column]) 12 | 13 | reference = None 14 | if is_ref_data: 15 | reference = IntervalSeries.from_data( 16 | df_for_bins[df_for_bins.data == "ref"].groupby("target_binned", observed=False).apply(_apply) 17 | ) 18 | 19 | result = RegressionMetricScatter( 20 | current=IntervalSeries.from_data( 21 | df_for_bins[df_for_bins.data == "curr"].groupby("target_binned", observed=False).apply(_apply) 22 | ), 23 | reference=reference, 24 | ) 25 | 26 | return result 27 | -------------------------------------------------------------------------------- /src/evidently/legacy/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/model/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/model/dashboard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | from typing import List 5 | 6 | from evidently._pydantic_compat import BaseModel 7 | from evidently.legacy.model.widget import BaseWidgetInfo 8 | 9 | 10 | class DashboardInfo(BaseModel): 11 | name: str 12 | widgets: List[BaseWidgetInfo] 13 | -------------------------------------------------------------------------------- /src/evidently/legacy/options/__init__.py: -------------------------------------------------------------------------------- 1 | from .color_scheme import BERLIN_AUTUMN_COLOR_OPTIONS 2 | from .color_scheme import KARACHI_SUNRISE_COLOR_OPTIONS 3 | from .color_scheme import NIGHTOWL_COLOR_OPTIONS 4 | from .color_scheme import SOLARIZED_COLOR_OPTIONS 5 | from .color_scheme import ColorOptions 6 | 7 | __all__ = [ 8 | "BERLIN_AUTUMN_COLOR_OPTIONS", 9 | "KARACHI_SUNRISE_COLOR_OPTIONS", 10 | "NIGHTOWL_COLOR_OPTIONS", 11 | "SOLARIZED_COLOR_OPTIONS", 12 | "ColorOptions", 13 | ] 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/options/agg_data.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from evidently.legacy.options.option import Option 4 | 5 | 6 | class RenderOptions(Option): 7 | raw_data: bool = False 8 | 9 | 10 | class DataDefinitionOptions(Option): 11 | categorical_features_cardinality: Optional[int] = None 12 | -------------------------------------------------------------------------------- /src/evidently/legacy/options/option.py: -------------------------------------------------------------------------------- 1 | from evidently.pydantic_utils import FrozenBaseModel 2 | 3 | 4 | class Option(FrozenBaseModel): 5 | pass 6 | -------------------------------------------------------------------------------- /src/evidently/legacy/pipeline/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/pipeline/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/renderers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/renderers/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/report/__init__.py: -------------------------------------------------------------------------------- 1 | from .report import Report 2 | 3 | __all__ = ["Report"] 4 | -------------------------------------------------------------------------------- /src/evidently/legacy/runner/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/runner/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/spark/__init__.py: -------------------------------------------------------------------------------- 1 | from .engine import SparkEngine 2 | from .metrics import data_drift 3 | from .metrics import feature_importance 4 | 5 | __all__ = ["SparkEngine", "data_drift", "feature_importance"] 6 | -------------------------------------------------------------------------------- /src/evidently/legacy/spark/calculations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/spark/calculations/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/spark/calculations/stattests/__init__.py: -------------------------------------------------------------------------------- 1 | from . import chisquare 2 | from . import jensenshannon 3 | from . import psi 4 | from . import wasserstein 5 | 6 | __all__ = ["chisquare", "wasserstein", "psi", "jensenshannon"] 7 | -------------------------------------------------------------------------------- /src/evidently/legacy/spark/calculations/stattests/jensenshannon.py: -------------------------------------------------------------------------------- 1 | from scipy.spatial import distance 2 | 3 | from evidently.legacy.calculations.stattests import jensenshannon_stat_test 4 | from evidently.legacy.calculations.stattests.registry import StatTestFuncReturns 5 | from evidently.legacy.core import ColumnType 6 | 7 | from .base import SparkStatTestImpl 8 | from .base import SpartStatTestData 9 | from .utils import get_binned_data 10 | 11 | 12 | class SparkJensenShannon(SparkStatTestImpl): 13 | base_stat_test = jensenshannon_stat_test 14 | 15 | def __call__(self, data: SpartStatTestData, feature_type: ColumnType, threshold: float) -> StatTestFuncReturns: 16 | cur = data.current_data 17 | ref = data.reference_data 18 | column_name = data.column_name 19 | reference_percents, current_percents = get_binned_data(ref, cur, column_name, feature_type, False) 20 | jensenshannon_value = distance.jensenshannon(reference_percents, current_percents, base=None) 21 | return jensenshannon_value, jensenshannon_value >= threshold 22 | -------------------------------------------------------------------------------- /src/evidently/legacy/spark/calculations/stattests/psi.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from evidently.legacy.calculations.stattests import psi_stat_test 4 | from evidently.legacy.calculations.stattests.registry import StatTestFuncReturns 5 | from evidently.legacy.core import ColumnType 6 | 7 | from .base import SparkStatTestImpl 8 | from .base import SpartStatTestData 9 | from .utils import get_binned_data 10 | 11 | 12 | class SparkPSI(SparkStatTestImpl): 13 | base_stat_test = psi_stat_test 14 | 15 | def __call__(self, data: SpartStatTestData, feature_type: ColumnType, threshold: float) -> StatTestFuncReturns: 16 | cur = data.current_data 17 | ref = data.reference_data 18 | column_name = data.column_name 19 | reference_percents, current_percents = get_binned_data(ref, cur, column_name, feature_type) 20 | 21 | psi_values = (reference_percents - current_percents) * np.log(reference_percents / current_percents) 22 | psi_value = np.sum(psi_values) 23 | 24 | return psi_value, psi_value >= threshold 25 | -------------------------------------------------------------------------------- /src/evidently/legacy/spark/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/spark/metrics/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/suite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/suite/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/test_preset/__init__.py: -------------------------------------------------------------------------------- 1 | """Predefined Test Presets for Test Suite""" 2 | 3 | from . import _registry 4 | from .classification_binary import BinaryClassificationTestPreset 5 | from .classification_binary_topk import BinaryClassificationTopKTestPreset 6 | from .classification_multiclass import MulticlassClassificationTestPreset 7 | from .data_drift import DataDriftTestPreset 8 | from .data_quality import DataQualityTestPreset 9 | from .data_stability import DataStabilityTestPreset 10 | from .no_target_performance import NoTargetPerformanceTestPreset 11 | from .recsys import RecsysTestPreset 12 | from .regression import RegressionTestPreset 13 | 14 | __all__ = [ 15 | "BinaryClassificationTestPreset", 16 | "BinaryClassificationTopKTestPreset", 17 | "MulticlassClassificationTestPreset", 18 | "DataDriftTestPreset", 19 | "DataQualityTestPreset", 20 | "DataStabilityTestPreset", 21 | "NoTargetPerformanceTestPreset", 22 | "RegressionTestPreset", 23 | "RecsysTestPreset", 24 | "_registry", 25 | ] 26 | -------------------------------------------------------------------------------- /src/evidently/legacy/test_preset/test_preset.py: -------------------------------------------------------------------------------- 1 | import abc 2 | from typing import Any 3 | from typing import Dict 4 | from typing import List 5 | from typing import Optional 6 | from typing import Union 7 | 8 | from evidently.legacy.base_metric import BasePreset 9 | from evidently.legacy.tests.base_test import Test 10 | from evidently.legacy.utils.data_preprocessing import DataDefinition 11 | from evidently.legacy.utils.generators import BaseGenerator 12 | 13 | AnyTest = Union[Test, BaseGenerator[Test]] 14 | 15 | 16 | class TestPreset(BasePreset): 17 | class Config: 18 | is_base_type = True 19 | 20 | @abc.abstractmethod 21 | def generate_tests( 22 | self, data_definition: DataDefinition, additional_data: Optional[Dict[str, Any]] 23 | ) -> List[AnyTest]: 24 | raise NotImplementedError 25 | -------------------------------------------------------------------------------- /src/evidently/legacy/test_suite/__init__.py: -------------------------------------------------------------------------------- 1 | from .test_suite import TestSuite 2 | 3 | __all__ = ["TestSuite"] 4 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/.gitignore -------------------------------------------------------------------------------- /src/evidently/legacy/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/ui/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/api/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/ui/api/service.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Optional 3 | 4 | from litestar import Router 5 | from litestar import get 6 | 7 | import evidently 8 | from evidently.legacy.ui.api.models import Version 9 | 10 | EVIDENTLY_APPLICATION_NAME = "Evidently UI" 11 | 12 | 13 | @get("/version") 14 | async def version() -> Version: 15 | return Version( 16 | application=EVIDENTLY_APPLICATION_NAME, 17 | version=evidently.__version__, 18 | commit=get_git_revision_short_hash(os.path.dirname(evidently.__file__)) or "-", 19 | ) 20 | 21 | 22 | def get_git_revision_short_hash(path: str) -> Optional[str]: 23 | from_env = os.environ.get("GIT_COMMIT") 24 | if from_env is not None: 25 | return from_env 26 | import subprocess 27 | 28 | try: 29 | return subprocess.check_output(["git", "rev-parse", "--short", "HEAD"], cwd=path).decode("ascii").strip() 30 | except Exception: 31 | return None 32 | 33 | 34 | def service_api(): 35 | return Router("", route_handlers=[version]) 36 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/api/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pathlib 3 | 4 | import litestar 5 | from litestar import MediaType 6 | from litestar import Response 7 | from litestar.response import File 8 | from litestar.router import Router 9 | from litestar.static_files import create_static_files_router 10 | 11 | BASE_PATH = str(pathlib.Path(__file__).parent.parent.resolve() / "assets") 12 | 13 | 14 | def assets_router(base_path: str = BASE_PATH): 15 | @litestar.get( 16 | ["/", "/projects/{path:path}"], 17 | include_in_schema=False, 18 | ) 19 | async def index() -> Response: 20 | return File( 21 | path=os.path.join(base_path, "index.html"), 22 | filename="index.html", 23 | media_type=MediaType.HTML, 24 | content_disposition_type="inline", 25 | ) 26 | 27 | return Router( 28 | path="", 29 | route_handlers=[ 30 | index, 31 | create_static_files_router("/", directories=[base_path]), 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/assets/favicon-16x16.png -------------------------------------------------------------------------------- /src/evidently/legacy/ui/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/assets/favicon-32x32.png -------------------------------------------------------------------------------- /src/evidently/legacy/ui/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/assets/favicon.ico -------------------------------------------------------------------------------- /src/evidently/legacy/ui/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | Evidently - ML Monitoring Demo 14 | 15 | 16 | 17 | 18 |
19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Evidently.AI", 3 | "name": "Evidently.AI Dashboards", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "favicon-96x96.png", 12 | "type": "image/png", 13 | "sizes": "96x96" 14 | } 15 | ], 16 | "start_url": ".", 17 | "display": "standalone", 18 | "theme_color": "#000000", 19 | "background_color": "#ffffff" 20 | } 21 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/assets/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/assets/static/css/index-CJbKDbyR.css: -------------------------------------------------------------------------------- 1 | body{margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale} 2 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/assets/static/js/snapshot-view-main-Z97zaUJO.js: -------------------------------------------------------------------------------- 1 | import{j as t,B as h,c as r,r as p,u as i}from"./index-Nx_mkSx_.js";import{D as c,C as j,S as m,J as n}from"./WidgetsContent-CSRajGBd.js";const _=({data:a,dashboardContextState:s})=>t.jsx(t.Fragment,{children:t.jsx(c.Provider,{value:j(s),children:t.jsx(h,{py:2,children:t.jsx(m,{widgets:a.widgets})})})}),C="/projects/:projectId/reports/:snapshotId",x={param:"snapshotId"},I={crumb:x},l=({params:a})=>{const{projectId:s,snapshotId:e}=a;return r.GET("/api/projects/{project_id}/{snapshot_id}/data",{params:{path:{project_id:s,snapshot_id:e}},parseAs:"text"}).then(p()).then(n)},D=()=>{const{loaderData:a,params:s}=i(),{projectId:e,snapshotId:d}=s;return t.jsx(_,{data:a,dashboardContextState:{getAdditionGraphData:o=>r.GET("/api/projects/{project_id}/{snapshot_id}/graphs_data/{graph_id}",{params:{path:{project_id:e,snapshot_id:d,graph_id:encodeURIComponent(o)}},parseAs:"text"}).then(p()).then(n),getAdditionWidgetData:o=>r.GET("/api/projects/{project_id}/{snapshot_id}/graphs_data/{graph_id}",{params:{path:{project_id:e,snapshot_id:d,graph_id:encodeURIComponent(o)}},parseAs:"text"}).then(p()).then(n)}})};export{D as Component,C as currentRoutePath,I as handle,l as loadData}; 2 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/components/__init__.py: -------------------------------------------------------------------------------- 1 | from .local_storage import DataStorageComponent 2 | from .local_storage import InMemoryDataStorage 3 | from .local_storage import MetadataStorageComponent 4 | 5 | __all__ = ["InMemoryDataStorage", "DataStorageComponent", "MetadataStorageComponent"] 6 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/dashboards/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import DashboardConfig 2 | from .base import PanelValue 3 | from .base import ReportFilter 4 | from .reports import CounterAgg 5 | from .reports import DashboardPanelCounter 6 | from .reports import DashboardPanelDistribution 7 | from .reports import DashboardPanelHistogram 8 | from .reports import DashboardPanelPlot 9 | from .reports import HistBarMode 10 | from .reports import PlotType 11 | from .test_suites import DashboardPanelTestSuite 12 | from .test_suites import DashboardPanelTestSuiteCounter 13 | from .test_suites import TestFilter 14 | from .test_suites import TestSuitePanelType 15 | 16 | __all__ = [ 17 | "DashboardPanelPlot", 18 | "DashboardConfig", 19 | "DashboardPanelTestSuite", 20 | "DashboardPanelTestSuiteCounter", 21 | "TestSuitePanelType", 22 | "TestFilter", 23 | "ReportFilter", 24 | "DashboardPanelCounter", 25 | "PanelValue", 26 | "CounterAgg", 27 | "PlotType", 28 | "HistBarMode", 29 | "DashboardPanelTestSuiteCounter", 30 | "DashboardPanelDistribution", 31 | "DashboardPanelHistogram", 32 | ] 33 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/demo_projects/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from .adult import adult_demo_project 4 | from .base import DemoProject 5 | from .bikes import bikes_demo_project 6 | from .reviews import reviews_demo_project 7 | from .simple import simple_demo_project 8 | 9 | # from .bikes_v2 import bikes_v2_demo_project 10 | # from .reviews_v2 import reviews_v2_demo_project 11 | 12 | DEMO_PROJECTS: Dict[str, DemoProject] = { 13 | "bikes": bikes_demo_project, 14 | "reviews": reviews_demo_project, 15 | "adult": adult_demo_project, 16 | } 17 | 18 | DEMO_PROJECTS_NAMES = list(DEMO_PROJECTS.keys()) 19 | 20 | __all__ = [ 21 | "DemoProject", 22 | "DEMO_PROJECTS", 23 | "simple_demo_project", 24 | "reviews_demo_project", 25 | "bikes_demo_project", 26 | "adult_demo_project", 27 | ] 28 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/managers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/managers/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/ui/remote.py: -------------------------------------------------------------------------------- 1 | """For backward compatibility with evidently <= 4.9""" 2 | 3 | import warnings 4 | 5 | from evidently.legacy.ui.workspace import RemoteWorkspace 6 | 7 | __all__ = ["RemoteWorkspace"] 8 | 9 | warnings.warn( 10 | "Importing RemoteWorkspace from evidently.legacy.ui.remote is deprecated. Please import from evidently.legacy.ui.workspace", 11 | DeprecationWarning, 12 | ) 13 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/security/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/security/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/ui/security/no_security.py: -------------------------------------------------------------------------------- 1 | from litestar import Request 2 | 3 | from ..base import User 4 | from ..components.security import NoSecurityComponent 5 | from .service import SecurityService 6 | 7 | 8 | class NoSecurityService(SecurityService): 9 | def __init__(self, security_config: NoSecurityComponent): 10 | self.security_config = security_config 11 | 12 | def authenticate(self, request: Request) -> User: 13 | return User(id=self.security_config.dummy_user_id, name="") 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/security/service.py: -------------------------------------------------------------------------------- 1 | import abc 2 | from typing import Optional 3 | 4 | from litestar import Request 5 | 6 | from evidently.legacy.ui.base import User 7 | 8 | 9 | class SecurityService: 10 | @abc.abstractmethod 11 | def authenticate(self, request: Request) -> Optional[User]: 12 | raise NotImplementedError() 13 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/security/token.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from litestar import Request 4 | 5 | from evidently.legacy.ui.components.security import TokenSecurityComponent 6 | from evidently.legacy.ui.security.service import SecurityService 7 | from evidently.legacy.ui.security.service import User 8 | from evidently.legacy.ui.type_aliases import ZERO_UUID 9 | 10 | SECRET_HEADER_NAME = "evidently-secret" 11 | 12 | 13 | default_user = User(id=ZERO_UUID, name="") 14 | 15 | 16 | class TokenSecurity(SecurityService): 17 | def __init__(self, config: TokenSecurityComponent): 18 | self.config = config 19 | 20 | def authenticate(self, request: Request) -> Optional[User]: 21 | if request.headers.get(SECRET_HEADER_NAME) == self.config.token.get_secret_value(): 22 | return default_user 23 | return None 24 | -------------------------------------------------------------------------------- /src/evidently/legacy/ui/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/storage/__init__.py -------------------------------------------------------------------------------- /src/evidently/legacy/ui/storage/config.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/legacy/ui/storage/config.py -------------------------------------------------------------------------------- /src/evidently/legacy/ui/workspace/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import WorkspaceBase 2 | from .cloud import CloudWorkspace 3 | from .remote import RemoteWorkspace 4 | from .view import Workspace 5 | 6 | __all__ = ["WorkspaceBase", "Workspace", "RemoteWorkspace", "CloudWorkspace"] 7 | -------------------------------------------------------------------------------- /src/evidently/legacy/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .numpy_encoder import NumpyEncoder 2 | 3 | __all__ = ["NumpyEncoder"] 4 | -------------------------------------------------------------------------------- /src/evidently/legacy/utils/llm/__init__.py: -------------------------------------------------------------------------------- 1 | from . import _registry 2 | 3 | __all__ = ["_registry"] 4 | -------------------------------------------------------------------------------- /src/evidently/legacy/utils/llm/_registry.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa: E501 2 | # fmt: off 3 | from evidently.legacy.utils.llm.prompts import PromptBlock 4 | from evidently.legacy.utils.llm.prompts import PromptTemplate 5 | from evidently.pydantic_utils import register_type_alias 6 | 7 | register_type_alias(PromptBlock, "evidently.legacy.utils.llm.prompts.Anchor", "evidently:prompt_block:Anchor") 8 | register_type_alias(PromptBlock, "evidently.legacy.utils.llm.prompts.JsonOutputFormatBlock", "evidently:prompt_block:JsonOutputFormatBlock") 9 | register_type_alias(PromptBlock, "evidently.legacy.utils.llm.prompts.NoopOutputFormat", "evidently:prompt_block:NoopOutputFormat") 10 | register_type_alias(PromptBlock, "evidently.legacy.utils.llm.prompts.SimpleBlock", "evidently:prompt_block:SimpleBlock") 11 | register_type_alias(PromptBlock, "evidently.legacy.utils.llm.prompts.StringFormatBlock", "evidently:prompt_block:StringFormatBlock") 12 | register_type_alias(PromptBlock, "evidently.legacy.utils.llm.prompts.StringListFormatBlock", "evidently:prompt_block:StringListFormatBlock") 13 | register_type_alias(PromptTemplate, "evidently.legacy.utils.llm.prompts.BlockPromptTemplate", "evidently:prompt_template:BlockPromptTemplate") 14 | -------------------------------------------------------------------------------- /src/evidently/legacy/utils/llm/base.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | from typing import Any 3 | from typing import Dict 4 | 5 | 6 | @dataclasses.dataclass(unsafe_hash=True, frozen=True) 7 | class LLMMessage: 8 | role: str 9 | content: str 10 | 11 | @classmethod 12 | def user(cls, message: str): 13 | return LLMMessage("user", message) 14 | 15 | @classmethod 16 | def system(cls, message: str): 17 | return LLMMessage("system", message) 18 | 19 | 20 | LLMResponse = Dict[str, Any] 21 | -------------------------------------------------------------------------------- /src/evidently/legacy/utils/llm/errors.py: -------------------------------------------------------------------------------- 1 | from evidently.errors import EvidentlyError 2 | 3 | 4 | class EvidentlyLLMError(EvidentlyError): 5 | pass 6 | 7 | 8 | class LLMResponseParseError(EvidentlyLLMError): 9 | def __init__(self, message: str, response): 10 | self.message = message 11 | self.response = response 12 | 13 | def get_message(self): 14 | return f"{self.__class__.__name__}: {self.message}" 15 | 16 | 17 | class LLMRequestError(EvidentlyLLMError): 18 | def __init__(self, message: str, original_error: Exception = None): 19 | super().__init__(message) 20 | self.original_error = original_error 21 | 22 | 23 | class LLMRateLimitError(LLMRequestError): 24 | pass 25 | -------------------------------------------------------------------------------- /src/evidently/legacy/utils/sync.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import threading 3 | from functools import wraps 4 | from typing import Awaitable 5 | from typing import Callable 6 | from typing import TypeVar 7 | 8 | _loop = asyncio.new_event_loop() 9 | 10 | _thr = threading.Thread(target=_loop.run_forever, name="Async Runner", daemon=True) 11 | 12 | TA = TypeVar("TA") 13 | 14 | 15 | def async_to_sync(awaitable: Awaitable[TA]) -> TA: 16 | try: 17 | asyncio.get_running_loop() 18 | # we are in sync context but inside a running loop 19 | if not _thr.is_alive(): 20 | _thr.start() 21 | future = asyncio.run_coroutine_threadsafe(awaitable, _loop) 22 | return future.result() 23 | except RuntimeError: 24 | pass 25 | new_loop = asyncio.new_event_loop() 26 | asyncio.set_event_loop(new_loop) 27 | try: 28 | return new_loop.run_until_complete(awaitable) 29 | finally: 30 | new_loop.close() 31 | 32 | 33 | def sync_api(f: Callable[..., Awaitable[TA]]) -> Callable[..., TA]: 34 | @wraps(f) 35 | def sync_call(*args, **kwargs): 36 | return async_to_sync(f(*args, **kwargs)) 37 | 38 | return sync_call 39 | -------------------------------------------------------------------------------- /src/evidently/llm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/llm/__init__.py -------------------------------------------------------------------------------- /src/evidently/llm/options.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.utils.llm.wrapper import AnthropicOptions 2 | from evidently.legacy.utils.llm.wrapper import DeepSeekOptions 3 | from evidently.legacy.utils.llm.wrapper import GeminiOptions 4 | from evidently.legacy.utils.llm.wrapper import LLMOptions 5 | from evidently.legacy.utils.llm.wrapper import MistralOptions 6 | from evidently.legacy.utils.llm.wrapper import OllamaOptions 7 | from evidently.legacy.utils.llm.wrapper import OpenAIOptions 8 | from evidently.legacy.utils.llm.wrapper import VertexAIOptions 9 | 10 | __all__ = [ 11 | "AnthropicOptions", 12 | "LLMOptions", 13 | "OpenAIOptions", 14 | "GeminiOptions", 15 | "DeepSeekOptions", 16 | "VertexAIOptions", 17 | "MistralOptions", 18 | "OllamaOptions", 19 | ] 20 | -------------------------------------------------------------------------------- /src/evidently/llm/templates.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.descriptors.llm_judges import BinaryClassificationPromptTemplate 2 | from evidently.legacy.descriptors.llm_judges import MulticlassClassificationPromptTemplate 3 | 4 | __all__ = ["BinaryClassificationPromptTemplate", "MulticlassClassificationPromptTemplate"] 5 | -------------------------------------------------------------------------------- /src/evidently/nbextension/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | def _jupyter_nbextension_paths(): 6 | return [{"section": "notebook", "src": "nbextension/static", "dest": "evidently", "require": "evidently/extension"}] 7 | -------------------------------------------------------------------------------- /src/evidently/nbextension/static/extension.js: -------------------------------------------------------------------------------- 1 | // Entry point for the notebook bundle containing custom model definitions. 2 | // 3 | define(function() { 4 | "use strict"; 5 | 6 | window['requirejs'].config({ 7 | map: { 8 | '*': { 9 | 'evidently': 'nbextensions/evidently/index', 10 | }, 11 | } 12 | }); 13 | // Export the required load_ipython_extension function 14 | return { 15 | load_ipython_extension : function() {} 16 | }; 17 | }); 18 | -------------------------------------------------------------------------------- /src/evidently/nbextension/static/material-ui-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/nbextension/static/material-ui-icons.woff2 -------------------------------------------------------------------------------- /src/evidently/presets/__init__.py: -------------------------------------------------------------------------------- 1 | from .classification import ClassificationDummyQuality 2 | from .classification import ClassificationPreset 3 | from .classification import ClassificationQuality 4 | from .classification import ClassificationQualityByLabel 5 | from .dataset_stats import DatasetStats 6 | from .dataset_stats import DataSummaryPreset 7 | from .dataset_stats import TextEvals 8 | from .dataset_stats import ValueStats 9 | from .drift import DataDriftPreset 10 | from .regression import RegressionDummyQuality 11 | from .regression import RegressionPreset 12 | from .regression import RegressionQuality 13 | 14 | __all__ = [ 15 | "ClassificationDummyQuality", 16 | "ClassificationPreset", 17 | "ClassificationQuality", 18 | "ClassificationQualityByLabel", 19 | "ValueStats", 20 | "TextEvals", 21 | "DatasetStats", 22 | "DataSummaryPreset", 23 | "RegressionDummyQuality", 24 | "RegressionQuality", 25 | "RegressionPreset", 26 | "DataDriftPreset", 27 | ] 28 | -------------------------------------------------------------------------------- /src/evidently/sdk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/sdk/__init__.py -------------------------------------------------------------------------------- /src/evidently/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from .aliases import eq 2 | from .aliases import gt 3 | from .aliases import gte 4 | from .aliases import is_in 5 | from .aliases import lt 6 | from .aliases import lte 7 | from .aliases import not_eq 8 | from .aliases import not_in 9 | from .reference import Reference 10 | 11 | __all__ = [ 12 | "eq", 13 | "not_eq", 14 | "gt", 15 | "gte", 16 | "is_in", 17 | "not_in", 18 | "lt", 19 | "lte", 20 | "Reference", 21 | ] 22 | -------------------------------------------------------------------------------- /src/evidently/tests/reference.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from evidently._pydantic_compat import BaseModel 4 | 5 | 6 | class Reference(BaseModel): 7 | relative: Optional[float] = None 8 | absolute: Optional[float] = None 9 | 10 | def __hash__(self) -> int: 11 | return hash(self.relative) + hash(self.absolute) 12 | -------------------------------------------------------------------------------- /src/evidently/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/service/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/.gitignore -------------------------------------------------------------------------------- /src/evidently/ui/service/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/service/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/api/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/service/api/service.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Optional 3 | 4 | from litestar import Router 5 | from litestar import get 6 | 7 | import evidently 8 | from evidently.legacy.ui.api.models import Version 9 | 10 | EVIDENTLY_APPLICATION_NAME = "Evidently UI" 11 | 12 | 13 | @get("/version") 14 | async def version() -> Version: 15 | return Version( 16 | application=EVIDENTLY_APPLICATION_NAME, 17 | version=evidently.__version__, 18 | commit=get_git_revision_short_hash(os.path.dirname(evidently.__file__)) or "-", 19 | ) 20 | 21 | 22 | def get_git_revision_short_hash(path: str) -> Optional[str]: 23 | from_env = os.environ.get("GIT_COMMIT") 24 | if from_env is not None: 25 | return from_env 26 | import subprocess 27 | 28 | try: 29 | return subprocess.check_output(["git", "rev-parse", "--short", "HEAD"], cwd=path).decode("ascii").strip() 30 | except Exception: 31 | return None 32 | 33 | 34 | def service_api(): 35 | return Router("", route_handlers=[version]) 36 | -------------------------------------------------------------------------------- /src/evidently/ui/service/api/static.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pathlib 3 | 4 | import litestar 5 | from litestar import MediaType 6 | from litestar import Response 7 | from litestar.response import File 8 | from litestar.router import Router 9 | from litestar.static_files import create_static_files_router 10 | 11 | BASE_PATH = str(pathlib.Path(__file__).parent.parent.resolve() / "assets") 12 | 13 | 14 | def assets_router(base_path: str = BASE_PATH): 15 | @litestar.get( 16 | ["/", "/projects/{path:path}"], 17 | include_in_schema=False, 18 | ) 19 | async def index() -> Response: 20 | return File( 21 | path=os.path.join(base_path, "index.html"), 22 | filename="index.html", 23 | media_type=MediaType.HTML, 24 | content_disposition_type="inline", 25 | ) 26 | 27 | return Router( 28 | path="", 29 | route_handlers=[ 30 | index, 31 | create_static_files_router("/", directories=[base_path]), 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /src/evidently/ui/service/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/assets/favicon-16x16.png -------------------------------------------------------------------------------- /src/evidently/ui/service/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/assets/favicon-32x32.png -------------------------------------------------------------------------------- /src/evidently/ui/service/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/assets/favicon.ico -------------------------------------------------------------------------------- /src/evidently/ui/service/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | Evidently - ML Monitoring Demo 14 | 15 | 16 | 17 | 18 |
19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/evidently/ui/service/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Evidently.AI", 3 | "name": "Evidently.AI Dashboards", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "favicon-96x96.png", 12 | "type": "image/png", 13 | "sizes": "96x96" 14 | } 15 | ], 16 | "start_url": ".", 17 | "display": "standalone", 18 | "theme_color": "#000000", 19 | "background_color": "#ffffff" 20 | } 21 | -------------------------------------------------------------------------------- /src/evidently/ui/service/assets/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/evidently/ui/service/assets/static/css/index-CJbKDbyR.css: -------------------------------------------------------------------------------- 1 | body{margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale} 2 | -------------------------------------------------------------------------------- /src/evidently/ui/service/components/__init__.py: -------------------------------------------------------------------------------- 1 | from .local_storage import DataStorageComponent 2 | from .local_storage import InMemoryDataStorage 3 | from .local_storage import MetadataStorageComponent 4 | 5 | __all__ = ["InMemoryDataStorage", "DataStorageComponent", "MetadataStorageComponent"] 6 | -------------------------------------------------------------------------------- /src/evidently/ui/service/components/dashboard.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from litestar.di import Provide 4 | 5 | from evidently.ui.service.components.base import Component 6 | from evidently.ui.service.components.base import ComponentContext 7 | from evidently.ui.service.services.dashbord.file import JsonFileDashboardManager 8 | 9 | 10 | class DashboardComponent(Component): 11 | __service_name__ = "dashboards" 12 | storage_type = "file" 13 | path: str = "workspace" 14 | 15 | def provider(self): 16 | if self.storage_type == "file": 17 | 18 | async def factory(): 19 | return JsonFileDashboardManager(self.path) 20 | 21 | return factory 22 | raise ValueError("Unknown storage type {}".format(self.storage_type)) 23 | 24 | def get_dependencies(self, ctx: ComponentContext) -> Dict[str, Provide]: 25 | return { 26 | "dashboard_manager": Provide(self.provider()), 27 | } 28 | -------------------------------------------------------------------------------- /src/evidently/ui/service/demo_projects/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from .base import DemoProject 4 | from .bikes import bikes_demo_project 5 | 6 | DEMO_PROJECTS: Dict[str, DemoProject] = { 7 | "bikes": bikes_demo_project, 8 | } 9 | 10 | DEMO_PROJECTS_NAMES = list(DEMO_PROJECTS.keys()) 11 | 12 | __all__ = [ 13 | "DemoProject", 14 | "DEMO_PROJECTS", 15 | "bikes_demo_project", 16 | ] 17 | -------------------------------------------------------------------------------- /src/evidently/ui/service/managers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/managers/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/service/security/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/security/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/service/security/no_security.py: -------------------------------------------------------------------------------- 1 | from litestar import Request 2 | 3 | from ..base import User 4 | from ..components.security import NoSecurityComponent 5 | from .service import SecurityService 6 | 7 | 8 | class NoSecurityService(SecurityService): 9 | def __init__(self, security_config: NoSecurityComponent): 10 | self.security_config = security_config 11 | 12 | def authenticate(self, request: Request) -> User: 13 | return User(id=self.security_config.dummy_user_id, name="") 14 | -------------------------------------------------------------------------------- /src/evidently/ui/service/security/service.py: -------------------------------------------------------------------------------- 1 | import abc 2 | from typing import Optional 3 | 4 | from litestar import Request 5 | 6 | from evidently.ui.service.base import User 7 | 8 | 9 | class SecurityService: 10 | @abc.abstractmethod 11 | def authenticate(self, request: Request) -> Optional[User]: 12 | raise NotImplementedError() 13 | -------------------------------------------------------------------------------- /src/evidently/ui/service/security/token.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from litestar import Request 4 | 5 | from evidently.ui.service.components.security import TokenSecurityComponent 6 | from evidently.ui.service.security.service import SecurityService 7 | from evidently.ui.service.security.service import User 8 | from evidently.ui.service.type_aliases import ZERO_UUID 9 | 10 | SECRET_HEADER_NAME = "evidently-secret" 11 | 12 | 13 | default_user = User(id=ZERO_UUID, name="") 14 | 15 | 16 | class TokenSecurity(SecurityService): 17 | def __init__(self, config: TokenSecurityComponent): 18 | self.config = config 19 | 20 | def authenticate(self, request: Request) -> Optional[User]: 21 | if request.headers.get(SECRET_HEADER_NAME) == self.config.token.get_secret_value(): 22 | return default_user 23 | return None 24 | -------------------------------------------------------------------------------- /src/evidently/ui/service/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/services/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/service/services/dashbord/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/services/dashbord/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/service/services/dashbord/base.py: -------------------------------------------------------------------------------- 1 | import abc 2 | 3 | from evidently.sdk.models import DashboardModel 4 | from evidently.ui.service.type_aliases import ProjectID 5 | 6 | 7 | class DashboardManager: 8 | @abc.abstractmethod 9 | async def get_dashboard(self, project_id: ProjectID) -> DashboardModel: 10 | raise NotImplementedError 11 | 12 | @abc.abstractmethod 13 | async def save_dashboard(self, project_id: ProjectID, dashboard: DashboardModel) -> None: 14 | raise NotImplementedError 15 | -------------------------------------------------------------------------------- /src/evidently/ui/service/services/dashbord/file.py: -------------------------------------------------------------------------------- 1 | import typing 2 | from typing import Optional 3 | 4 | from evidently.sdk.models import DashboardModel 5 | from evidently.ui.service.services.dashbord.base import DashboardManager 6 | from evidently.ui.service.type_aliases import ProjectID 7 | 8 | if typing.TYPE_CHECKING: 9 | from evidently.ui.service.storage.local import LocalState 10 | 11 | 12 | class JsonFileDashboardManager(DashboardManager): 13 | def __init__(self, path: str, local_state: Optional["LocalState"] = None): 14 | from evidently.ui.service.storage.local import LocalState 15 | 16 | self._path = path 17 | self._state = local_state or LocalState(path, None) 18 | 19 | async def get_dashboard(self, project_id: ProjectID) -> DashboardModel: 20 | return self._state.read_dashboard(project_id) 21 | 22 | async def save_dashboard(self, project_id: ProjectID, dashboard: DashboardModel) -> None: 23 | return self._state.write_dashboard(project_id, dashboard) 24 | -------------------------------------------------------------------------------- /src/evidently/ui/service/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/storage/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/service/storage/config.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/service/storage/config.py -------------------------------------------------------------------------------- /src/evidently/ui/service/workspace/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import WorkspaceBase 2 | from .view import Workspace 3 | 4 | __all__ = ["WorkspaceBase", "Workspace"] 5 | -------------------------------------------------------------------------------- /src/evidently/ui/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/storage/__init__.py -------------------------------------------------------------------------------- /src/evidently/ui/storage/local/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/ui/storage/local/__init__.py -------------------------------------------------------------------------------- /src/evidently/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/utils/__init__.py -------------------------------------------------------------------------------- /src/evidently/utils/llm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/src/evidently/utils/llm/__init__.py -------------------------------------------------------------------------------- /src/evidently/utils/llm/wrapper.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.utils.llm.wrapper import * # noqa: F403 2 | -------------------------------------------------------------------------------- /test_data/adults.CITATION: -------------------------------------------------------------------------------- 1 | Title: Adult 2 | Description: Predict whether annual income of an individual exceeds $50K/yr based on census data. Also known as "Census Income" dataset. 3 | Dataset source: https://archive.ics.uci.edu/dataset/2/adult 4 | Creators: Barry Becker, Ronny Kohavi 5 | Licence: CC BY 4.0 6 | -------------------------------------------------------------------------------- /test_data/adults.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/test_data/adults.parquet -------------------------------------------------------------------------------- /test_data/reviews.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/test_data/reviews.parquet -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/__init__.py -------------------------------------------------------------------------------- /tests/calculation_engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/calculation_engine/__init__.py -------------------------------------------------------------------------------- /tests/calculations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/calculations/__init__.py -------------------------------------------------------------------------------- /tests/calculations/stattests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/calculations/stattests/__init__.py -------------------------------------------------------------------------------- /tests/calculations/test_recommender_systems.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/calculations/test_recommender_systems.py -------------------------------------------------------------------------------- /tests/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/cli/__init__.py -------------------------------------------------------------------------------- /tests/cli/test_ui.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | import pytest 4 | 5 | from evidently.cli import app 6 | from evidently.ui.service.demo_projects import DEMO_PROJECTS_NAMES 7 | 8 | 9 | @pytest.fixture() 10 | def ui_command(): 11 | command = [c for c in app.registered_commands if c.name == "ui"][0] 12 | argspec = inspect.getfullargspec(command.callback) 13 | return dict(zip(argspec.annotations.keys(), argspec.defaults)) 14 | 15 | 16 | @pytest.mark.parametrize("demo_project", DEMO_PROJECTS_NAMES) 17 | def test_all_demo_projects_in_help(demo_project, ui_command): 18 | assert demo_project in ui_command["demo_projects"].help 19 | -------------------------------------------------------------------------------- /tests/collector/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/collector/__init__.py -------------------------------------------------------------------------------- /tests/dataset_generator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/dataset_generator/__init__.py -------------------------------------------------------------------------------- /tests/features/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/features/__init__.py -------------------------------------------------------------------------------- /tests/features/test_OOV_words_percentage_feature.py: -------------------------------------------------------------------------------- 1 | import nltk 2 | import pandas as pd 3 | 4 | from evidently.legacy.features.OOV_words_percentage_feature import OOVWordsPercentage 5 | from evidently.legacy.pipeline.column_mapping import ColumnMapping 6 | from evidently.legacy.utils.data_preprocessing import create_data_definition 7 | 8 | nltk.download("words") 9 | nltk.download("wordnet") 10 | nltk.download("omw-1.4") 11 | 12 | 13 | def test_oov_words_percentage(): 14 | feature_generator = OOVWordsPercentage("column_1", ignore_words=("foobar",)) 15 | data = pd.DataFrame(dict(column_1=["Who ate apples? Go iaehb!", "Who ate apples? Go foobar! ", "the"])) 16 | result = feature_generator.generate_feature( 17 | data=data, 18 | data_definition=create_data_definition(None, data, ColumnMapping()), 19 | ) 20 | assert result.equals(pd.DataFrame(dict(column_1=[20.0, 0, 0]))) 21 | -------------------------------------------------------------------------------- /tests/features/test_custom_feature.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.legacy.features.custom_feature import CustomSingleColumnFeature 4 | from evidently.legacy.pipeline.column_mapping import ColumnMapping 5 | from evidently.legacy.utils.data_preprocessing import create_data_definition 6 | 7 | 8 | def test_custom_feature(): 9 | def add_two(data: pd.Series) -> pd.Series: 10 | return data + 2 11 | 12 | feature_generator = CustomSingleColumnFeature(column_name="column_1", display_name="cl", func=add_two, name="cf") 13 | data = pd.DataFrame(dict(column_1=[1, 2, 3])) 14 | result = feature_generator.generate_feature( 15 | data=data, 16 | data_definition=create_data_definition(None, data, ColumnMapping()), 17 | ) 18 | 19 | pd.testing.assert_frame_equal(result, pd.DataFrame(dict(cf=[3, 4, 5]))) 20 | assert feature_generator.as_column().display_name == "cl" 21 | -------------------------------------------------------------------------------- /tests/features/test_exact_feature.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import pytest 3 | 4 | from evidently.legacy.features.exact_match_feature import ExactMatchFeature 5 | from evidently.legacy.pipeline.column_mapping import ColumnMapping 6 | from evidently.legacy.utils.data_preprocessing import create_data_definition 7 | 8 | 9 | @pytest.mark.parametrize( 10 | ("value1", "value2", "expected"), 11 | [ 12 | ("this is same", "this is same", True), 13 | ("this is same", "this is different", False), 14 | ], 15 | ) 16 | def test_exact_match_feature(value1: str, value2: str, expected: bool): 17 | feature_generator = ExactMatchFeature(columns=["column_1", "column_2"]) 18 | data = pd.DataFrame(dict(column_1=[value1], column_2=[value2])) 19 | result = feature_generator.generate_feature( 20 | data=data, data_definition=create_data_definition(None, data, ColumnMapping()) 21 | ) 22 | expected_df = pd.DataFrame([[expected]], columns=["column_1|column_2"]) 23 | pd.testing.assert_frame_equal(result, expected_df) 24 | -------------------------------------------------------------------------------- /tests/features/test_is_valid_json_feature.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import pytest 3 | 4 | from evidently.legacy.features.is_valid_json_feature import IsValidJSON 5 | from evidently.legacy.pipeline.column_mapping import ColumnMapping 6 | from evidently.legacy.utils.data_preprocessing import create_data_definition 7 | 8 | 9 | @pytest.mark.parametrize( 10 | ("item", "expected"), 11 | [ 12 | ('{"test": "abc"}', True), 13 | ("not json", False), 14 | ], 15 | ) 16 | def test_is_valid_json_feature(item: str, expected: bool): 17 | feature_generator = IsValidJSON("column_1") 18 | data = pd.DataFrame(dict(column_1=[item])) 19 | result = feature_generator.generate_feature( 20 | data=data, 21 | data_definition=create_data_definition(None, data, ColumnMapping()), 22 | ) 23 | assert result.equals(pd.DataFrame(dict(column_1=[expected]))) 24 | -------------------------------------------------------------------------------- /tests/features/test_non_letter_character_percentage_feature.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.legacy.features.non_letter_character_percentage_feature import NonLetterCharacterPercentage 4 | from evidently.legacy.pipeline.column_mapping import ColumnMapping 5 | from evidently.legacy.utils.data_preprocessing import create_data_definition 6 | 7 | 8 | def test_non_letter_character_percentage(): 9 | feature_generator = NonLetterCharacterPercentage(column_name="column_1", display_name="cl") 10 | data = pd.DataFrame(dict(column_1=["2Ad <4", "abc ", "144&&?1"])) 11 | result = feature_generator.generate_feature( 12 | data=data, 13 | data_definition=create_data_definition(None, data, ColumnMapping()), 14 | ) 15 | 16 | assert result.equals(pd.DataFrame(dict(column_1=[100 * 3 / 6, 0, 100]))) 17 | assert feature_generator.as_column().display_name == "cl" 18 | -------------------------------------------------------------------------------- /tests/features/test_text_length_feature.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.legacy.features.text_length_feature import TextLength 4 | from evidently.legacy.pipeline.column_mapping import ColumnMapping 5 | from evidently.legacy.utils.data_preprocessing import create_data_definition 6 | 7 | 8 | def test_text_length_feature(): 9 | feature_generator = TextLength("column_1") 10 | data = pd.DataFrame(dict(column_1=["abcdefg", "abc", "a"])) 11 | result = feature_generator.generate_feature( 12 | data=data, 13 | data_definition=create_data_definition(None, data, ColumnMapping()), 14 | ) 15 | assert result.equals(pd.DataFrame(dict(column_1=[7, 3, 1]))) 16 | -------------------------------------------------------------------------------- /tests/features/test_trigger_words_present_feature.py: -------------------------------------------------------------------------------- 1 | import nltk 2 | import pandas as pd 3 | 4 | from evidently.legacy.features.trigger_words_presence_feature import TriggerWordsPresent 5 | from evidently.legacy.pipeline.column_mapping import ColumnMapping 6 | from evidently.legacy.utils.data_preprocessing import create_data_definition 7 | 8 | nltk.download("words") 9 | nltk.download("wordnet") 10 | nltk.download("omw-1.4") 11 | 12 | 13 | def test_trigger_words_present_feature(): 14 | feature_generator = TriggerWordsPresent("column_1", words_list=("apple",)) 15 | data = pd.DataFrame(dict(column_1=["Who are you and where are my apples?", "abc ", "a"])) 16 | result = feature_generator.generate_feature( 17 | data=data, 18 | data_definition=create_data_definition(None, data, ColumnMapping()), 19 | ) 20 | assert result.equals(pd.DataFrame(dict(column_1_apple_True=[1, 0, 0]))) 21 | -------------------------------------------------------------------------------- /tests/future/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/future/__init__.py -------------------------------------------------------------------------------- /tests/future/descriptors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/future/descriptors/__init__.py -------------------------------------------------------------------------------- /tests/future/generators/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/future/generators/__init__.py -------------------------------------------------------------------------------- /tests/future/generators/test_generator.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.core.report import Report 4 | from evidently.generators import ColumnMetricGenerator 5 | from evidently.presets import ValueStats 6 | 7 | 8 | def test_generator_renders(): 9 | generator = ColumnMetricGenerator(ValueStats, columns=["a", "b"]) 10 | report = Report([generator]) 11 | snapshot = report.run(pd.DataFrame(data={"a": [1, 2, 3, 4], "b": [1, 2, 3, 4]})) 12 | assert len(snapshot._widgets) == 2 13 | -------------------------------------------------------------------------------- /tests/future/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/future/metrics/__init__.py -------------------------------------------------------------------------------- /tests/future/metrics/category_count.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import pytest 3 | 4 | from evidently.core.datasets import Dataset 5 | from evidently.core.metric_types import CountValue 6 | from evidently.core.report import Report 7 | from evidently.metrics import CategoryCount 8 | 9 | 10 | @pytest.mark.parametrize( 11 | "data,category,expected_count", 12 | [ 13 | (["a", "a", "a"], "a", 3), 14 | (["a", "a", "a"], "b", 0), 15 | ([True, True, True], True, 3), 16 | ([True, True, True], False, 0), 17 | ([True, True, None], True, 2), 18 | ([True, True, None], False, 0), 19 | ([False, False, None], False, 2), 20 | ], 21 | ) 22 | def test_category_count_metric(data, category, expected_count): 23 | dataset = Dataset.from_pandas(pd.DataFrame(data=dict(column=data))) 24 | metric = CategoryCount("column", category=category) 25 | report = Report([metric]) 26 | snapshot = report.run(dataset, None) 27 | metric_result = snapshot._context.get_metric_result(metric.metric_id) 28 | assert isinstance(metric_result, CountValue) 29 | assert metric_result.count == expected_count 30 | -------------------------------------------------------------------------------- /tests/future/metrics/test_in_range_metric.py: -------------------------------------------------------------------------------- 1 | from evidently.metrics import InRangeValueCount 2 | 3 | 4 | def test_in_range_metric(): 5 | metric = InRangeValueCount(column="a", left=0.5, right=1) 6 | assert metric.left == 0.5 7 | assert metric.right == 1 8 | -------------------------------------------------------------------------------- /tests/future/metrics/test_min_metric.py: -------------------------------------------------------------------------------- 1 | def test_min_metric(): 2 | assert True 3 | -------------------------------------------------------------------------------- /tests/future/metrics/test_missing_count.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import pytest 4 | 5 | from evidently.core.datasets import Dataset 6 | from evidently.core.metric_types import CountValue 7 | from evidently.core.report import Report 8 | from evidently.metrics import DatasetMissingValueCount 9 | 10 | 11 | @pytest.mark.parametrize( 12 | "data,metric,result", 13 | [(pd.DataFrame({"a": [1, 2, np.nan, np.nan]}), DatasetMissingValueCount(), {"count": 2, "share": 0.5})], 14 | ) 15 | def test_missing_count(data, metric, result): 16 | dataset = Dataset.from_pandas(data) 17 | report = Report([metric]) 18 | run = report.run(dataset, None) 19 | res = run._context.get_metric_result(metric) 20 | assert isinstance(res, CountValue) 21 | assert res.count.value == result["count"] 22 | assert res.share.value == result["share"] 23 | -------------------------------------------------------------------------------- /tests/future/metrics/test_out_range_metric.py: -------------------------------------------------------------------------------- 1 | from evidently.metrics import OutRangeValueCount 2 | 3 | 4 | def test_out_range_metric(): 5 | metric = OutRangeValueCount(column="a", left=0.5, right=1) 6 | assert metric.left == 0.5 7 | assert metric.right == 1 8 | -------------------------------------------------------------------------------- /tests/future/presets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/future/presets/__init__.py -------------------------------------------------------------------------------- /tests/future/report/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/future/report/__init__.py -------------------------------------------------------------------------------- /tests/future/test_ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/future/test_ui/__init__.py -------------------------------------------------------------------------------- /tests/future/test_ui/test_workspace/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/future/test_ui/test_workspace/__init__.py -------------------------------------------------------------------------------- /tests/metric_preset/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/metric_preset/__init__.py -------------------------------------------------------------------------------- /tests/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/metrics/__init__.py -------------------------------------------------------------------------------- /tests/metrics/data_drift/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/metrics/data_drift/__init__.py -------------------------------------------------------------------------------- /tests/metrics/data_interity/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/metrics/data_interity/__init__.py -------------------------------------------------------------------------------- /tests/metrics/data_quality/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/metrics/data_quality/__init__.py -------------------------------------------------------------------------------- /tests/metrics/recsys/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/metrics/recsys/__init__.py -------------------------------------------------------------------------------- /tests/metrics/recsys/test_scores_distribution.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | 4 | from evidently.legacy.metrics import ScoreDistribution 5 | from evidently.legacy.pipeline.column_mapping import ColumnMapping 6 | from evidently.legacy.report import Report 7 | 8 | 9 | def test_score_distribution(): 10 | current = pd.DataFrame( 11 | data=dict( 12 | user_id=["a", "a", "a", "b", "b", "b", "c", "c", "c"], 13 | prediction=[1.25, 1.0, 0.3, 0.9, 0.8, 0.7, 1.0, 0.5, 0.3], 14 | target=[1, 0, 0, 0, 0, 0, 0, 0, 1], 15 | ), 16 | ) 17 | 18 | metric = ScoreDistribution(k=3) 19 | report = Report(metrics=[metric]) 20 | column_mapping = ColumnMapping() 21 | report.run(reference_data=None, current_data=current, column_mapping=column_mapping) 22 | 23 | results = metric.get_result() 24 | assert np.isclose(results.current_entropy, 2.15148438) 25 | -------------------------------------------------------------------------------- /tests/metrics/regression_performance/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/metrics/regression_performance/__init__.py -------------------------------------------------------------------------------- /tests/multitest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/multitest/__init__.py -------------------------------------------------------------------------------- /tests/multitest/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/multitest/metrics/__init__.py -------------------------------------------------------------------------------- /tests/multitest/metrics/custom.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.legacy.base_metric import InputData 4 | from evidently.legacy.metrics.custom_metric import CustomValueMetric 5 | from tests.multitest.conftest import AssertResultFields 6 | from tests.multitest.datasets import TestDataset 7 | from tests.multitest.metrics.conftest import TestMetric 8 | from tests.multitest.metrics.conftest import metric 9 | 10 | 11 | def custom_func(data: InputData) -> float: 12 | return 0.3 13 | 14 | 15 | @metric 16 | def custom_callable_metric(): 17 | reference_data = current_data = pd.DataFrame({"text": [1, 2, 3]}) 18 | 19 | return TestMetric( 20 | name="custom_callable_metric", 21 | metric=CustomValueMetric(func=custom_func, title="aaa"), 22 | fingerprint="bf2e25a384e9d1ad621d73862c661a95", 23 | outcomes=AssertResultFields({"value": 0.3}), 24 | datasets=[ 25 | TestDataset("custom_callable_metric_data", current=current_data, reference=reference_data, tags=[]), 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /tests/options/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/options/__init__.py -------------------------------------------------------------------------------- /tests/options/test_base.py: -------------------------------------------------------------------------------- 1 | from evidently.legacy.options import ColorOptions 2 | from evidently.legacy.options.base import Options 3 | from evidently.legacy.options.color_scheme import GREY 4 | from evidently.legacy.options.option import Option 5 | 6 | 7 | def test_options_creation(): 8 | opt = ColorOptions(primary_color=GREY) 9 | 10 | obj = Options.from_any_options([opt]) 11 | assert obj.color_options == opt 12 | 13 | obj = Options.from_any_options({"color": opt}) 14 | assert obj.color_options == opt 15 | 16 | 17 | class CustomOption(Option): 18 | field: int = 10 19 | 20 | 21 | def test_custom_option(): 22 | option = CustomOption(field=10) 23 | 24 | obj = Options.from_any_options([option]) 25 | assert obj.get(CustomOption) == option 26 | 27 | obj = Options.from_any_options({"custom": {CustomOption: option}}) 28 | assert obj.get(CustomOption) == option 29 | -------------------------------------------------------------------------------- /tests/report/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/report/__init__.py -------------------------------------------------------------------------------- /tests/spark/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/spark/__init__.py -------------------------------------------------------------------------------- /tests/spark/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/spark/metrics/__init__.py -------------------------------------------------------------------------------- /tests/stattests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/stattests/__init__.py -------------------------------------------------------------------------------- /tests/test_preset/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/test_preset/__init__.py -------------------------------------------------------------------------------- /tests/test_preset/test_classification_multiclass.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.legacy.test_preset import MulticlassClassificationTestPreset 4 | from evidently.legacy.test_suite import TestSuite 5 | 6 | 7 | def test_no_target_performance_preset(): 8 | test_current_dataset = pd.DataFrame( 9 | { 10 | "category_feature": ["y", "y", "n", "p"], 11 | "numerical_feature": [0.4, -12, 0, 234], 12 | "target": [1, 1, 0, 1], 13 | "prediction": [0, 0, 1, 0], 14 | } 15 | ) 16 | test_reference_dataset = pd.DataFrame( 17 | { 18 | "category_feature": ["y", "n", "n", "y"], 19 | "numerical_feature": [0, 1, 2, 5], 20 | "target": [0, 0, 0, 1], 21 | "prediction": [0, 0, 0, 1], 22 | } 23 | ) 24 | data_quality_suite = TestSuite( 25 | tests=[ 26 | MulticlassClassificationTestPreset(stattest="psi"), 27 | ] 28 | ) 29 | 30 | data_quality_suite.run(current_data=test_current_dataset, reference_data=test_reference_dataset) 31 | assert not data_quality_suite 32 | assert len(data_quality_suite.as_dict()["tests"]) == 8 33 | -------------------------------------------------------------------------------- /tests/test_preset/test_data_quality.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.legacy.test_preset import DataQualityTestPreset 4 | from evidently.legacy.test_suite import TestSuite 5 | 6 | 7 | def test_data_quality_preset(): 8 | test_current_dataset = pd.DataFrame( 9 | { 10 | "category_feature": ["n", "y", "n", "p"], 11 | "numerical_feature": [0.4, -12, None, 234], 12 | "target": [1, 1, None, 1], 13 | "prediction": [0, 0, None, 0], 14 | } 15 | ) 16 | test_reference_dataset = pd.DataFrame( 17 | { 18 | "category_feature": ["y", "n", "n", "y"], 19 | "numerical_feature": [0, 1, 2, 5], 20 | "target": [0, 0, 0, 1], 21 | "prediction": [0, 0, 0, 1], 22 | } 23 | ) 24 | data_quality_suite = TestSuite( 25 | tests=[ 26 | DataQualityTestPreset(), 27 | ] 28 | ) 29 | 30 | data_quality_suite.run(current_data=test_current_dataset, reference_data=test_reference_dataset) 31 | assert not data_quality_suite 32 | assert len(data_quality_suite.as_dict()["tests"]) == 11 33 | -------------------------------------------------------------------------------- /tests/test_preset/test_data_stability.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.legacy.test_preset import DataStabilityTestPreset 4 | from evidently.legacy.test_suite import TestSuite 5 | 6 | 7 | def test_data_stability_preset(): 8 | test_current_dataset = pd.DataFrame( 9 | { 10 | "category_feature": ["t", "e", "", ""], 11 | "numerical_feature": [0.4, -12, None, 234], 12 | "target": [1, 1, None, 1], 13 | "prediction": [0, 0, None, 0], 14 | } 15 | ) 16 | test_reference_dataset = pd.DataFrame( 17 | { 18 | "category_feature": ["y", "n", "n", "y"], 19 | "numerical_feature": [0, 1, 2, 5], 20 | "target": [0, 0, 0, 1], 21 | "prediction": [0, 0, 0, 1], 22 | } 23 | ) 24 | data_quality_suite = TestSuite( 25 | tests=[ 26 | DataStabilityTestPreset(), 27 | ] 28 | ) 29 | 30 | data_quality_suite.run(current_data=test_current_dataset, reference_data=test_reference_dataset) 31 | assert not data_quality_suite 32 | assert len(data_quality_suite.as_dict()["tests"]) == 10 33 | -------------------------------------------------------------------------------- /tests/test_preset/test_no_target_performance.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from evidently.legacy.test_preset import NoTargetPerformanceTestPreset 4 | from evidently.legacy.test_suite import TestSuite 5 | 6 | 7 | def test_no_target_performance_preset(): 8 | test_current_dataset = pd.DataFrame( 9 | { 10 | "category_feature": ["y", "y", "n", "p"], 11 | "numerical_feature": [0.4, -12, 0, 234], 12 | "target": [1, 1, 0, 1], 13 | "prediction": [0, 0, 1, 0], 14 | } 15 | ) 16 | test_reference_dataset = pd.DataFrame( 17 | { 18 | "category_feature": ["y", "n", "n", "y"], 19 | "numerical_feature": [0, 1, 2, 5], 20 | "target": [0, 0, 0, 1], 21 | "prediction": [0, 0, 0, 1], 22 | } 23 | ) 24 | data_quality_suite = TestSuite( 25 | tests=[ 26 | NoTargetPerformanceTestPreset(), 27 | ] 28 | ) 29 | 30 | data_quality_suite.run(current_data=test_current_dataset, reference_data=test_reference_dataset) 31 | assert not data_quality_suite 32 | assert len(data_quality_suite.as_dict()["tests"]) == 10 33 | -------------------------------------------------------------------------------- /tests/test_setup.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from setup import setup_args 4 | 5 | 6 | def test_minimal_requirements(): 7 | path = Path(__file__).parent.parent 8 | with open(path / "requirements.min.txt") as f: 9 | lines = {line.strip().split("#")[0] for line in f.readlines()} 10 | min_reqs = { 11 | k.split("[")[0]: _get_min_version(v) 12 | for line in lines 13 | if line.strip() 14 | for k, v in (line.strip().split("=="),) 15 | } 16 | 17 | install_reqs = { 18 | k.split("[")[0]: _get_min_version(v) for r in setup_args["install_requires"] for k, v in (r.split(">="),) 19 | } 20 | extra = [] 21 | wrong_version = [] 22 | for m, v in install_reqs.items(): 23 | if m not in min_reqs: 24 | extra.append(f"{m}>={v}") 25 | continue 26 | if v != min_reqs[m]: 27 | wrong_version.append(f"{m}>={v}") 28 | continue 29 | 30 | assert ( 31 | len(extra) == 0 and len(wrong_version) == 0 32 | ), f"install_requires has extra reqs {extra} and wrong versions of {wrong_version}" 33 | 34 | 35 | def _get_min_version(value): 36 | return value.split(",")[0] 37 | -------------------------------------------------------------------------------- /tests/test_suite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/test_suite/__init__.py -------------------------------------------------------------------------------- /tests/test_torch_numpy.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | 4 | 5 | def test_torch_to_numpy(): 6 | array = torch.tensor([1, 2, 3]).numpy() 7 | assert np.array_equal(array, np.array([1, 2, 3])) 8 | -------------------------------------------------------------------------------- /tests/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/tests/__init__.py -------------------------------------------------------------------------------- /tests/ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/ui/__init__.py -------------------------------------------------------------------------------- /tests/ui/test_demo_project.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from evidently.legacy.ui.demo_projects import DEMO_PROJECTS 4 | from evidently.legacy.ui.demo_projects import DEMO_PROJECTS_NAMES 5 | from evidently.legacy.ui.workspace import Workspace 6 | from tests.conftest import slow 7 | 8 | 9 | @slow 10 | @pytest.mark.parametrize("demo_project", DEMO_PROJECTS_NAMES) 11 | def test_create_demo_project(demo_project, tmp_path): 12 | dp = DEMO_PROJECTS[demo_project] 13 | dp.count = 2 14 | dp.create(str(tmp_path)) 15 | 16 | ws = Workspace(path=str(tmp_path)) 17 | assert len(ws.search_project(dp.name)) > 0 18 | -------------------------------------------------------------------------------- /tests/ui/test_ui_basic.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from litestar.testing import TestClient 3 | 4 | from evidently.legacy.ui.app import create_app 5 | from evidently.legacy.ui.demo_projects import DEMO_PROJECTS 6 | from evidently.legacy.ui.local_service import LocalConfig 7 | from tests.conftest import slow 8 | 9 | 10 | @pytest.fixture 11 | def test_client_with_demo(tmp_path): 12 | dp = DEMO_PROJECTS["bikes"] 13 | dp.create(str(tmp_path)) 14 | config = LocalConfig() 15 | config.storage.path = str(tmp_path) 16 | 17 | return TestClient(create_app(config=config)) 18 | 19 | 20 | @slow 21 | def test_root_route(test_client): 22 | response = test_client.get("/") 23 | assert response.status_code == 200 24 | -------------------------------------------------------------------------------- /tests/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/tests/utils/__init__.py -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .tsc-dts 3 | playwright-report 4 | test-results 5 | vite-bundle-report 6 | tsconfig.tsbuildinfo 7 | -------------------------------------------------------------------------------- /ui/html-visual-testing/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | config.json 4 | -------------------------------------------------------------------------------- /ui/html-visual-testing/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/ui/html-visual-testing/README.md -------------------------------------------------------------------------------- /ui/html-visual-testing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-visual-testing", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "test": "playwright test" 7 | }, 8 | "dependencies": { 9 | }, 10 | "devDependencies": { 11 | "wait-on": "^7.1.0", 12 | "@playwright/test": "^1.43.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ui/html-visual-testing/tests/.gitignore: -------------------------------------------------------------------------------- 1 | /visual.spec.ts-snapshots 2 | -------------------------------------------------------------------------------- /ui/html-visual-testing/tests/helpers.ts: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs' 2 | import path from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | 5 | export const HELPERS = { 6 | getDirname: () => { 7 | const __filename = fileURLToPath(import.meta.url) 8 | const __dirname = path.dirname(__filename) 9 | return { __dirname } 10 | }, 11 | getConfigPath: () => path.join(HELPERS.getDirname().__dirname, '..', './config.json'), 12 | readConfig: () => JSON.parse(fs.readFileSync(HELPERS.getConfigPath())) as Record 13 | } 14 | -------------------------------------------------------------------------------- /ui/html-visual-testing/tests/visual.spec.ts: -------------------------------------------------------------------------------- 1 | import { type Page, expect, test } from '@playwright/test' 2 | import { HELPERS } from './helpers' 3 | 4 | const config = HELPERS.readConfig() 5 | 6 | test.describe.configure({ mode: 'parallel' }) 7 | 8 | const testByName = async ({ name, page, folder }: { folder: string; name: string; page: Page }) => { 9 | await page.goto('/') 10 | 11 | await page.getByRole('link', { name: folder }).click() 12 | await page.getByRole('link', { name, exact: true }).click() 13 | 14 | await expect(page.getByRole('button').first()).toBeVisible() 15 | 16 | await expect(page).toHaveScreenshot({ fullPage: true, maxDiffPixels: 0 }) 17 | } 18 | 19 | for (const [folder, files] of Object.entries(config)) { 20 | test.describe(folder, () => { 21 | for (const name of files) { 22 | for (const colorScheme of ['light', 'dark'] as const) { 23 | test.describe(name, () => { 24 | test.use({ colorScheme }) 25 | 26 | test(`(${colorScheme})`, ({ page }) => testByName({ page, name, folder })) 27 | }) 28 | } 29 | } 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /ui/html-visual-testing/tests/visual.spec.ts-snapshots.dvc: -------------------------------------------------------------------------------- 1 | outs: 2 | - md5: 27092ecec5fc12bf94107b38b7f8df11.dir 3 | size: 5481840 4 | nfiles: 28 5 | hash: md5 6 | path: visual.spec.ts-snapshots 7 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/.gitignore: -------------------------------------------------------------------------------- 1 | src/api/types/endpoints.d.ts 2 | src/api/types/endpoints_v2.d.ts 3 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/README.md: -------------------------------------------------------------------------------- 1 | # Evidently UI 2 | 3 | Package common to `service` and `standalone` 4 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/api/types/index.ts: -------------------------------------------------------------------------------- 1 | // This file was auto-generated by openapi-typescript. 2 | // TODO: add docs 3 | import type { components, paths } from '~/api/types/endpoints' 4 | 5 | // TODO: fix this `WidgetInfo` import 6 | // use Schemas['BaseWidgetInfo'] instead 7 | import type { WidgetInfo } from '~/api' 8 | 9 | export type BackendPaths = paths 10 | /////////////////////////////// 11 | /// TYPES 12 | /////////////////////////////// 13 | type Schemas = components['schemas'] 14 | 15 | type OmitNever = { [K in keyof T as T[K] extends never ? never : K]: T[K] } 16 | 17 | export type GetSearchParamsAPIs = OmitNever<{ 18 | [P in keyof paths]: paths[P] extends Record ? Z : never 19 | }> 20 | 21 | export type ProjectModel = Schemas['Project'] 22 | export type ReportModel = Schemas['ReportModel'] 23 | export type TestSuiteModel = Schemas['TestSuiteModel'] 24 | 25 | // TODO: fix this `WidgetInfo` 26 | export type DashboardInfoModel = Omit & { 27 | widgets: WidgetInfo[] 28 | } 29 | 30 | export type MetadataModel = ReportModel['metadata'] 31 | 32 | export type DownloadSnapshotURL = keyof BackendPaths 33 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/api/types/utils.ts: -------------------------------------------------------------------------------- 1 | export type ID = { id: string } 2 | export type OptionalID = { id?: string | null | undefined } 3 | export type StrictID = Omit & ID 4 | 5 | // This version supports for NaN, -Infinity and Infinity. 6 | export type JSONStrExtended = string 7 | 8 | export type ErrorResponse = { status_code: number | false; detail: string } 9 | 10 | export type ErrorData = { error: ErrorResponse } 11 | 12 | /////////////////////////////// 13 | // TYPES TEST 14 | // see details here: 15 | // https://frontendmasters.com/blog/testing-types-in-typescript/ 16 | /////////////////////////////// 17 | 18 | export type Expect = T 19 | type ShapesMatch = [T] extends [U] ? true : false 20 | 21 | export type TYPE_SATISFIED = ShapesMatch extends true 22 | ? ShapesMatch extends true 23 | ? true 24 | : false 25 | : false 26 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/api/types/v2.ts: -------------------------------------------------------------------------------- 1 | /////////////////////////////// 2 | /// V2 TYPES 3 | /////////////////////////////// 4 | import type { components, paths } from '~/api/types/endpoints_v2' 5 | 6 | export type BackendPaths = paths 7 | 8 | type Schemas = components['schemas'] 9 | 10 | type OmitNever = { [K in keyof T as T[K] extends never ? never : K]: T[K] } 11 | 12 | export type GetSearchParamsAPIs = OmitNever<{ 13 | [P in keyof paths]: paths[P] extends Record ? Z : never 14 | }> 15 | 16 | export type SeriesModel = Schemas['SeriesResponse'] 17 | export type BatchMetricDataModel = Schemas['BatchMetricData'] 18 | export type DashboardModel = Schemas['DashboardModel'] 19 | export type DashboardPanelPlotModel = Schemas['DashboardModel']['panels'][number] 20 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/api/utils.ts: -------------------------------------------------------------------------------- 1 | import type { ErrorData, OptionalID, StrictID } from '~/api/types/utils' 2 | 3 | export const ensureID: (e: Entity) => StrictID = (e) => { 4 | if (e.id) { 5 | return { ...e, id: e.id } 6 | } 7 | 8 | throw `"id" is missing in object: ${JSON.stringify(e)}` 9 | } 10 | 11 | export const ensureIDInArray: (e: Entity[]) => StrictID[] = ( 12 | e 13 | ) => { 14 | return e.map(ensureID) 15 | } 16 | 17 | export const expectJsonRequest = (request: Request) => { 18 | if (request.headers.get('Content-type') !== 'application/json') { 19 | throw new Response('Unsupported Media Type', { status: 415 }) 20 | } 21 | } 22 | 23 | export const isSuccessData = (e: T): e is Exclude => 24 | !e || typeof e !== 'object' || !('error' in e) 25 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/AlertThemed.tsx: -------------------------------------------------------------------------------- 1 | import { Alert, type AlertProps } from '@mui/material' 2 | import { useThemeMode } from '~/hooks/theme' 3 | 4 | export const AlertThemed: React.FC & { forseFilled?: boolean }> = ({ 5 | forseFilled, 6 | sx, 7 | ...props 8 | }) => { 9 | const mode = useThemeMode() 10 | 11 | return ( 12 | theme.applyStyles('light', { border: 'none' }), 15 | ...(Array.isArray(sx) ? sx : [sx]) 16 | ]} 17 | variant={mode === 'dark' ? (forseFilled ? 'filled' : 'outlined') : undefined} 18 | {...props} 19 | /> 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/AutoTabs.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import BaseTabs, { type TabInfo } from './BaseTabs' 3 | 4 | /*** 5 | * Provides interactive tabs without needs to control it behavior (ie track active tabs), 6 | * but all tabs loads from start, so it could be negative on performance 7 | */ 8 | 9 | interface AutoTabsProps { 10 | tabs: TabInfo[] 11 | } 12 | 13 | interface AutoTabsState { 14 | activeTab: number 15 | } 16 | 17 | const AutoTabs: React.FunctionComponent = (props) => { 18 | const [state, setState] = useState({ activeTab: 0 }) 19 | return ( 20 | 21 | setState((s) => ({ ...s, activeTab: newTabIdx }))} 24 | tabs={props.tabs} 25 | /> 26 | 27 | ) 28 | } 29 | 30 | export default AutoTabs 31 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/DashboardWidgets.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from '@mui/material' 2 | import type { WidgetInfo } from '~/api' 3 | import { DrawWidgets } from '~/components/WidgetsContent' 4 | 5 | export const DashboardWidgets = ({ widgets }: { widgets: WidgetInfo[] }) => { 6 | if (widgets.length === 0) { 7 | return ( 8 | 9 | This dashboard is currently empty. Please add a monitoring panel to start. 10 | 11 | ) 12 | } 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/JsonView.tsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from '@mui/material' 2 | import JsonView, { type JsonViewProps } from '@uiw/react-json-view' 3 | import { githubDarkTheme } from '@uiw/react-json-view/githubDark' 4 | import { githubLightTheme } from '@uiw/react-json-view/githubLight' 5 | import { useMemo } from 'react' 6 | import { useThemeMode } from '~/hooks/theme' 7 | 8 | export const JsonViewThemed: React.FC< 9 | React.PropsWithRef< 10 | Omit, 'style' | 'displayObjectSize' | 'displayDataTypes'> 11 | > 12 | > = ({ ...props }) => { 13 | const mode = useThemeMode() 14 | 15 | const { 16 | palette: { 17 | primary: { main } 18 | } 19 | } = useTheme() 20 | 21 | const theme = useMemo( 22 | () => ({ 23 | ...(mode === 'light' ? githubLightTheme : githubDarkTheme), 24 | '--w-rjv-background-color': 'transparent', 25 | '--w-rjv-ellipsis-color': main 26 | }), 27 | [mode, main] 28 | ) 29 | 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/TextWithCopyIcon.tsx: -------------------------------------------------------------------------------- 1 | import ContentCopyIcon from '@mui/icons-material/ContentCopy' 2 | import { IconButton } from '@mui/material' 3 | import { Box } from '@mui/material' 4 | 5 | export const TextWithCopyIcon = ({ 6 | showText, 7 | copyText 8 | }: { 9 | showText: string 10 | copyText: string 11 | }) => { 12 | return ( 13 | 14 | {showText} 15 | navigator.clipboard.writeText(copyText)} 19 | > 20 | 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/WidgetGroup.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | import { Box, Divider, Grid, Typography } from '@mui/material' 4 | 5 | interface WidgetGroupProps { 6 | title: string 7 | children: React.ReactNode 8 | } 9 | 10 | const WidgetGroup: React.FunctionComponent = (props) => ( 11 | 12 | {props.title} 13 | 14 | {props.children} 15 | 16 | 17 | 18 | ) 19 | 20 | export default WidgetGroup 21 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/v2/Dashboard/Panels/DashboardPanel.tsx: -------------------------------------------------------------------------------- 1 | import { assertNever } from '~/utils' 2 | import { CounterDashboardPanel, type CounterPanelProps } from './implementations/Counter' 3 | import { PieDashboardPanel, type PiePanelProps } from './implementations/Pie' 4 | import { PlotDashboardPanel, type PlotPanelProps } from './implementations/Plot' 5 | import { TextDashboardPanel, type TextPanelProps } from './implementations/Text' 6 | 7 | export type DashboardPanelProps = 8 | | PlotPanelProps 9 | | CounterPanelProps 10 | | TextPanelProps 11 | | PiePanelProps 12 | 13 | export const DashboardPanel = (props: DashboardPanelProps) => { 14 | if (props.type === 'bar' || props.type === 'line') { 15 | return 16 | } 17 | 18 | if (props.type === 'pie') { 19 | return 20 | } 21 | 22 | if (props.type === 'counter') { 23 | return 24 | } 25 | 26 | if (props.type === 'text') { 27 | return 28 | } 29 | 30 | assertNever(props.type) 31 | } 32 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/v2/Dashboard/Panels/implementations/Text.tsx: -------------------------------------------------------------------------------- 1 | import type { MakePanel } from '~/components/v2/Dashboard/Panels/types' 2 | import { PanelCardGeneral } from './helpers/general' 3 | 4 | export type TextPanelProps = MakePanel<{ 5 | type: 'text' 6 | size: 'full' | 'half' 7 | title?: string 8 | description?: string 9 | }> 10 | 11 | export const TextDashboardPanel = ({ title, description }: TextPanelProps) => { 12 | return ( 13 | <> 14 | 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/components/v2/Dashboard/Panels/types.tsx: -------------------------------------------------------------------------------- 1 | type IPanelProps = { type: string; size: 'full' | 'half' } 2 | 3 | export type MakePanel = T 4 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/contexts/DashboardViewParams.tsx: -------------------------------------------------------------------------------- 1 | import type { PlotMouseEvent } from 'plotly.js' 2 | import React, { useContext } from 'react' 3 | 4 | export type DashboardViewParams = { 5 | isXaxisAsCategorical: boolean 6 | OnClickedPointComponent?: ({ event }: { event: PlotMouseEvent }) => JSX.Element 7 | OnHoveredPlotComponent?: () => JSX.Element 8 | } | null 9 | 10 | export const DashboardViewParamsContext = React.createContext(null) 11 | 12 | export const useDashboardViewParams = () => useContext(DashboardViewParamsContext) 13 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/contexts/DashboardViewParamsV2.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | 3 | export type DashboardViewParams = { 4 | OnClickedPointComponent?: (data: { snapshotId: string }) => JSX.Element 5 | } | null 6 | 7 | export const DashboardViewParamsContext = React.createContext(null) 8 | 9 | export const useDashboardViewParams = () => useContext(DashboardViewParamsContext) 10 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/contexts/WidgetWrapper.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | 3 | export type WidgetWrapper = { 4 | WidgetWrapper: ({ id, children }: { id: string; children: React.ReactNode }) => JSX.Element 5 | } 6 | 7 | const EmptyWidgetWrapper: WidgetWrapper['WidgetWrapper'] = ({ children }) => <>{children} 8 | 9 | export const widgetWrapperContext = React.createContext({ 10 | WidgetWrapper: EmptyWidgetWrapper 11 | }) 12 | 13 | export const useWidgetWrapper = () => useContext(widgetWrapperContext) 14 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/hooks/index.tsx: -------------------------------------------------------------------------------- 1 | export * from '@uidotdev/usehooks' 2 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/hooks/theme.ts: -------------------------------------------------------------------------------- 1 | import { useColorScheme, useMediaQuery } from '@mui/material' 2 | import { useMemo } from 'react' 3 | 4 | export const useThemeMode = () => { 5 | const { mode } = useColorScheme() 6 | const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)') 7 | 8 | return !mode || mode === 'system' ? (prefersDarkMode ? 'dark' : 'light') : mode 9 | } 10 | 11 | export const useNivoTheme = () => { 12 | const mode = useThemeMode() 13 | 14 | const theme = useMemo( 15 | () => 16 | mode === 'dark' 17 | ? { 18 | tooltip: { 19 | container: { 20 | background: '#000', 21 | color: '#fff' 22 | } 23 | } 24 | } 25 | : undefined, 26 | [mode] 27 | ) 28 | 29 | return theme 30 | } 31 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/hooks/useUpdateQueryStringValueWithoutNavigation.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | 3 | export function useUpdateQueryStringValueWithoutNavigation(queryKey: string, queryValue: string) { 4 | useEffect(() => { 5 | const currentSearchParams = new URLSearchParams(window.location.search) 6 | const oldQuery = currentSearchParams.get(queryKey) ?? '' 7 | if (queryValue === oldQuery) return 8 | 9 | if (queryValue) { 10 | currentSearchParams.set(queryKey, queryValue) 11 | } else { 12 | currentSearchParams.delete(queryKey) 13 | } 14 | const newUrl = [window.location.pathname, currentSearchParams.toString()] 15 | .filter(Boolean) 16 | .join('?') 17 | 18 | window.history.replaceState(null, '', newUrl) 19 | }, [queryKey, queryValue]) 20 | } 21 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/router-utils/components/breadcrumbs.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Breadcrumbs as BreadcrumbsMaterial, Link } from '@mui/material' 2 | import { Link as RouterLink } from 'react-router-dom' 3 | 4 | interface Crumb { 5 | to: string 6 | linkText: string 7 | } 8 | 9 | export const BreadCrumbs = ({ crumbs }: { crumbs: Crumb[] }) => { 10 | return ( 11 | 12 | 13 | {crumbs.map((crumb) => ( 14 | 15 | {crumb.linkText} 16 | 17 | ))} 18 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/router-utils/utils.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from 'react-router-dom' 2 | import { makeRouteUrl } from '~/router-utils/router-builder' 3 | import type { GetLinkParamsByPathOnly } from '~/router-utils/types' 4 | 5 | export const createRedirect = () => { 6 | const _redirect = ({ to, paramsToReplace = {} }: GetLinkParamsByPathOnly) => 7 | redirect(makeRouteUrl({ paramsToReplace, path: to })) as never 8 | 9 | return _redirect 10 | } 11 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/routes-components/snapshotId/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@mui/material' 2 | import type { DashboardInfoModel } from '~/api/types' 3 | import { SnapshotWidgets } from '~/components/WidgetsContent' 4 | import DashboardContext, { 5 | CreateDashboardContextState, 6 | type DashboardContextState 7 | } from '~/contexts/DashboardContext' 8 | 9 | export const SnapshotTemplateComponent = ({ 10 | data, 11 | dashboardContextState 12 | }: { 13 | dashboardContextState: DashboardContextState 14 | data: DashboardInfoModel 15 | }) => { 16 | return ( 17 | <> 18 | 19 | 20 | 21 | 22 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/shared-dependencies/mui-icons-material.tsx: -------------------------------------------------------------------------------- 1 | export * from '@mui/icons-material' 2 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/shared-dependencies/mui-material.tsx: -------------------------------------------------------------------------------- 1 | export * from '@mui/material' 2 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/shared-dependencies/openapi-fetch.tsx: -------------------------------------------------------------------------------- 1 | import createClientFunction from 'openapi-fetch' 2 | export const createClient = createClientFunction 3 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/shared-dependencies/react-hook-form.tsx: -------------------------------------------------------------------------------- 1 | export * from 'react-hook-form' 2 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/shared-dependencies/react-router-dom.tsx: -------------------------------------------------------------------------------- 1 | export * from 'react-router-dom' 2 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/shared-dependencies/zod.tsx: -------------------------------------------------------------------------------- 1 | export { zodResolver } from '@hookform/resolvers/zod' 2 | export * from 'zod' 3 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/utils/index.tsx: -------------------------------------------------------------------------------- 1 | // https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html 2 | export function assertNever(x: never): never { 3 | throw `Unexpected object: ${x}` 4 | } 5 | 6 | export function assertNeverActionVariant(x: never): never { 7 | throw `Unexpected action variant: ${x}` 8 | } 9 | 10 | export const REST_PARAMS_FOR_FETCHER_SUBMIT = { 11 | method: 'post', 12 | encType: 'application/json' 13 | } as const 14 | 15 | export const clamp = ({ value, min, max }: Record<'value' | 'min' | 'max', number>) => 16 | Math.min(Math.max(value, min), max) 17 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/AlertStatBlock.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | import { Typography } from '@mui/material' 4 | import type { AlertStats } from '~/api' 5 | import AlertBlock from './AlertBlock' 6 | 7 | interface AlertStatBlockProps { 8 | alertStats: AlertStats 9 | } 10 | 11 | const AlertStatBlock: React.FunctionComponent = (props) => { 12 | const { alertStats } = props 13 | return ( 14 | 23 |
    24 |
  • {alertStats.triggered.period} alerts triggered in the period
  • 25 |
  • {alertStats.triggered.last_24h} alerts triggered in 24 hours
  • 26 |
  • {alertStats.active} total active alerts
  • 27 |
28 | 29 | } 30 | /> 31 | ) 32 | } 33 | 34 | export default AlertStatBlock 35 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/BigTableWidget/GraphDetails.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | import type { WidgetSize } from '~/api' 4 | import LoadableView from '~/components/LoadableVIew' 5 | import DashboardContext from '~/contexts/DashboardContext' 6 | import BigGraphWidgetContent from '~/widgets/BigGraphWidgetContent' 7 | 8 | interface RowDetailsProps { 9 | graphId: string 10 | widgetSize: WidgetSize 11 | } 12 | 13 | export const GraphDetails: React.FunctionComponent = (props) => { 14 | return ( 15 | 16 | {(dashboardContext) => ( 17 | dashboardContext.getAdditionGraphData(props.graphId)}> 18 | {(params) => } 19 | 20 | )} 21 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/InsightBlock.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | import { AlertTitle } from '@mui/material' 4 | 5 | import type { InsightsParams } from '~/api' 6 | import { AlertThemed } from '~/components/AlertThemed' 7 | 8 | interface InsightBlockProps { 9 | data: InsightsParams 10 | } 11 | 12 | const InsightBlock: React.FunctionComponent = (props) => { 13 | return ( 14 | 15 | {props.data.title} 16 | {props.data.text} 17 | 18 | ) 19 | } 20 | 21 | export default InsightBlock 22 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/NotImplementedWidgetContent.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | const NotImplementedWidgetContent: React.FunctionComponent = () =>
Not implemented
4 | 5 | export default NotImplementedWidgetContent 6 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/ProgressWidgetContent.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | import { Box, LinearProgress, Typography } from '@mui/material' 4 | 5 | import type { PercentWidgetParams } from '~/api' 6 | 7 | const ProgressWidgetContent: React.FunctionComponent = (props) => ( 8 |
9 | 10 | 11 | 12 | 13 | 14 | {`${Math.round( 15 | (props.value / props.maxValue) * 100 16 | )}%`} 17 | 18 | 19 | 20 | 21 | {props.details ?? ''} 22 | 23 | 24 |
25 | ) 26 | 27 | export default ProgressWidgetContent 28 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/TabbedGraphWidgetContent.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | import type { MultiTabGraphWidgetParams } from '~/api' 4 | 5 | import AutoTabs from '~/components/AutoTabs' 6 | import BigGraphWidgetContent from './BigGraphWidgetContent' 7 | 8 | const TabbedGraphWidgetContent: React.FunctionComponent< 9 | MultiTabGraphWidgetParams & { widgetSize: number } 10 | > = (props) => ( 11 | ({ 13 | title: g.title, 14 | tab: ( 15 | 20 | ) 21 | }))} 22 | /> 23 | ) 24 | 25 | export default TabbedGraphWidgetContent 26 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/TabbedWidgetContent.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | 3 | import type { MultiTabWidgetParams } from '~/api' 4 | 5 | import AutoTabs from '~/components/AutoTabs' 6 | import { WidgetRenderer } from './WidgetRenderer' 7 | 8 | const TabbedWidgetContent: React.FunctionComponent< 9 | MultiTabWidgetParams & { id: string; widgetSize: number } 10 | > = (props) => { 11 | return ( 12 | ({ 14 | title: g.title, 15 | tab: 16 | }))} 17 | /> 18 | ) 19 | } 20 | 21 | export default TabbedWidgetContent 22 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/TextWidgetContent.tsx: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | import ReactMarkdown from 'react-markdown' 3 | 4 | import type { TextWidgetParams } from '~/api' 5 | 6 | const TextWidgetContent: React.FunctionComponent = (props) => { 7 | return ( 8 | <> 9 | {props.text} 10 | 11 | ) 12 | } 13 | 14 | export default TextWidgetContent 15 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/src/widgets/WidgetPanel.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Grid } from '@mui/material' 4 | 5 | interface WidgetPanelProps { 6 | children: React.ReactNode 7 | } 8 | 9 | class WidgetPanel extends React.Component { 10 | render() { 11 | return ( 12 | 13 | {this.props.children} 14 | 15 | ) 16 | } 17 | } 18 | 19 | export default WidgetPanel 20 | -------------------------------------------------------------------------------- /ui/packages/evidently-ui-lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.ui", 3 | "compilerOptions": { 4 | "composite": true, 5 | "outDir": ".tsc-dts", 6 | "noEmit": false, 7 | "emitDeclarationOnly": true, 8 | "declaration": true, 9 | "baseUrl": "./src", 10 | "paths": { 11 | "~/*": ["*"] 12 | } 13 | }, 14 | "include": ["src"] 15 | } 16 | -------------------------------------------------------------------------------- /ui/pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | # all packages in direct subdirs of packages/ 3 | - 'packages/*' 4 | - 'service' 5 | - 'service_v2' 6 | - 'standalone' 7 | - 'html-visual-testing' 8 | -------------------------------------------------------------------------------- /ui/service/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /ui/service/README.md: -------------------------------------------------------------------------------- 1 | # Evidently UI service 2 | -------------------------------------------------------------------------------- /ui/service/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | Evidently - ML Monitoring Demo 14 | 15 | 16 |
17 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ui/service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "service", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "type-check": "tsc --build", 8 | "build": "vite build", 9 | "code-check": "biome check", 10 | "preview": "vite preview", 11 | "test": "playwright test" 12 | }, 13 | "dependencies": { 14 | "evidently-ui-lib": "workspace:*", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0" 17 | }, 18 | "devDependencies": { 19 | "wait-on": "^7.1.0", 20 | "@playwright/test": "^1.43.0", 21 | "@types/react": "^18.2.0", 22 | "@types/react-dom": "^18.2.0", 23 | "vite": "^5.2.12", 24 | "@vitejs/plugin-react-swc": "^3.7.0", 25 | "vite-tsconfig-paths": "^4.2.1", 26 | "typescript": "^5.7.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ui/service/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/ui/service/public/favicon-16x16.png -------------------------------------------------------------------------------- /ui/service/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/ui/service/public/favicon-32x32.png -------------------------------------------------------------------------------- /ui/service/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/ui/service/public/favicon.ico -------------------------------------------------------------------------------- /ui/service/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Evidently.AI", 3 | "name": "Evidently.AI Dashboards", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "favicon-96x96.png", 12 | "type": "image/png", 13 | "sizes": "96x96" 14 | } 15 | ], 16 | "start_url": ".", 17 | "display": "standalone", 18 | "theme_color": "#000000", 19 | "background_color": "#ffffff" 20 | } 21 | -------------------------------------------------------------------------------- /ui/service/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /ui/service/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import type { BackendPaths } from 'evidently-ui-lib/api/types' 2 | import { createClient } from 'evidently-ui-lib/shared-dependencies/openapi-fetch' 3 | 4 | export const clientAPI = createClient({ baseUrl: '/' }) 5 | -------------------------------------------------------------------------------- /ui/service/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body { 7 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira 8 | Sans, Droid Sans, Helvetica Neue, sans-serif; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | -------------------------------------------------------------------------------- /ui/service/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | 4 | import { CssBaseline, ThemeProvider } from 'evidently-ui-lib/shared-dependencies/mui-material' 5 | import { RouterProvider } from 'evidently-ui-lib/shared-dependencies/react-router-dom' 6 | import { theme } from 'evidently-ui-lib/theme/index' 7 | import { router } from './routes/router' 8 | 9 | import './index.css' 10 | 11 | const rootElement = document.getElementById('root') 12 | 13 | if (rootElement) { 14 | ReactDOM.createRoot(rootElement).render( 15 | 16 | 21 | 22 | 23 | 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /ui/service/src/routes/components.tsx: -------------------------------------------------------------------------------- 1 | import { CreateRouterLinkComponent } from 'evidently-ui-lib/router-utils/components/navigations' 2 | import type { Routes } from 'routes/types' 3 | 4 | export const RouterLink = CreateRouterLinkComponent() 5 | -------------------------------------------------------------------------------- /ui/service/src/routes/hooks.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | createUseLoaderGeneral, 3 | createUseSubmitFetcherGeneral 4 | } from 'evidently-ui-lib/router-utils/fetchers' 5 | import { createUseMatchRouter } from 'evidently-ui-lib/router-utils/hooks' 6 | import type { Routes } from 'routes/types' 7 | 8 | export const useSubmitFetcher = createUseSubmitFetcherGeneral() 9 | export const useLoader = createUseLoaderGeneral() 10 | export const useMatchRouter = createUseMatchRouter() 11 | -------------------------------------------------------------------------------- /ui/service/src/routes/router.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | decorateAllRoutes, 3 | decorateTopLevelRoutes 4 | } from 'evidently-ui-lib/router-utils/router-builder' 5 | import { createBrowserRouter } from 'evidently-ui-lib/shared-dependencies/react-router-dom' 6 | import { routes } from '~/routes/src' 7 | 8 | const finalRoutes = routes.map((r) => decorateTopLevelRoutes(r)).map((r) => decorateAllRoutes(r)) 9 | 10 | export const router = createBrowserRouter(finalRoutes) 11 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/dashboard/import.tsx: -------------------------------------------------------------------------------- 1 | // export * as Dashboard from './dashboard-main' 2 | 3 | // we export it lazy because of ~2.6 MB chunk size :) plotly.js is too big :3 4 | const lazy = () => import('./dashboard-main') 5 | export const DashboardLazy = { lazy } as const 6 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/home/components.tsx: -------------------------------------------------------------------------------- 1 | import { EvidentlyLogoSvg } from 'evidently-ui-lib/components/LogoSvg' 2 | import { RouterLink } from '~/routes/components' 3 | 4 | export const HomeLink = () => ( 5 | , 10 | sx: (theme) => ({ 11 | color: '#4d4d4d', 12 | ...theme.applyStyles('dark', { 13 | color: theme.palette.text.primary 14 | }), 15 | '&:hover': { 16 | borderRadius: '5px', 17 | color: theme.palette.text.disabled, 18 | ...theme.applyStyles('dark', { 19 | color: theme.palette.text.secondary 20 | }) 21 | } 22 | }) 23 | }} 24 | /> 25 | ) 26 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/home/import.tsx: -------------------------------------------------------------------------------- 1 | export * as Home from './home-main' 2 | 3 | // const lazy = () => import('./home-main') 4 | // export const Home = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/project/import.tsx: -------------------------------------------------------------------------------- 1 | export * as Project from './project-main' 2 | 3 | // const lazy = () => import('./project-main') 4 | // export const Project = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/projects-layout/import.tsx: -------------------------------------------------------------------------------- 1 | export * as ProjectsLayout from './projects-layout-main' 2 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/projects-layout/projects-layout-main.tsx: -------------------------------------------------------------------------------- 1 | /////////////////// 2 | // ROUTE 3 | /////////////////// 4 | 5 | export const currentRoutePath = '/projects' 6 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/projects-list/import.tsx: -------------------------------------------------------------------------------- 1 | export * as ProjectsList from './projects-list-main' 2 | 3 | // const lazy = () => import('./projects-list-main') 4 | // export const ProjectsList = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/reports-layout/import.tsx: -------------------------------------------------------------------------------- 1 | export * as ReportsLayout from './reports-layout-main' 2 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/reports-layout/reports-layout-main.tsx: -------------------------------------------------------------------------------- 1 | import type { CrumbDefinition } from 'evidently-ui-lib/router-utils/router-builder' 2 | 3 | /////////////////// 4 | // ROUTE 5 | /////////////////// 6 | 7 | export const currentRoutePath = '/projects/:projectId/reports' 8 | 9 | const crumb: CrumbDefinition = { title: 'Reports' } 10 | 11 | export const handle = { crumb } 12 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/reports-list/import.tsx: -------------------------------------------------------------------------------- 1 | export * as ReportsList from './reports-list-main' 2 | 3 | // const lazy = () => import('./reports-list-main') 4 | // export const Reports = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/snapshot-view/import.tsx: -------------------------------------------------------------------------------- 1 | // export * as SnapshotId from '~/routes/src/snapshot-view/snapshot-view-main' 2 | 3 | // we export it lazy because of ~2.6 MB chunk size :) plotly.js is too big :3 4 | const lazy = () => import('./snapshot-view-main') 5 | export const SnapshotIdLazy = { lazy } as const 6 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/test-suites-layout/import.tsx: -------------------------------------------------------------------------------- 1 | export * as TestSuitesLayout from './test-suites-layout-main' 2 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/test-suites-layout/test-suites-layout-main.tsx: -------------------------------------------------------------------------------- 1 | import type { CrumbDefinition } from 'evidently-ui-lib/router-utils/router-builder' 2 | 3 | /////////////////// 4 | // ROUTE 5 | /////////////////// 6 | 7 | export const currentRoutePath = '/projects/:projectId/test-suites' 8 | 9 | const crumb: CrumbDefinition = { title: 'Test Suites' } 10 | 11 | export const handle = { crumb } 12 | -------------------------------------------------------------------------------- /ui/service/src/routes/src/test-suites-list/import.tsx: -------------------------------------------------------------------------------- 1 | export * as TestSuitesList from './test-suites-list-main' 2 | 3 | // const lazy = () => import('./projects-list-main') 4 | // export const Reports = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service/src/routes/types.tsx: -------------------------------------------------------------------------------- 1 | import type { Expect } from 'evidently-ui-lib/api/types/utils' 2 | 3 | import type { 4 | ExpectEqActuall, 5 | GetMatches, 6 | GetRouteStructure 7 | } from 'evidently-ui-lib/router-utils/types' 8 | 9 | import type { routes } from '~/routes/src' 10 | 11 | export type Routes = GetMatches 12 | 13 | export type Paths = Routes['path'] 14 | 15 | export type GetRouteByPath = Extract 16 | 17 | export type __TESTS_ROUTE_STRUCTURE = Expect>> 18 | -------------------------------------------------------------------------------- /ui/service/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /ui/service/tests/.gitignore: -------------------------------------------------------------------------------- 1 | /visual.spec.ts-snapshots 2 | -------------------------------------------------------------------------------- /ui/service/tests/visual.spec.ts-snapshots.dvc: -------------------------------------------------------------------------------- 1 | outs: 2 | - md5: cfa0fff0fbbc55830bbe5db6416ad390.dir 3 | size: 4019193 4 | nfiles: 8 5 | hash: md5 6 | path: visual.spec.ts-snapshots 7 | -------------------------------------------------------------------------------- /ui/service/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.ui", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "paths": { 6 | "~/*": ["*"] 7 | } 8 | }, 9 | "include": ["src"], 10 | "references": [{ "path": "../packages/evidently-ui-lib/" }] 11 | } 12 | -------------------------------------------------------------------------------- /ui/service/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import tsconfigPaths from 'vite-tsconfig-paths' 3 | import react from '@vitejs/plugin-react-swc' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), tsconfigPaths()], 8 | server: { 9 | port: 3000, 10 | proxy: { 11 | '/api': 'http://127.0.0.1:8000' 12 | } 13 | }, 14 | build: { 15 | rollupOptions: { 16 | output: { 17 | assetFileNames: (assetInfo) => { 18 | let [extType] = assetInfo.name.split('.').reverse() 19 | if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) { 20 | // don't hash images 21 | return `static/img/[name][extname]` 22 | } 23 | // hash everything else (like css) 24 | return `static/${extType}/[name]-[hash][extname]` 25 | }, 26 | chunkFileNames: 'static/js/[name]-[hash].js', 27 | entryFileNames: 'static/js/[name]-[hash].js' 28 | } 29 | } 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /ui/service_v2/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /ui/service_v2/README.md: -------------------------------------------------------------------------------- 1 | # Evidently UI service 2 | -------------------------------------------------------------------------------- /ui/service_v2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | Evidently - ML Monitoring Demo 14 | 15 | 16 |
17 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ui/service_v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "service_v2", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "type-check": "tsc --build", 8 | "build": "vite build", 9 | "code-check": "biome check", 10 | "preview": "vite preview", 11 | "test": "playwright test" 12 | }, 13 | "dependencies": { 14 | "evidently-ui-lib": "workspace:*", 15 | "dayjs": "^1.11.13", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "tiny-invariant": "^1.3.3" 19 | }, 20 | "devDependencies": { 21 | "wait-on": "^7.1.0", 22 | "@playwright/test": "^1.43.0", 23 | "@types/react": "^18.2.0", 24 | "@types/react-dom": "^18.2.0", 25 | "vite": "^5.2.12", 26 | "@vitejs/plugin-react-swc": "^3.7.0", 27 | "vite-tsconfig-paths": "^4.2.1", 28 | "typescript": "^5.7.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ui/service_v2/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/ui/service_v2/public/favicon-16x16.png -------------------------------------------------------------------------------- /ui/service_v2/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/ui/service_v2/public/favicon-32x32.png -------------------------------------------------------------------------------- /ui/service_v2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evidentlyai/evidently/5938939af1cf99bbe55ad4c37cee347c2e592015/ui/service_v2/public/favicon.ico -------------------------------------------------------------------------------- /ui/service_v2/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Evidently.AI", 3 | "name": "Evidently.AI Dashboards", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "favicon-96x96.png", 12 | "type": "image/png", 13 | "sizes": "96x96" 14 | } 15 | ], 16 | "start_url": ".", 17 | "display": "standalone", 18 | "theme_color": "#000000", 19 | "background_color": "#ffffff" 20 | } 21 | -------------------------------------------------------------------------------- /ui/service_v2/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /ui/service_v2/src/Components/GoToSnapshotButton.tsx: -------------------------------------------------------------------------------- 1 | import { useIsAnyLoaderOrActionRunning } from 'evidently-ui-lib/router-utils/hooks' 2 | import invariant from 'tiny-invariant' 3 | import { useProjectInfo } from '~/contexts/project' 4 | import { RouterLink } from '~/routes/components' 5 | 6 | const GoToSnapshotByPoint = ({ snapshotId }: { snapshotId: string }) => { 7 | const { project } = useProjectInfo() 8 | invariant(project) 9 | 10 | const isLoading = useIsAnyLoaderOrActionRunning() 11 | 12 | return ( 13 | 21 | ) 22 | } 23 | 24 | export const OnClickedPointComponent = GoToSnapshotByPoint 25 | -------------------------------------------------------------------------------- /ui/service_v2/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import type { BackendPaths } from 'evidently-ui-lib/api/types/v2' 2 | import { createClient } from 'evidently-ui-lib/shared-dependencies/openapi-fetch' 3 | 4 | export const clientAPI = createClient({ baseUrl: '/' }) 5 | -------------------------------------------------------------------------------- /ui/service_v2/src/contexts/project.tsx: -------------------------------------------------------------------------------- 1 | import type { ProjectModel } from 'evidently-ui-lib/api/types' 2 | import type { StrictID } from 'evidently-ui-lib/api/types/utils' 3 | import React from 'react' 4 | 5 | export const ProjectContext = React.createContext<{ project: StrictID | null }>({ 6 | project: null 7 | }) 8 | 9 | export const useProjectInfo = () => React.useContext(ProjectContext) 10 | -------------------------------------------------------------------------------- /ui/service_v2/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body { 7 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira 8 | Sans, Droid Sans, Helvetica Neue, sans-serif; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | -------------------------------------------------------------------------------- /ui/service_v2/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | 4 | import { CssBaseline, ThemeProvider } from 'evidently-ui-lib/shared-dependencies/mui-material' 5 | import { RouterProvider } from 'evidently-ui-lib/shared-dependencies/react-router-dom' 6 | import { theme } from 'evidently-ui-lib/theme/index' 7 | import { router } from './routes/router' 8 | 9 | import './index.css' 10 | 11 | const rootElement = document.getElementById('root') 12 | 13 | if (rootElement) { 14 | ReactDOM.createRoot(rootElement).render( 15 | 16 | 21 | 22 | 23 | 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/components.tsx: -------------------------------------------------------------------------------- 1 | import { CreateRouterLinkComponent } from 'evidently-ui-lib/router-utils/components/navigations' 2 | import type { Routes } from 'routes/types' 3 | 4 | export const RouterLink = CreateRouterLinkComponent() 5 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/hooks.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | createUseLoaderGeneral, 3 | createUseSubmitFetcherGeneral 4 | } from 'evidently-ui-lib/router-utils/fetchers' 5 | import { createUseMatchRouter } from 'evidently-ui-lib/router-utils/hooks' 6 | import type { Routes } from 'routes/types' 7 | 8 | export const useSubmitFetcher = createUseSubmitFetcherGeneral() 9 | export const useLoader = createUseLoaderGeneral() 10 | export const useMatchRouter = createUseMatchRouter() 11 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/router.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | decorateAllRoutes, 3 | decorateTopLevelRoutes 4 | } from 'evidently-ui-lib/router-utils/router-builder' 5 | import { createBrowserRouter } from 'evidently-ui-lib/shared-dependencies/react-router-dom' 6 | import { routes } from '~/routes/src' 7 | 8 | const finalRoutes = routes.map((r) => decorateTopLevelRoutes(r)).map((r) => decorateAllRoutes(r)) 9 | 10 | export const router = createBrowserRouter(finalRoutes) 11 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/dashboard/import.tsx: -------------------------------------------------------------------------------- 1 | // export * as Dashboard from './dashboard-main' 2 | 3 | // we export it lazy because of ~2.6 MB chunk size :) plotly.js is too big :3 4 | const lazy = () => import('./dashboard-main') 5 | export const DashboardLazy = { lazy } as const 6 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/home/components.tsx: -------------------------------------------------------------------------------- 1 | import { EvidentlyLogoSvg } from 'evidently-ui-lib/components/LogoSvg' 2 | import { RouterLink } from '~/routes/components' 3 | 4 | export const HomeLink = () => ( 5 | , 10 | sx: (theme) => ({ 11 | color: '#4d4d4d', 12 | ...theme.applyStyles('dark', { 13 | color: theme.palette.text.primary 14 | }), 15 | '&:hover': { 16 | borderRadius: '5px', 17 | color: theme.palette.text.disabled, 18 | ...theme.applyStyles('dark', { 19 | color: theme.palette.text.secondary 20 | }) 21 | } 22 | }) 23 | }} 24 | /> 25 | ) 26 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/home/import.tsx: -------------------------------------------------------------------------------- 1 | export * as Home from './home-main' 2 | 3 | // const lazy = () => import('./home-main') 4 | // export const Home = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/load-panel-points/import.tsx: -------------------------------------------------------------------------------- 1 | export * as LoadPanelPointsAPIV2 from './load-panel-points-main' 2 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/project/import.tsx: -------------------------------------------------------------------------------- 1 | export * as Project from './project-main' 2 | 3 | // const lazy = () => import('./project-main') 4 | // export const Project = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/projects-layout/import.tsx: -------------------------------------------------------------------------------- 1 | export * as ProjectsLayout from './projects-layout-main' 2 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/projects-layout/projects-layout-main.tsx: -------------------------------------------------------------------------------- 1 | /////////////////// 2 | // ROUTE 3 | /////////////////// 4 | 5 | export const currentRoutePath = '/projects' 6 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/projects-list/import.tsx: -------------------------------------------------------------------------------- 1 | export * as ProjectsList from './projects-list-main' 2 | 3 | // const lazy = () => import('./projects-list-main') 4 | // export const ProjectsList = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/reports-layout/import.tsx: -------------------------------------------------------------------------------- 1 | export * as ReportsLayout from './reports-layout-main' 2 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/reports-layout/reports-layout-main.tsx: -------------------------------------------------------------------------------- 1 | import type { CrumbDefinition } from 'evidently-ui-lib/router-utils/router-builder' 2 | 3 | /////////////////// 4 | // ROUTE 5 | /////////////////// 6 | 7 | export const currentRoutePath = '/projects/:projectId/reports' 8 | 9 | const crumb: CrumbDefinition = { title: 'Reports' } 10 | 11 | export const handle = { crumb } 12 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/reports-list/import.tsx: -------------------------------------------------------------------------------- 1 | export * as ReportsList from './reports-list-main' 2 | 3 | // const lazy = () => import('./reports-list-main') 4 | // export const Reports = { lazy } as const 5 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/src/snapshot-view/import.tsx: -------------------------------------------------------------------------------- 1 | // export * as SnapshotId from '~/routes/src/snapshot-view/snapshot-view-main' 2 | 3 | // we export it lazy because of ~2.6 MB chunk size :) plotly.js is too big :3 4 | const lazy = () => import('./snapshot-view-main') 5 | export const SnapshotIdLazy = { lazy } as const 6 | -------------------------------------------------------------------------------- /ui/service_v2/src/routes/types.tsx: -------------------------------------------------------------------------------- 1 | import type { Expect } from 'evidently-ui-lib/api/types/utils' 2 | 3 | import type { 4 | ExpectEqActuall, 5 | GetMatches, 6 | GetRouteStructure 7 | } from 'evidently-ui-lib/router-utils/types' 8 | 9 | import type { routes } from 'routes/src' 10 | 11 | export type Routes = GetMatches 12 | 13 | export type Paths = Routes['path'] 14 | 15 | export type GetRouteByPath = Extract 16 | 17 | export type __TESTS_ROUTE_STRUCTURE = Expect>> 18 | -------------------------------------------------------------------------------- /ui/service_v2/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /ui/service_v2/tests/.gitignore: -------------------------------------------------------------------------------- 1 | /visual.spec.ts-snapshots 2 | -------------------------------------------------------------------------------- /ui/service_v2/tests/visual.spec.ts-snapshots.dvc: -------------------------------------------------------------------------------- 1 | outs: 2 | - md5: a766691a583eca68217d1d55514d7ed5.dir 3 | size: 1662020 4 | nfiles: 4 5 | hash: md5 6 | path: visual.spec.ts-snapshots 7 | -------------------------------------------------------------------------------- /ui/service_v2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.ui", 3 | "compilerOptions": { 4 | "baseUrl": "./src", 5 | "paths": { 6 | "~/*": ["*"] 7 | } 8 | }, 9 | "include": ["src"], 10 | "references": [{ "path": "../packages/evidently-ui-lib/" }] 11 | } 12 | -------------------------------------------------------------------------------- /ui/service_v2/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import tsconfigPaths from 'vite-tsconfig-paths' 3 | import react from '@vitejs/plugin-react-swc' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), tsconfigPaths()], 8 | server: { 9 | port: 3000, 10 | proxy: { 11 | '/api': 'http://127.0.0.1:8000' 12 | } 13 | }, 14 | build: { 15 | rollupOptions: { 16 | output: { 17 | assetFileNames: (assetInfo) => { 18 | let [extType] = assetInfo.name.split('.').reverse() 19 | if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) { 20 | // don't hash images 21 | return `static/img/[name][extname]` 22 | } 23 | // hash everything else (like css) 24 | return `static/${extType}/[name]-[hash][extname]` 25 | }, 26 | chunkFileNames: 'static/js/[name]-[hash].js', 27 | entryFileNames: 'static/js/[name]-[hash].js' 28 | } 29 | } 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /ui/standalone/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /ui/standalone/README.md: -------------------------------------------------------------------------------- 1 | # Evidently standalone 2 | -------------------------------------------------------------------------------- /ui/standalone/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Evidently UI standalone 7 | 8 | 9 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ui/standalone/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "standalone", 3 | "version": "1.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "type-check": "tsc --build", 8 | "build": "vite build", 9 | "code-check": "biome check", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "evidently-ui-lib": "workspace:*", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.0", 19 | "@types/react-dom": "^18.2.0", 20 | "@vitejs/plugin-react-swc": "^3.7.0", 21 | "typescript": "^5.7.2", 22 | "vite": "^5.2.12", 23 | "vite-tsconfig-paths": "^4.2.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ui/standalone/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /ui/standalone/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.ui", 3 | "compilerOptions": { 4 | "baseUrl": "./src" 5 | }, 6 | "include": ["src"], 7 | "references": [{ "path": "../packages/evidently-ui-lib/" }] 8 | } 9 | -------------------------------------------------------------------------------- /ui/standalone/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import tsconfigPaths from 'vite-tsconfig-paths' 3 | import react from '@vitejs/plugin-react-swc' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), tsconfigPaths()], 8 | build: { 9 | rollupOptions: { 10 | output: { 11 | entryFileNames: 'index.js' 12 | } 13 | } 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /ui/tsconfig.base.ui.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | 23 | "verbatimModuleSyntax": true, 24 | } 25 | } 26 | --------------------------------------------------------------------------------