gooddata-eval 1.68.1.dev4__tar.gz → 1.69.0__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.dev4 → gooddata_eval-1.69.0}/PKG-INFO +2 -2
  2. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/pyproject.toml +2 -2
  3. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/cli/main.py +16 -4
  4. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_cli.py +41 -0
  5. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/.gitignore +0 -0
  6. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/LICENSE.txt +0 -0
  7. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/Makefile +0 -0
  8. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/README.md +0 -0
  9. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/__init__.py +0 -0
  10. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/_version.py +0 -0
  11. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/cli/__init__.py +0 -0
  12. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/cli/agentic_runner.py +0 -0
  13. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/__init__.py +0 -0
  14. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/__init__.py +0 -0
  15. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/_catalog.py +0 -0
  16. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/_langfuse.py +0 -0
  17. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/alert_skill.py +0 -0
  18. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/conversation.py +0 -0
  19. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/general_question.py +0 -0
  20. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/guardrail.py +0 -0
  21. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/metric_skill.py +0 -0
  22. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/search_tool.py +0 -0
  23. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/agentic/visualization.py +0 -0
  24. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/chat/__init__.py +0 -0
  25. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/chat/sse_client.py +0 -0
  26. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/config.py +0 -0
  27. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/connection.py +0 -0
  28. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/dataset/__init__.py +0 -0
  29. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/dataset/langfuse_source.py +0 -0
  30. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/dataset/local.py +0 -0
  31. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/__init__.py +0 -0
  32. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/_deep_subset.py +0 -0
  33. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/_llm_judge.py +0 -0
  34. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/_text_utils.py +0 -0
  35. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/alert_skill.py +0 -0
  36. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/base.py +0 -0
  37. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/general_question.py +0 -0
  38. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/guardrail.py +0 -0
  39. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/metric_skill.py +0 -0
  40. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/search_tool.py +0 -0
  41. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/summary.py +0 -0
  42. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/evaluators/visualization.py +0 -0
  43. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/langfuse/__init__.py +0 -0
  44. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/langfuse/sink.py +0 -0
  45. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/models.py +0 -0
  46. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/reporting/__init__.py +0 -0
  47. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/reporting/console.py +0 -0
  48. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/reporting/json_report.py +0 -0
  49. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/runner.py +0 -0
  50. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/scoring.py +0 -0
  51. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/summary/__init__.py +0 -0
  52. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/summary/http_client.py +0 -0
  53. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/src/gooddata_eval/core/workspace.py +0 -0
  54. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/__init__.py +0 -0
  55. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/conftest.py +0 -0
  56. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/fixtures/sample_dataset/metric_skill_create.json +0 -0
  57. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/fixtures/sample_dataset/visualization_revenue.json +0 -0
  58. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/fixtures/sse_visualization_stream.txt +0 -0
  59. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_agentic_alert_skill.py +0 -0
  60. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_agentic_conversation.py +0 -0
  61. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_agentic_general_question.py +0 -0
  62. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_agentic_guardrail.py +0 -0
  63. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_agentic_metric_skill.py +0 -0
  64. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_agentic_search_tool.py +0 -0
  65. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_agentic_visualization.py +0 -0
  66. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_alert_skill_evaluator.py +0 -0
  67. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_connection.py +0 -0
  68. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_deep_subset.py +0 -0
  69. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_langfuse_sink.py +0 -0
  70. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_langfuse_source.py +0 -0
  71. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_llm_judge.py +0 -0
  72. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_local_loader.py +0 -0
  73. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_metric_skill_evaluator.py +0 -0
  74. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_models.py +0 -0
  75. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_reporting.py +0 -0
  76. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_runner.py +0 -0
  77. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_scoring.py +0 -0
  78. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_search_tool_evaluator.py +0 -0
  79. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_sse_client.py +0 -0
  80. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_summary_client.py +0 -0
  81. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_summary_evaluator.py +0 -0
  82. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_text_evaluators.py +0 -0
  83. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/tests/test_visualization_evaluator.py +0 -0
  84. {gooddata_eval-1.68.1.dev4 → gooddata_eval-1.69.0}/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.dev4
3
+ Version: 1.69.0
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.dev4
20
+ Requires-Dist: gooddata-sdk~=1.69.0
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.dev4"
4
+ version = "1.69.0"
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.dev4",
14
+ "gooddata-sdk~=1.69.0",
15
15
  "httpx>=0.27,<1.0",
16
16
  "orjson>=3.9.15,<4.0.0",
17
17
  "pydantic>=2.6,<3.0",
@@ -3,6 +3,7 @@
3
3
 
4
4
  import argparse
5
5
  import sys
6
+ import threading
6
7
  from datetime import datetime, timezone
7
8
  from pathlib import Path
8
9
 
@@ -139,14 +140,24 @@ def _parse_model_arg(val: str) -> tuple[str | None, str]:
139
140
 
140
141
 
141
142
  def _make_progress_callbacks(console: Console):
142
- """Build (on_item_start, on_run_done, on_item_done) callbacks that stream progress."""
143
+ """Build (on_item_start, on_run_done, on_item_done) callbacks that stream progress.
144
+
145
+ A threading lock guards all console.print() calls so that concurrent
146
+ ``--concurrency 2+`` workers do not deadlock when stdout is piped
147
+ (e.g. running in a background process).
148
+ """
149
+ _print_lock = threading.Lock()
143
150
 
144
151
  def on_item_start(index: int, total: int, item: DatasetItem) -> None:
145
- console.print(f"[dim]\\[{index}/{total}][/dim] [cyan]{item.id}[/cyan] {_truncate(item.question)}")
152
+ with _print_lock:
153
+ console.print(f"[dim]\\[{index}/{total}][/dim] [cyan]{item.id}[/cyan] {_truncate(item.question)}")
146
154
 
147
155
  def on_run_done(index: int, total: int, run_index: int, runs: int, passed: bool, latency: float) -> None:
148
156
  tag = "[green]pass[/green]" if passed else "[red]fail[/red]"
149
- console.print(f"[dim]\\[{index}/{total}][/dim] run {run_index}/{runs} {tag} [dim]{latency:.2f}s[/dim]")
157
+ with _print_lock:
158
+ console.print(
159
+ f"[dim]\\[{index}/{total}][/dim] run {run_index}/{runs} {tag} [dim]{latency:.2f}s[/dim]"
160
+ )
150
161
 
151
162
  def on_item_done(index: int, total: int, report: ItemReport) -> None:
152
163
  if report.skipped:
@@ -165,7 +176,8 @@ def _make_progress_callbacks(console: Console):
165
176
  f" [dim]({report.latency_s:.2f}s total, {report.avg_latency_s:.2f}s avg, "
166
177
  f"quality={quality_str}, {report.runs} run(s))[/dim]"
167
178
  )
168
- console.print(f"[dim]\\[{index}/{total}][/dim] -> {tag} [cyan]{report.id}[/cyan]{suffix}")
179
+ with _print_lock:
180
+ console.print(f"[dim]\\[{index}/{total}][/dim] -> {tag} [cyan]{report.id}[/cyan]{suffix}")
169
181
 
170
182
  return on_item_start, on_run_done, on_item_done
171
183
 
@@ -530,3 +530,44 @@ def test_cli_rejects_negative_concurrency(monkeypatch, fixtures_dir):
530
530
  ]
531
531
  )
532
532
  assert exit_code == 2
533
+
534
+
535
+ def test_progress_callbacks_thread_safe():
536
+ """Verify progress callbacks can be called from multiple threads without error."""
537
+ import io
538
+ import threading
539
+ from concurrent.futures import ThreadPoolExecutor, as_completed
540
+
541
+ console = Console(file=io.StringIO(), force_terminal=False)
542
+ on_item_start, on_run_done, on_item_done = cli_main._make_progress_callbacks(console)
543
+
544
+ errors: list[Exception] = []
545
+
546
+ def _worker(index: int) -> None:
547
+ try:
548
+ item = DatasetItem(
549
+ id=f"test-{index}",
550
+ dataset_name="test",
551
+ test_kind="general_question",
552
+ question=f"Question {index}",
553
+ expected_output="answer",
554
+ )
555
+ on_item_start(index, 100, item)
556
+ on_run_done(index, 100, 1, 1, index % 2 == 0, 1.5)
557
+ report = ItemReport(id=f"test-{index}", dataset_name="test", test_kind="general_question")
558
+ report.runs = 1
559
+ report.latency_s = 1.5
560
+ report.pass_at_k = index % 2 == 0
561
+ on_item_done(index, 100, report)
562
+ except Exception as e:
563
+ errors.append(e)
564
+
565
+ with ThreadPoolExecutor(max_workers=8) as pool:
566
+ futures = [pool.submit(_worker, i) for i in range(50)]
567
+ for f in as_completed(futures):
568
+ f.result() # re-raise if any thread failed
569
+
570
+ assert not errors, f"Thread-safety violation: {errors}"
571
+ output = console.file.getvalue()
572
+ assert "test-1" in output
573
+ assert "test-49" in output