Skip to content

Commit 413d756

Browse files
committed
Merge remote-tracking branch 'origin/master' into feat/chat-client-reasoning-steps
# Conflicts: # packages/gooddata-eval/src/gooddata_eval/cli/agentic_runner.py # packages/gooddata-eval/src/gooddata_eval/core/agentic/alert_skill.py # packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py # packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py
2 parents f3486db + 17bcb5c commit 413d756

20 files changed

Lines changed: 537 additions & 39 deletions

‎packages/gooddata-eval/README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ Both provider name and provider id are accepted as the prefix.
9292
|---|---|---|
9393
| `--runs K` | `2` | Independent runs per item (pass@K). An item passes if any run passes. |
9494
| `--concurrency K` | `1` | Number of items evaluated concurrently. `1` = sequential (default). Increase to load-test the agent under simultaneous requests. Progress output interleaves when K > 1. |
95+
| `--reasoning-effort LEVEL` | server default | `LOW`, `MEDIUM` or `HIGH`, sent as `options.reasoningEffort` on every chat message. Requires the `enableGenAiReasoningEffort` feature flag on the target organization — without it the server ignores the value. Applies to chat items only; `dashboard_summary` items go through the summary endpoint, which has no such option. |
9596

9697
#### Output
9798

@@ -104,7 +105,7 @@ Both provider name and provider id are accepted as the prefix.
104105

105106
| Flag | Description |
106107
|---|---|
107-
| `--langfuse` | Log scores and traces to Langfuse after each item. Requires `--langfuse-dataset`. Creates one named experiment run per model (`gd-eval-{timestamp}-{model}`). Requires `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_HOST`. |
108+
| `--langfuse` | Log scores and traces to Langfuse after each item. Requires `--langfuse-dataset`. Creates one named experiment run per model (`gd-eval-{timestamp}-{model}`, suffixed `-effort-{level}` when `--reasoning-effort` is set so runs differing only by effort stay separate). Requires `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_HOST`. |
108109

109110
### JSON report shape
110111

‎packages/gooddata-eval/src/gooddata_eval/cli/agentic_runner.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from gooddata_eval.core.agentic.metric_skill import evaluate_agentic_metric_skill
1515
from gooddata_eval.core.agentic.search_tool import evaluate_agentic_search_tool
1616
from gooddata_eval.core.agentic.visualization import evaluate_agentic_visualization
17+
from gooddata_eval.core.config import ReasoningEffort
1718
from gooddata_eval.core.models import CreatedVisualization, DatasetItem
1819
from gooddata_eval.core.runner import EvalReport, ItemReport
1920

@@ -24,6 +25,7 @@ class _LfKw(TypedDict, total=False):
2425
dataset_name: str
2526
run_timestamp: str
2627
model_version_override: str | None
28+
reasoning_effort: ReasoningEffort | None
2729

2830

