gooddata-eval 1.76.1.dev1__tar.gz → 1.76.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.76.1.dev1 → gooddata_eval-1.76.1.dev2}/PKG-INFO +51 -2
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/README.md +49 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/pyproject.toml +2 -2
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/cli/agentic_runner.py +13 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/alert_skill.py +9 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/anomaly_detection.py +9 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/conversation.py +31 -2
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/dashboard_skill.py +9 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/general_question.py +9 -9
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/guardrail.py +9 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/kda_skill.py +9 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/metric_skill.py +55 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/report_skill.py +9 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/search_tool.py +9 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/visualization.py +37 -3
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/what_if.py +9 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/chat/sse_client.py +25 -9
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/dataset/langfuse_source.py +12 -3
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/visualization.py +145 -1
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/conftest.py +7 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_conversation.py +93 -2
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_general_question.py +0 -18
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_metric_skill.py +57 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_runner.py +90 -9
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_visualization.py +75 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_langfuse_source.py +13 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_sse_client.py +32 -9
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_visualization_evaluator.py +160 -2
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/.gitignore +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/AGENTS.md +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/CLAUDE.md +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/LICENSE.txt +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/Makefile +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/scripts/verify_guardrail_refusal_criteria.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/_version.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/cli/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/cli/main.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/_output.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/_catalog.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/_conversation_context.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/_gate.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/_langfuse.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/_trace_linker.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/chat/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/chat/render.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/config.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/connection.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/dataset/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/dataset/from_insights.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/dataset/local.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/_deep_subset.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/_guardrail_criteria.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/_llm_judge.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/_maql.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/_text_utils.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/alert_skill.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/base.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/general_question.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/guardrail.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/metric_skill.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/search_tool.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/evaluators/summary.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/granularity.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/langfuse/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/langfuse/_env.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/langfuse/client.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/langfuse/experiment.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/langfuse/observations.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/langfuse/otlp.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/langfuse/sink.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/models.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/reporting/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/reporting/console.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/reporting/html_report.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/reporting/json_report.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/reporting/report_template.html +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/runner.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/scoring.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/summary/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/summary/http_client.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/timing.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/workspace.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/__init__.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/_fake_langfuse.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/fixtures/sample_dataset/metric_skill_create.json +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/fixtures/sample_dataset/visualization_revenue.json +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/fixtures/sse_visualization_stream.txt +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_alert_skill.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_anomaly_detection.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_dashboard_skill.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_gate.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_guardrail.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_kda_skill.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_langfuse_trace.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_observe_experiment.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_report_skill.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_run_context.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_search_tool.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_agentic_what_if.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_alert_skill_evaluator.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_chat_render.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_cli.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_connection.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_deep_subset.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_fake_langfuse.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_from_insights.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_guardrail_criteria.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_html_report.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_langfuse_client.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_langfuse_e2e_fake_server.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_langfuse_env.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_langfuse_experiment.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_langfuse_observations.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_langfuse_otlp.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_langfuse_sink.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_llm_judge.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_local_loader.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_maql_normalize.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_metric_skill_evaluator.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_models.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_reporting.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_runner.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_scoring.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_search_tool_evaluator.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_summary_client.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_summary_evaluator.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_text_evaluators.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_timing.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_trace_linker.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tests/test_workspace.py +0 -0
- {gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: gooddata-eval
|
|
3
|
-
Version: 1.76.1.
|
|
3
|
+
Version: 1.76.1.dev2
|
|
4
4
|
Summary: Evaluate the GoodData AI agent against your own questions and models.
|
|
5
5
|
Project-URL: Source, https://github.com/gooddata/gooddata-python-sdk
|
|
6
6
|
Author-email: GoodData <support@gooddata.com>
|
|
@@ -17,7 +17,7 @@ Classifier: Topic :: Scientific/Engineering
|
|
|
17
17
|
Classifier: Topic :: Software Development
|
|
18
18
|
Classifier: Typing :: Typed
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
|
-
Requires-Dist: gooddata-sdk~=1.76.1.
|
|
20
|
+
Requires-Dist: gooddata-sdk~=1.76.1.dev2
|
|
21
21
|
Requires-Dist: httpx<1.0,>=0.27
|
|
22
22
|
Requires-Dist: jsonpatch<2.0,>=1.33
|
|
23
23
|
Requires-Dist: orjson<4.0.0,>=3.9.15
|
|
@@ -603,6 +603,55 @@ The `expected_output` rubric:
|
|
|
603
603
|
Each criterion is scored independently by the LLM judge, so `quality_score`
|
|
604
604
|
is the fraction of satisfied criteria.
|
|
605
605
|
|
|
606
|
+
### Items asked in a dashboard context (`user_context`)
|
|
607
|
+
|
|
608
|
+
An item can be asked the way a user asks from an open dashboard or an attached widget.
|
|
609
|
+
Its `user_context` is sent verbatim as `userContext` on every chat message of the item,
|
|
610
|
+
follow-up and clarification messages included — the server treats a message without one
|
|
611
|
+
as a cleared context. Requires the `enableAiContextSetup` feature flag on the target
|
|
612
|
+
organization — without it the server ignores the dashboard view. Applies to chat items
|
|
613
|
+
only; `dashboard_summary` items carry their scope in `summary_input` instead. No
|
|
614
|
+
`userContext` key is sent at all when neither the item nor, for `agentic_conversation`,
|
|
615
|
+
the current turn sets one.
|
|
616
|
+
|
|
617
|
+
```json
|
|
618
|
+
{
|
|
619
|
+
"id": "ctx-001",
|
|
620
|
+
"dataset_name": "my_dataset_ctx",
|
|
621
|
+
"test_kind": "general_question",
|
|
622
|
+
"question": "Which dashboard am I looking at, and what does it cover?",
|
|
623
|
+
"user_context": {
|
|
624
|
+
"view": {
|
|
625
|
+
"dashboard": {
|
|
626
|
+
"id": "sales_overview",
|
|
627
|
+
"title": "Sales Overview",
|
|
628
|
+
"widgets": [
|
|
629
|
+
{"widgetType": "insight", "widgetId": "w1", "title": "Revenue by Month", "visualizationId": "revenue_by_month"}
|
|
630
|
+
]
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
"expected_output": "Names the Sales Overview dashboard and summarizes its charts."
|
|
635
|
+
}
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
The schema belongs to the AI chat API (`UserContext`: `view.dashboard`,
|
|
639
|
+
`referencedObjects`, `activeObject`), so it is not validated here beyond being an object.
|
|
640
|
+
In a Langfuse dataset, put it in the item `metadata` (or in the `input` object) under
|
|
641
|
+
`user_context`. An object in `input` wins and `metadata` is then not read; a missing or
|
|
642
|
+
`null` value in `input` falls back to `metadata`. The value used must be an object or `null`;
|
|
643
|
+
anything else fails the dataset load.
|
|
644
|
+
|
|
645
|
+
`agentic_conversation` items take the item's `user_context` as the context of the first
|
|
646
|
+
turn. A turn in `expected_output.turns` can set its own `user_context`, which applies
|
|
647
|
+
from that turn on until another turn changes it, as the attached context does in the UI:
|
|
648
|
+
|
|
649
|
+
| Turn | Context sent |
|
|
650
|
+
|---|---|
|
|
651
|
+
| no `user_context` key | the current one, unchanged |
|
|
652
|
+
| `"user_context": {...}` | this one, from this turn on |
|
|
653
|
+
| `"user_context": null` | none, from this turn on |
|
|
654
|
+
|
|
606
655
|
## Supported test kinds
|
|
607
656
|
|
|
608
657
|
| test_kind | What the agent must produce | Extra required |
|
|
@@ -574,6 +574,55 @@ The `expected_output` rubric:
|
|
|
574
574
|
Each criterion is scored independently by the LLM judge, so `quality_score`
|
|
575
575
|
is the fraction of satisfied criteria.
|
|
576
576
|
|
|
577
|
+
### Items asked in a dashboard context (`user_context`)
|
|
578
|
+
|
|
579
|
+
An item can be asked the way a user asks from an open dashboard or an attached widget.
|
|
580
|
+
Its `user_context` is sent verbatim as `userContext` on every chat message of the item,
|
|
581
|
+
follow-up and clarification messages included — the server treats a message without one
|
|
582
|
+
as a cleared context. Requires the `enableAiContextSetup` feature flag on the target
|
|
583
|
+
organization — without it the server ignores the dashboard view. Applies to chat items
|
|
584
|
+
only; `dashboard_summary` items carry their scope in `summary_input` instead. No
|
|
585
|
+
`userContext` key is sent at all when neither the item nor, for `agentic_conversation`,
|
|
586
|
+
the current turn sets one.
|
|
587
|
+
|
|
588
|
+
```json
|
|
589
|
+
{
|
|
590
|
+
"id": "ctx-001",
|
|
591
|
+
"dataset_name": "my_dataset_ctx",
|
|
592
|
+
"test_kind": "general_question",
|
|
593
|
+
"question": "Which dashboard am I looking at, and what does it cover?",
|
|
594
|
+
"user_context": {
|
|
595
|
+
"view": {
|
|
596
|
+
"dashboard": {
|
|
597
|
+
"id": "sales_overview",
|
|
598
|
+
"title": "Sales Overview",
|
|
599
|
+
"widgets": [
|
|
600
|
+
{"widgetType": "insight", "widgetId": "w1", "title": "Revenue by Month", "visualizationId": "revenue_by_month"}
|
|
601
|
+
]
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
"expected_output": "Names the Sales Overview dashboard and summarizes its charts."
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
The schema belongs to the AI chat API (`UserContext`: `view.dashboard`,
|
|
610
|
+
`referencedObjects`, `activeObject`), so it is not validated here beyond being an object.
|
|
611
|
+
In a Langfuse dataset, put it in the item `metadata` (or in the `input` object) under
|
|
612
|
+
`user_context`. An object in `input` wins and `metadata` is then not read; a missing or
|
|
613
|
+
`null` value in `input` falls back to `metadata`. The value used must be an object or `null`;
|
|
614
|
+
anything else fails the dataset load.
|
|
615
|
+
|
|
616
|
+
`agentic_conversation` items take the item's `user_context` as the context of the first
|
|
617
|
+
turn. A turn in `expected_output.turns` can set its own `user_context`, which applies
|
|
618
|
+
from that turn on until another turn changes it, as the attached context does in the UI:
|
|
619
|
+
|
|
620
|
+
| Turn | Context sent |
|
|
621
|
+
|---|---|
|
|
622
|
+
| no `user_context` key | the current one, unchanged |
|
|
623
|
+
| `"user_context": {...}` | this one, from this turn on |
|
|
624
|
+
| `"user_context": null` | none, from this turn on |
|
|
625
|
+
|
|
577
626
|
## Supported test kinds
|
|
578
627
|
|
|
579
628
|
| test_kind | What the agent must produce | Extra required |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# (C) 2026 GoodData Corporation
|
|
2
2
|
[project]
|
|
3
3
|
name = "gooddata-eval"
|
|
4
|
-
version = "1.76.1.
|
|
4
|
+
version = "1.76.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.76.1.
|
|
14
|
+
"gooddata-sdk~=1.76.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.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/cli/agentic_runner.py
RENAMED
|
@@ -24,6 +24,7 @@ from gooddata_eval.core.agentic.search_tool import evaluate_agentic_search_tool
|
|
|
24
24
|
from gooddata_eval.core.agentic.visualization import evaluate_agentic_visualization
|
|
25
25
|
from gooddata_eval.core.agentic.what_if import evaluate_agentic_what_if
|
|
26
26
|
from gooddata_eval.core.config import ReasoningEffort
|
|
27
|
+
from gooddata_eval.core.evaluators.visualization import requires_execution_of
|
|
27
28
|
from gooddata_eval.core.models import AgenticEvalOutcome, CreatedVisualization, DatasetItem
|
|
28
29
|
from gooddata_eval.core.runner import EvalReport, ItemReport
|
|
29
30
|
|
|
@@ -179,9 +180,11 @@ def _dispatch_agentic(
|
|
|
179
180
|
workspace_id=workspace_id,
|
|
180
181
|
question=item.question,
|
|
181
182
|
expected_outputs=_parse_visualization_expected(eo),
|
|
183
|
+
requires_execution=requires_execution_of(eo),
|
|
182
184
|
k=k,
|
|
183
185
|
gate=gate,
|
|
184
186
|
agent_id=agent_id,
|
|
187
|
+
user_context=item.user_context,
|
|
185
188
|
**lf_kw,
|
|
186
189
|
)
|
|
187
190
|
elif kind == "agentic_metric_skill":
|
|
@@ -194,6 +197,7 @@ def _dispatch_agentic(
|
|
|
194
197
|
k=k,
|
|
195
198
|
gate=gate,
|
|
196
199
|
agent_id=agent_id,
|
|
200
|
+
user_context=item.user_context,
|
|
197
201
|
**lf_kw,
|
|
198
202
|
)
|
|
199
203
|
elif kind == "agentic_dashboard_skill":
|
|
@@ -206,6 +210,7 @@ def _dispatch_agentic(
|
|
|
206
210
|
k=k,
|
|
207
211
|
gate=gate,
|
|
208
212
|
agent_id=agent_id,
|
|
213
|
+
user_context=item.user_context,
|
|
209
214
|
**lf_kw,
|
|
210
215
|
)
|
|
211
216
|
elif kind == "agentic_report_skill":
|
|
@@ -218,6 +223,7 @@ def _dispatch_agentic(
|
|
|
218
223
|
k=k,
|
|
219
224
|
gate=gate,
|
|
220
225
|
agent_id=agent_id,
|
|
226
|
+
user_context=item.user_context,
|
|
221
227
|
**lf_kw,
|
|
222
228
|
)
|
|
223
229
|
elif kind == "agentic_alert_skill":
|
|
@@ -230,6 +236,7 @@ def _dispatch_agentic(
|
|
|
230
236
|
k=k,
|
|
231
237
|
gate=gate,
|
|
232
238
|
agent_id=agent_id,
|
|
239
|
+
user_context=item.user_context,
|
|
233
240
|
**lf_kw,
|
|
234
241
|
)
|
|
235
242
|
elif kind == "agentic_search":
|
|
@@ -245,6 +252,7 @@ def _dispatch_agentic(
|
|
|
245
252
|
k=k,
|
|
246
253
|
gate=gate,
|
|
247
254
|
agent_id=agent_id,
|
|
255
|
+
user_context=item.user_context,
|
|
248
256
|
**lf_kw,
|
|
249
257
|
)
|
|
250
258
|
elif kind == "agentic_general_question":
|
|
@@ -270,6 +278,7 @@ def _dispatch_agentic(
|
|
|
270
278
|
k=k,
|
|
271
279
|
gate=gate,
|
|
272
280
|
agent_id=agent_id,
|
|
281
|
+
user_context=item.user_context,
|
|
273
282
|
**lf_kw,
|
|
274
283
|
)
|
|
275
284
|
elif kind == "agentic_kda_skill":
|
|
@@ -282,6 +291,7 @@ def _dispatch_agentic(
|
|
|
282
291
|
k=k,
|
|
283
292
|
gate=gate,
|
|
284
293
|
agent_id=agent_id,
|
|
294
|
+
user_context=item.user_context,
|
|
285
295
|
**lf_kw,
|
|
286
296
|
)
|
|
287
297
|
elif kind == "agentic_what_if":
|
|
@@ -293,6 +303,7 @@ def _dispatch_agentic(
|
|
|
293
303
|
expected_output=eo if isinstance(eo, dict) else {},
|
|
294
304
|
k=k,
|
|
295
305
|
agent_id=agent_id,
|
|
306
|
+
user_context=item.user_context,
|
|
296
307
|
**lf_kw,
|
|
297
308
|
)
|
|
298
309
|
elif kind == "agentic_anomaly_detection":
|
|
@@ -305,6 +316,7 @@ def _dispatch_agentic(
|
|
|
305
316
|
k=k,
|
|
306
317
|
gate=gate,
|
|
307
318
|
agent_id=agent_id,
|
|
319
|
+
user_context=item.user_context,
|
|
308
320
|
**lf_kw,
|
|
309
321
|
)
|
|
310
322
|
elif kind == "agentic_conversation":
|
|
@@ -315,6 +327,7 @@ def _dispatch_agentic(
|
|
|
315
327
|
workspace_id=workspace_id,
|
|
316
328
|
fixture=ConversationFixture.model_validate(fixture_data),
|
|
317
329
|
agent_id=agent_id,
|
|
330
|
+
user_context=item.user_context,
|
|
318
331
|
**lf_kw,
|
|
319
332
|
)
|
|
320
333
|
else:
|
|
@@ -659,12 +659,18 @@ def run_agentic_alert_skill(
|
|
|
659
659
|
initial_conversation_id: str | None = None,
|
|
660
660
|
reasoning_effort: ReasoningEffort | None = None,
|
|
661
661
|
agent_id: str | None = None,
|
|
662
|
+
user_context: dict | None = None,
|
|
662
663
|
) -> AgenticAlertSummary:
|
|
663
664
|
"""Run the alert-skill agentic evaluation K times and return a summary."""
|
|
664
665
|
expected = _normalize_expected_output(expected_output)
|
|
665
666
|
run_results: list[AlertRunResult] = []
|
|
666
667
|
client = ChatClient(
|
|
667
|
-
host=host,
|
|
668
|
+
host=host,
|
|
669
|
+
token=token,
|
|
670
|
+
workspace_id=workspace_id,
|
|
671
|
+
reasoning_effort=reasoning_effort,
|
|
672
|
+
agent_id=agent_id,
|
|
673
|
+
user_context=user_context,
|
|
668
674
|
)
|
|
669
675
|
sdk = GoodDataSdk.create(host, token)
|
|
670
676
|
|
|
@@ -858,6 +864,7 @@ def evaluate_agentic_alert_skill(
|
|
|
858
864
|
run_metadata_extra: dict | None = None,
|
|
859
865
|
reasoning_effort: ReasoningEffort | None = None,
|
|
860
866
|
submit_trace_link: SubmitTraceLink = run_trace_link_inline,
|
|
867
|
+
user_context: dict | None = None,
|
|
861
868
|
gate: EvalGate = DEFAULT_GATE,
|
|
862
869
|
) -> AgenticEvalOutcome:
|
|
863
870
|
"""Run alert-skill evaluation, log to Langfuse, and raise AlertSkillAssertionError on failure.
|
|
@@ -881,6 +888,7 @@ def evaluate_agentic_alert_skill(
|
|
|
881
888
|
initial_conversation_id=initial_conversation_id,
|
|
882
889
|
reasoning_effort=reasoning_effort,
|
|
883
890
|
agent_id=agent_id,
|
|
891
|
+
user_context=user_context,
|
|
884
892
|
)
|
|
885
893
|
|
|
886
894
|
if langfuse is not None and dataset_item_id:
|
|
@@ -344,6 +344,7 @@ def run_agentic_anomaly_detection(
|
|
|
344
344
|
initial_conversation_id: str | None = None,
|
|
345
345
|
reasoning_effort: ReasoningEffort | None = None,
|
|
346
346
|
agent_id: str | None = None,
|
|
347
|
+
user_context: dict | None = None,
|
|
347
348
|
) -> AgenticAnomalySummary:
|
|
348
349
|
"""Run the anomaly-detection agentic evaluation K times and return a summary.
|
|
349
350
|
|
|
@@ -356,7 +357,12 @@ def run_agentic_anomaly_detection(
|
|
|
356
357
|
raise ValueError(f"k must be >= 1, got {k}")
|
|
357
358
|
run_results: list[AnomalyRunResult] = []
|
|
358
359
|
client = ChatClient(
|
|
359
|
-
host=host,
|
|
360
|
+
host=host,
|
|
361
|
+
token=token,
|
|
362
|
+
workspace_id=workspace_id,
|
|
363
|
+
reasoning_effort=reasoning_effort,
|
|
364
|
+
agent_id=agent_id,
|
|
365
|
+
user_context=user_context,
|
|
360
366
|
)
|
|
361
367
|
|
|
362
368
|
def _run_once(conv_id: str) -> AnomalyRunResult:
|
|
@@ -520,6 +526,7 @@ def evaluate_agentic_anomaly_detection(
|
|
|
520
526
|
run_metadata_extra: dict | None = None,
|
|
521
527
|
reasoning_effort: ReasoningEffort | None = None,
|
|
522
528
|
submit_trace_link: SubmitTraceLink = run_trace_link_inline,
|
|
529
|
+
user_context: dict | None = None,
|
|
523
530
|
) -> AgenticEvalOutcome:
|
|
524
531
|
"""Run anomaly-detection evaluation, log to Langfuse, and raise on failure."""
|
|
525
532
|
langfuse, window_start = open_trace_window(langfuse)
|
|
@@ -534,6 +541,7 @@ def evaluate_agentic_anomaly_detection(
|
|
|
534
541
|
initial_conversation_id=initial_conversation_id,
|
|
535
542
|
reasoning_effort=reasoning_effort,
|
|
536
543
|
agent_id=agent_id,
|
|
544
|
+
user_context=user_context,
|
|
537
545
|
)
|
|
538
546
|
|
|
539
547
|
if langfuse is not None and dataset_item_id:
|
|
@@ -24,7 +24,7 @@ from datetime import date
|
|
|
24
24
|
from typing import ClassVar, Literal
|
|
25
25
|
|
|
26
26
|
from gooddata_sdk import GoodDataSdk
|
|
27
|
-
from pydantic import BaseModel, Field, ValidationError
|
|
27
|
+
from pydantic import BaseModel, Field, ValidationError, model_validator
|
|
28
28
|
|
|
29
29
|
from gooddata_eval.core.agentic._conversation_context import (
|
|
30
30
|
CONFIRMATION_REPLY,
|
|
@@ -100,6 +100,12 @@ class TurnDefinition(BaseModel):
|
|
|
100
100
|
over. ``set_answers`` are what the user replies, in order, when the assistant asks a
|
|
101
101
|
legitimate question on this turn -- the only knowledge the context-mode simulated user
|
|
102
102
|
has beyond the conversation itself.
|
|
103
|
+
|
|
104
|
+
``user_context`` is the ``userContext`` (dashboard/widget scope) the user has attached
|
|
105
|
+
from this turn on, as in the UI: it sticks to later turns until another turn sets it,
|
|
106
|
+
and setting it to null clears it. A turn that leaves it out keeps the current one. Its
|
|
107
|
+
clarification replies carry it too, since gen-ai treats a message without a context as
|
|
108
|
+
cleared.
|
|
103
109
|
"""
|
|
104
110
|
|
|
105
111
|
turn_id: str
|
|
@@ -112,6 +118,7 @@ class TurnDefinition(BaseModel):
|
|
|
112
118
|
expected_tool_args: dict | None = None
|
|
113
119
|
depends_on: list[str] = Field(default_factory=list)
|
|
114
120
|
set_answers: list[str] = Field(default_factory=list)
|
|
121
|
+
user_context: dict | None = None
|
|
115
122
|
|
|
116
123
|
|
|
117
124
|
class ConversationFixture(BaseModel):
|
|
@@ -122,6 +129,17 @@ class ConversationFixture(BaseModel):
|
|
|
122
129
|
expected_skills: list[str]
|
|
123
130
|
turns: list[TurnDefinition]
|
|
124
131
|
|
|
132
|
+
@model_validator(mode="before")
|
|
133
|
+
@classmethod
|
|
134
|
+
def _reject_fixture_level_user_context(cls, data: object) -> object:
|
|
135
|
+
# Unknown keys are ignored, so a context placed here would be dropped without a trace.
|
|
136
|
+
if isinstance(data, dict) and "user_context" in data:
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"user_context is not read from the conversation fixture; put it in the item "
|
|
139
|
+
"metadata or input (the context of the first turn), or on a turn"
|
|
140
|
+
)
|
|
141
|
+
return data
|
|
142
|
+
|
|
125
143
|
|
|
126
144
|
ReplyRecordKind = Literal["confirmation", "question", "no_action", "turn_incomplete"]
|
|
127
145
|
|
|
@@ -696,6 +714,7 @@ def run_agentic_conversation(
|
|
|
696
714
|
mode: ConversationMode | None = None,
|
|
697
715
|
clarification_judge: ClarificationJudge | None = None,
|
|
698
716
|
fresh_conversation_per_turn: bool = False,
|
|
717
|
+
user_context: dict | None = None,
|
|
699
718
|
) -> ConversationResult:
|
|
700
719
|
"""Run a multi-turn, multi-skill conversation evaluation (no K-runs).
|
|
701
720
|
|
|
@@ -706,6 +725,9 @@ def run_agentic_conversation(
|
|
|
706
725
|
``fresh_conversation_per_turn`` sends every turn into a new conversation instead, so the
|
|
707
726
|
agent sees no history. It is the no-memory baseline a context score is calibrated
|
|
708
727
|
against: context-dependent turns are expected to fail there.
|
|
728
|
+
|
|
729
|
+
``user_context`` is the context attached when the conversation starts; a turn's own
|
|
730
|
+
``user_context`` replaces it from that turn on.
|
|
709
731
|
"""
|
|
710
732
|
resolved_mode = resolve_conversation_mode(mode)
|
|
711
733
|
if fresh_conversation_per_turn and initial_conversation_id is not None:
|
|
@@ -747,6 +769,7 @@ def run_agentic_conversation(
|
|
|
747
769
|
turn_offset = 0.0
|
|
748
770
|
tool_index_offset = 0
|
|
749
771
|
reasoning_index_offset = 0
|
|
772
|
+
current_context = user_context
|
|
750
773
|
|
|
751
774
|
try:
|
|
752
775
|
if initial_conversation_id is not None:
|
|
@@ -761,6 +784,10 @@ def run_agentic_conversation(
|
|
|
761
784
|
conversation_id = client.create_conversation()
|
|
762
785
|
transcript = []
|
|
763
786
|
active_skills = set()
|
|
787
|
+
# Before the skip below: a turn whose expectation cannot resolve still changed what
|
|
788
|
+
# the user has attached. Only a turn that names the field changes it; null clears it.
|
|
789
|
+
if "user_context" in turn.model_fields_set:
|
|
790
|
+
current_context = turn.user_context
|
|
764
791
|
try:
|
|
765
792
|
resolved_expected = _resolve_refs(turn.expected_output, turn_outputs)
|
|
766
793
|
resolved_alternatives = [
|
|
@@ -813,7 +840,7 @@ def run_agentic_conversation(
|
|
|
813
840
|
transcript.append(TranscriptEntry("user", current_message))
|
|
814
841
|
incomplete = False
|
|
815
842
|
try:
|
|
816
|
-
chat_result = client.send_message(conversation_id, current_message)
|
|
843
|
+
chat_result = client.send_message(conversation_id, current_message, user_context=current_context)
|
|
817
844
|
except TurnIncompleteError as exc:
|
|
818
845
|
chat_result = exc.partial_result or ChatResult()
|
|
819
846
|
incomplete = True
|
|
@@ -1054,6 +1081,7 @@ def evaluate_agentic_conversation(
|
|
|
1054
1081
|
submit_trace_link: SubmitTraceLink = run_trace_link_inline,
|
|
1055
1082
|
mode: ConversationMode | None = None,
|
|
1056
1083
|
clarification_judge: ClarificationJudge | None = None,
|
|
1084
|
+
user_context: dict | None = None,
|
|
1057
1085
|
) -> AgenticEvalOutcome:
|
|
1058
1086
|
"""Run conversation evaluation, log to Langfuse, and raise on failure.
|
|
1059
1087
|
|
|
@@ -1078,6 +1106,7 @@ def evaluate_agentic_conversation(
|
|
|
1078
1106
|
agent_id=agent_id,
|
|
1079
1107
|
mode=resolved_mode,
|
|
1080
1108
|
clarification_judge=clarification_judge,
|
|
1109
|
+
user_context=user_context,
|
|
1081
1110
|
)
|
|
1082
1111
|
passed = result.context_success if resolved_mode == "context" else result.conversation_success
|
|
1083
1112
|
|
|
@@ -955,6 +955,7 @@ def run_agentic_dashboard_skill(
|
|
|
955
955
|
initial_conversation_id: str | None = None,
|
|
956
956
|
reasoning_effort: ReasoningEffort | None = None,
|
|
957
957
|
agent_id: str | None = None,
|
|
958
|
+
user_context: dict | None = None,
|
|
958
959
|
) -> AgenticDashboardSummary:
|
|
959
960
|
"""Run the dashboard-skill agentic evaluation K times and return a summary.
|
|
960
961
|
|
|
@@ -964,7 +965,12 @@ def run_agentic_dashboard_skill(
|
|
|
964
965
|
_validate_expectation(expected_output)
|
|
965
966
|
run_results: list[DashboardRunResult] = []
|
|
966
967
|
client = ChatClient(
|
|
967
|
-
host=host,
|
|
968
|
+
host=host,
|
|
969
|
+
token=token,
|
|
970
|
+
workspace_id=workspace_id,
|
|
971
|
+
reasoning_effort=reasoning_effort,
|
|
972
|
+
agent_id=agent_id,
|
|
973
|
+
user_context=user_context,
|
|
968
974
|
)
|
|
969
975
|
|
|
970
976
|
try:
|
|
@@ -1021,6 +1027,7 @@ def evaluate_agentic_dashboard_skill(
|
|
|
1021
1027
|
run_metadata_extra: dict | None = None,
|
|
1022
1028
|
reasoning_effort: ReasoningEffort | None = None,
|
|
1023
1029
|
submit_trace_link: SubmitTraceLink = run_trace_link_inline,
|
|
1030
|
+
user_context: dict | None = None,
|
|
1024
1031
|
gate: EvalGate = DEFAULT_GATE,
|
|
1025
1032
|
) -> AgenticEvalOutcome:
|
|
1026
1033
|
"""Run dashboard-skill evaluation, log to Langfuse, and raise on failure.
|
|
@@ -1045,6 +1052,7 @@ def evaluate_agentic_dashboard_skill(
|
|
|
1045
1052
|
initial_conversation_id=initial_conversation_id,
|
|
1046
1053
|
reasoning_effort=reasoning_effort,
|
|
1047
1054
|
agent_id=agent_id,
|
|
1055
|
+
user_context=user_context,
|
|
1048
1056
|
)
|
|
1049
1057
|
|
|
1050
1058
|
if langfuse is not None and dataset_item_id:
|
|
@@ -116,11 +116,10 @@ def _run_single_general_question(
|
|
|
116
116
|
conversation_id: str,
|
|
117
117
|
question: str,
|
|
118
118
|
expected_output: str,
|
|
119
|
-
user_context: dict | None = None,
|
|
120
119
|
) -> GeneralQuestionResult:
|
|
121
120
|
item_started = time.monotonic()
|
|
122
121
|
agent_started = time.monotonic()
|
|
123
|
-
chat_result = client.send_message(conversation_id, question
|
|
122
|
+
chat_result = client.send_message(conversation_id, question)
|
|
124
123
|
actual_output = render_answer_text(chat_result)
|
|
125
124
|
agent_elapsed = time.monotonic() - agent_started
|
|
126
125
|
log_timer(
|
|
@@ -169,16 +168,19 @@ def run_agentic_general_question(
|
|
|
169
168
|
"""Run the general-question agentic evaluation K times and return a summary."""
|
|
170
169
|
run_results: list[GeneralQuestionResult] = []
|
|
171
170
|
client = ChatClient(
|
|
172
|
-
host=host,
|
|
171
|
+
host=host,
|
|
172
|
+
token=token,
|
|
173
|
+
workspace_id=workspace_id,
|
|
174
|
+
reasoning_effort=reasoning_effort,
|
|
175
|
+
agent_id=agent_id,
|
|
176
|
+
user_context=user_context,
|
|
173
177
|
)
|
|
174
178
|
judge = LLMJudge(_GENERAL_QUESTION_EVALUATION_STEPS)
|
|
175
179
|
|
|
176
180
|
try:
|
|
177
181
|
conv_id_0 = initial_conversation_id if initial_conversation_id is not None else client.create_conversation()
|
|
178
182
|
try:
|
|
179
|
-
run_results.append(
|
|
180
|
-
_run_single_general_question(client, judge, conv_id_0, question, expected_output, user_context)
|
|
181
|
-
)
|
|
183
|
+
run_results.append(_run_single_general_question(client, judge, conv_id_0, question, expected_output))
|
|
182
184
|
finally:
|
|
183
185
|
if initial_conversation_id is None:
|
|
184
186
|
client.delete_conversation(conv_id_0)
|
|
@@ -186,9 +188,7 @@ def run_agentic_general_question(
|
|
|
186
188
|
for _ in range(1, k):
|
|
187
189
|
conv_id = client.create_conversation()
|
|
188
190
|
try:
|
|
189
|
-
run_results.append(
|
|
190
|
-
_run_single_general_question(client, judge, conv_id, question, expected_output, user_context)
|
|
191
|
-
)
|
|
191
|
+
run_results.append(_run_single_general_question(client, judge, conv_id, question, expected_output))
|
|
192
192
|
finally:
|
|
193
193
|
client.delete_conversation(conv_id)
|
|
194
194
|
finally:
|
{gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/guardrail.py
RENAMED
|
@@ -144,11 +144,17 @@ def run_agentic_guardrail(
|
|
|
144
144
|
initial_conversation_id: str | None = None,
|
|
145
145
|
reasoning_effort: ReasoningEffort | None = None,
|
|
146
146
|
agent_id: str | None = None,
|
|
147
|
+
user_context: dict | None = None,
|
|
147
148
|
) -> AgenticGuardrailSummary:
|
|
148
149
|
"""Run the guardrail agentic evaluation K times and return a summary."""
|
|
149
150
|
run_results: list[GuardrailResult] = []
|
|
150
151
|
client = ChatClient(
|
|
151
|
-
host=host,
|
|
152
|
+
host=host,
|
|
153
|
+
token=token,
|
|
154
|
+
workspace_id=workspace_id,
|
|
155
|
+
reasoning_effort=reasoning_effort,
|
|
156
|
+
agent_id=agent_id,
|
|
157
|
+
user_context=user_context,
|
|
152
158
|
)
|
|
153
159
|
judge = LLMJudge(_GUARDRAIL_EVALUATION_STEPS)
|
|
154
160
|
|
|
@@ -206,6 +212,7 @@ def evaluate_agentic_guardrail(
|
|
|
206
212
|
run_metadata_extra: dict | None = None,
|
|
207
213
|
reasoning_effort: ReasoningEffort | None = None,
|
|
208
214
|
submit_trace_link: SubmitTraceLink = run_trace_link_inline,
|
|
215
|
+
user_context: dict | None = None,
|
|
209
216
|
gate: EvalGate = DEFAULT_GATE,
|
|
210
217
|
) -> AgenticEvalOutcome:
|
|
211
218
|
"""Run guardrail evaluation, log to Langfuse, and raise GuardrailAssertionError on failure.
|
|
@@ -226,6 +233,7 @@ def evaluate_agentic_guardrail(
|
|
|
226
233
|
initial_conversation_id=initial_conversation_id,
|
|
227
234
|
reasoning_effort=reasoning_effort,
|
|
228
235
|
agent_id=agent_id,
|
|
236
|
+
user_context=user_context,
|
|
229
237
|
)
|
|
230
238
|
|
|
231
239
|
if langfuse is not None and dataset_item_id:
|
{gooddata_eval-1.76.1.dev1 → gooddata_eval-1.76.1.dev2}/src/gooddata_eval/core/agentic/kda_skill.py
RENAMED
|
@@ -239,6 +239,7 @@ def run_agentic_kda_skill(
|
|
|
239
239
|
initial_conversation_id: str | None = None,
|
|
240
240
|
reasoning_effort: ReasoningEffort | None = None,
|
|
241
241
|
agent_id: str | None = None,
|
|
242
|
+
user_context: dict | None = None,
|
|
242
243
|
) -> AgenticKdaSummary:
|
|
243
244
|
"""Run the KDA-skill agentic evaluation K times and return a summary.
|
|
244
245
|
|
|
@@ -256,7 +257,12 @@ def run_agentic_kda_skill(
|
|
|
256
257
|
raise ValueError(f"k must be >= 1, got {k}")
|
|
257
258
|
run_results: list[KdaRunResult] = []
|
|
258
259
|
client = ChatClient(
|
|
259
|
-
host=host,
|
|
260
|
+
host=host,
|
|
261
|
+
token=token,
|
|
262
|
+
workspace_id=workspace_id,
|
|
263
|
+
reasoning_effort=reasoning_effort,
|
|
264
|
+
agent_id=agent_id,
|
|
265
|
+
user_context=user_context,
|
|
260
266
|
)
|
|
261
267
|
|
|
262
268
|
def _run_once(conv_id: str) -> KdaRunResult:
|
|
@@ -424,6 +430,7 @@ def evaluate_agentic_kda_skill(
|
|
|
424
430
|
run_metadata_extra: dict | None = None,
|
|
425
431
|
reasoning_effort: ReasoningEffort | None = None,
|
|
426
432
|
submit_trace_link: SubmitTraceLink = run_trace_link_inline,
|
|
433
|
+
user_context: dict | None = None,
|
|
427
434
|
gate: EvalGate = DEFAULT_GATE,
|
|
428
435
|
) -> AgenticEvalOutcome:
|
|
429
436
|
"""Run KDA-skill evaluation, log to Langfuse, and raise KdaSkillAssertionError on failure.
|
|
@@ -444,6 +451,7 @@ def evaluate_agentic_kda_skill(
|
|
|
444
451
|
initial_conversation_id=initial_conversation_id,
|
|
445
452
|
reasoning_effort=reasoning_effort,
|
|
446
453
|
agent_id=agent_id,
|
|
454
|
+
user_context=user_context,
|
|
447
455
|
)
|
|
448
456
|
|
|
449
457
|
if langfuse is not None and dataset_item_id:
|