gooddata-eval 1.78.1.dev1__tar.gz → 1.78.1.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gooddata_eval-1.78.1.dev1/README.md → gooddata_eval-1.78.1.dev2/PKG-INFO +86 -0
- gooddata_eval-1.78.1.dev1/PKG-INFO → gooddata_eval-1.78.1.dev2/README.md +57 -29
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/pyproject.toml +2 -2
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/cli/agentic_runner.py +4 -2
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/dashboard_skill.py +80 -1
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/visualization.py +132 -22
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/chat/sse_client.py +4 -3
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/dataset/from_insights.py +10 -6
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/visualization.py +296 -6
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/models.py +20 -4
- gooddata_eval-1.78.1.dev2/src/gooddata_eval/core/number_format.py +179 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/scoring.py +148 -8
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_dashboard_skill.py +113 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_runner.py +47 -5
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_visualization.py +272 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_from_insights.py +24 -0
- gooddata_eval-1.78.1.dev2/tests/test_number_format.py +195 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_sse_client.py +17 -0
- gooddata_eval-1.78.1.dev2/tests/test_visualization_evaluator.py +765 -0
- gooddata_eval-1.78.1.dev1/tests/test_visualization_evaluator.py +0 -369
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/.gitignore +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/AGENTS.md +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/CLAUDE.md +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/LICENSE.txt +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/Makefile +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/scripts/calibrate_takeaway_judge.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/scripts/verify_guardrail_refusal_criteria.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/_version.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/cli/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/cli/main.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/_output.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/_retry.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/_catalog.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/_conversation_context.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/_failed_runs.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/_gate.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/_langfuse.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/_trace_linker.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/alert_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/anomaly_detection.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/conversation.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/document_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/general_question.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/guardrail.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/kda_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/metric_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/report_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/search_tool.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/agentic/what_if.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/chat/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/chat/render.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/config.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/connection.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/dataset/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/dataset/langfuse_source.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/dataset/local.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/_deep_subset.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/_guardrail_criteria.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/_llm_judge.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/_maql.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/_rubric.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/_text_utils.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/alert_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/base.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/general_question.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/guardrail.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/metric_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/search_tool.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/summary.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/evaluators/takeaway.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/granularity.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/interaction.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/langfuse/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/langfuse/_env.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/langfuse/client.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/langfuse/experiment.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/langfuse/item_scope.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/langfuse/observations.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/langfuse/otlp.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/langfuse/sink.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/reporting/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/reporting/console.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/reporting/html_report.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/reporting/json_report.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/reporting/report_template.html +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/runner.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/summary/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/summary/http_client.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/takeaway/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/takeaway/evaluate.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/takeaway/http_client.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/timing.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/core/workspace.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/__init__.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/_fake_langfuse.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/conftest.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/fixtures/sample_dataset/metric_skill_create.json +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/fixtures/sample_dataset/visualization_revenue.json +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/fixtures/sse_visualization_stream.txt +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/fixtures/takeaway_calibration.json +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_alert_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_anomaly_detection.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_conversation.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_document_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_gate.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_general_question.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_guardrail.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_join_trace.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_kda_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_langfuse_trace.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_metric_skill.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_observe_experiment.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_run_context.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_search_tool.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_agentic_what_if.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_alert_skill_evaluator.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_chat_render.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_cli.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_connection.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_deep_subset.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_fake_langfuse.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_guardrail_criteria.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_html_report.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_interaction.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_client.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_e2e_fake_server.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_env.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_experiment.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_item_scope.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_observations.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_otlp.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_sink.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_langfuse_source.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_llm_judge.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_local_loader.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_maql_normalize.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_metric_skill_evaluator.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_models.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_reporting.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_runner.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_scoring.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_search_tool_evaluator.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_summary_client.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_summary_evaluator.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_takeaway_client.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_takeaway_evaluate.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_takeaway_evaluator.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_text_evaluators.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_timing.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_trace_linker.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tests/test_workspace.py +0 -0
- {gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/tox.ini +0 -0
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gooddata-eval
|
|
3
|
+
Version: 1.78.1.dev2
|
|
4
|
+
Summary: Evaluate the GoodData AI agent against your own questions and models.
|
|
5
|
+
Project-URL: Source, https://github.com/gooddata/gooddata-python-sdk
|
|
6
|
+
Author-email: GoodData <support@gooddata.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE.txt
|
|
9
|
+
Keywords: ai,analytics,cli,evaluation,gooddata,llm
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering
|
|
17
|
+
Classifier: Topic :: Software Development
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: gooddata-sdk~=1.78.1.dev2
|
|
21
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
22
|
+
Requires-Dist: jsonpatch<2.0,>=1.33
|
|
23
|
+
Requires-Dist: orjson<4.0.0,>=3.9.15
|
|
24
|
+
Requires-Dist: pydantic<3.0,>=2.6
|
|
25
|
+
Requires-Dist: rich<15.0,>=13.0
|
|
26
|
+
Provides-Extra: llm-judge
|
|
27
|
+
Requires-Dist: openai<2.0,>=1.45; extra == 'llm-judge'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
1
30
|
# gooddata-eval
|
|
2
31
|
|
|
3
32
|
CLI to evaluate the GoodData AI agent against a dataset of natural-language
|
|
@@ -709,6 +738,63 @@ A dataset is a folder of `.json` files, one per question:
|
|
|
709
738
|
Supported `test_kind` values: `visualization`, `metric_skill`, `alert_skill`,
|
|
710
739
|
`search_tool`, `general_question`, `guardrail`, `dashboard_summary`, `document_takeaway`.
|
|
711
740
|
|
|
741
|
+
### `visualization` items
|
|
742
|
+
|
|
743
|
+
The `expected_output` lists the candidate charts in the AAC format, and the item passes when
|
|
744
|
+
the agent's chart matches one of them. Keys beside the candidates add item-level checks:
|
|
745
|
+
|
|
746
|
+
```json
|
|
747
|
+
{
|
|
748
|
+
"id": "sankey-001",
|
|
749
|
+
"dataset_name": "agent_visualization_extended_charts",
|
|
750
|
+
"test_kind": "visualization",
|
|
751
|
+
"question": "Show how net sales flow from product category to customer country.",
|
|
752
|
+
"expected_output": {
|
|
753
|
+
"visualization": {
|
|
754
|
+
"type": "sankey_chart",
|
|
755
|
+
"query": {"fields": {"m": {"using": "metric/net_sales"}, "c": {"using": "label/product_category"},
|
|
756
|
+
"d": {"using": "label/customer_country"}}},
|
|
757
|
+
"metrics": ["m"],
|
|
758
|
+
"from": "c",
|
|
759
|
+
"to": "d",
|
|
760
|
+
"buckets_strict": true
|
|
761
|
+
},
|
|
762
|
+
"requires_visualization_part": true
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
Item-level keys, all optional except the candidates:
|
|
768
|
+
|
|
769
|
+
- `visualization` — one candidate chart, or a list of them. `expected_outputs`
|
|
770
|
+
(`[{"visualization": {...}}, ...]`) is the same list in the agentic fixture form.
|
|
771
|
+
- `requires_execution` — the agent must also run the chart it built.
|
|
772
|
+
- `expects_visualization: false` — the agent must answer without a chart, for example by asking
|
|
773
|
+
for a part the request leaves out. The item then lists no candidates.
|
|
774
|
+
- `visualization_optional: true` — either an answer without a chart, or a chart that matches a
|
|
775
|
+
candidate, is right.
|
|
776
|
+
- `answer_must_include` / `answer_must_not_match` — regular expressions, matched without regard
|
|
777
|
+
to case against the final answer. Every `answer_must_include` pattern must match, and no
|
|
778
|
+
`answer_must_not_match` pattern may.
|
|
779
|
+
- `format_intents` — per metric uri, how the metric's numbers must read, for example
|
|
780
|
+
`{"metric/net_sales": {"divide_by": 1000000, "decimals": 1, "suffix": "M"}}`.
|
|
781
|
+
- `requires_visualization_part` — the chart must come back as a part of the answer, not only as
|
|
782
|
+
the arguments of the chart tool call.
|
|
783
|
+
|
|
784
|
+
Switches on a candidate chart:
|
|
785
|
+
|
|
786
|
+
- `buckets_strict` — compare bucket by bucket, not only the union of the buckets, so swapped
|
|
787
|
+
heatmap rows and columns or a reversed sankey fail.
|
|
788
|
+
- `metrics_ordered` — compare the metrics by position, for bullet roles and bubble axes.
|
|
789
|
+
|
|
790
|
+
Any other key, at item level or on a candidate chart, makes the item error before any request
|
|
791
|
+
is sent, because a misspelled key would otherwise be dropped and its check would never run:
|
|
792
|
+
|
|
793
|
+
```
|
|
794
|
+
ValueError: the expectation states ['answer_must_inlcude'], which is not one of ['answer_must_include', ...]
|
|
795
|
+
ValueError: a candidate chart states ['bucket_strict'], which the chart model does not read
|
|
796
|
+
```
|
|
797
|
+
|
|
712
798
|
### `dashboard_summary` items
|
|
713
799
|
|
|
714
800
|
Summary items call the dedicated summary endpoint
|
|
@@ -1,32 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: gooddata-eval
|
|
3
|
-
Version: 1.78.1.dev1
|
|
4
|
-
Summary: Evaluate the GoodData AI agent against your own questions and models.
|
|
5
|
-
Project-URL: Source, https://github.com/gooddata/gooddata-python-sdk
|
|
6
|
-
Author-email: GoodData <support@gooddata.com>
|
|
7
|
-
License-Expression: MIT
|
|
8
|
-
License-File: LICENSE.txt
|
|
9
|
-
Keywords: ai,analytics,cli,evaluation,gooddata,llm
|
|
10
|
-
Classifier: Development Status :: 4 - Beta
|
|
11
|
-
Classifier: Environment :: Console
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Classifier: Topic :: Scientific/Engineering
|
|
17
|
-
Classifier: Topic :: Software Development
|
|
18
|
-
Classifier: Typing :: Typed
|
|
19
|
-
Requires-Python: >=3.10
|
|
20
|
-
Requires-Dist: gooddata-sdk~=1.78.1.dev1
|
|
21
|
-
Requires-Dist: httpx<1.0,>=0.27
|
|
22
|
-
Requires-Dist: jsonpatch<2.0,>=1.33
|
|
23
|
-
Requires-Dist: orjson<4.0.0,>=3.9.15
|
|
24
|
-
Requires-Dist: pydantic<3.0,>=2.6
|
|
25
|
-
Requires-Dist: rich<15.0,>=13.0
|
|
26
|
-
Provides-Extra: llm-judge
|
|
27
|
-
Requires-Dist: openai<2.0,>=1.45; extra == 'llm-judge'
|
|
28
|
-
Description-Content-Type: text/markdown
|
|
29
|
-
|
|
30
1
|
# gooddata-eval
|
|
31
2
|
|
|
32
3
|
CLI to evaluate the GoodData AI agent against a dataset of natural-language
|
|
@@ -738,6 +709,63 @@ A dataset is a folder of `.json` files, one per question:
|
|
|
738
709
|
Supported `test_kind` values: `visualization`, `metric_skill`, `alert_skill`,
|
|
739
710
|
`search_tool`, `general_question`, `guardrail`, `dashboard_summary`, `document_takeaway`.
|
|
740
711
|
|
|
712
|
+
### `visualization` items
|
|
713
|
+
|
|
714
|
+
The `expected_output` lists the candidate charts in the AAC format, and the item passes when
|
|
715
|
+
the agent's chart matches one of them. Keys beside the candidates add item-level checks:
|
|
716
|
+
|
|
717
|
+
```json
|
|
718
|
+
{
|
|
719
|
+
"id": "sankey-001",
|
|
720
|
+
"dataset_name": "agent_visualization_extended_charts",
|
|
721
|
+
"test_kind": "visualization",
|
|
722
|
+
"question": "Show how net sales flow from product category to customer country.",
|
|
723
|
+
"expected_output": {
|
|
724
|
+
"visualization": {
|
|
725
|
+
"type": "sankey_chart",
|
|
726
|
+
"query": {"fields": {"m": {"using": "metric/net_sales"}, "c": {"using": "label/product_category"},
|
|
727
|
+
"d": {"using": "label/customer_country"}}},
|
|
728
|
+
"metrics": ["m"],
|
|
729
|
+
"from": "c",
|
|
730
|
+
"to": "d",
|
|
731
|
+
"buckets_strict": true
|
|
732
|
+
},
|
|
733
|
+
"requires_visualization_part": true
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Item-level keys, all optional except the candidates:
|
|
739
|
+
|
|
740
|
+
- `visualization` — one candidate chart, or a list of them. `expected_outputs`
|
|
741
|
+
(`[{"visualization": {...}}, ...]`) is the same list in the agentic fixture form.
|
|
742
|
+
- `requires_execution` — the agent must also run the chart it built.
|
|
743
|
+
- `expects_visualization: false` — the agent must answer without a chart, for example by asking
|
|
744
|
+
for a part the request leaves out. The item then lists no candidates.
|
|
745
|
+
- `visualization_optional: true` — either an answer without a chart, or a chart that matches a
|
|
746
|
+
candidate, is right.
|
|
747
|
+
- `answer_must_include` / `answer_must_not_match` — regular expressions, matched without regard
|
|
748
|
+
to case against the final answer. Every `answer_must_include` pattern must match, and no
|
|
749
|
+
`answer_must_not_match` pattern may.
|
|
750
|
+
- `format_intents` — per metric uri, how the metric's numbers must read, for example
|
|
751
|
+
`{"metric/net_sales": {"divide_by": 1000000, "decimals": 1, "suffix": "M"}}`.
|
|
752
|
+
- `requires_visualization_part` — the chart must come back as a part of the answer, not only as
|
|
753
|
+
the arguments of the chart tool call.
|
|
754
|
+
|
|
755
|
+
Switches on a candidate chart:
|
|
756
|
+
|
|
757
|
+
- `buckets_strict` — compare bucket by bucket, not only the union of the buckets, so swapped
|
|
758
|
+
heatmap rows and columns or a reversed sankey fail.
|
|
759
|
+
- `metrics_ordered` — compare the metrics by position, for bullet roles and bubble axes.
|
|
760
|
+
|
|
761
|
+
Any other key, at item level or on a candidate chart, makes the item error before any request
|
|
762
|
+
is sent, because a misspelled key would otherwise be dropped and its check would never run:
|
|
763
|
+
|
|
764
|
+
```
|
|
765
|
+
ValueError: the expectation states ['answer_must_inlcude'], which is not one of ['answer_must_include', ...]
|
|
766
|
+
ValueError: a candidate chart states ['bucket_strict'], which the chart model does not read
|
|
767
|
+
```
|
|
768
|
+
|
|
741
769
|
### `dashboard_summary` items
|
|
742
770
|
|
|
743
771
|
Summary items call the dedicated summary endpoint
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# (C) 2026 GoodData Corporation
|
|
2
2
|
[project]
|
|
3
3
|
name = "gooddata-eval"
|
|
4
|
-
version = "1.78.1.
|
|
4
|
+
version = "1.78.1.dev2"
|
|
5
5
|
description = "Evaluate the GoodData AI agent against your own questions and models."
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = "MIT"
|
|
@@ -11,7 +11,7 @@ authors = [
|
|
|
11
11
|
keywords = ["gooddata", "ai", "evaluation", "llm", "analytics", "cli"]
|
|
12
12
|
requires-python = ">=3.10"
|
|
13
13
|
dependencies = [
|
|
14
|
-
"gooddata-sdk~=1.78.1.
|
|
14
|
+
"gooddata-sdk~=1.78.1.dev2",
|
|
15
15
|
"httpx>=0.27,<1.0",
|
|
16
16
|
"jsonpatch>=1.33,<2.0",
|
|
17
17
|
"orjson>=3.9.15,<4.0.0",
|
{gooddata_eval-1.78.1.dev1 → gooddata_eval-1.78.1.dev2}/src/gooddata_eval/cli/agentic_runner.py
RENAMED
|
@@ -26,7 +26,7 @@ from gooddata_eval.core.agentic.visualization import evaluate_agentic_visualizat
|
|
|
26
26
|
from gooddata_eval.core.agentic.what_if import evaluate_agentic_what_if
|
|
27
27
|
from gooddata_eval.core.config import ReasoningEffort
|
|
28
28
|
from gooddata_eval.core.evaluators._llm_judge import JudgeResponseError
|
|
29
|
-
from gooddata_eval.core.evaluators.visualization import
|
|
29
|
+
from gooddata_eval.core.evaluators.visualization import expectation_options
|
|
30
30
|
from gooddata_eval.core.models import AgenticEvalOutcome, CreatedVisualization, DatasetItem
|
|
31
31
|
from gooddata_eval.core.runner import EvalReport, ItemReport
|
|
32
32
|
from gooddata_eval.core.timing import TraceRecorder
|
|
@@ -119,6 +119,8 @@ def _parse_visualization_expected(expected_output: Any) -> list[CreatedVisualiza
|
|
|
119
119
|
{"visualization": {...}} or {"visualization": [{...}]} <- single/multi candidate
|
|
120
120
|
[{"visualization": {...}}, ...] <- bare list
|
|
121
121
|
"""
|
|
122
|
+
if isinstance(expected_output, dict) and expected_output.get("expects_visualization") is False:
|
|
123
|
+
return []
|
|
122
124
|
if isinstance(expected_output, dict):
|
|
123
125
|
raw_list = expected_output.get("expected_outputs")
|
|
124
126
|
if raw_list is not None:
|
|
@@ -187,7 +189,7 @@ def _dispatch_agentic(
|
|
|
187
189
|
workspace_id=workspace_id,
|
|
188
190
|
question=item.question,
|
|
189
191
|
expected_outputs=_parse_visualization_expected(eo),
|
|
190
|
-
|
|
192
|
+
options=expectation_options(eo),
|
|
191
193
|
k=k,
|
|
192
194
|
gate=gate,
|
|
193
195
|
agent_id=agent_id,
|
|
@@ -40,6 +40,7 @@ from gooddata_eval.core.models import (
|
|
|
40
40
|
build_latency_breakdown,
|
|
41
41
|
shift_and_index_events,
|
|
42
42
|
)
|
|
43
|
+
from gooddata_eval.core.scoring import AAC_VISUALIZATION_TYPES, same_viz_type
|
|
43
44
|
from gooddata_eval.core.timing import PhaseTimings, log_timer, sum_timings
|
|
44
45
|
|
|
45
46
|
_DEFAULT_K = 1
|
|
@@ -582,6 +583,64 @@ def _check_date_bindings(widgets: list[dict], new_ids: set[str], expected: list[
|
|
|
582
583
|
return failures
|
|
583
584
|
|
|
584
585
|
|
|
586
|
+
def _has_chart_types(expected_output: dict) -> bool:
|
|
587
|
+
"""Whether any expected chart states the type it must be placed as."""
|
|
588
|
+
return any(isinstance(v, dict) and "chart_type" in v for v in expected_output.get("visualizations") or [])
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def _bodies_by_id(*references: dict) -> dict[str, dict]:
|
|
592
|
+
"""Every chart body the response's references carry, existing and authored, by chart id."""
|
|
593
|
+
bodies: dict[str, dict] = {}
|
|
594
|
+
for refs in references:
|
|
595
|
+
for key in ("visualizations", "new_visualizations"):
|
|
596
|
+
for body in refs.get(key) or []:
|
|
597
|
+
if isinstance(body, dict) and body.get("id"):
|
|
598
|
+
bodies[body["id"]] = body
|
|
599
|
+
return bodies
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _check_chart_types(
|
|
603
|
+
widgets: list[dict], new_ids: set[str], expected: list[dict], bodies: dict[str, dict]
|
|
604
|
+
) -> tuple[list[str], list[str]]:
|
|
605
|
+
"""Every widget of a chart that states a ``chart_type`` must show a chart of exactly that type.
|
|
606
|
+
|
|
607
|
+
A widget names only the chart id, so the type is read from the chart body the references
|
|
608
|
+
carry. The title alone cannot catch a chart authored as another type than the one asked
|
|
609
|
+
for, which is the silent swap GDAI-2054 rules out.
|
|
610
|
+
|
|
611
|
+
Returns the failures and the notes. A widget whose chart has no body in the references
|
|
612
|
+
cannot be checked: gen-ai carries a body only when it could resolve one, so a missing
|
|
613
|
+
body is a platform fault, noted as ``_check_references`` notes a missing id, and not
|
|
614
|
+
scored as the agent's.
|
|
615
|
+
"""
|
|
616
|
+
failures: list[str] = []
|
|
617
|
+
notes: list[str] = []
|
|
618
|
+
for exp in expected:
|
|
619
|
+
if not isinstance(exp, dict) or "chart_type" not in exp:
|
|
620
|
+
continue
|
|
621
|
+
wanted = exp["chart_type"]
|
|
622
|
+
label = exp.get("title") if exp.get("id") is None else exp.get("id")
|
|
623
|
+
candidates = _matching_widgets(widgets, new_ids, exp)
|
|
624
|
+
if not candidates:
|
|
625
|
+
failures.append(f"cannot check the type of chart {label!r}: it is not on the dashboard")
|
|
626
|
+
continue
|
|
627
|
+
types: list[str] = []
|
|
628
|
+
for widget in candidates:
|
|
629
|
+
chart_type = (bodies.get(widget.get("visualization")) or {}).get("type")
|
|
630
|
+
if isinstance(chart_type, str):
|
|
631
|
+
types.append(chart_type)
|
|
632
|
+
else:
|
|
633
|
+
notes.append(
|
|
634
|
+
f"cannot check the type of chart {label!r}: the response's references carry no body "
|
|
635
|
+
f"for {widget.get('visualization')!r}"
|
|
636
|
+
)
|
|
637
|
+
# Every placement, as for date bindings: one right placement must not hide a wrong one.
|
|
638
|
+
if not all(same_viz_type(wanted, t) for t in types):
|
|
639
|
+
found = ", ".join(repr(t) for t in types)
|
|
640
|
+
failures.append(f"chart {label!r} is {found}, expected {wanted!r}")
|
|
641
|
+
return failures, notes
|
|
642
|
+
|
|
643
|
+
|
|
585
644
|
def _tabs_of(dashboard: dict) -> list[dict]:
|
|
586
645
|
"""The dashboard's tabs, reading a document with root sections as its single tab."""
|
|
587
646
|
tabs = dashboard.get("tabs")
|
|
@@ -936,6 +995,12 @@ def _validate_layout_expectations(expected_output: dict) -> None:
|
|
|
936
995
|
for chart in expected_output.get("visualizations") or []:
|
|
937
996
|
if isinstance(chart, dict) and "date_dataset" in chart and not _is_dataset_or_null(chart["date_dataset"]):
|
|
938
997
|
raise ValueError(f"date_dataset must name a date dataset or be null, got {chart!r}")
|
|
998
|
+
if (
|
|
999
|
+
isinstance(chart, dict)
|
|
1000
|
+
and "chart_type" in chart
|
|
1001
|
+
and (not isinstance(chart["chart_type"], str) or chart["chart_type"] not in AAC_VISUALIZATION_TYPES)
|
|
1002
|
+
):
|
|
1003
|
+
raise ValueError(f"chart_type must be an AAC visualization type, got {chart!r}")
|
|
939
1004
|
|
|
940
1005
|
if "tabs" in expected_output:
|
|
941
1006
|
tabs = expected_output["tabs"]
|
|
@@ -1037,6 +1102,7 @@ class _Applies:
|
|
|
1037
1102
|
tab_filters: bool = False
|
|
1038
1103
|
date_filters: bool = False
|
|
1039
1104
|
preserved_widgets: bool = False
|
|
1105
|
+
chart_types: bool = False
|
|
1040
1106
|
|
|
1041
1107
|
@classmethod
|
|
1042
1108
|
def of(cls, expected_output: dict) -> _Applies:
|
|
@@ -1049,6 +1115,7 @@ class _Applies:
|
|
|
1049
1115
|
tab_filters="tab_filters" in expected_output,
|
|
1050
1116
|
date_filters="date_filters" in expected_output,
|
|
1051
1117
|
preserved_widgets="preserved_widgets" in expected_output,
|
|
1118
|
+
chart_types=_has_chart_types(expected_output),
|
|
1052
1119
|
)
|
|
1053
1120
|
|
|
1054
1121
|
|
|
@@ -1089,6 +1156,7 @@ class DashboardEvaluation:
|
|
|
1089
1156
|
tab_filters_correct: bool = False
|
|
1090
1157
|
date_filters_correct: bool = False
|
|
1091
1158
|
widgets_preserved: bool = False
|
|
1159
|
+
chart_types_correct: bool = False
|
|
1092
1160
|
failures: list[str] = field(default_factory=list)
|
|
1093
1161
|
notes: list[str] = field(default_factory=list)
|
|
1094
1162
|
|
|
@@ -1129,6 +1197,8 @@ class DashboardEvaluation:
|
|
|
1129
1197
|
checks["dashboard_date_filters_correct"] = self.date_filters_correct
|
|
1130
1198
|
if self.applies.preserved_widgets:
|
|
1131
1199
|
checks["dashboard_widgets_preserved"] = self.widgets_preserved
|
|
1200
|
+
if self.applies.chart_types:
|
|
1201
|
+
checks["dashboard_chart_types_correct"] = self.chart_types_correct
|
|
1132
1202
|
return checks
|
|
1133
1203
|
|
|
1134
1204
|
@property
|
|
@@ -1280,12 +1350,14 @@ def evaluate_dashboard_response(
|
|
|
1280
1350
|
| {v.get("id") for v in patch_references.get("visualizations") or [] if v.get("id")}
|
|
1281
1351
|
| new_ids
|
|
1282
1352
|
)
|
|
1353
|
+
bodies = _bodies_by_id(base_references, patch_references)
|
|
1283
1354
|
else:
|
|
1284
1355
|
if actual_saved_id is not None:
|
|
1285
1356
|
saved_failures.append(f"a new draft must not be saved yet, but it reports id {actual_saved_id!r}")
|
|
1286
1357
|
document = base
|
|
1287
1358
|
new_ids = {v.get("id") for v in base_references.get("new_visualizations") or [] if v.get("id")}
|
|
1288
1359
|
known_ids = {v.get("id") for v in base_references.get("visualizations") or [] if v.get("id")} | new_ids
|
|
1360
|
+
bodies = _bodies_by_id(base_references)
|
|
1289
1361
|
|
|
1290
1362
|
if document is None:
|
|
1291
1363
|
return DashboardEvaluation(
|
|
@@ -1337,6 +1409,11 @@ def evaluate_dashboard_response(
|
|
|
1337
1409
|
preserved_failures = (
|
|
1338
1410
|
_check_preserved_widgets(widgets, expected_output["preserved_widgets"]) if applies.preserved_widgets else []
|
|
1339
1411
|
)
|
|
1412
|
+
chart_type_failures, chart_type_notes = (
|
|
1413
|
+
_check_chart_types(widgets, new_ids, expected_output.get("visualizations") or [], bodies)
|
|
1414
|
+
if applies.chart_types
|
|
1415
|
+
else ([], [])
|
|
1416
|
+
)
|
|
1340
1417
|
|
|
1341
1418
|
return DashboardEvaluation(
|
|
1342
1419
|
drafted=True,
|
|
@@ -1356,6 +1433,7 @@ def evaluate_dashboard_response(
|
|
|
1356
1433
|
tab_filters_correct=not tab_filter_failures,
|
|
1357
1434
|
date_filters_correct=not date_filter_failures,
|
|
1358
1435
|
widgets_preserved=not preserved_failures,
|
|
1436
|
+
chart_types_correct=not chart_type_failures,
|
|
1359
1437
|
failures=[
|
|
1360
1438
|
*chart_failures,
|
|
1361
1439
|
*saved_failures,
|
|
@@ -1366,9 +1444,10 @@ def evaluate_dashboard_response(
|
|
|
1366
1444
|
*tab_filter_failures,
|
|
1367
1445
|
*date_filter_failures,
|
|
1368
1446
|
*preserved_failures,
|
|
1447
|
+
*chart_type_failures,
|
|
1369
1448
|
*new_failures,
|
|
1370
1449
|
],
|
|
1371
|
-
notes=[*title_notes, *reference_notes],
|
|
1450
|
+
notes=[*title_notes, *reference_notes, *chart_type_notes],
|
|
1372
1451
|
)
|
|
1373
1452
|
|
|
1374
1453
|
|