Skip to content

feat(experiments): add group default metric sort, applied client-side#578

Merged
shanaiabuggy merged 6 commits into
mainfrom
sbuggy/ase-325
Jul 6, 2026
Merged

feat(experiments): add group default metric sort, applied client-side#578
shanaiabuggy merged 6 commits into
mainfrom
sbuggy/ase-325

Conversation

@shanaiabuggy

@shanaiabuggy shanaiabuggy commented Jul 6, 2026

Copy link
Copy Markdown
Contributor
  • Add a default metric sort to experiment groups — a single sort field (e.g. -cost_usd.mean) stored on the group as a sort-param string.
  • Add UI to set it in the experiment group create and edit modals, via a new shared DefaultSortControl (pick a metric — Avg Cost, Avg Latency, Run count, or a discovered evaluator — plus an Asc/Desc toggle).
  • Add an Edit action + edit modal on the experiment group detail page.
  • The client now reads default_metric_sort from the group and applies it as the experiments list's sort param on load, so the sorted column's header reflects the active sort; users can still re-sort by clicking any column.
  • Make the intake list endpoint a plain sort executor — it applies only the sort it's given and defaults to -created_at (pinned first) when omitted, instead of computing a group's default server-side.
  • Change how default metric sort is stored from the old SortCriterion[] model to a single default_metric_sort string (and remove the now-unused SortCriterion model). This feature has no users today, so the change is safe to make with no migration.
  • Regenerate the OpenAPI spec and the platform SDKs (web + python/Stainless) for the new shape.
Screen.Recording.2026-07-06.at.2.33.18.PM.mov

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added experiment group editing for default sort (description + default ordering).
    • Added a metric/evaluator-based default sort selector to create and edit flows.
    • Experiment group tables now initialize from the group’s stored default_sort.
  • Bug Fixes
    • When experiments sort is omitted, ordering is now consistently pinned first, then newest (-created_at).
    • Validation now uses the single sort-parameter string format; legacy/unsupported values are coerced to -created_at.
  • Documentation
    • Updated OpenAPI specs to reflect default_sort as a string (and removed the old structured sort criterion schema).

@shanaiabuggy
shanaiabuggy requested review from a team as code owners July 6, 2026 18:53
@github-actions github-actions Bot added the feat label Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Experiment group sorting now uses a single sort string end to end. OpenAPI, backend validation, tests, and Studio UI switched from structured criteria to string values, and omitted experiment-list sorting now defaults to -created_at with pinned experiments first.

Changes

Experiment group sort string migration

Layer / File(s) Summary
Schema and entity contract
openapi/ga/individual/platform.openapi.yaml, openapi/ga/openapi.yaml, openapi/openapi.yaml, services/intake/src/nmp/intake/entities/experiments.py, services/intake/src/nmp/intake/api/v2/experiments/schemas.py, services/intake/src/nmp/intake/api/v2/experiments/endpoints.py, services/intake/tests/test_experiment_default_sort.py
default_sort becomes a string-based sort value, SortCriterion is removed, the list endpoint documents -created_at when sort is omitted, backend validation accepts string sort grammar, and tests cover validation plus legacy coercion.
DefaultSortControl and helpers
web/packages/studio/src/components/DefaultSortControl/index.tsx, web/packages/studio/src/components/DefaultSortControl/util.ts, web/packages/studio/src/components/DefaultSortControl/util.test.ts
Adds sort-string parsing/formatting helpers, evaluator-field helpers, tests, and the reusable sort editor UI.
Create modal wiring
web/packages/studio/src/components/ExperimentGroupCreateModal/index.tsx
Stores default sort locally, resets it on close, submits it with create, and renders the sort control.
Edit modal and route wiring
web/packages/studio/src/components/ExperimentGroupEditModal/index.tsx, web/packages/studio/src/routes/ExperimentGroupDetailRoute/index.tsx
Adds edit handling for group description and default sort, evaluator option derivation, cache invalidation, and route-level edit/error wiring.
ExperimentGroupDataView sort seeding
web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx, web/packages/studio/src/components/dataViews/ExperimentGroupDataView/useExperimentGroupExperiments.ts
The data view now consumes a loaded group, seeds table sort from the group default, and omits the API sort parameter when no column sort is active.

Possibly related PRs

Suggested reviewers: walston

🚥 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 matches the main change: experiment groups now have a default metric sort applied client-side.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sbuggy/ase-325

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 (2)
web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx (1)

45-63: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize seedSortFromDefault result.

seedSortFromDefault(group.default_metric_sort) returns a fresh object literal every render, even when default_metric_sort hasn't changed. useStudioDataViewState uses defaultSort inside a useMemo([sortParam, defaultSort]) (see web/packages/common/src/hooks/useStudioDataViewState/index.ts) and in defaultSortString; if downstream code treats sorting as anything other than a one-time initial value, an unstable reference here could cause unwanted re-syncs of the sort state on every render (defeating the "initialized once" comment at lines 83-85).

♻️ Stabilize with useMemo
-  const dataViewState = useStudioDataViewState<ExperimentFilter>({
-    // Seed the sort from default_metric_sort so its column header reflects the order on load.
-    defaultSort: seedSortFromDefault(group.default_metric_sort),
+  const seededSort = useMemo(
+    () => seedSortFromDefault(group.default_metric_sort),
+    [group.default_metric_sort]
+  );
+  const dataViewState = useStudioDataViewState<ExperimentFilter>({
+    // Seed the sort from default_metric_sort so its column header reflects the order on load.
+    defaultSort: seededSort,

Also applies to: 99-108

🤖 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
`@web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx`
around lines 45 - 63, `seedSortFromDefault` currently returns a new sort object
each render, which makes the `defaultSort` value passed from
`ExperimentGroupDataView` unstable. Memoize the computed result where
`group.default_metric_sort` is converted into `defaultSort` (or wrap the call in
`useMemo`) so the same object reference is reused until `default_metric_sort`
changes; keep the existing `seedSortFromDefault` logic and ensure the
`useStudioDataViewState` consumers see a stable `defaultSort`.
web/packages/studio/src/components/DefaultSortControl/util.ts (1)

17-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Missing explicit return types on exported helpers.

evaluatorField, isEvaluatorField, evaluatorNameOf are public exports but lack explicit return type annotations, unlike parseSortString/formatSortString below them.

Proposed fix
-export const evaluatorField = (name: string) => `evaluators.${name}.mean`;
-export const isEvaluatorField = (field: string) => EVALUATOR_FIELD.test(field);
-export const evaluatorNameOf = (field: string) => field.match(EVALUATOR_FIELD)?.[1] ?? '';
+export const evaluatorField = (name: string): string => `evaluators.${name}.mean`;
+export const isEvaluatorField = (field: string): boolean => EVALUATOR_FIELD.test(field);
+export const evaluatorNameOf = (field: string): string => field.match(EVALUATOR_FIELD)?.[1] ?? '';
As per coding guidelines, "Use explicit return types for public APIs and complex functions in TypeScript".
🤖 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 `@web/packages/studio/src/components/DefaultSortControl/util.ts` around lines
17 - 20, Add explicit return type annotations to the exported helper functions
in evaluator utilities: update evaluatorField, isEvaluatorField, and
evaluatorNameOf in util.ts to declare their return types, matching the style
used by parseSortString and formatSortString. Keep the existing behavior
unchanged; just make the public API signatures explicit so these helpers are
consistently typed.

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 `@web/packages/studio/src/components/DefaultSortControl/util.ts`:
- Around line 5-20: Preserve non-.mean sort fields in DefaultSortControl by
updating the field helpers in util.ts so they don’t assume every non-static
value is an evaluator mean. Adjust evaluatorField, isEvaluatorField, and
evaluatorNameOf to support arbitrary rollup suffixes or add a raw-field
fallback, and make sure selectValueFor and labelForOption can round-trip values
like cost_usd.p95 and evaluators.foo.p95 without collapsing them to evaluator:
or rendering them as Avg.

In `@web/packages/studio/src/components/ExperimentGroupCreateModal/index.tsx`:
- Around line 64-66: The comment above defaultSort is stale and still refers to
the old SortCriterion[] array-of-objects implementation. Update the inline
comment near the defaultSort state in ExperimentGroupCreateModal to accurately
describe the current single-string control and its handling outside
react-hook-form, keeping the note aligned with how handleSubmit merges the value
into the payload.

In `@web/packages/studio/src/routes/ExperimentGroupDetailRoute/index.tsx`:
- Around line 18-22: The experiment-group fetch result is only reading data from
useGetExperimentGroup, so failures are left unhandled and the route can
half-render without feedback. Update ExperimentGroupDetailRoute to also read the
hook’s error state and render an appropriate error/empty state when the fetch
fails, instead of continuing as if data is available. Apply the same error
handling pattern in ExperimentGroupMetrics, since it makes the same uncaught
request.

---

Nitpick comments:
In
`@web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx`:
- Around line 45-63: `seedSortFromDefault` currently returns a new sort object
each render, which makes the `defaultSort` value passed from
`ExperimentGroupDataView` unstable. Memoize the computed result where
`group.default_metric_sort` is converted into `defaultSort` (or wrap the call in
`useMemo`) so the same object reference is reused until `default_metric_sort`
changes; keep the existing `seedSortFromDefault` logic and ensure the
`useStudioDataViewState` consumers see a stable `defaultSort`.

In `@web/packages/studio/src/components/DefaultSortControl/util.ts`:
- Around line 17-20: Add explicit return type annotations to the exported helper
functions in evaluator utilities: update evaluatorField, isEvaluatorField, and
evaluatorNameOf in util.ts to declare their return types, matching the style
used by parseSortString and formatSortString. Keep the existing behavior
unchanged; just make the public API signatures explicit so these helpers are
consistently typed.
🪄 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: b667b79e-8e55-43c9-be29-97602f1173e2

📥 Commits

Reviewing files that changed from the base of the PR and between fb50b86 and eb2fe4c.

⛔ Files ignored due to path filters (14)
  • sdk/python/nemo-platform/.nmpcontext/openapi.yaml is excluded by !sdk/**
  • sdk/python/nemo-platform/.nmpcontext/stainless.yaml is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/experiment_groups/api.md is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/resources/experiment_groups/experiment_groups.py 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/experiment_groups/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/experiment_groups/experiment_group_create_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/experiment_groups/experiment_group_response.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/experiment_groups/experiment_group_update_params.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/experiment_groups/sort_criterion.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/types/experiment_groups/sort_criterion_param.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_experiment_groups.py is excluded by !sdk/**
  • sdk/stainless.yaml is excluded by !sdk/**
📒 Files selected for processing (15)
  • 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/src/nmp/intake/api/v2/experiments/schemas.py
  • services/intake/src/nmp/intake/entities/experiments.py
  • services/intake/tests/test_experiment_default_sort.py
  • web/packages/studio/src/components/DefaultSortControl/index.tsx
  • web/packages/studio/src/components/DefaultSortControl/util.test.ts
  • web/packages/studio/src/components/DefaultSortControl/util.ts
  • web/packages/studio/src/components/ExperimentGroupCreateModal/index.tsx
  • web/packages/studio/src/components/ExperimentGroupEditModal/index.tsx
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx
  • web/packages/studio/src/components/dataViews/ExperimentGroupDataView/useExperimentGroupExperiments.ts
  • web/packages/studio/src/routes/ExperimentGroupDetailRoute/index.tsx

Comment thread web/packages/studio/src/components/DefaultSortControl/util.ts Outdated
Comment thread web/packages/studio/src/components/ExperimentGroupCreateModal/index.tsx Outdated
Comment thread web/packages/studio/src/routes/ExperimentGroupDetailRoute/index.tsx
Signed-off-by: shanaiabuggy <[email protected]>

# Conflicts:
#	web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 23269/30434 76.5% 61.2%
Integration Tests 13613/29114 46.8% 20.0%

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

@nakolean nakolean 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.

FE code LGTM

Comment thread services/intake/src/nmp/intake/api/v2/experiments/endpoints.py Outdated
Comment thread services/intake/src/nmp/intake/entities/experiments.py
Signed-off-by: shanaiabuggy <[email protected]>

# Conflicts:
#	web/packages/studio/src/components/dataViews/ExperimentGroupDataView/index.tsx
@shanaiabuggy
shanaiabuggy added this pull request to the merge queue Jul 6, 2026
Merged via the queue into main with commit feee4ba Jul 6, 2026
53 checks passed
@shanaiabuggy
shanaiabuggy deleted the sbuggy/ase-325 branch July 6, 2026 23:14
arpitsardhana pushed a commit that referenced this pull request Jul 9, 2026
…#578)

* feat(experiments): add group default metric sort, applied client-side

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

* lint

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

* lint

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

* default_metric_sort -> default_sort

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

---------

Signed-off-by: shanaiabuggy <[email protected]>
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.

3 participants