Skip to content

Commit 8d094ef

Browse files
Merge pull request #1778 from gooddata/snapshot-master-58485168-to-rel/dev
[bot] Merge master/58485168 into rel/dev
2 parents 1bc5a95 + 5848516 commit 8d094ef

13 files changed

Lines changed: 312 additions & 59 deletions

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

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
ReasoningStepEvent,
3030
ToolCallEvent,
3131
build_latency_breakdown,
32+
shift_and_index_events,
3233
)
3334

3435
try:
@@ -526,21 +527,14 @@ def _run_once(conv_id: str) -> AlertRunResult:
526527
chat_result = client.send_message(conv_id, current_question)
527528
reasoning_steps.extend(chat_result.reasoning_steps or [])
528529
response_id = chat_result.response_id or response_id
529-
for tc in chat_result.tool_call_events or []:
530-
if tc.call_ts is not None:
531-
tc.call_ts += turn_offset
532-
if tc.result_ts is not None:
533-
tc.result_ts += turn_offset
534-
if tc.index is not None:
535-
tc.index += tool_index_offset
536-
for rs in chat_result.reasoning_step_events or []:
537-
rs.ts += turn_offset
538-
rs.index += reasoning_index_offset
530+
turn_offset, tool_index_offset, reasoning_index_offset = shift_and_index_events(
531+
chat_result,
532+
turn_offset=turn_offset,
533+
tool_index_offset=tool_index_offset,
534+
reasoning_index_offset=reasoning_index_offset,
535+
)
539536
all_tool_call_events.extend(chat_result.tool_call_events or [])
540537
all_reasoning_step_events.extend(chat_result.reasoning_step_events or [])
541-
tool_index_offset += len(chat_result.tool_call_events or [])
542-
reasoning_index_offset += len(chat_result.reasoning_step_events or [])
543-
turn_offset += chat_result.turn_wall_clock_sec or 0.0
544538
alert_id, actual_args, tool_called = _extract_alert_call(chat_result.tool_call_events or [])
545539
if tool_called:
546540
alert_id_to_delete = alert_id

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

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
ReasoningStepEvent,
3232
ToolCallEvent,
3333
build_latency_breakdown,
34+
shift_and_index_events,
3435
)
3536
from gooddata_eval.core.scoring import (
3637
check_filters,
@@ -368,22 +369,15 @@ def run_agentic_conversation(
368369
for _iter in range(max_clarification_turns + 1):
369370
chat_result = client.send_message(conversation_id, current_message)
370371
final_result = chat_result
371-
for tc in chat_result.tool_call_events or []:
372-
if tc.call_ts is not None:
373-
tc.call_ts += turn_offset
374-
if tc.result_ts is not None:
375-
tc.result_ts += turn_offset
376-
if tc.index is not None:
377-
tc.index += tool_index_offset
378-
for rs in chat_result.reasoning_step_events or []:
379-
rs.ts += turn_offset
380-
rs.index += reasoning_index_offset
372+
turn_offset, tool_index_offset, reasoning_index_offset = shift_and_index_events(
373+
chat_result,
374+
turn_offset=turn_offset,
375+
tool_index_offset=tool_index_offset,
376+
reasoning_index_offset=reasoning_index_offset,
377+
)
381378
all_tool_calls.extend(chat_result.tool_call_events or [])
382379
conversation_tool_call_events.extend(chat_result.tool_call_events or [])
383380
conversation_reasoning_step_events.extend(chat_result.reasoning_step_events or [])
384-
tool_index_offset += len(chat_result.tool_call_events or [])
385-
reasoning_index_offset += len(chat_result.reasoning_step_events or [])
386-
turn_offset += chat_result.turn_wall_clock_sec or 0.0
387381
reasoning_steps.extend(chat_result.reasoning_steps or [])
388382
response_id = chat_result.response_id or response_id
389383

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@
1818
from gooddata_eval.core.chat.sse_client import ChatClient
1919
from gooddata_eval.core.config import ReasoningEffort
2020
from gooddata_eval.core.evaluators._llm_judge import JudgeResponseError, LLMJudge, score_run
21-
from gooddata_eval.core.models import AgenticAssertionError, AgenticEvalOutcome
21+
from gooddata_eval.core.models import (
22+
AgenticAssertionError,
23+
AgenticEvalOutcome,
24+
ReasoningStepEvent,
25+
ToolCallEvent,
26+
build_latency_breakdown,
27+
)
2228
from gooddata_eval.core.timing import PhaseTimings, log_timer, sum_timings
2329

2430
_DEFAULT_K = 1
@@ -71,6 +77,8 @@ class GeneralQuestionResult:
7177
# excluded from pass@K and from Langfuse scoring rather than counted as a failure:
7278
# scoring it 0 would publish a verdict the judge never gave.
7379
judge_error: str | None = None
80+
tool_call_events: list[ToolCallEvent] = field(default_factory=list)
81+
reasoning_step_events: list[ReasoningStepEvent] = field(default_factory=list)
7482

7583

7684
@dataclass
@@ -132,6 +140,8 @@ def _run_single_general_question(
132140
# agent still answered, and that measurement is the one worth keeping.
133141
timings=PhaseTimings(agent_s=agent_elapsed, judge_s=judge_elapsed),
134142
judge_error=verdict.error,
143+
tool_call_events=list(chat_result.tool_call_events or []),
144+
reasoning_step_events=list(chat_result.reasoning_step_events or []),
135145
)
136146

137147

@@ -303,6 +313,7 @@ def _write_scores(ctx: RunTraceContext) -> None:
303313
"judge_passed": best.passed,
304314
"judge_reasoning": best.reasoning,
305315
"actual_output": best.actual_output,
316+
"latency_breakdown": build_latency_breakdown(best.tool_call_events, best.reasoning_step_events),
306317
# Only present when it happened, so the usual JSON shape is unchanged. A
307318
# pass@K computed over fewer runs than --runs asked for is a weaker result and
308319
# the report has to say so.

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@
1818
)
1919
from gooddata_eval.core.chat.sse_client import ChatClient
2020
from gooddata_eval.core.config import ReasoningEffort
21-
from gooddata_eval.core.models import AgenticAssertionError, AgenticEvalOutcome, ToolCallEvent
21+
from gooddata_eval.core.models import (
22+
AgenticAssertionError,
23+
AgenticEvalOutcome,
24+
ChatResult,
25+
ReasoningStepEvent,
26+
ToolCallEvent,
27+
build_latency_breakdown,
28+
shift_and_index_events,
29+
)
2230

2331
_log = logging.getLogger(__name__)
2432

@@ -170,6 +178,8 @@ class KdaRunResult:
170178
turn_wall_clock_sec: float | None = None
171179
reasoning_steps: list[str] = field(default_factory=list)
172180
response_id: str | None = None
181+
tool_call_events: list[ToolCallEvent] = field(default_factory=list)
182+
reasoning_step_events: list[ReasoningStepEvent] = field(default_factory=list)
173183

174184

175185
@dataclass
@@ -240,6 +250,22 @@ def _run_once(conv_id: str) -> KdaRunResult:
240250
current_question = question
241251
reasoning_steps: list[str] = []
242252
response_id: str | None = None
253+
all_tool_call_events: list[ToolCallEvent] = []
254+
all_reasoning_step_events: list[ReasoningStepEvent] = []
255+
turn_offset = 0.0 # each turn's call_ts/ts restarts near 0 -- shift by prior turns' wall time
256+
tool_index_offset = 0
257+
reasoning_index_offset = 0
258+
259+
def _accumulate(result: ChatResult) -> None:
260+
nonlocal turn_offset, tool_index_offset, reasoning_index_offset
261+
turn_offset, tool_index_offset, reasoning_index_offset = shift_and_index_events(
262+
result,
263+
turn_offset=turn_offset,
264+
tool_index_offset=tool_index_offset,
265+
reasoning_index_offset=reasoning_index_offset,
266+
)
267+
all_tool_call_events.extend(result.tool_call_events or [])
268+
all_reasoning_step_events.extend(result.reasoning_step_events or [])
243269

244270
for iteration in range(max_iterations):
245271
try:
@@ -250,13 +276,15 @@ def _run_once(conv_id: str) -> KdaRunResult:
250276
if partial is not None:
251277
reasoning_steps.extend(partial.reasoning_steps or [])
252278
response_id = partial.response_id or response_id
279+
_accumulate(partial)
253280
create_args, execute_result = _extract_kda_calls(partial.tool_call_events or [])
254281
if create_args is not None:
255282
turn_wall_clock_sec = partial.turn_wall_clock_sec
256283
turn_completed = False
257284
break
258285
reasoning_steps.extend(chat_result.reasoning_steps or [])
259286
response_id = chat_result.response_id or response_id
287+
_accumulate(chat_result)
260288
create_args, execute_result = _extract_kda_calls(chat_result.tool_call_events or [])
261289
response_text = (chat_result.text_response or "").strip()
262290
turn_completed = chat_result.stream_ended and bool(response_text)
@@ -295,6 +323,8 @@ def _run_once(conv_id: str) -> KdaRunResult:
295323
turn_wall_clock_sec=turn_wall_clock_sec,
296324
reasoning_steps=reasoning_steps,
297325
response_id=response_id,
326+
tool_call_events=all_tool_call_events,
327+
reasoning_step_events=all_reasoning_step_events,
298328
)
299329

