Skip to content

fix(gooddata-eval): delete metrics created during agentic eval runs#1685

Open
myhoai wants to merge 1 commit into
masterfrom
QA-28379-eval-metric-cleanup
Open

fix(gooddata-eval): delete metrics created during agentic eval runs#1685
myhoai wants to merge 1 commit into
masterfrom
QA-28379-eval-metric-cleanup

Conversation

@myhoai

@myhoai myhoai commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Metric-skill and conversation evaluators create metrics in a shared, persistent eval workspace but never removed them. A leftover metric is detected and reused by a later test (the agent returns SELECT {existing_id} instead of full MAQL), so the assertion fails — and it makes some conversation metric-turns produce no create_metric call at all.

Delete each metric a run creates on the way out, keyed on the exact metric_id from the create_metric tool result, in a finally block:

  • metric_skill: per-run cleanup in _execute_single_metric_run.
  • conversation: collect metric_ids across turns, delete after the conversation completes (deferred so a later turn's $ref still resolves).

Mirrors the existing alert_skill._delete_alert pattern. Parallelism-safe: each run deletes only the id it created, so concurrent tests sharing the workspace never touch each other's objects. No version bump.

JIRA: QA-28379
risk: nonprod

Summary by CodeRabbit

  • Bug Fixes
    • Automatically cleans up metrics created during simulated multi-turn conversations.
    • Automatically cleans up metrics created during metric-skill evaluations.
    • Performs cleanup as best-effort even when an evaluation fails partway through, preventing leftover test metrics from impacting later runs.
  • Tests
    • Added unit tests covering metric deletion for both conversation and metric-skill flows, including validation of the cleanup request behavior and error handling.

@myhoai myhoai requested review from hkad98, lupko and pcerny as code owners July 10, 2026 10:23
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Agentic conversations and metric-skill runs now track created metrics and delete them during final cleanup, while preserving metrics for $ref resolution across conversation turns.

Changes

Metric cleanup

Layer / File(s) Summary
Metric deletion and skill-run cleanup
packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py, packages/gooddata-eval/tests/test_agentic_metric_skill.py
Adds workspace metric deletion, tracks created metrics during each run, performs cleanup in finally, passes workspace_id through all run invocations, and tests deletion and failure handling.
Conversation metric tracking
packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py, packages/gooddata-eval/tests/test_agentic_conversation.py
Collects unique metric IDs from create_metric calls across turns, deletes them after completion, and verifies the cleanup URL.

Estimated code review effort: 3 (Moderate) | ~15–30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MetricSkill
  participant ChatClient
  participant GoodDataAPI
  MetricSkill->>ChatClient: Run metric evaluation
  ChatClient->>GoodDataAPI: Create metric
  GoodDataAPI-->>ChatClient: Return metric_id
  MetricSkill->>GoodDataAPI: Delete workspace metric
  GoodDataAPI-->>MetricSkill: Return deletion status
Loading

Poem

I’m a rabbit with metrics tucked neat,
No stray little measures remain in the suite.
Across turns, refs hop where they should,
Then cleanup nibbles them—just as it could.
Snip, hop, delete: the workspace looks good!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: cleanup of metrics created during agentic evaluation runs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.91837% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.95%. Comparing base (f8f7256) to head (67ec82b).

Files with missing lines Patch % Lines
...val/src/gooddata_eval/core/agentic/metric_skill.py 95.12% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1685      +/-   ##
==========================================
+ Coverage   77.85%   77.95%   +0.10%     
==========================================
  Files         271      271              
  Lines       18603    18637      +34     
==========================================
+ Hits        14483    14529      +46     
+ Misses       4120     4108      -12     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py`:
- Around line 130-145: Metric cleanup currently ignores unsuccessful DELETE
responses, allowing stale metrics to remain. In _delete_metric, store the
response from client._client.delete and call raise_for_status() before
returning, while preserving the existing exception logging behavior; follow the
pattern used by alert_skill._delete_alert.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d5fc3906-3ce0-41f2-8394-c8f113bb3c9f

📥 Commits

Reviewing files that changed from the base of the PR and between f8f7256 and a6682fc.

📒 Files selected for processing (2)
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py

@myhoai myhoai force-pushed the QA-28379-eval-metric-cleanup branch from a6682fc to 0b49776 Compare July 10, 2026 11:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py (1)

143-143: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Suppress the intentional broad-catch warning.

Best-effort cleanup justifies catching Exception, but Ruff still reports BLE001. Add the suppression already proposed in the previous review.

-    except Exception as exc:
+    except Exception as exc:  # noqa: BLE001 - best-effort cleanup
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py` at
line 143, Suppress Ruff’s BLE001 warning for the intentional broad `Exception`
handler in the cleanup logic by adding the previously agreed inline suppression
to the `except Exception as exc` statement.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py`:
- Around line 162-166: Update the conversation cleanup logic in the metric-skill
evaluation function and its create-metric response handling to collect every
successfully returned metric ID, rather than retaining only the first result.
Preserve the first creation result for evaluation, pass the complete ID
collection to _delete_metric, and ensure all successful create_metric calls are
tracked across responses.

---

Duplicate comments:
In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py`:
- Line 143: Suppress Ruff’s BLE001 warning for the intentional broad `Exception`
handler in the cleanup logic by adding the previously agreed inline suppression
to the `except Exception as exc` statement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6a104d28-ad34-48b7-a98b-f6be343741f4

📥 Commits

Reviewing files that changed from the base of the PR and between a6682fc and 0b49776.

📒 Files selected for processing (2)
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py

Comment on lines +162 to +166
"""Drive one full multi-turn metric-skill conversation and evaluate the result.

Any metric the agent creates during this run is deleted on the way out (see
``_delete_metric``) so it cannot leak into — and be reused by — a later test
sharing the workspace.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Track every successful create_metric call.

Only the first result’s ID is retained. If one response contains multiple successful creation calls, additional metrics remain in the shared workspace. Collect all returned IDs while preserving the first result for evaluation.

Proposed fix
     metric_result: dict | None = None
-    metric_id_to_delete: str | None = None
+    metric_ids_to_delete: list[str] = []
     turns = 0
@@
             turns += 1
             chat_result = client.send_message(conversation_id, current_question)
-            candidate = _extract_metric_result(chat_result.tool_call_events or [])
+            tool_call_events = chat_result.tool_call_events or []
+            candidate = _extract_metric_result(tool_call_events)
+            for tc in tool_call_events:
+                if tc.function_name != "create_metric" or not tc.result:
+                    continue
+                result_data = tc.parsed_result()
+                data = result_data.get("data", result_data) if isinstance(result_data, dict) else None
+                metric_id = data.get("metric_id") if isinstance(data, dict) else None
+                if metric_id and metric_id not in metric_ids_to_delete:
+                    metric_ids_to_delete.append(metric_id)
+
             if candidate is not None:
                 metric_result = candidate
-                metric_id_to_delete = candidate.get("metric_id")
                 break
@@
     finally:
-        if metric_id_to_delete:
-            _delete_metric(client, workspace_id, metric_id_to_delete)
+        for metric_id in metric_ids_to_delete:
+            _delete_metric(client, workspace_id, metric_id)

Also applies to: 170-182, 200-202

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py` around
lines 162 - 166, Update the conversation cleanup logic in the metric-skill
evaluation function and its create-metric response handling to collect every
successfully returned metric ID, rather than retaining only the first result.
Preserve the first creation result for evaluation, pass the complete ID
collection to _delete_metric, and ensure all successful create_metric calls are
tracked across responses.

@myhoai myhoai force-pushed the QA-28379-eval-metric-cleanup branch from 0b49776 to e50fad2 Compare July 10, 2026 11:54

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/gooddata-eval/tests/test_agentic_conversation.py (1)

174-216: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Cover multi-turn and teardown-failure cleanup.

This test covers only one successful turn. Add a two-turn fixture with $ref resolution and duplicate/new metric IDs, plus a failure-path case; assert each unique ID is deleted once even when the conversation exits exceptionally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gooddata-eval/tests/test_agentic_conversation.py` around lines 174 -
216, Expand test_run_agentic_conversation_deletes_created_metrics to use a
two-turn fixture with $ref resolution, exercising duplicate and newly created
metric IDs and asserting each unique metric is deleted exactly once. Add a
separate failure-path test where the conversation raises an exception, then
verify teardown still deletes all unique created metric IDs once.
packages/gooddata-eval/tests/test_agentic_metric_skill.py (1)

170-201: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add failure-path and multi-run cleanup coverage.

This only exercises one successful metric-skill run. Add coverage for cleanup when evaluation raises and for multiple iterations, asserting that every created metric ID is deleted exactly once.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gooddata-eval/tests/test_agentic_metric_skill.py` around lines 170 -
201, Add tests alongside test_run_agentic_metric_skill_deletes_created_metric
covering an evaluation failure that raises after metric creation, asserting the
created metric is still deleted exactly once, and covering multiple iterations
that create distinct metrics, asserting every returned metric ID is deleted
exactly once. Reuse the existing ChatClient mock setup and configure the
evaluation inputs or mocked responses to exercise both cleanup paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py`:
- Around line 178-182: Update the metric cleanup logic around
_extract_metric_result and the surrounding chat-result loop to collect every
successful create_metric metric_id, rather than assigning only the first
candidate and breaking. Mirror conversation.run_agentic_conversation’s
aggregation behavior, then ensure all collected IDs are passed to cleanup so no
created metrics leak.
- Line 143: Add an inline <code># noqa: BLE001</code> comment with a brief
rationale to the intentional broad exception handler in the cleanup function
containing <code>except Exception as exc</code>, documenting that cleanup is
best-effort and must not propagate errors.

---

Nitpick comments:
In `@packages/gooddata-eval/tests/test_agentic_conversation.py`:
- Around line 174-216: Expand
test_run_agentic_conversation_deletes_created_metrics to use a two-turn fixture
with $ref resolution, exercising duplicate and newly created metric IDs and
asserting each unique metric is deleted exactly once. Add a separate
failure-path test where the conversation raises an exception, then verify
teardown still deletes all unique created metric IDs once.

In `@packages/gooddata-eval/tests/test_agentic_metric_skill.py`:
- Around line 170-201: Add tests alongside
test_run_agentic_metric_skill_deletes_created_metric covering an evaluation
failure that raises after metric creation, asserting the created metric is still
deleted exactly once, and covering multiple iterations that create distinct
metrics, asserting every returned metric ID is deleted exactly once. Reuse the
existing ChatClient mock setup and configure the evaluation inputs or mocked
responses to exercise both cleanup paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: da56ecf8-f8a5-4362-af36-476d65d00ae5

📥 Commits

Reviewing files that changed from the base of the PR and between 0b49776 and e50fad2.

📒 Files selected for processing (4)
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py
  • packages/gooddata-eval/tests/test_agentic_conversation.py
  • packages/gooddata-eval/tests/test_agentic_metric_skill.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py

try:
response = client._client.delete(url, headers=client._auth)
response.raise_for_status()
except Exception as exc:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Silence the Ruff BLE001 warning on the intentional best-effort catch.

The blind except Exception is deliberate (cleanup must never propagate), but Ruff still flags it. Add a # noqa: BLE001 with a short rationale to keep the lint clean.

🧹 Proposed fix
-    except Exception as exc:
+    except Exception as exc:  # noqa: BLE001 - best-effort cleanup, log and continue
         print(f"[CLEANUP] Failed to delete metric {metric_id}: {exc}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
except Exception as exc:
except Exception as exc: # noqa: BLE001 - best-effort cleanup, log and continue
🧰 Tools
🪛 Ruff (0.15.20)

[warning] 143-143: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py` at
line 143, Add an inline <code># noqa: BLE001</code> comment with a brief
rationale to the intentional broad exception handler in the cleanup function
containing <code>except Exception as exc</code>, documenting that cleanup is
best-effort and must not propagate errors.

Source: Linters/SAST tools

Comment on lines +178 to +182
candidate = _extract_metric_result(chat_result.tool_call_events or [])
if candidate is not None:
metric_result = candidate
metric_id_to_delete = candidate.get("metric_id")
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Only the first created metric is tracked for cleanup.

_extract_metric_result returns just the first create_metric result and the loop breaks immediately, so if a single response contains multiple successful create_metric calls, the extra metrics leak into the shared workspace — the exact failure this PR targets. The sibling conversation.run_agentic_conversation already collects every returned metric_id; consider mirroring that here for consistency.

🐛 Proposed fix
     metric_result: dict | None = None
-    metric_id_to_delete: str | None = None
+    metric_ids_to_delete: list[str] = []
     turns = 0
     current_question = question

     try:
         for _iteration in range(max_iterations):
             turns += 1
             chat_result = client.send_message(conversation_id, current_question)
-            candidate = _extract_metric_result(chat_result.tool_call_events or [])
+            tool_call_events = chat_result.tool_call_events or []
+            candidate = _extract_metric_result(tool_call_events)
+            for tc in tool_call_events:
+                if tc.function_name != "create_metric" or not tc.result:
+                    continue
+                result_data = tc.parsed_result()
+                data = result_data.get("data", result_data) if isinstance(result_data, dict) else None
+                metric_id = data.get("metric_id") if isinstance(data, dict) else None
+                if metric_id and metric_id not in metric_ids_to_delete:
+                    metric_ids_to_delete.append(metric_id)
             if candidate is not None:
                 metric_result = candidate
-                metric_id_to_delete = candidate.get("metric_id")
                 break
@@
     finally:
-        if metric_id_to_delete:
-            _delete_metric(client, workspace_id, metric_id_to_delete)
+        for metric_id in metric_ids_to_delete:
+            _delete_metric(client, workspace_id, metric_id)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py` around
lines 178 - 182, Update the metric cleanup logic around _extract_metric_result
and the surrounding chat-result loop to collect every successful create_metric
metric_id, rather than assigning only the first candidate and breaking. Mirror
conversation.run_agentic_conversation’s aggregation behavior, then ensure all
collected IDs are passed to cleanup so no created metrics leak.

Metric-skill and conversation evaluators create metrics in a shared,
persistent eval workspace but never removed them. A leftover metric is
detected and reused by a later test (the agent returns
`SELECT {existing_id}` instead of full MAQL), so the assertion fails —
and it makes some conversation metric-turns produce no create_metric
call at all.

Delete each metric a run creates on the way out, keyed on the exact
metric_id from the create_metric tool result, in a finally block:
- metric_skill: per-run cleanup in _execute_single_metric_run.
- conversation: collect metric_ids across turns, delete after the
  conversation completes (deferred so a later turn's $ref still resolves).

Mirrors the existing alert_skill._delete_alert pattern. Parallelism-safe:
each run deletes only the id it created, so concurrent tests sharing the
workspace never touch each other's objects. No version bump.

JIRA: QA-28379
risk: nonprod
@myhoai myhoai force-pushed the QA-28379-eval-metric-cleanup branch from e50fad2 to 67ec82b Compare July 10, 2026 12:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/gooddata-eval/tests/test_agentic_metric_skill.py (1)

170-201: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for multi-metric-per-turn cleanup.

This test only exercises a single create_metric result per turn, so it doesn't catch the leak described in the metric_skill.py review (only the first metric per turn is tracked for deletion). Once that's fixed, add a case with two create_metric tool call events in one response and assert both are deleted.

def test_run_agentic_metric_skill_deletes_all_created_metrics_in_one_turn():
    mock_client = MagicMock()
    mock_client._base = "http://host/api/v1/ai/workspaces/ws1/chat/conversations"
    mock_client.create_conversation.return_value = "conv-1"
    mock_client.send_message.return_value = ChatResult.model_validate(
        {
            "textResponse": "done",
            "toolCallEvents": [
                {
                    "functionName": "create_metric",
                    "functionArguments": "{}",
                    "result": '{"data": {"maql": "SELECT {metric/foo}", "metric_id": "foo_metric"}}',
                },
                {
                    "functionName": "create_metric",
                    "functionArguments": "{}",
                    "result": '{"data": {"maql": "SELECT {metric/bar}", "metric_id": "bar_metric"}}',
                },
            ],
            "reasoningStepCount": 1,
        }
    )
    with patch("gooddata_eval.core.agentic.metric_skill.ChatClient", return_value=mock_client):
        run_agentic_metric_skill(
            host="http://host/api/v1/actions/workspaces/ws1/ai",
            token="tok",
            workspace_id="ws1",
            question="Create metric foo",
            expected_output={"maql": "SELECT {metric/foo}"},
            k=1,
            max_iterations=1,
        )
    assert mock_client._client.delete.call_count == 2
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gooddata-eval/tests/test_agentic_metric_skill.py` around lines 170 -
201, Add a regression test named
test_run_agentic_metric_skill_deletes_all_created_metrics_in_one_turn in the
existing metric skill test module, configuring ChatResult with two create_metric
events containing distinct metric IDs. Run run_agentic_metric_skill with the
mocked ChatClient, then assert mock_client._client.delete is called twice and,
preferably, verify both metric entity URLs are deleted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py`:
- Around line 130-147: Update _execute_single_metric_run to track all created
metric IDs using _extract_created_metric_ids(tool_call_events), rather than only
candidate.get("metric_id"). Ensure cleanup deletes every returned ID, including
multiple successful create_metric results in one turn, and remove the single-ID
tracking path.

---

Nitpick comments:
In `@packages/gooddata-eval/tests/test_agentic_metric_skill.py`:
- Around line 170-201: Add a regression test named
test_run_agentic_metric_skill_deletes_all_created_metrics_in_one_turn in the
existing metric skill test module, configuring ChatResult with two create_metric
events containing distinct metric IDs. Run run_agentic_metric_skill with the
mocked ChatClient, then assert mock_client._client.delete is called twice and,
preferably, verify both metric entity URLs are deleted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1a0368e4-f245-4593-9867-1aa0ae711286

📥 Commits

Reviewing files that changed from the base of the PR and between e50fad2 and 67ec82b.

📒 Files selected for processing (4)
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py
  • packages/gooddata-eval/tests/test_agentic_conversation.py
  • packages/gooddata-eval/tests/test_agentic_metric_skill.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/gooddata-eval/src/gooddata_eval/core/agentic/conversation.py

Comment on lines +130 to +147
def _extract_created_metric_ids(tool_call_events: list[ToolCallEvent]) -> list[str]:
"""Ids of every metric created by ``create_metric`` calls (a turn may create more than one).

Used for cleanup so no created metric leaks — unlike ``_extract_metric_result``, which
returns only the first result for MAQL evaluation. Shared with conversation evaluation.
"""
metric_ids: list[str] = []
for tc in tool_call_events:
if tc.function_name != "create_metric" or not tc.result:
continue
result_data = tc.parsed_result()
if not result_data:
continue
data = result_data.get("data", result_data)
metric_id = data.get("metric_id") if isinstance(data, dict) else None
if metric_id and metric_id not in metric_ids:
metric_ids.append(metric_id)
return metric_ids

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Only the first metric created per turn is tracked for cleanup — leaks on multi-metric turns.

_execute_single_metric_run still tracks a single metric_id_to_delete from candidate.get("metric_id") instead of using the sibling _extract_created_metric_ids helper (defined right above specifically for this purpose, and already used correctly in conversation.py). If a turn's response contains more than one successful create_metric call, only the one backing metric_result gets deleted; the rest leak into the shared workspace, defeating the cleanup this PR adds and contradicting the function's own docstring claim that "any metric the agent creates during this run is deleted on the way out."

🐛 Proposed fix
     primary_expected = expected_outputs[0] if expected_outputs else {}
     metric_result: dict | None = None
-    metric_id_to_delete: str | None = None
+    metric_ids_to_delete: list[str] = []
     turns = 0
     current_question = question

     try:
         for _iteration in range(max_iterations):
             turns += 1
             chat_result = client.send_message(conversation_id, current_question)
-            candidate = _extract_metric_result(chat_result.tool_call_events or [])
+            tool_call_events = chat_result.tool_call_events or []
+            candidate = _extract_metric_result(tool_call_events)
+            for metric_id in _extract_created_metric_ids(tool_call_events):
+                if metric_id not in metric_ids_to_delete:
+                    metric_ids_to_delete.append(metric_id)
             if candidate is not None:
                 metric_result = candidate
-                metric_id_to_delete = candidate.get("metric_id")
                 break
             response_text = (chat_result.text_response or "").strip()
             if _is_asking_clarification(response_text):
                 current_question = generate_simulated_response(response_text, primary_expected)
             else:
                 break
@@
     finally:
-        if metric_id_to_delete:
-            _delete_metric(client, workspace_id, metric_id_to_delete)
+        for metric_id in metric_ids_to_delete:
+            _delete_metric(client, workspace_id, metric_id)

Also applies to: 189-222

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gooddata-eval/src/gooddata_eval/core/agentic/metric_skill.py` around
lines 130 - 147, Update _execute_single_metric_run to track all created metric
IDs using _extract_created_metric_ids(tool_call_events), rather than only
candidate.get("metric_id"). Ensure cleanup deletes every returned ID, including
multiple successful create_metric results in one turn, and remove the single-ID
tracking path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant