gooddata-eval 1.68.1.dev3__tar.gz → 1.68.1.dev4__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.
Files changed (84) hide show
  1. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/PKG-INFO +2 -2
  2. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/pyproject.toml +2 -2
  3. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/alert_skill.py +31 -14
  4. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/conversation.py +1 -1
  5. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/scoring.py +3 -1
  6. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/.gitignore +0 -0
  7. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/LICENSE.txt +0 -0
  8. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/Makefile +0 -0
  9. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/README.md +0 -0
  10. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/__init__.py +0 -0
  11. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/_version.py +0 -0
  12. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/cli/__init__.py +0 -0
  13. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/cli/agentic_runner.py +0 -0
  14. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/cli/main.py +0 -0
  15. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/__init__.py +0 -0
  16. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/__init__.py +0 -0
  17. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/_catalog.py +0 -0
  18. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/_langfuse.py +0 -0
  19. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/general_question.py +0 -0
  20. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/guardrail.py +0 -0
  21. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/metric_skill.py +0 -0
  22. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/search_tool.py +0 -0
  23. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/agentic/visualization.py +0 -0
  24. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/chat/__init__.py +0 -0
  25. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/chat/sse_client.py +0 -0
  26. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/config.py +0 -0
  27. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/connection.py +0 -0
  28. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/dataset/__init__.py +0 -0
  29. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/dataset/langfuse_source.py +0 -0
  30. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/dataset/local.py +0 -0
  31. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/__init__.py +0 -0
  32. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/_deep_subset.py +0 -0
  33. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/_llm_judge.py +0 -0
  34. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/_text_utils.py +0 -0
  35. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/alert_skill.py +0 -0
  36. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/base.py +0 -0
  37. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/general_question.py +0 -0
  38. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/guardrail.py +0 -0
  39. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/metric_skill.py +0 -0
  40. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/search_tool.py +0 -0
  41. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/summary.py +0 -0
  42. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/evaluators/visualization.py +0 -0
  43. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/langfuse/__init__.py +0 -0
  44. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/langfuse/sink.py +0 -0
  45. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/models.py +0 -0
  46. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/reporting/__init__.py +0 -0
  47. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/reporting/console.py +0 -0
  48. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/reporting/json_report.py +0 -0
  49. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/runner.py +0 -0
  50. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/summary/__init__.py +0 -0
  51. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/summary/http_client.py +0 -0
  52. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/src/gooddata_eval/core/workspace.py +0 -0
  53. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/__init__.py +0 -0
  54. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/conftest.py +0 -0
  55. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/fixtures/sample_dataset/metric_skill_create.json +0 -0
  56. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/fixtures/sample_dataset/visualization_revenue.json +0 -0
  57. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/fixtures/sse_visualization_stream.txt +0 -0
  58. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_agentic_alert_skill.py +0 -0
  59. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_agentic_conversation.py +0 -0
  60. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_agentic_general_question.py +0 -0
  61. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_agentic_guardrail.py +0 -0
  62. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_agentic_metric_skill.py +0 -0
  63. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_agentic_search_tool.py +0 -0
  64. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_agentic_visualization.py +0 -0
  65. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_alert_skill_evaluator.py +0 -0
  66. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_cli.py +0 -0
  67. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_connection.py +0 -0
  68. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_deep_subset.py +0 -0
  69. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_langfuse_sink.py +0 -0
  70. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_langfuse_source.py +0 -0
  71. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_llm_judge.py +0 -0
  72. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_local_loader.py +0 -0
  73. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_metric_skill_evaluator.py +0 -0
  74. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_models.py +0 -0
  75. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_reporting.py +0 -0
  76. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_runner.py +0 -0
  77. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_scoring.py +0 -0
  78. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_search_tool_evaluator.py +0 -0
  79. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_sse_client.py +0 -0
  80. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_summary_client.py +0 -0
  81. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_summary_evaluator.py +0 -0
  82. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_text_evaluators.py +0 -0
  83. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_visualization_evaluator.py +0 -0
  84. {gooddata_eval-1.68.1.dev3 → gooddata_eval-1.68.1.dev4}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gooddata-eval
3
- Version: 1.68.1.dev3
3
+ Version: 1.68.1.dev4
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.68.1.dev3
20
+ Requires-Dist: gooddata-sdk~=1.68.1.dev4
21
21
  Requires-Dist: httpx<1.0,>=0.27
22
22
  Requires-Dist: orjson<4.0.0,>=3.9.15
23
23
  Requires-Dist: pydantic<3.0,>=2.6
@@ -1,7 +1,7 @@
1
1
  # (C) 2026 GoodData Corporation
2
2
  [project]
3
3
  name = "gooddata-eval"
4
- version = "1.68.1.dev3"
4
+ version = "1.68.1.dev4"
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.68.1.dev3",
14
+ "gooddata-sdk~=1.68.1.dev4",
15
15
  "httpx>=0.27,<1.0",
16
16
  "orjson>=3.9.15,<4.0.0",
17
17
  "pydantic>=2.6,<3.0",
@@ -77,6 +77,8 @@ def _check_threshold(expected: CatalogMetricAlert, actual_args: dict) -> bool:
77
77
 
78
78
 
79
79
  def _check_trigger(expected: CatalogMetricAlert, actual_args: dict) -> bool:
80
+ if expected.operator == "ANOMALY":
81
+ return True
80
82
  exp_trigger = expected.trigger
81
83
  act_trigger = actual_args.get("trigger", actual_args.get("triggerMode", "ALWAYS"))
82
84
  if exp_trigger in _ALWAYS_TRIGGER_VALUES:
@@ -235,27 +237,42 @@ class AgenticAlertSummary:
235
237
  best: AlertRunResult
236
238
 
237
239
 
240
+ def _case_insensitive_get(d: dict, *keys: str) -> Any:
241
+ """Look up a value by key, preferring an exact match then a case-insensitive one."""
242
+ for k in keys:
243
+ if k in d:
244
+ return d[k]
245
+ lowered = {str(k).lower(): v for k, v in d.items()}
246
+ for k in keys:
247
+ if k.lower() in lowered:
248
+ return lowered[k.lower()]
249
+ return None
250
+
251
+
238
252
  def _normalize_expected_output(expected: dict) -> CatalogMetricAlert:
239
253
  """Parse expected_output dict into CatalogMetricAlert, accepting display-format or internal-format keys."""
240
- operator = expected.get("operator") or expected.get("Operator") or "GREATER_THAN"
241
- threshold = expected.get("threshold") or expected.get("Threshold")
242
- threshold_from = expected.get("threshold_from")
243
- threshold_to = expected.get("threshold_to")
244
- trigger = expected.get("trigger") or expected.get("Trigger") or "not specified"
245
-
246
- metric_id = expected.get("metric_id")
247
- if not metric_id and "Metric" in expected:
248
- m = re.search(r"\(([^)]+)\)\s*$", str(expected["Metric"]))
249
- if m:
250
- metric_id = m.group(1).strip()
251
-
252
- raw_recip = expected.get("recipients") or expected.get("Recipient(s)") or []
254
+ operator = _case_insensitive_get(expected, "operator") or "GREATER_THAN"
255
+ threshold = _case_insensitive_get(expected, "threshold")
256
+ threshold_from = _case_insensitive_get(expected, "threshold_from")
257
+ threshold_to = _case_insensitive_get(expected, "threshold_to")
258
+
259
+ trigger = _case_insensitive_get(expected, "trigger") or "not specified"
260
+ trigger = _TRIGGER_DISPLAY_TO_API.get(trigger, trigger)
261
+
262
+ metric_id = _case_insensitive_get(expected, "metric_id")
263
+ if not metric_id:
264
+ metric_disp = _case_insensitive_get(expected, "metric")
265
+ if metric_disp:
266
+ m = re.search(r"\(([^)]+)\)\s*$", str(metric_disp))
267
+ metric_id = m.group(1).strip() if m else None
268
+
269
+ raw_recip = _case_insensitive_get(expected, "recipients", "recipient(s)") or []
253
270
  if isinstance(raw_recip, str):
254
271
  recipients = [r.strip() for r in raw_recip.replace(";", ",").split(",") if r.strip()]
255
272
  else:
256
273
  recipients = list(raw_recip)
257
274
 
258
- filters = expected.get("filters") or expected.get("Time window/Filters")
275
+ filters = _case_insensitive_get(expected, "filters")
259
276
  if isinstance(filters, str) and any(kw in filters for kw in ("None", "All time")):
260
277
  filters = None
261
278
 
@@ -102,7 +102,7 @@ def _activated_skills(tool_call_events: list[ToolCallEvent]) -> list[str]:
102
102
  if tc.function_name != "set_skills":
103
103
  continue
104
104
  args = tc.parsed_arguments() or {}
105
- skills.extend(args.get("skills", []))
105
+ skills.extend(args.get("skill_names") or args.get("skills") or [])
106
106
  return list(set(skills))
107
107
 
108
108
 
@@ -71,7 +71,9 @@ def validate_cross_references(viz: CreatedVisualization) -> tuple[bool, list[str
71
71
  continue
72
72
  using_val = filter_dict.get("using", "")
73
73
  using_uri = _resolve_alias_to_uri(using_val, fields)
74
- if not using_uri.startswith(("metric/", "fact/")):
74
+ field_def = fields.get(using_val)
75
+ is_adhoc_agg = isinstance(field_def, AacQueryField) and bool(field_def.aggregation)
76
+ if not using_uri.startswith(("metric/", "fact/")) and not is_adhoc_agg:
75
77
  errors.append(
76
78
  f"ranking filter '{filter_key}': using='{using_val}' "
77
79
  f"resolves to '{using_uri}' — expected a metric/ or fact/ URI"