300330
try:
@@ -451,6 +481,7 @@ def _write_scores(ctx: RunTraceContext) -> None:
451481
"disambiguated": ev.disambiguated,
452482
"actual_create_args": best.actual_create_args,
453483
"actual_execute_result": best.actual_execute_result,
484+
"latency_breakdown": build_latency_breakdown(best.tool_call_events, best.reasoning_step_events),
454485
}
455486

456487
if not summary.pass_at_k:

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

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
ReasoningStepEvent,
2929
ToolCallEvent,
3030
build_latency_breakdown,
31+
shift_and_index_events,
3132
)
3233
from gooddata_eval.core.timing import PhaseTimings, log_timer, sum_timings
3334

@@ -321,21 +322,14 @@ def _execute_single_metric_run(
321322
timings.agent_s += agent_elapsed
322323
reasoning_steps.extend(chat_result.reasoning_steps or [])
323324
response_id = chat_result.response_id or response_id
324-
for tc in chat_result.tool_call_events or []:
325-
if tc.call_ts is not None:
326-
tc.call_ts += turn_offset
327-
if tc.result_ts is not None:
328-
tc.result_ts += turn_offset
329-
if tc.index is not None:
330-
tc.index += tool_index_offset
331-
for rs in chat_result.reasoning_step_events or []:
332-
rs.ts += turn_offset
333-
rs.index += reasoning_index_offset
325+
turn_offset, tool_index_offset, reasoning_index_offset = shift_and_index_events(
326+
chat_result,
327+
turn_offset=turn_offset,
328+
tool_index_offset=tool_index_offset,
329+
reasoning_index_offset=reasoning_index_offset,
330+
)
334331
all_tool_call_events.extend(chat_result.tool_call_events or [])
335332
all_reasoning_step_events.extend(chat_result.reasoning_step_events or [])
336-
tool_index_offset += len(chat_result.tool_call_events or [])
337-
reasoning_index_offset += len(chat_result.reasoning_step_events or [])
338-
turn_offset += chat_result.turn_wall_clock_sec or 0.0
339333
for metric_id in _extract_created_metric_ids(chat_result.tool_call_events or []):
340334
if metric_id not in created_metric_ids:
341335
created_metric_ids.append(metric_id)

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@
1616
)
1717
from gooddata_eval.core.chat.sse_client import ChatClient
1818
from gooddata_eval.core.config import ReasoningEffort
19-
from gooddata_eval.core.models import AgenticAssertionError, AgenticEvalOutcome, ToolCallEvent
19+
from gooddata_eval.core.models import (
20+
AgenticAssertionError,
21+
AgenticEvalOutcome,
22+
ReasoningStepEvent,
23+
ToolCallEvent,
24+
build_latency_breakdown,
25+
)
2026