2931
AGENTIC_TEST_KINDS = frozenset(
@@ -80,6 +82,7 @@ def _dispatch_agentic(
8082
langfuse: Any,
8183
run_ts: str,
8284
model_version_override: str | None,
85+
reasoning_effort: ReasoningEffort | None = None,
8386
) -> list[str] | None:
8487
"""Call the appropriate evaluate_agentic_* function for the item's test_kind.
8588
@@ -94,6 +97,7 @@ def _dispatch_agentic(
9497
"dataset_name": item.dataset_name,
9598
"run_timestamp": run_ts,
9699
"model_version_override": model_version_override,
100+
"reasoning_effort": reasoning_effort,
97101
}
98102

99103
if kind in ("vis_agentic", "agentic_visualization"):
@@ -180,6 +184,7 @@ def run_agentic_items(
180184
*,
181185
k: int = 2,
182186
model_version: str | None = None,
187+
reasoning_effort: ReasoningEffort | None = None,
183188
use_langfuse: bool = False,
184189
run_ts: str,
185190
on_item_start: Any = None,
@@ -206,7 +211,9 @@ def run_agentic_items(
206211
)
207212
t0 = time.perf_counter()
208213
try:
209-
reasoning_steps = _dispatch_agentic(item, host, token, workspace_id, k, langfuse, run_ts, model_version)
214+
reasoning_steps = _dispatch_agentic(
215+
item, host, token, workspace_id, k, langfuse, run_ts, model_version, reasoning_effort
216+
)
210217
item_report.pass_at_k = True
211218
item_report.runs = k
212219
item_report.reasoning_steps = reasoning_steps or []

‎packages/gooddata-eval/src/gooddata_eval/cli/main.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import threading
77
from datetime import datetime, timezone
88
from pathlib import Path
9+
from typing import get_args
910

1011
import httpx
1112
from gooddata_api_client.exceptions import ApiException
@@ -14,7 +15,7 @@
1415

1516
from gooddata_eval.cli.agentic_runner import AGENTIC_TEST_KINDS, run_agentic_items
1617
from gooddata_eval.core.chat.sse_client import ChatClient
17-
from gooddata_eval.core.config import RunConfig
18+
from gooddata_eval.core.config import ReasoningEffort, RunConfig
1819
from gooddata_eval.core.connection import ConnectionError_, resolve_connection
1920
from gooddata_eval.core.dataset.local import load_local_dataset
2021
from gooddata_eval.core.langfuse.sink import LangfuseSink
@@ -104,6 +105,13 @@ def _build_parser() -> argparse.ArgumentParser:
104105
dest="preserve_failed",
105106
help="Keep failed conversations on the server for post-mortem inspection.",
106107
)
108+
run.add_argument(
109+
"--reasoning-effort",
110+
dest="reasoning_effort",
111+
choices=list(get_args(ReasoningEffort)),
112+
help="Reasoning effort requested per message. Requires the enableGenAiReasoningEffort "
113+
"feature flag on the target organization; without it the server ignores the value.",
114+
)
107115
run.add_argument(
108116
"--langfuse",
109117
action="store_true",
@@ -292,6 +300,10 @@ def _run(config: RunConfig) -> int:
292300
progress_console.print(f"Provider={provider_display}, model={resolved.model_id}{switched}")
293301

294302
run_name = f"gd-eval-{run_ts}-{resolved.model_id}"
303+
if config.reasoning_effort:
304+
# Without this two runs differing only by effort share a name and are
305+
# indistinguishable in the report, which is the comparison this exists for.
306+
run_name = f"{run_name}-effort-{config.reasoning_effort.lower()}"
295307
if progress_console and config.log_to_langfuse:
296308
progress_console.print(f"Logging to Langfuse run '{run_name}'...")
297309

@@ -307,6 +319,7 @@ def _run(config: RunConfig) -> int:
307319
run_name=run_name,
308320
model_id=resolved.model_id,
309321
provider_type=resolved.provider_type,
322+
reasoning_effort=config.reasoning_effort,
310323
)
311324

312325
def on_langfuse_item_done(
@@ -329,6 +342,7 @@ def on_langfuse_item_done(
329342
workspace_id=config.workspace_id,
330343
k=config.runs,
331344
model_version=resolved.model_id,
345+
reasoning_effort=config.reasoning_effort,
332346
use_langfuse=config.log_to_langfuse,
333347
run_ts=run_ts,
334348
on_item_start=on_item_start,
@@ -342,6 +356,7 @@ def on_langfuse_item_done(
342356
token=config.token,
343357
workspace_id=config.workspace_id,
344358
preserve_failed=config.preserve_failed,
359+
reasoning_effort=config.reasoning_effort,
345360
),
346361
SummaryClient(host=config.host, token=config.token, workspace_id=config.workspace_id),
347362
)
@@ -433,6 +448,7 @@ def main(argv: list[str] | None = None) -> int:
433448
quiet=args.quiet,
434449
kind=args.kind,
435450
preserve_failed=args.preserve_failed,
451+
reasoning_effort=args.reasoning_effort,
436452
)
437453
return _run(config)
438454
except (

‎packages/gooddata-eval/src/gooddata_eval/core/agentic/_langfuse.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
import httpx
1717

18+
from gooddata_eval.core.config import ReasoningEffort, normalize_reasoning_effort
19+
1820
_log = logging.getLogger(__name__)
1921

2022
# ---------------------------------------------------------------------------
@@ -384,6 +386,7 @@ def build_run_context(
384386
run_timestamp: str | None,
385387
model_version_override: str | None,
386388
run_metadata_extra: dict[str, Any] | None = None,
389+
reasoning_effort: ReasoningEffort | None = None,
387390
) -> tuple[str, dict[str, Any]]:
388391
"""Return (run_name_base, run_metadata) with model version resolved from workspace API.
389392
@@ -399,15 +402,24 @@ def build_run_context(
399402
(e.g. a testing-framework tag or a CI run id for scoping). Default None keeps
400403
behavior unchanged. The SDK-derived model_version is applied last and cannot
401404
be overwritten by this dict.
405+
reasoning_effort: Effort the run requested, stamped into both the run name and
406+
the metadata so effort-varying runs stay comparable side by side.
402407
"""
408+
effort = normalize_reasoning_effort(reasoning_effort)
403409
model = get_model_version(host, token, workspace_id, model_version_override)
404410
ts = run_timestamp or datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
405411
base = f"{dataset_name}_{ts}"
406412
if model:
407413
base = f"{base}_{model}"
414+
# Part of the run name, not just metadata: two runs that differ only by effort would
415+
# otherwise collide on the same name and be indistinguishable in the report.
416+
if effort:
417+
base = f"{base}_effort-{effort.lower()}"
408418
# Caller supplies its own run tags (e.g. testing_framework); model_version is applied
409419
# last so the SDK-derived value cannot be overwritten by run_metadata_extra.
410420
metadata: dict[str, Any] = dict(run_metadata_extra) if run_metadata_extra else {}
421+
if effort:
422+
metadata["reasoning_effort"] = effort
411423
if model:
412424
metadata["model_version"] = model
413425
return base, metadata

‎packages/gooddata-eval/src/gooddata_eval/core/agentic/alert_skill.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from gooddata_eval.core.agentic._catalog import CatalogMetricAlert
1515
from gooddata_eval.core.chat.sse_client import ChatClient
16+
from gooddata_eval.core.config import ReasoningEffort
1617
from gooddata_eval.core.models import ToolCallEvent
1718

1819
try:
@@ -343,11 +344,12 @@ def run_agentic_alert_skill(
343344
k: int = _DEFAULT_K,
344345
max_iterations: int = _DEFAULT_MAX_ITERATIONS,
345346
initial_conversation_id: str | None = None,
347+
reasoning_effort: ReasoningEffort | None = None,
346348
) -> AgenticAlertSummary:
347349
"""Run the alert-skill agentic evaluation K times and return a summary."""
348350
expected = _normalize_expected_output(expected_output)
349351
run_results: list[AlertRunResult] = []
350-
client = ChatClient(host=host, token=token, workspace_id=workspace_id)
352+
client = ChatClient(host=host, token=token, workspace_id=workspace_id, reasoning_effort=reasoning_effort)
351353
sdk = GoodDataSdk.create(host, token)
352354

353355
def _run_once(conv_id: str) -> AlertRunResult:
@@ -466,6 +468,7 @@ def evaluate_agentic_alert_skill(
466468
run_timestamp: str | None = None,
467469
model_version_override: str | None = None,
468470
run_metadata_extra: dict | None = None,
471+
reasoning_effort: ReasoningEffort | None = None,
469472
) -> list[str]:
470473
"""Run alert-skill evaluation, log to Langfuse, and raise AlertSkillAssertionError on failure.
471474
@@ -490,6 +493,7 @@ def evaluate_agentic_alert_skill(
490493
k=k,
491494
max_iterations=max_iterations,
492495
initial_conversation_id=initial_conversation_id,
496+
reasoning_effort=reasoning_effort,
493497
)
494498

495499
if langfuse is not None and dataset_item_id:
@@ -509,6 +513,7 @@ def evaluate_agentic_alert_skill(
509513
run_timestamp,
510514
model_version_override,
511515
run_metadata_extra,
516+
reasoning_effort,
512517
)
513518
traces_by_conv = find_traces_per_conversation(
514519
langfuse,

‎packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from gooddata_eval.core.agentic.alert_skill import render_alert_proposal
1515
from gooddata_eval.core.agentic.metric_skill import _delete_metric, _extract_created_metric_ids
1616
from gooddata_eval.core.chat.sse_client import ChatClient
17+
from gooddata_eval.core.config import ReasoningEffort
1718
from gooddata_eval.core.models import ChatResult, ToolCallEvent
1819
from gooddata_eval.core.scoring import (
1920
check_filters,
@@ -279,14 +280,15 @@ def run_agentic_conversation(
279280
fixture: ConversationFixture,
280281
max_clarification_turns: int = 20,
281282
initial_conversation_id: str | None = None,
283+
reasoning_effort: ReasoningEffort | None = None,
282284
) -> ConversationResult:
283285
"""Run a multi-turn, multi-skill conversation evaluation (no K-runs).
284286
285287
A single conversation is used for all turns in the fixture. Each turn may
286288
trigger up to *max_clarification_turns* additional rounds of simulated-user
287289
replies before the agent produces the expected output.
288290
"""
289-
client = ChatClient(host=host, token=token, workspace_id=workspace_id)
291+
client = ChatClient(host=host, token=token, workspace_id=workspace_id, reasoning_effort=reasoning_effort)
290292
sdk = GoodDataSdk.create(host, token)
291293
turn_results: list[TurnResult] = []
292294
turn_outputs: dict[str, dict] = {}
@@ -407,6 +409,7 @@ def evaluate_agentic_conversation(
407409
run_timestamp: str | None = None,
408410
model_version_override: str | None = None,
409411
run_metadata_extra: dict | None = None,
412+
reasoning_effort: ReasoningEffort | None = None,
410413
) -> list[str]:
411414
"""Run conversation evaluation, log to Langfuse, and raise on failure.
412415
@@ -430,6 +433,7 @@ def evaluate_agentic_conversation(
430433
fixture=fixture,
431434
max_clarification_turns=max_clarification_turns,
432435
initial_conversation_id=initial_conversation_id,
436+
reasoning_effort=reasoning_effort,
433437
)
434438

435439
if langfuse is not None and dataset_item_id:
@@ -449,6 +453,7 @@ def evaluate_agentic_conversation(
449453
run_timestamp,
450454
model_version_override,
451455
run_metadata_extra,
456+
reasoning_effort,
452457
)
453458
traces_by_conv = find_traces_per_conversation(
454459
langfuse,

‎packages/gooddata-eval/src/gooddata_eval/core/agentic/general_question.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from dataclasses import dataclass
77

88
from gooddata_eval.core.chat.sse_client import ChatClient
9+
from gooddata_eval.core.config import ReasoningEffort
910
from gooddata_eval.core.evaluators._llm_judge import LLMJudge
1011

1112
_DEFAULT_K = 1
@@ -71,10 +72,11 @@ def run_agentic_general_question(
7172
expected_output: str,
7273
k: int = _DEFAULT_K,
7374
initial_conversation_id: str | None = None,
75+
reasoning_effort: ReasoningEffort | None = None,
7476
) -> AgenticGeneralQuestionSummary:
7577
"""Run the general-question agentic evaluation K times and return a summary."""
7678
run_results: list[GeneralQuestionResult] = []
77-
client = ChatClient(host=host, token=token, workspace_id=workspace_id)
79+
client = ChatClient(host=host, token=token, workspace_id=workspace_id, reasoning_effort=reasoning_effort)
7880
judge = LLMJudge(_GENERAL_QUESTION_EVALUATION_STEPS, model="gpt-4o")
7981

8082
try:
@@ -153,6 +155,7 @@ def evaluate_agentic_general_question(
153155
run_timestamp: str | None = None,
154156
model_version_override: str | None = None,
155157
run_metadata_extra: dict | None = None,
158+
reasoning_effort: ReasoningEffort | None = None,
156159
) -> None:
157160
"""Run general-question evaluation, log to Langfuse, and raise on failure."""
158161
from datetime import datetime as _dt # noqa: PLC0415
@@ -171,6 +174,7 @@ def evaluate_agentic_general_question(
171174
expected_output=expected_output,
172175
k=k,
173176
initial_conversation_id=initial_conversation_id,
177+
reasoning_effort=reasoning_effort,
174178
)
175179

176180
if langfuse is not None and dataset_item_id:
@@ -190,6 +194,7 @@ def evaluate_agentic_general_question(
190194
run_timestamp,
191195
model_version_override,
192196
run_metadata_extra,
197+
reasoning_effort,
193198
)
194199
traces_by_conv = find_traces_per_conversation(
195200
langfuse,

‎packages/gooddata-eval/src/gooddata_eval/core/agentic/guardrail.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from dataclasses import dataclass
77

88
from gooddata_eval.core.chat.sse_client import ChatClient
9+
from gooddata_eval.core.config import ReasoningEffort
910
from gooddata_eval.core.evaluators._llm_judge import LLMJudge
1011

1112
_DEFAULT_K = 1
@@ -68,10 +69,11 @@ def run_agentic_guardrail(
6869
expected_output: str,
6970
k: int = _DEFAULT_K,
7071
initial_conversation_id: str | None = None,
72+
reasoning_effort: ReasoningEffort | None = None,
7173
) -> AgenticGuardrailSummary:
7274
"""Run the guardrail agentic evaluation K times and return a summary."""
7375
run_results: list[GuardrailResult] = []
74-
client = ChatClient(host=host, token=token, workspace_id=workspace_id)
76+
client = ChatClient(host=host, token=token, workspace_id=workspace_id, reasoning_effort=reasoning_effort)
7577
judge = LLMJudge(_GUARDRAIL_EVALUATION_STEPS, model="gpt-4o")
7678

7779
try:
@@ -150,6 +152,7 @@ def evaluate_agentic_guardrail(
150152
run_timestamp: str | None = None,
151153
model_version_override: str | None = None,
152154
run_metadata_extra: dict | None = None,
155+
reasoning_effort: ReasoningEffort | None = None,
153156
) -> None:
154157
"""Run guardrail evaluation, log to Langfuse, and raise on failure."""
155158
from datetime import datetime as _dt # noqa: PLC0415
@@ -168,6 +171,7 @@ def evaluate_agentic_guardrail(
168171
expected_output=expected_output,
169172
k=k,
170173
initial_conversation_id=initial_conversation_id,
174+
reasoning_effort=reasoning_effort,
171175
)
172176

173177
if langfuse is not None and dataset_item_id:
@@ -187,6 +191,7 @@ def evaluate_agentic_guardrail(
187191
run_timestamp,
188192
model_version_override,
189193
run_metadata_extra,
194+
reasoning_effort,
190195
)
191196
traces_by_conv = find_traces_per_conversation(
192197
langfuse,

0 commit comments

Comments
 (0)