Skip to content

feat(experiments): sort the experiments list by rollup metrics#448

Merged
shanaiabuggy merged 5 commits into
mainfrom
sbuggy/ase-319-v2
Jun 24, 2026
Merged

feat(experiments): sort the experiments list by rollup metrics#448
shanaiabuggy merged 5 commits into
mainfrom
sbuggy/ase-319-v2

Conversation

@shanaiabuggy

@shanaiabuggy shanaiabuggy commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Sort the experiments list by rollup metrics (avg evaluator score, cost, latency, run count), not just entity columns.

Since those metrics live in ClickHouse and entity rows live in Postgres, the list endpoint now does a compute-on-read merge: fetch the group's experiments, hydrate their rollups, then sort/paginate in the app. sort accepts run_count, cost_usd.<stat>, latency_ms.<stat>, evaluators.<name>.<stat> (stat: mean/median/p90/p95/p99/sum/count), plus the existing entity columns; - prefixes descending, missing metrics sort last.

Bounded to ~1000 experiments per group (logged if exceeded) — the point at which we'd switch to denormalizing metrics for entity-store sorting instead.

Summary by CodeRabbit

  • New Features
    • Experiments list sorting now supports additional sort targets via a free-form sort expression, including run_count, cost_usd.<stat>, latency_ms.<stat>, and evaluators.<name>.<stat> (default: -created_at).
  • Bug Fixes
    • Metric-based sorts now return clearer errors: 400 for unsupported sort expressions, 503 when telemetry rollups are unavailable, and 413 when too many experiments are selected.
    • Experiments with missing metric values reliably appear last with deterministic tie-breaking.
  • Tests
    • Added unit and integration coverage for metric sorting, missing-data ordering, and the new error conditions.

@shanaiabuggy
shanaiabuggy requested review from a team as code owners June 24, 2026 21:48
@github-actions github-actions Bot added the feat label Jun 24, 2026
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The experiments list endpoint now accepts free-form sort strings, validates them at runtime, hydrates rollups, and sorts/paginates the hydrated list in memory to support entity fields and aggregate metrics. The OpenAPI contracts and tests were updated for the new sort expressions.

Changes

Experiment sort expansion

Layer / File(s) Summary
Sort contract
openapi/ga/individual/platform.openapi.yaml, openapi/ga/openapi.yaml, openapi/openapi.yaml, services/intake/src/nmp/intake/api/v2/experiments/endpoints.py
The experiments sort parameter becomes a free string in the OpenAPI contracts, the -created_at default stays in place, and the endpoint module defines the allowed entity-field, metric-stat, and cap constants.
Sort helpers and unit tests
services/intake/src/nmp/intake/api/v2/experiments/endpoints.py, services/intake/tests/test_experiment_sort.py
_validate_sort_field, _experiment_sort_value, and _sort_experiments add runtime field checks and deterministic in-memory ordering; the unit tests cover entity fields, evaluator paths, missing metrics, tie-breaking, and invalid inputs.
Integration metric-sort coverage
services/intake/tests/integration/spans/test_experiment_metric_sort.py, services/intake/tests/test_experiment_sort_endpoint.py, services/intake/src/nmp/intake/api/v2/experiments/endpoints.py
The endpoint now rejects oversize requests with 413, returns 503 when metric sorts run without rollups, and the integration tests verify -cost_usd.mean ordering, missing metrics last, 400 responses for unknown sort fields, and 413 cap enforcement.

Possibly related PRs

Suggested reviewers

  • BrianNewsom
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding rollup-metric sorting to the experiments list.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sbuggy/ase-319-v2

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
services/intake/tests/integration/spans/test_experiment_metric_sort.py (1)

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

Drop postponed annotations here.

This file already imports every annotated type directly, so from __future__ import annotations just turns them into deferred/string annotations for no gain.