2127
_DEFAULT_K = 1
2228

@@ -59,6 +65,8 @@ class SearchResult:
5965
tool_call_names: list[str]
6066
reasoning_steps: list[str] = field(default_factory=list)
6167
response_id: str | None = None
68+
tool_call_events: list[ToolCallEvent] = field(default_factory=list)
69+
reasoning_step_events: list[ReasoningStepEvent] = field(default_factory=list)
6270

6371

6472
@dataclass
@@ -103,6 +111,8 @@ def run_agentic_search_tool(
103111
tool_call_names=[tc.function_name for tc in tcs],
104112
reasoning_steps=list(chat_result.reasoning_steps or []),
105113
response_id=chat_result.response_id,
114+
tool_call_events=list(chat_result.tool_call_events or []),
115+
reasoning_step_events=list(chat_result.reasoning_step_events or []),
106116
)
107117
)
108118
finally:
@@ -124,6 +134,8 @@ def run_agentic_search_tool(
124134
tool_call_names=[tc.function_name for tc in tcs],
125135
reasoning_steps=list(chat_result.reasoning_steps or []),
126136
response_id=chat_result.response_id,
137+
tool_call_events=list(chat_result.tool_call_events or []),
138+
reasoning_step_events=list(chat_result.reasoning_step_events or []),
127139
)
128140
)
129141
finally:
@@ -233,6 +245,7 @@ def _write_scores(ctx: RunTraceContext) -> None:
233245
"tool_selected": best.tool_selected,
234246
"tool_correct": best.tool_correct,
235247
"tool_call_names": best.tool_call_names,
248+
"latency_breakdown": build_latency_breakdown(best.tool_call_events, best.reasoning_step_events),
236249
}
237250

238251
if not summary.pass_at_k:

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

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
ReasoningStepEvent,
3535
ToolCallEvent,
3636
build_latency_breakdown,
37+
shift_and_index_events,
3738
)
3839
from gooddata_eval.core.scoring import get_dimension_uri_set, get_metric_uri_set, uri_to_display_name
3940

@@ -192,23 +193,16 @@ def _execute_single_run(
192193
for iteration in range(max_iterations):
193194
total_turns += 1.0
194195
total_steps += float(current_result.reasoning_step_count)
195-
for tc in current_result.tool_call_events:
196-
if tc.call_ts is not None:
197-
tc.call_ts += turn_offset
198-
if tc.result_ts is not None:
199-
tc.result_ts += turn_offset
200-
if tc.index is not None:
201-
tc.index += tool_index_offset
202-
for rs in current_result.reasoning_step_events:
203-
rs.ts += turn_offset
204-
rs.index += reasoning_index_offset
196+
turn_offset, tool_index_offset, reasoning_index_offset = shift_and_index_events(
197+
current_result,
198+
turn_offset=turn_offset,
199+
tool_index_offset=tool_index_offset,
200+
reasoning_index_offset=reasoning_index_offset,
201+
)
205202
all_tool_call_events.extend(current_result.tool_call_events)
206203
all_reasoning_step_events.extend(current_result.reasoning_step_events)
207-
tool_index_offset += len(current_result.tool_call_events)
208-
reasoning_index_offset += len(current_result.reasoning_step_events)
209204
reasoning_steps.extend(current_result.reasoning_steps or [])
210205
response_id = current_result.response_id or response_id
211-
turn_offset += current_result.turn_wall_clock_sec or 0.0
212206

213207
viz_produced = bool(current_result.created_visualizations and current_result.created_visualizations.objects)
214208
if viz_produced:

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from gooddata_eval.core.evaluators._deep_subset import deep_subset
88
from gooddata_eval.core.evaluators.base import ItemEvaluation
9-
from gooddata_eval.core.models import ChatResult, DatasetItem
9+
from gooddata_eval.core.models import ChatResult, DatasetItem, build_latency_breakdown
1010

1111
_TRIGGER_MAP = {"Every time": "ALWAYS", "One time": "ONCE"}
1212

@@ -72,7 +72,13 @@ def evaluate(self, item: DatasetItem, chat_result: ChatResult) -> ItemEvaluation
7272
return ItemEvaluation(
7373
passed=False,
7474
rank_key=(False,) * 7,
75-
detail={"alert_created": False, "automation_id": None},
75+
detail={
76+
"alert_created": False,
77+
"automation_id": None,
78+
"latency_breakdown": build_latency_breakdown(
79+
chat_result.tool_call_events, chat_result.reasoning_step_events
80+
),
81+
},
7682
)
7783

7884
args = tool_event.parsed_arguments()
@@ -145,5 +151,8 @@ def evaluate(self, item: DatasetItem, chat_result: ChatResult) -> ItemEvaluation
145151
# lets a caller (e.g. a cleanup step) delete the exact object
146152
# created instead of diffing the workspace catalog before/after.
147153
"automation_id": automation_id,
154+
"latency_breakdown": build_latency_breakdown(
155+
chat_result.tool_call_events, chat_result.reasoning_step_events
156+
),
148157
},
149158
)

‎packages/gooddata-eval/src/gooddata_eval/core/evaluators/metric_skill.py‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"""Evaluator for metric_skill: agent must create the correct metric via create_metric tool call."""
33

44
from gooddata_eval.core.evaluators.base import ItemEvaluation
5-
from gooddata_eval.core.models import ChatResult, DatasetItem
5+
from gooddata_eval.core.models import ChatResult, DatasetItem, build_latency_breakdown
66

77

88
def _find_create_metric(chat_result: ChatResult):
@@ -28,7 +28,15 @@ def evaluate(self, item: DatasetItem, chat_result: ChatResult) -> ItemEvaluation
2828
return ItemEvaluation(
2929
passed=False,
3030
rank_key=(False, False, False),
31-
detail={"metric_created": False, "maql_correct": False, "format_correct": False, "metric_id": None},
31+
detail={
32+
"metric_created": False,
33+
"maql_correct": False,
34+
"format_correct": False,
35+
"metric_id": None,
36+
"latency_breakdown": build_latency_breakdown(
37+
chat_result.tool_call_events, chat_result.reasoning_step_events
38+
),
39+
},
3240
)
3341

3442
result = tool_event.parsed_result()
@@ -59,5 +67,8 @@ def evaluate(self, item: DatasetItem, chat_result: ChatResult) -> ItemEvaluation
5967
# delete the exact object created instead of diffing the workspace
6068
# catalog before/after and guessing by name.
6169
"metric_id": payload.get("metric_id"),
70+
"latency_breakdown": build_latency_breakdown(
71+
chat_result.tool_call_events, chat_result.reasoning_step_events
72+
),
6273
},
6374
)

0 commit comments

Comments
 (0)