As per coding guidelines, **/*.py: Always prefer concrete type hints over string-based ones in Python code; do not import types under TYPE_CHECKING, instead import types as regular imports when possible.

🤖 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 `@services/intake/tests/integration/spans/test_experiment_metric_sort.py` at
line 6, Remove the unnecessary postponed-annotations import from
test_experiment_metric_sort since all annotated types are already imported
directly, so the module should use normal concrete type hints instead of
deferred string annotations. Update the top of the file by dropping the from
__future__ import annotations line and keep the existing direct type imports in
place; no TYPE_CHECKING-based workaround is needed.

Source: Coding guidelines

🤖 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 `@openapi/openapi.yaml`:
- Around line 3716-3726: Add the missing HTTP 400 response to this operation’s
OpenAPI definition because sort validation now happens at runtime instead of
schema validation. Update the response block alongside the existing 200/422
entries in the sort-related operation, and make the same change in the mirrored
GA OpenAPI specs so generated clients match the new contract.

In `@services/intake/src/nmp/intake/api/v2/experiments/endpoints.py`:
- Around line 389-390: The experiments sort flow is silently falling back to
name ordering when rollup hydration cannot run, so metric-based sorts can return
a misleading 200 instead of being rejected. In the endpoint that calls
_hydrate_rollups and then _sort_experiments, detect when the requested sort
field depends on rollups and hydration was skipped or unavailable (for example
when rollup_repository is None or ClickHouse is down), and fail the request
rather than continuing to sort. Use the existing _hydrate_rollups,
_sort_experiments, and sort_field handling in this endpoint to gate
rollup-backed sorts before ordering the responses.

In `@services/intake/tests/integration/spans/test_experiment_metric_sort.py`:
- Around line 18-23: Scope the integration test to isolated data so it does not
depend on shared workspace state. Update _ensure_group(), _create_experiment(),
and the list/assertion path in test_experiment_metric_sort.py to generate unique
names per run and to query experiments with experiment_group_id filtering
instead of relying on the global list. Keep the helper behavior tied to the
current test’s group so reruns cannot collide with existing groups or
experiments. Ensure the final name comparison only inspects experiments created
by this test.

---

Nitpick comments:
In `@services/intake/tests/integration/spans/test_experiment_metric_sort.py`:
- Line 6: Remove the unnecessary postponed-annotations import from
test_experiment_metric_sort since all annotated types are already imported
directly, so the module should use normal concrete type hints instead of
deferred string annotations. Update the top of the file by dropping the from
__future__ import annotations line and keep the existing direct type imports in
place; no TYPE_CHECKING-based workaround is needed.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 02c4079b-3569-4aa6-8284-b293a9f2afce

📥 Commits

Reviewing files that changed from the base of the PR and between c6fe97b and 719efc9.

⛔ Files ignored due to path filters (5)
  • sdk/python/nemo-platform/.nmpcontext/openapi.yaml is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/experiments/experiments.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/experiments/experiment_list_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/api_resources/test_experiments.py is excluded by !sdk/**
  • web/packages/sdk/generated/agents/schema/DeploymentLogsResponse.ts is excluded by !**/generated/**
📒 Files selected for processing (6)
  • openapi/ga/individual/platform.openapi.yaml
  • openapi/ga/openapi.yaml
  • openapi/openapi.yaml
  • services/intake/src/nmp/intake/api/v2/experiments/endpoints.py
  • services/intake/tests/integration/spans/test_experiment_metric_sort.py
  • services/intake/tests/test_experiment_sort.py

Comment thread openapi/openapi.yaml
Comment thread services/intake/src/nmp/intake/api/v2/experiments/endpoints.py Outdated
Comment thread services/intake/tests/integration/spans/test_experiment_metric_sort.py Outdated
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 20910/27478 76.1% 61.2%
Integration Tests 12107/26247 46.1% 19.5%

Signed-off-by: shanaiabuggy <[email protected]>
Signed-off-by: shanaiabuggy <[email protected]>
Signed-off-by: shanaiabuggy <[email protected]>

@callingmedic911 callingmedic911 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should fail loudly than work with subset of data. Looks good otherwise!

Comment thread services/intake/src/nmp/intake/api/v2/experiments/endpoints.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
services/intake/tests/test_experiment_sort_endpoint.py (1)

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

Add a concrete type for monkeypatch.

Annotate monkeypatch as pytest.MonkeyPatch to satisfy the Python typing guideline.

Suggested patch
+from pytest import MonkeyPatch
 ...
-def test_too_many_experiments_to_sort_returns_413(client: TestClient, monkeypatch) -> None:
+def test_too_many_experiments_to_sort_returns_413(client: TestClient, monkeypatch: MonkeyPatch) -> None:
🤖 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 `@services/intake/tests/test_experiment_sort_endpoint.py` at line 51, The test
function test_too_many_experiments_to_sort_returns_413 is using an untyped
monkeypatch fixture; update the function signature to annotate monkeypatch with
pytest.MonkeyPatch so it follows the typing guideline. Keep the change localized
to this test function and use the existing pytest import or add it if needed to
reference the MonkeyPatch type.

Source: Coding guidelines

🤖 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 `@services/intake/tests/test_experiment_sort_endpoint.py`:
- Around line 58-60: The test reads group_resp.json()["id"] immediately after
client.post(GROUPS, ...) without verifying the group creation succeeded. Update
the test around group_resp so it asserts the POST response is successful before
accessing id, using the existing client.post and group_resp variable in
test_experiment_sort_endpoint, so failures surface as a clear API assertion
instead of a KeyError/JSON error.

---

Nitpick comments:
In `@services/intake/tests/test_experiment_sort_endpoint.py`:
- Line 51: The test function test_too_many_experiments_to_sort_returns_413 is
using an untyped monkeypatch fixture; update the function signature to annotate
monkeypatch with pytest.MonkeyPatch so it follows the typing guideline. Keep the
change localized to this test function and use the existing pytest import or add
it if needed to reference the MonkeyPatch type.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f1397339-27d8-4e58-b5f3-d8935a763329

📥 Commits

Reviewing files that changed from the base of the PR and between 6d5d926 and 6398bc5.

⛔ Files ignored due to path filters (1)
  • sdk/python/nemo-platform/.nmpcontext/openapi.yaml is excluded by !sdk/**
📒 Files selected for processing (6)
  • openapi/ga/individual/platform.openapi.yaml
  • openapi/ga/openapi.yaml
  • openapi/openapi.yaml
  • services/intake/src/nmp/intake/api/v2/experiments/endpoints.py
  • services/intake/tests/integration/spans/test_experiment_metric_sort.py
  • services/intake/tests/test_experiment_sort_endpoint.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • openapi/ga/individual/platform.openapi.yaml
  • openapi/ga/openapi.yaml
  • openapi/openapi.yaml
  • services/intake/tests/integration/spans/test_experiment_metric_sort.py
  • services/intake/src/nmp/intake/api/v2/experiments/endpoints.py

Comment thread services/intake/tests/test_experiment_sort_endpoint.py
@shanaiabuggy
shanaiabuggy enabled auto-merge June 24, 2026 23:19
@shanaiabuggy
shanaiabuggy added this pull request to the merge queue Jun 24, 2026
Merged via the queue into main with commit b4473e8 Jun 24, 2026
53 checks passed
@shanaiabuggy
shanaiabuggy deleted the sbuggy/ase-319-v2 branch June 24, 2026 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants